#!/usr/bin/env php
<?php
/**
 * Generated by Box.
 *
 * @link https://github.com/herrera-io/php-box/
 */
if (class_exists('Phar')) {
Phar::mapPhar('default.phar');
require 'phar://' . __FILE__ . '/cli';
}
__HALT_COMPILER(); ?>
;¦  ç                 vendor/autoload.php²   ž­¨Z²   Û¸¶      .   vendor/php-amqplib/php-amqplib/spec/parser.php2  ž­¨Z2  ƒjl§¶      :   vendor/php-amqplib/php-amqplib/spec/amqp-rabbitmq-0.8.json�—  ž­¨Z�—  _xH¶      <   vendor/php-amqplib/php-amqplib/spec/amqp-rabbitmq-0.9.1.json k  ž­¨Z k  ü_y¶      >   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.php�  ž­¨Z�  éˆªË¶      A   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AbstractClient.php±  ž­¨Z±  ªæô¶      <   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPTable.php¦  ž­¨Z¦  «ÿ/¶      =   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPReader.php€8  ž­¨Z€8  `¸ªe¶      ?   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants080.php  ž­¨Z  Ü?¾ç¶      >   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/StreamIO.phpI2  ž­¨ZI2  Ñ†¶      @   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/AbstractIO.php'  ž­¨Z'  ©ï¶      >   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/SocketIO.php¼  ž­¨Z¼  „(cM¶      ?   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants091.phpæ  ž­¨Zæ  ’+Mž¶      I   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPAbstractCollection.phpk1  ž­¨Zk1  ÏŒŠ¶      =   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPWriter.phpH8  ž­¨ZH8  _³+¶      A   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/GenericContent.php½  ž­¨Z½  hcñy¶      <   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPArray.phpN  ž­¨ZN  æþ3¶      M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSocketConnection.phpÒ  ž­¨ZÒ  );QÂ¶      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSSLConnection.phpµ  ž­¨Zµ  óJÇ¶      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.phpQ  ž­¨ZQ  eÎè ¶      Q   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySocketConnection.phpÙ  ž­¨ZÙ  Á5u¶      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AbstractConnection.phpm  ž­¨Zm  ŽkUó¶      G   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnection.php¢   ž­¨Z¢   R…Z²¶      M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPStreamConnection.php@  ž­¨Z@  {û›€¶      A   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.php€	  ž­¨Z€	  #f{¶      A   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.phpáš  ž­¨Záš  ƒÓºE¶      E   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php}<  ž­¨Z}<  tÿú–¶      ?   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/MiscHelper.phpÂ  ž­¨ZÂ  j—</¶      I   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol091.php•Z  ž­¨Z•Z  šG$:¶      I   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol080.php«ƒ  ž­¨Z«ƒ  0x£m¶      E   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait091.php»
  ž­¨Z»
  ’;oÉ¶      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap091.php½  ž­¨Z½  Ó[I¶      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap080.phpŒ  ž­¨ZŒ  ó3è\¶      E   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait080.php®  ž­¨Z®  ˆ%k
¶      @   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/DebugHelper.phpÍ  ž­¨ZÍ  öƒS@¶      N   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPExceptionInterface.phpL   ž­¨ZL   ;f[Ü¶      P   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.phpG  ž­¨ZG  ßÞ4w¶      E   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPException.phpØ  ž­¨ZØ  ŠDŠ¶      M   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolException.phpÊ  ž­¨ZÊ  ‰ûí®¶      J   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.php   ž­¨Z   $dL¶      L   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.php‚   ž­¨Z‚   ™	¶      T   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolChannelException.phpl   ž­¨Zl   ØàÏ¯¶      W   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolConnectionException.phpo   ž­¨Zo   žu#]¶      T   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidArgumentException.phpŠ   ž­¨ZŠ   kfm¶      K   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOWaitException.phpb   ž­¨Zb   Öü®¶      L   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPRuntimeException.php‚   ž­¨Z‚   �š¬)¶      P   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfBoundsException.phpŠ   ž­¨ZŠ   Í¿�,¶      O   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfRangeException.php‰   ž­¨Z‰   …™Y¶      L   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelException.phpœ   ž­¨Zœ   ;Ü7×¶      O   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionException.php¢   ž­¨Z¢   ¹!o
¶      G   vendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOException.phpv   ž­¨Zv   —†^¶      ,   vendor/php-amqplib/php-amqplib/composer.json%  ž­¨Z%  V?B¶      '   vendor/composer/autoload_namespaces.php•   ž­¨Z•   tÛ!×¶         vendor/composer/ClassLoader.php‹4  ž­¨Z‹4  QŸ”¼¶      !   vendor/composer/autoload_psr4.php—  ž­¨Z—  ÝÌiœ¶      %   vendor/composer/autoload_classmap.phpÓ  ž­¨ZÓ  5³J¶      #   vendor/composer/autoload_static.phphñ  ž­¨Zhñ  ˜!7ì¶      !   vendor/composer/autoload_real.phpn	  ž­¨Zn	  Q§±V¶         vendor/composer/installed.jsong  ž­¨Zg  mtv€¶      "   vendor/composer/autoload_files.phpó   ž­¨Zó   <õÍ¶      %   vendor/firebase/php-jwt/composer.jsonä  ž­¨Zä  8³ëY¶      9   vendor/firebase/php-jwt/src/SignatureInvalidException.phpf   ž­¨Zf   ¤ŠY¶      4   vendor/firebase/php-jwt/src/BeforeValidException.phpa   ž­¨Za   (S6ÿ¶      0   vendor/firebase/php-jwt/src/ExpiredException.php]   ž­¨Z]   z¶·¶      #   vendor/firebase/php-jwt/src/JWT.php8  ž­¨Z8  ¨ôö¶      $   vendor/robmorgan/phinx/app/phinx.phpñ  ž­¨Zñ  =µ¥å¶      "   vendor/robmorgan/phinx/app/web.php  ž­¨Z  ·_ŒÂ¶      $   vendor/robmorgan/phinx/composer.jsonë  ž­¨Zë  yÌ—G¶      7   vendor/robmorgan/phinx/src/Phinx/Seed/SeedInterface.phpä  ž­¨Zä  4‹~¶      6   vendor/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php  ž­¨Z  ;÷>¿¶      .   vendor/robmorgan/phinx/src/Phinx/Util/Util.phpÌ  ž­¨ZÌ  ÇÀZE¶      8   vendor/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php"  ž­¨Z"  �6˜o¶      C   vendor/robmorgan/phinx/src/Phinx/Config/NamespaceAwareInterface.phpÌ  ž­¨ZÌ  1™ö¶      ;   vendor/robmorgan/phinx/src/Phinx/Config/ConfigInterface.phpô  ž­¨Zô  ë­� ¶      ?   vendor/robmorgan/phinx/src/Phinx/Config/NamespaceAwareTrait.phpˆ
  ž­¨Zˆ
  �ÿdœ¶      2   vendor/robmorgan/phinx/src/Phinx/Config/Config.phpb.  ž­¨Zb.  †€¶¢¶      <   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.phpè!  ž­¨Zè!  åÆ3Ò¶      :   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.phpà&  ž­¨Zà&  ÁžWn¶      B   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.phpf"  ž­¨Zf"  ±¹€¼¶      <   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php¸Š  ž­¨Z¸Š  a¼Þ]¶      >   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.phpg  ž­¨Zg  ÕIaí¶      B   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php   ž­¨Z   �¢€?¶      @   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.phpH8  ž­¨ZH8  Ýk%É¶      ?   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.phpõ�  ž­¨Zõ�  u5}¶      >   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php‡,  ž­¨Z‡,  ¸WJÍ¶      @   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php‚‹  ž­¨Z‚‹  ~µ›ó¶      =   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php»  ž­¨Z»  a¶º…¶      ?   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.phpX  ž­¨ZX  |x0¶      @   vendor/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php×  ž­¨Z×  8œ†ª¶      3   vendor/robmorgan/phinx/src/Phinx/Db/Table/Index.php  ž­¨Z  ¬Tž]¶      8   vendor/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php"  ž­¨Z"  ‚xYÇ¶      4   vendor/robmorgan/phinx/src/Phinx/Db/Table/Column.php€1  ž­¨Z€1  •ËCº¶      -   vendor/robmorgan/phinx/src/Phinx/Db/Table.php%G  ž­¨Z%G  ©l¶      G   vendor/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.phpD
  ž­¨ZD
  �¬qÿ¶      @   vendor/robmorgan/phinx/src/Phinx/Migration/CreationInterface.phpƒ  ž­¨Zƒ  8r{Ï¶      M   vendor/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php¤  ž­¨Z¤  rs®¶      B   vendor/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php"+  ž­¨Z"+  ã›… ¶      6   vendor/robmorgan/phinx/src/Phinx/Migration/Manager.php³„  ž­¨Z³„  ¯Í-H¶      A   vendor/robmorgan/phinx/src/Phinx/Migration/MigrationInterface.phpY  ž­¨ZY  öpÎ¶      @   vendor/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.phpV  ž­¨ZV  pì1¶      =   vendor/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php  ž­¨Z  }@6¶      9   vendor/robmorgan/phinx/src/Phinx/Console/Command/Init.phpi  ž­¨Zi  So4Ú¶      <   vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.phpã  ž­¨Zã  ·­þD¶      <   vendor/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php“  ž­¨Z“  ¢ÔT¯¶      ;   vendor/robmorgan/phinx/src/Phinx/Console/Command/Create.php…1  ž­¨Z…1  0/z¶      =   vendor/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php!  ž­¨Z!  —(Çñ¶      9   vendor/robmorgan/phinx/src/Phinx/Console/Command/Test.phpC  ž­¨ZC  d}»�¶      ;   vendor/robmorgan/phinx/src/Phinx/Console/Command/Status.php?  ž­¨Z?  ä"c¶      ?   vendor/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php¾  ž­¨Z¾  Päp¶      D   vendor/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.phpò(  ž­¨Zò(  KIã¶      ?   vendor/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.phpU  ž­¨ZU  9£õ¶      2   vendor/robmorgan/phinx/src/composer_autoloader.phpM  ž­¨ZM  e\ˆ¶      /   vendor/psr/log/Psr/Log/LoggerAwareInterface.php)  ž­¨Z)  Èj±¶      #   vendor/psr/log/Psr/Log/LogLevel.phpP  ž­¨ZP  �òº¶      3   vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.phpõ  ž­¨Zõ  êÐ$s¶      +   vendor/psr/log/Psr/Log/LoggerAwareTrait.php�  ž­¨Z�  z%Ô¶      3   vendor/psr/log/Psr/Log/InvalidArgumentException.php`   ž­¨Z`    ˆX1¶      %   vendor/psr/log/Psr/Log/NullLogger.php‹  ž­¨Z‹  šöZf¶      *   vendor/psr/log/Psr/Log/LoggerInterface.phpß  ž­¨Zß  ?}¶      &   vendor/psr/log/Psr/Log/LoggerTrait.php  ž­¨Z  ›Ã½õ¶      )   vendor/psr/log/Psr/Log/AbstractLogger.php  ž­¨Z  šGl¶         vendor/psr/log/composer.json1  ž­¨Z1  Â—²¶      #   vendor/voku/php-hooks/composer.json`  ž­¨Z`  âö¸�¶      /   vendor/voku/php-hooks/src/voku/helper/Hooks.php  ž­¨Z  =\JG¶          vendor/symfony/dotenv/Dotenv.php!6  ž­¨Z!6  
<TO¶      3   vendor/symfony/dotenv/Exception/FormatException.php†  ž­¨Z†  å®í¶      1   vendor/symfony/dotenv/Exception/PathException.php¯  ž­¨Z¯  ÂÑy$¶      6   vendor/symfony/dotenv/Exception/ExceptionInterface.php—  ž­¨Z—  Ðà=¶      :   vendor/symfony/dotenv/Exception/FormatExceptionContext.php   ž­¨Z   oÃ^Ù¶      #   vendor/symfony/dotenv/composer.jsont  ž­¨Zt  !F�¿¶      4   vendor/symfony/config/ResourceCheckerConfigCache.php€  ž­¨Z€  ¢äOˆ¶      '   vendor/symfony/config/Util/XmlUtils.php»$  ž­¨Z»$  .öK8¶      <   vendor/symfony/config/Util/Exception/XmlParsingException.phpÒ  ž­¨ZÒ  ñï_‘¶      <   vendor/symfony/config/Util/Exception/InvalidXmlException.php%  ž­¨Z%   A¬i¶      .   vendor/symfony/config/ConfigCacheInterface.php0  ž­¨Z0  úed¶      .   vendor/symfony/config/FileLocatorInterface.php  ž­¨Z  âXE¶      %   vendor/symfony/config/ConfigCache.php5  ž­¨Z5  F À¶      5   vendor/symfony/config/ConfigCacheFactoryInterface.phpÎ  ž­¨ZÎ  aÿ³¶      %   vendor/symfony/config/FileLocator.php
  ž­¨Z
  E"¿¶      2   vendor/symfony/config/ResourceCheckerInterface.php  ž­¨Z  œR¶      '   vendor/symfony/config/Loader/Loader.phpJ  ž­¨ZJ  CoP¶      +   vendor/symfony/config/Loader/FileLoader.php˜  ž­¨Z˜  �ê\¿¶      0   vendor/symfony/config/Loader/LoaderInterface.phpN  ž­¨ZN  7‹
�¶      8   vendor/symfony/config/Loader/LoaderResolverInterface.php  ž­¨Z  !ŠKÄ¶      /   vendor/symfony/config/Loader/LoaderResolver.php9  ž­¨Z9  x² ¶      1   vendor/symfony/config/Loader/DelegatingLoader.phpý  ž­¨Zý  VH9¶      /   vendor/symfony/config/Loader/GlobFileLoader.phpÊ  ž­¨ZÊ  ïÑQ¶      .   vendor/symfony/config/Definition/FloatNode.phpP  ž­¨ZP  ÇK”À¶      -   vendor/symfony/config/Definition/BaseNode.phps%  ž­¨Zs%  õæ…¯¶      0   vendor/symfony/config/Definition/NumericNode.phpÍ  ž­¨ZÍ  ‰©<ç¶      0   vendor/symfony/config/Definition/IntegerNode.phpÊ  ž­¨ZÊ   œ@�¶      ;   vendor/symfony/config/Definition/PrototypeNodeInterface.phpw  ž­¨Zw  ÿ€e¶      8   vendor/symfony/config/Definition/PrototypedArrayNode.php21  ž­¨Z21  W$§¼¶      .   vendor/symfony/config/Definition/Processor.php'  ž­¨Z'  Uú§¶      2   vendor/symfony/config/Definition/NodeInterface.phpŠ  ž­¨ZŠ  ²÷ˆ¶      -   vendor/symfony/config/Definition/EnumNode.phpý  ž­¨Zý  ]™13¶      0   vendor/symfony/config/Definition/BooleanNode.php©  ž­¨Z©  ÿ_Ê¶      .   vendor/symfony/config/Definition/ArrayNode.php¼,  ž­¨Z¼,  ÷bY¶      /   vendor/symfony/config/Definition/ScalarNode.php)  ž­¨Z)  iPi4¶      ;   vendor/symfony/config/Definition/ConfigurationInterface.phpe  ž­¨Ze  \Û –¶      8   vendor/symfony/config/Definition/Builder/ExprBuilder.php  ž­¨Z  H÷Ù¶      ?   vendor/symfony/config/Definition/Builder/EnumNodeDefinition.phpÎ  ž­¨ZÎ  ¤UŠ¶      9   vendor/symfony/config/Definition/Builder/MergeBuilder.php4  ž­¨Z4  Ž¥Ä¶      B   vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php  ž­¨Z  ó^Ó¶      8   vendor/symfony/config/Definition/Builder/NodeBuilder.php¶  ž­¨Z¶  Þ–ê•¶      C   vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php¹  ž­¨Z¹  „7-v¶      @   vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.phpZ>  ž­¨ZZ>  †ë�®¶      >   vendor/symfony/config/Definition/Builder/ValidationBuilder.phpÊ  ž­¨ZÊ  «’q¶      8   vendor/symfony/config/Definition/Builder/TreeBuilder.phpÑ  ž­¨ZÑ  Ÿ8ñ1¶      B   vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php  ž­¨Z  'nÚ­¶      @   vendor/symfony/config/Definition/Builder/NodeParentInterface.php¿  ž­¨Z¿  YîÇÓ¶      A   vendor/symfony/config/Definition/Builder/NormalizationBuilder.php‘  ž­¨Z‘  —ƒ¶      B   vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.phpõ  ž­¨Zõ  ûÞLk¶      J   vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php`  ž­¨Z`  8*ÿ=¶      A   vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.phpÞ  ž­¨ZÞ  ÏYÜ÷¶      @   vendor/symfony/config/Definition/Builder/FloatNodeDefinition.phpù  ž­¨Zù  t£p¶      ;   vendor/symfony/config/Definition/Builder/NodeDefinition.php#  ž­¨Z#  u»4Û¶      D   vendor/symfony/config/Definition/Exception/DuplicateKeyException.phpE  ž­¨ZE  ø|÷¶      I   vendor/symfony/config/Definition/Exception/InvalidDefinitionException.phpÚ  ž­¨ZÚ  ]F4ç¶      C   vendor/symfony/config/Definition/Exception/InvalidTypeException.phpí  ž­¨Zí  ¾Ä@¶      @   vendor/symfony/config/Definition/Exception/UnsetKeyException.php  ž­¨Z  þ¤½¿¶      L   vendor/symfony/config/Definition/Exception/InvalidConfigurationException.phpR  ž­¨ZR  h'Nñ¶      J   vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.phpQ  ž­¨ZQ  ¨:2&¶      8   vendor/symfony/config/Definition/Exception/Exception.phpË  ž­¨ZË  ¹po:¶      1   vendor/symfony/config/Definition/VariableNode.phpž  ž­¨Zž  .[Íd¶      ?   vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php#  ž­¨Z#  míŸ¶      >   vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php (  ž­¨Z (  ëuµ~¶      @   vendor/symfony/config/Resource/SelfCheckingResourceInterface.phpB  ž­¨ZB  Ñé£¶      /   vendor/symfony/config/Resource/FileResource.php™  ž­¨Z™  ‘ÃN¶      :   vendor/symfony/config/Resource/ReflectionClassResource.phpQ  ž­¨ZQ  yÝæ§¶      >   vendor/symfony/config/Resource/SelfCheckingResourceChecker.phpâ  ž­¨Zâ  ÿ¬f¶      3   vendor/symfony/config/Resource/ComposerResource.phpñ  ž­¨Zñ  #åò>¶      8   vendor/symfony/config/Resource/FileExistenceResource.phpk  ž­¨Zk  ‡”¶      4   vendor/symfony/config/Resource/ResourceInterface.php  ž­¨Z  ý?Š‰¶      9   vendor/symfony/config/Resource/ClassExistenceResource.php:  ž­¨Z:  ‰lÃF¶      4   vendor/symfony/config/Resource/DirectoryResource.php0  ž­¨Z0  ü7À¡¶      /   vendor/symfony/config/Resource/GlobResource.php_  ž­¨Z_  ¦$ðf¶      =   vendor/symfony/config/DependencyInjection/ConfigCachePass.phpC  ž­¨ZC  ŸÔ‰I¶      ;   vendor/symfony/config/Exception/FileLoaderLoadException.phpš  ž­¨Zš  �T_¶      N   vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.phpC  ž­¨ZC  W×¯ø¶      D   vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php  ž­¨Z  ÊV†‰¶      #   vendor/symfony/config/composer.jsonƒ  ž­¨Zƒ  ”	¸Ù¶      ,   vendor/symfony/config/ConfigCacheFactory.php  ž­¨Z  )@7;¶      ;   vendor/symfony/config/ResourceCheckerConfigCacheFactory.phpô  ž­¨Zô  ç®úí¶      (   vendor/symfony/filesystem/Filesystem.phpo  ž­¨Zo  š9P ¶      )   vendor/symfony/filesystem/LockHandler.phpå  ž­¨Zå  Å¨ý7¶      <   vendor/symfony/filesystem/Exception/IOExceptionInterface.php¡  ž­¨Z¡  k/’ï¶      :   vendor/symfony/filesystem/Exception/ExceptionInterface.php½  ž­¨Z½  hª ¶      3   vendor/symfony/filesystem/Exception/IOException.phpª  ž­¨Zª  Ñ‘„A¶      =   vendor/symfony/filesystem/Exception/FileNotFoundException.php·  ž­¨Z·  þ0zÂ¶      '   vendor/symfony/filesystem/composer.json  ž­¨Z  dßÑ†¶         vendor/symfony/yaml/Parser.phpŠ³  ž­¨ZŠ³  @*‚¶         vendor/symfony/yaml/Escaper.phpg  ž­¨Zg  bÚ”ƒ¶      !   vendor/symfony/yaml/Unescaper.phpQ  ž­¨ZQ  !û+×¶         vendor/symfony/yaml/Dumper.php»  ž­¨Z»  �3Þ¶         vendor/symfony/yaml/Inline.phpb’  ž­¨Zb’  &8†¶         vendor/symfony/yaml/Yaml.phpb  ž­¨Zb  ‰¹¨¶      +   vendor/symfony/yaml/Command/LintCommand.phpX  ž­¨ZX  ÊÙ¶      /   vendor/symfony/yaml/Exception/DumpException.phpÇ  ž­¨ZÇ  Œ¶      4   vendor/symfony/yaml/Exception/ExceptionInterface.php»  ž­¨Z»  Þ^KA¶      2   vendor/symfony/yaml/Exception/RuntimeException.phpå  ž­¨Zå  ô_q¦¶      0   vendor/symfony/yaml/Exception/ParseException.php[  ž­¨Z[  6�
Ì¶      '   vendor/symfony/yaml/Tag/TaggedValue.phpG  ž­¨ZG  ÊiY¶      !   vendor/symfony/yaml/composer.jsonØ  ž­¨ZØ  ·B�h¶         vendor/symfony/debug/Debug.phpý  ž­¨Zý  ö�f¶      M   vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.phpõ  ž­¨Zõ  Ìgý�¶      E   vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php¿  ž­¨Z¿  ÇiA¼¶      K   vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.phpZ  ž­¨ZZ  Ã2¶      I   vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.phpÌ  ž­¨ZÌ  äž(¶      )   vendor/symfony/debug/ExceptionHandler.phpè  ž­¨Zè  G]3¶      )   vendor/symfony/debug/DebugClassLoader.phpÕ@  ž­¨ZÕ@  H5¯¶      (   vendor/symfony/debug/BufferingLogger.phpð  ž­¨Zð  M0	=¶      %   vendor/symfony/debug/ErrorHandler.phpèp  ž­¨Zèp  ]ª¶      6   vendor/symfony/debug/Exception/FatalThrowableError.php4  ž­¨Z4   Ãì¶      7   vendor/symfony/debug/Exception/OutOfMemoryException.php¨  ž­¨Z¨  ªøh¶      3   vendor/symfony/debug/Exception/FlattenException.php(  ž­¨Z(  ýDôD¶      ;   vendor/symfony/debug/Exception/UndefinedMethodException.php&  ž­¨Z&  Ã¯yß¶      =   vendor/symfony/debug/Exception/UndefinedFunctionException.php+  ž­¨Z+  ÛOù¶      6   vendor/symfony/debug/Exception/FatalErrorException.php  ž­¨Z  U¹®l¶      7   vendor/symfony/debug/Exception/SilencedErrorContext.phph  ž­¨Zh  z=†¶      9   vendor/symfony/debug/Exception/ClassNotFoundException.php<  ž­¨Z<  'D»�¶      8   vendor/symfony/debug/Exception/ContextErrorException.phpS  ž­¨ZS  c3¶¶      "   vendor/symfony/debug/composer.jsonÇ  ž­¨ZÇ  4™\¶      (   vendor/symfony/console/ConsoleEvents.php!  ž­¨Z!  Ÿ“Î©¶      &   vendor/symfony/console/Application.phpª¦  ž­¨Zª¦  ,ÙØ½¶      /   vendor/symfony/console/Logger/ConsoleLogger.php  ž­¨Z   zúþ¶      9   vendor/symfony/console/Input/StreamableInputInterface.phpi  ž­¨Zi  õ™í¶      +   vendor/symfony/console/Input/ArrayInput.php¿  ž­¨Z¿  ¹pGW¶      *   vendor/symfony/console/Input/ArgvInput.phpÝ(  ž­¨ZÝ(  ÄïÓŠ¶      .   vendor/symfony/console/Input/InputArgument.phpü  ž­¨Zü  	Ù¶      4   vendor/symfony/console/Input/InputAwareInterface.php^  ž­¨Z^  9Kèh¶      ,   vendor/symfony/console/Input/InputOption.php!  ž­¨Z!  ÿy¶      0   vendor/symfony/console/Input/InputDefinition.php,  ž­¨Z,  Y³ªZ¶      ,   vendor/symfony/console/Input/StringInput.phpP	  ž­¨ZP	  ÚI7¶      &   vendor/symfony/console/Input/Input.phpƒ  ž­¨Zƒ  �«ÚÅ¶      /   vendor/symfony/console/Input/InputInterface.php'  ž­¨Z'  Y u¶      #   vendor/symfony/console/Terminal.php¤  ž­¨Z¤  hñ«7¶      8   vendor/symfony/console/Output/ConsoleOutputInterface.phpÞ  ž­¨ZÞ  ¿ ƒ¶      0   vendor/symfony/console/Output/BufferedOutput.phpH  ž­¨ZH   9¿Ô¶      1   vendor/symfony/console/Output/OutputInterface.php  ž­¨Z  íçð9¶      (   vendor/symfony/console/Output/Output.php{  ž­¨Z{  ëmÂn¶      /   vendor/symfony/console/Output/ConsoleOutput.phpª  ž­¨Zª  é£ÉU¶      ,   vendor/symfony/console/Output/NullOutput.phpn  ž­¨Zn  t�D»¶      .   vendor/symfony/console/Output/StreamOutput.php÷  ž­¨Z÷  …Âêì¶      ,   vendor/symfony/console/Style/OutputStyle.phpý  ž­¨Zý  é¨gÝ¶      -   vendor/symfony/console/Style/SymfonyStyle.php	/  ž­¨Z	/  ¶ö¶      /   vendor/symfony/console/Style/StyleInterface.phpO  ž­¨ZO  üë®Q¶      2   vendor/symfony/console/Question/ChoiceQuestion.phpÀ  ž­¨ZÀ  ºìÖ¶      ,   vendor/symfony/console/Question/Question.phpæ  ž­¨Zæ  ÜŽå¶      8   vendor/symfony/console/Question/ConfirmationQuestion.php  ž­¨Z  ¾™·�¶      9   vendor/symfony/console/Formatter/OutputFormatterStyle.php…  ž­¨Z…  œ—ƒÊ¶      B   vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php7  ž­¨Z7  –.@Y¶      =   vendor/symfony/console/Formatter/OutputFormatterInterface.php¦  ž­¨Z¦  #7êu¶      4   vendor/symfony/console/Formatter/OutputFormatter.php_  ž­¨Z_  ß…6¶      >   vendor/symfony/console/Formatter/OutputFormatterStyleStack.php¸	  ž­¨Z¸	  \gˆV¶      ?   vendor/symfony/console/CommandLoader/CommandLoaderInterface.php¹  ž­¨Z¹  ¼NEe¶      ?   vendor/symfony/console/CommandLoader/ContainerCommandLoader.phpW  ž­¨ZW  ÿŒÈž¶      =   vendor/symfony/console/CommandLoader/FactoryCommandLoader.php7  ž­¨Z7  ‚m_ò¶      4   vendor/symfony/console/Descriptor/TextDescriptor.phpŒ0  ž­¨ZŒ0   4Øt¶      <   vendor/symfony/console/Descriptor/ApplicationDescription.php  ž­¨Z  £”®¶      3   vendor/symfony/console/Descriptor/XmlDescriptor.php$  ž­¨Z$  {gæL¶      9   vendor/symfony/console/Descriptor/DescriptorInterface.phpá  ž­¨Zá  JZ0<¶      0   vendor/symfony/console/Descriptor/Descriptor.php�  ž­¨Z�  –j¶      4   vendor/symfony/console/Descriptor/JsonDescriptor.phpd  ž­¨Zd  Ùmùh¶      8   vendor/symfony/console/Descriptor/MarkdownDescriptor.phpã  ž­¨Zã  ¯¨`ï¶      /   vendor/symfony/console/Tester/CommandTester.phpÅ  ž­¨ZÅ  	AŽ¶      3   vendor/symfony/console/Tester/ApplicationTester.php  ž­¨Z  e5¶      0   vendor/symfony/console/Command/LockableTrait.phpä  ž­¨Zä  ŠîO¶      .   vendor/symfony/console/Command/HelpCommand.phpI	  ž­¨ZI	  ´)~¡¶      .   vendor/symfony/console/Command/ListCommand.php	
  ž­¨Z	
  êÂ4¶      *   vendor/symfony/console/Command/Command.php¾I  ž­¨Z¾I  RÓ²¶      ,   vendor/symfony/console/Helper/TableStyle.phpˆ  ž­¨Zˆ  wîU\¶      7   vendor/symfony/console/Helper/SymfonyQuestionHelper.phpÀ  ž­¨ZÀ  KVÝk¶      1   vendor/symfony/console/Helper/HelperInterface.phpp  ž­¨Zp  ‘àñn¶      +   vendor/symfony/console/Helper/TableCell.phpy  ž­¨Zy  p/#6¶      /   vendor/symfony/console/Helper/ProcessHelper.phpò  ž­¨Zò  €2Ræ¶      0   vendor/symfony/console/Helper/TableSeparator.php  ž­¨Z  Ô=_¶      1   vendor/symfony/console/Helper/FormatterHelper.php’  ž­¨Z’  µí·ÿ¶      -   vendor/symfony/console/Helper/ProgressBar.phpÁC  ž­¨ZÁC  ‰Vde¶      6   vendor/symfony/console/Helper/DebugFormatterHelper.phpV  ž­¨ZV  ˜Ý”ª¶      +   vendor/symfony/console/Helper/HelperSet.phpÊ	  ž­¨ZÊ	  .ƒˆØ¶      2   vendor/symfony/console/Helper/InputAwareHelper.phpë  ž­¨Zë  ì“ëË¶      0   vendor/symfony/console/Helper/QuestionHelper.php9  ž­¨Z9  P˜¶      (   vendor/symfony/console/Helper/Helper.phpß  ž­¨Zß  
FY>¶      '   vendor/symfony/console/Helper/Table.php«N  ž­¨Z«N  ½÷NÔ¶      3   vendor/symfony/console/Helper/ProgressIndicator.php  ž­¨Z  fG.¶      2   vendor/symfony/console/Helper/DescriptorHelper.php1
  ž­¨Z1
  i–¶      D   vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.phpÂ  ž­¨ZÂ  ý�X¶      3   vendor/symfony/console/Exception/LogicException.phpª  ž­¨Zª  SML�¶      ;   vendor/symfony/console/Exception/InvalidOptionException.phpü  ž­¨Zü  í“;¶      7   vendor/symfony/console/Exception/ExceptionInterface.php–  ž­¨Z–  ãëÚU¶      5   vendor/symfony/console/Exception/RuntimeException.php®  ž­¨Z®  ¶*b¶      =   vendor/symfony/console/Exception/InvalidArgumentException.php¾  ž­¨Z¾  îu i¶      =   vendor/symfony/console/Exception/CommandNotFoundException.phpÂ  ž­¨ZÂ  ¯›Í¶      6   vendor/symfony/console/Event/ConsoleTerminateEvent.php  ž­¨Z  {eð¶      2   vendor/symfony/console/Event/ConsoleErrorEvent.phpÁ  ž­¨ZÁ  T/ß[¶      4   vendor/symfony/console/Event/ConsoleCommandEvent.php%  ž­¨Z%  {Ë¾à¶      -   vendor/symfony/console/Event/ConsoleEvent.phpÄ  ž­¨ZÄ  xS*¶      6   vendor/symfony/console/Event/ConsoleExceptionEvent.phpS  ž­¨ZS  ¼ˆÃÖ¶      $   vendor/symfony/console/composer.json‚  ž­¨Z‚  u\î ¶      6   vendor/symfony/console/EventListener/ErrorListener.phpø
  ž­¨Zø
  �/Å±¶      @   vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpºI  ž­¨ZºI  ê�Òˆ¶      @   vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php9J  ž­¨Z9J  |Qù¶      -   vendor/symfony/polyfill-mbstring/Mbstring.phpg  ž­¨Zg  ÖÁ„¶      .   vendor/symfony/polyfill-mbstring/bootstrap.phpI  ž­¨ZI  @ï¶      .   vendor/symfony/polyfill-mbstring/composer.jsono  ž­¨Zo  ÛU`V¶      8   vendor/linways/linways-base/test/testTaskQueueWorker.phpÒ  ž­¨ZÒ  ÛøYf¶      -   vendor/linways/linways-base/test/testHook.phpr   ž­¨Zr   ‚"Ñ�¶      5   vendor/linways/linways-base/test/testLiwnaysHooks.phpÅ   ž­¨ZÅ   ­F×ú¶      (   vendor/linways/linways-base/test/web.php¹   ž­¨Z¹   ºì>¶      5   vendor/linways/linways-base/test/testemailservice.php<  ž­¨Z<  õÿÑf¶      2   vendor/linways/linways-base/test/testTaskQueue.phpÚ  ž­¨ZÚ  ¢ß½¶      )   vendor/linways/linways-base/composer.jsono  ž­¨Zo  îkí¶      >   vendor/linways/linways-base/src/com/linways/base/dto/Email.phpp  ž­¨Zp  ÛQq¶      @   vendor/linways/linways-base/src/com/linways/base/dto/EmailCC.php  ž­¨Z  Ñ�UÊ¶      @   vendor/linways/linways-base/src/com/linways/base/dto/EmailTo.php  ž­¨Z  –³¶      D   vendor/linways/linways-base/src/com/linways/base/dto/PageSupport.phpk  ž­¨Zk  vÞŒ±¶      @   vendor/linways/linways-base/src/com/linways/base/dto/BaseDTO.phpú  ž­¨Zú  PX¹æ¶      B   vendor/linways/linways-base/src/com/linways/base/dto/EmailFrom.php  ž­¨Z  h`¾¶      F   vendor/linways/linways-base/src/com/linways/base/dto/AjaxPaginator.php‰	  ž­¨Z‰	  Uqö—¶      D   vendor/linways/linways-base/src/com/linways/base/dto/MySqlResult.phpe   ž­¨Ze   |Ñ¼l¶      J   vendor/linways/linways-base/src/com/linways/base/testcase/BaseTestCase.php™  ž­¨Z™  ’¶1t¶      L   vendor/linways/linways-base/src/com/linways/base/messages/CommonMessages.php  ž­¨Z  T‰’µ¶      J   vendor/linways/linways-base/src/com/linways/base/response/BaseResponse.phpº   ž­¨Zº   S€�®¶      C   vendor/linways/linways-base/src/com/linways/base/cache/LinCache.phpÃ  ž­¨ZÃ  ÔNˆí¶      N   vendor/linways/linways-base/src/com/linways/base/connection/MySqlConnector.php4  ž­¨Z4  ´ñãˆ¶      O   vendor/linways/linways-base/src/com/linways/base/connection/IMySqlConnector.php1  ž­¨Z1  ,gK–¶      J   vendor/linways/linways-base/src/com/linways/base/connection/MySqlQuery.php”  ž­¨Z”  ÒZþ€¶      D   vendor/linways/linways-base/src/com/linways/base/util/StringUtil.php  ž­¨Z  çùU¶      F   vendor/linways/linways-base/src/com/linways/base/util/CalendarUtil.phpf  ž­¨Zf  ?è#Ì¶      D   vendor/linways/linways-base/src/com/linways/base/util/CommonUtil.php¤  ž­¨Z¤  ›Õå�¶      E   vendor/linways/linways-base/src/com/linways/base/util/RequestUtil.phpÌ  ž­¨ZÌ  ª¢ô>¶      D   vendor/linways/linways-base/src/com/linways/base/util/MapperUtil.phph$  ž­¨Zh$  ÖÓXÊ¶      D   vendor/linways/linways-base/src/com/linways/base/util/ObjectUtil.php›  ž­¨Z›  šŽ,·¶      C   vendor/linways/linways-base/src/com/linways/base/util/PhoneUtil.phpG  ž­¨ZG  çž'"¶      C   vendor/linways/linways-base/src/com/linways/base/util/MySqlUtil.phpÎ
  ž­¨ZÎ
  ~¶;¶      B   vendor/linways/linways-base/src/com/linways/base/mapper/Result.phpÀ  ž­¨ZÀ  «Ïü¥¶      E   vendor/linways/linways-base/src/com/linways/base/mapper/ResultMap.php0  ž­¨Z0  òa›<¶      C   vendor/linways/linways-base/src/com/linways/base/mapper/IMapper.php²   ž­¨Z²   Ú<PL¶      M   vendor/linways/linways-base/src/com/linways/base/soap/soap_transport_http.php…é  ž­¨Z…é  G¼F¶      G   vendor/linways/linways-base/src/com/linways/base/soap/nusoap_client.php¥˜  ž­¨Z¥˜  "‚/¶      J   vendor/linways/linways-base/src/com/linways/base/soap/nusoap_xmlschema.phpk‰  ž­¨Zk‰  ‡øl¶      E   vendor/linways/linways-base/src/com/linways/base/soap/soap_server.phpu   ž­¨Zu   ‡ÞÂ1¶      E   vendor/linways/linways-base/src/com/linways/base/soap/nusoap_base.php”Š  ž­¨Z”Š  )?¶      E   vendor/linways/linways-base/src/com/linways/base/soap/soap_parser.phpt   ž­¨Zt   ‰»^ò¶      D   vendor/linways/linways-base/src/com/linways/base/soap/soap_fault.phpv   ž­¨Zv   tb~¶      A   vendor/linways/linways-base/src/com/linways/base/soap/soapval.php?
  ž­¨Z?
  Ÿàç©¶      >   vendor/linways/linways-base/src/com/linways/base/soap/wsdl.php²… ž­¨Z²… :¶      F   vendor/linways/linways-base/src/com/linways/base/soap/nusoap_fault.phpk	  ž­¨Zk	  Š/[¶      G   vendor/linways/linways-base/src/com/linways/base/soap/nusoap_server.phpïº  ž­¨Zïº  Z[ûç¶      G   vendor/linways/linways-base/src/com/linways/base/soap/nusoap_parser.php`u  ž­¨Z`u  ýª™Þ¶      C   vendor/linways/linways-base/src/com/linways/base/soap/XMLSchema.phpw   ž­¨Zw   _¯	¶      D   vendor/linways/linways-base/src/com/linways/base/soap/soapclient.phpT   ž­¨ZT    · q¶      G   vendor/linways/linways-base/src/com/linways/base/hooks/LinwaysHooks.php5  ž­¨Z5  }ö�ô¶      D   vendor/linways/linways-base/src/com/linways/base/mcrypt/CryptAES.php  ž­¨Z  Â‚«¶      D   vendor/linways/linways-base/src/com/linways/base/queue/TaskQueue.php–  ž­¨Z–  �[¹w¶      H   vendor/linways/linways-base/src/com/linways/base/request/BaseRequest.phpC  ž­¨ZC  šåYÍ¶      I   vendor/linways/linways-base/src/com/linways/base/service/EmailService.php¬  ž­¨Z¬  ‰¥\¶      I   vendor/linways/linways-base/src/com/linways/base/helper/ResultHandler.phpS  ž­¨ZS  *�³÷¶      K   vendor/linways/linways-base/src/com/linways/base/exception/SQLException.php^   ž­¨Z^   @ç…a¶      Q   vendor/linways/linways-base/src/com/linways/base/exception/TaskQueueException.phpË  ž­¨ZË  ÓTçç¶      M   vendor/linways/linways-base/src/com/linways/base/exception/EmailException.phpm  ž­¨Zm  ¶SÅ¶      N   vendor/linways/linways-base/src/com/linways/base/exception/MapperException.phpg  ž­¨Zg  3ÞÑ:¶      L   vendor/linways/linways-base/src/com/linways/base/exception/CoreException.php‰  ž­¨Z‰  1:¶      D   vendor/linways/linways-base/src/com/linways/base/email/PHPMailer.phpG×  ž­¨ZG×  K—ç¬¶      ?   vendor/linways/linways-base/src/com/linways/base/email/SMTP.php›ˆ  ž­¨Z›ˆ  iz-¶      8   vendor/linways/nucleus-core/test/db_conf/ams_db_conf.phpe   ž­¨Ze    uW·¶      <   vendor/linways/nucleus-core/test/db_conf/nucleus_db_conf.phpf   ž­¨Zf   iáÕ¶      ?   vendor/linways/nucleus-core/test/utils/UserServiceUtilsTest.php·  ž­¨Z·  Æ|å¶      7   vendor/linways/nucleus-core/test/scripts/basic_task.phpø  ž­¨Zø  tÁûˆ¶      =   vendor/linways/nucleus-core/test/scripts/testServiceClass.phpÅ  ž­¨ZÅ  îæ¶      @   vendor/linways/nucleus-core/test/scripts/basic_schedule_task.phpD   ž­¨ZD   9Pý¶      B   vendor/linways/nucleus-core/test/service/PermissionServiceTest.php†  ž­¨Z†  ï¼i]¶      =   vendor/linways/nucleus-core/test/service/AddonServiceTest.php¥*  ž­¨Z¥*  ôëžÏ¶      A   vendor/linways/nucleus-core/test/service/user/UserServiceTest.phpš  ž­¨Zš  ’áÞ¶      D   vendor/linways/nucleus-core/test/service/SingleSignonServiceTest.phph  ž­¨Zh  ý:4W¶      E   vendor/linways/nucleus-core/test/service/ConfigurationServiceTest.php4  ž­¨Z4  ¹ÿõª¶      F   vendor/linways/nucleus-core/test/service/APIAccessTokenServiceTest.php,  ž­¨Z,  |]o¶      F   vendor/linways/nucleus-core/test/service/AddonDataStoreServiceTest.php›  ž­¨Z›  Š‚2µ¶      =   vendor/linways/nucleus-core/test/service/AddonTestHelpers.php(  ž­¨Z(  VF2±¶      ?   vendor/linways/nucleus-core/test/service/CollegeServiceTest.php#  ž­¨Z#  Ì©
P¶      ?   vendor/linways/nucleus-core/test/service/ExpenseServiceTest.php�  ž­¨Z�  _�h¶      G   vendor/linways/nucleus-core/test/service/ExpenseCategoryServiceTest.php¹  ž­¨Z¹  `Z˜„¶      A   vendor/linways/nucleus-core/test/service/SchedulerServiceTest.php)  ž­¨Z)  fŒfh¶      >   vendor/linways/nucleus-core/test/service/ClientServiceTest.php 	  ž­¨Z 	  ¨h8Œ¶      Z   vendor/linways/nucleus-core/db/migrations/20180104061147_expense_category_audit_fields.phpr  ž­¨Zr  9]æ¶      T   vendor/linways/nucleus-core/db/migrations/20180103104034_college_db_audit_fields.phpf  ž­¨Zf   #Ž¶      Q   vendor/linways/nucleus-core/db/migrations/20171018082229_scheduler_migrations.phpL  ž­¨ZL  sUVM¶      M   vendor/linways/nucleus-core/db/migrations/20170925091052_addons_migration.phpñ  ž­¨Zñ  1Õ¶b¶      S   vendor/linways/nucleus-core/db/migrations/20180101124808_tables_for_sso_feature.phpl  ž­¨Zl  Ö¸�¶      N   vendor/linways/nucleus-core/db/migrations/20170913160539_initial_migration.php¸  ž­¨Z¸  åKi¶      N   vendor/linways/nucleus-core/db/migrations/20171102101414_college_db_fields.phpù  ž­¨Zù  «Â0¶      ^   vendor/linways/nucleus-core/db/migrations/20180108065035_tables_for_user_module_in_nucleus.php¯  ž­¨Z¯   Ðˆ«¶      V   vendor/linways/nucleus-core/db/migrations/20180103120805_college_db_is_active_flag.php±  ž­¨Z±  L!wÙ¶      T   vendor/linways/nucleus-core/db/migrations/20171226045009_client_table_migrations.phpø  ž­¨Zø  @ñ²±¶      M   vendor/linways/nucleus-core/db/migrations/20180116040330_dummy_super_user.php|#  ž­¨Z|#  g˜û;¶      U   vendor/linways/nucleus-core/db/migrations/20170913161044_api_access_key_migration.php�  ž­¨Z�  ÷"¶      7   vendor/linways/nucleus-core/db/seeds/DummySuperUser.phpS  ž­¨ZS  ­ÂVv¶      4   vendor/linways/nucleus-core/db/seeds/CollegeSeed.php�  ž­¨Z�  ³òÿ¶      2   vendor/linways/nucleus-core/db/seeds/AddonSeed.php¬  ž­¨Z¬  à1i‚¶      8   vendor/linways/nucleus-core/db/seeds/GreythrSeedData.phpT  ž­¨ZT  0,ær¶      7   vendor/linways/nucleus-core/db/seeds/PermissionSeed.phpË  ž­¨ZË  {L=g¶      *   vendor/linways/nucleus-core/db/manager.php–+  ž­¨Z–+  þx¶      )   vendor/linways/nucleus-core/composer.jsonu  ž­¨Zu  Œ›9˜¶      F   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/Addon.phpâ  ž­¨Zâ  ~[ª¶      T   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonAttributeValue.phpð  ž­¨Zð  œÃùE¶      O   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonAttribute.phpÁ  ž­¨ZÁ  Ž’¿™¶      W   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/ApiOrganizationDetails.php#  ž­¨Z#  1£ö¶      M   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonEnabled.phpå  ž­¨Zå  Kå´¶      J   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/user/User.phpí   ž­¨Zí   6öi)¶      O   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonDataStore.php±  ž­¨Z±  À,.¦¶      H   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/Expense.php  ž­¨Z  ‰ý
¶      N   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/ScheduledTask.phpA  ž­¨ZA  I5þD¶      H   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/College.phpÔ  ž­¨ZÔ  " ] ¶      P   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/ExpenseCategory.phpñ   ž­¨Zñ   ì—b¶      P   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/SingleSignonLog.phpæ   ž­¨Zæ   Ùì0¶      G   vendor/linways/nucleus-core/src/com/linways/nucleus/core/dto/Client.php@  ž­¨Z@  €ÁJH¶      X   vendor/linways/nucleus-core/src/com/linways/nucleus/core/ams_task_queue/AMSTaskQueue.php?  ž­¨Z?  �–4¶      L   vendor/linways/nucleus-core/src/com/linways/nucleus/core/constants/Scope.phpm   ž­¨Zm   \™�Ê¶      [   vendor/linways/nucleus-core/src/com/linways/nucleus/core/constants/SingleSignonConstant.phpm   ž­¨Zm   º•}¶      W   vendor/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ClientServiceMapper.phpÞ  ž­¨ZÞ  šÙ‚¶      Y   vendor/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ScheduleServiceMapper.php£  ž­¨Z£  §0ä¶      X   vendor/linways/nucleus-core/src/com/linways/nucleus/core/mapper/CollegeServiceMapper.phpA  ž­¨ZA  ñ_<¶      X   vendor/linways/nucleus-core/src/com/linways/nucleus/core/mapper/AddonDataStoreMapper.php  ž­¨Z  ¬NìÓ¶      X   vendor/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ExpenseServiceMapper.phpe  ž­¨Ze  >²ÞÒ¶      V   vendor/linways/nucleus-core/src/com/linways/nucleus/core/mapper/AddonServiceMapper.php-  ž­¨Z-  àir*¶      X   vendor/linways/nucleus-core/src/com/linways/nucleus/core/utils/user/UserServiceUtils.php¡  ž­¨Z¡  Vaò
¶      N   vendor/linways/nucleus-core/src/com/linways/nucleus/core/utils/StringUtils.php  ž­¨Z  $˜›¶      K   vendor/linways/nucleus-core/src/com/linways/nucleus/core/utils/JWTUtils.php_  ž­¨Z_  –	T§¶      T   vendor/linways/nucleus-core/src/com/linways/nucleus/core/request/ScheduleRequest.phpû  ž­¨Zû  æ\lL¶      Y   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/ConfigurationService.phpÇ  ž­¨ZÇ  p]Ž¶      V   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/PermissionService.phpÎ  ž­¨ZÎ  8ìè6¶      Q   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonService.phpi;  ž­¨Zi;  4½ú¶      U   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/user/UserService.php  ž­¨Z  úŽX~¶      [   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/ExpenseCategoryService.phpû	  ž­¨Zû	  ¹æ$¶      X   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/SingleSignonService.php  ž­¨Z   Ø¶      Z   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonAttributeService.phpƒ  ž­¨Zƒ  ]•Þ°¶      S   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/ExpenseService.php—  ž­¨Z—  œÇÊ¶      Y   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonDataStoreSevice.php�  ž­¨Z�  £Y¬¶      Z   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/APIAccessTokenService.php  ž­¨Z  b—ˆ¶      P   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/BaseService.php0
  ž­¨Z0
  2Àx¶      R   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/ClientService.php¿  ž­¨Z¿  eŒi¶      U   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/SchedulerService.phpP  ž­¨ZP  _UG¶      S   vendor/linways/nucleus-core/src/com/linways/nucleus/core/service/CollegeService.phpü%  ž­¨Zü%  fÇ£¶      _   vendor/linways/nucleus-core/src/com/linways/nucleus/core/exception/TaskQueueWorkerException.php  ž­¨Z  òwŠ¶      Y   vendor/linways/nucleus-core/src/com/linways/nucleus/core/exception/SchedulerException.phpØ   ž­¨ZØ   2œßš¶      W   vendor/linways/nucleus-core/src/com/linways/nucleus/core/exception/NucleusException.phpl  ž­¨Zl  ã6®…¶      2   vendor/dragonmantank/cron-expression/composer.json·  ž­¨Z·  =�Ý¿¶      >   vendor/dragonmantank/cron-expression/src/Cron/FieldFactory.phpU  ž­¨ZU  d‘"«¶      A   vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php  ž­¨Z  SðD¶      ;   vendor/dragonmantank/cron-expression/src/Cron/YearField.php  ž­¨Z  ÌÈ‡¶      @   vendor/dragonmantank/cron-expression/src/Cron/FieldInterface.php?  ž­¨Z?  šYäÎ¶      ?   vendor/dragonmantank/cron-expression/src/Cron/AbstractField.phpF  ž­¨ZF  Ô×,²¶      >   vendor/dragonmantank/cron-expression/src/Cron/MinutesField.php2  ž­¨Z2  UîuQ¶      <   vendor/dragonmantank/cron-expression/src/Cron/MonthField.phpÜ  ž­¨ZÜ  »Š¥O¶      <   vendor/dragonmantank/cron-expression/src/Cron/HoursField.php¤  ž­¨Z¤  œ	‡Ñ¶      @   vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php47  ž­¨Z47  é¶      @   vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php.  ž­¨Z.  ñ-D©¶         src/utils/MigrationUtils.phpƒ  ž­¨Zƒ  fƒ¬í¶      "   src/commands/db/MigrateCommand.phpŠ  ž­¨ZŠ  ëjt¶      &   src/commands/db/MigrateFakeCommand.phpþ  ž­¨Zþ  V"€¶      *   src/commands/db/CreateMigrationCommand.phpP  ž­¨ZP  v–j¶      ,   src/commands/git/UpdateStyleGuideCommand.php  ž­¨Z  zA4x¶      %   src/service/db/MigrateFakeService.php  ž­¨Z  á<%¶      !   src/service/db/MigrateService.phpj  ž­¨Zj  ’ÆÄî¶         clib  ž­¨Zb  Âi`8¶      !   MigrationCustom.template.php.dist[  ž­¨Z[  _¡¶      <?php

// autoload.php @generated by Composer

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

return ComposerAutoloaderInit555f246cb7db6869b6fb9f48d5e5f3d2::getLoader();
<?php

/**
 * Usage:
 * php spec/parser.php path.to.spec.json:
 *   - php spec/parser.php amqp-rabbitmq-0.8.json
 *   - php spec/parser.php amqp-rabbitmq-0.9.1.json
 */

if (empty($argv[1])) {
    echo "ERROR: You must provide a protocol file to parse.\n";
    echo "Usage: php spec/parser.php amqp-rabbitmq-0.9.1.json.\n";
    die(1);
}

$spec = file_get_contents(__DIR__ . "/" . $argv[1]);

$json_spec = json_decode($spec, true);

function to_camel_case($amqp_method)
{
    $words = explode('-', $amqp_method);
    $ret = array();
    foreach ($words as $w) {
        $ret[] = ucfirst($w);
    }

    return implode('', $ret);
}

function method_name($amqp_class, $amqp_method)
{
    return $amqp_class . to_camel_case($amqp_method);
}

function to_snake_case($arg)
{
    return str_replace('-', '_', $arg);
}

function addPhpDocParams($arguments)
{
    $ret = array();
    foreach ($arguments as $arg) {
        $ret[] = ' * @param ' . translateType($arg) . ' $' . to_snake_case($arg['name']);
    }

    return implode("\n", $ret);
}

function translateType($argument)
{
    $type = null;
    if (array_key_exists('default-value', $argument)) {
        $type = gettype($argument['default-value']);
    } elseif (array_key_exists('type', $argument)) {
        $type = $argument['type'];
    }

    switch ($type) {
        case 'longstr':
        case 'shortstr':
        case 'string':
            return 'string';
        case 'short':
        case 'long':
        case 'longlong':
        case 'integer':
        case 'int':
            return 'int';
        case 'bit':
        case 'boolean':
        case 'bool':
            return 'bool';
        case 'array':
            return 'array';
        default:
            return 'mixed';
    }
}

function argument_default_val($arg)
{
    return isset($arg['default-value']) ? " = " . default_value_to_string($arg['default-value']) : "";
}

function default_value_to_string($value)
{
    if (is_array($value)) {
        return 'array(' . implode(', ', array_map('default_value_to_string', $value)) . ')';
    }

    return var_export($value, true);
}

function indent($s, $level = 1, $chars = "    ")
{
    if ($level > 0) {
        $s = preg_replace('#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level), $s);
    }

    return $s;
}

function add_method_arguments($arguments)
{
    $ret = array();
    foreach ($arguments as $arg) {
        $ret[] = '$' . to_snake_case($arg['name']) . argument_default_val($arg);
    }

    return implode(", ", $ret);
}

/**
 * @param array $domains
 * @param string $domain
 * @return string
 * @throws Exception
 */
function domain_to_type($domains, $domain)
{
    foreach ($domains as $d) {
        if ($d[0] == $domain) {
            return $d[1];
        }
    }
    throw new \Exception("Invalid domain: " . $domain);
}

/**
 * @param array $domains
 * @param array $arg
 * @return string
 * @throws Exception
 */
function argument_type($domains, $arg)
{
    return isset($arg['type']) ? $arg['type'] : domain_to_type($domains, $arg['domain']);
}

class ArgumentWriter
{
    protected $bit_args = array();

    public function call_write_argument($domains, $arg)
    {
        $a_type = argument_type($domains, $arg);
        if ($a_type == 'bit') {
            $this->bit_args[] = '$' . to_snake_case($arg['name']);
            $ret = '';

        } else {
            $ret = $this->write_bits();

            $a_name = '$' . to_snake_case($arg['name']);
            $ret .= '$writer->write_' . $a_type . '(' . ($a_type === 'table' ? 'empty(' . $a_name . ') ? array() : ' : '') . $a_name . ");\n";
        }

        return $ret;
    }


    public function write_bits()
    {
        if (empty($this->bit_args)) {
            return '';
        }

        $ret = '$writer->write_bits(array(' . implode(', ', $this->bit_args) . "));\n";
        $this->bit_args = array();

        return $ret;
    }
}

function call_read_argument($domains, $arg)
{
    return '$reader->read_' . argument_type($domains, $arg) . "();\n";
}

function protocol_version($json_spec)
{
    if (isset($json_spec['revision'])) {
        return $json_spec['major-version'] . $json_spec['minor-version'] . $json_spec['revision'];
    } else {
        return '0' . $json_spec['major-version'] . $json_spec['minor-version'];
    }
}

function protocol_header($json_spec)
{
    if (isset($json_spec['revision'])) {
        $args = array(0, $json_spec['major-version'], $json_spec['minor-version'], $json_spec['revision']);

    } else {
        $args = array(1, 1, $json_spec['major-version'], $json_spec['minor-version']);
    }

    array_unshift($args, 'AMQP\x%02x\x%02x\x%02x\x%02x');

    return '"' . call_user_func_array('sprintf', $args) . '"';
}

$argumentWriter = new ArgumentWriter();

$out = '<?php' . "\n\n";
$out .= '/* This file was autogenerated by spec/parser.php - Do not modify */' . "\n\n";
$out .= 'namespace PhpAmqpLib\Helper\Protocol;' . "\n\n";
$out .= 'use PhpAmqpLib\Wire\AMQPWriter;' . "\n";
$out .= 'use PhpAmqpLib\Wire\AMQPReader;' . "\n\n";
$out .= 'class Protocol' . protocol_version($json_spec) . "\n";
$out .= "{";

$methods = '';

foreach ($json_spec['classes'] as $c) {
    foreach ($c['methods'] as $m) {
        $methods .= "\n";

        if ($m['id'] % 10 == 0) {
            $methodBody = '$writer = new AMQPWriter();' . "\n";
            foreach ($m['arguments'] as $arg) {
                $methodBody .= $argumentWriter->call_write_argument($json_spec['domains'], $arg);
            }
            $methodBody .= $argumentWriter->write_bits();
            $methodBody .= 'return array(' . $c['id'] . ", " . $m['id'] . ', $writer);';

            $methods .= '/**' . "\n";
            $methods .= addPhpDocParams($m['arguments']) . "\n";
            $methods .= ' * @return array' . "\n";
            $methods .= ' */' . "\n";
            $methods .= 'public function ' . method_name($c['name'], $m['name']) . "(";
            $methods .= add_method_arguments($m['arguments']);
            $methods .= ")\n{\n";
            $methods .= indent($methodBody) . "\n";
            $methods .= "}\n";

        } else {
            $methodBody = '$response = array();' . "\n";
            foreach ($m['arguments'] as $arg) {
                $methodBody .= '$response[] = ' . call_read_argument($json_spec['domains'], $arg);
            }
            $methodBody .= 'return $response;';

            $methods .= '/**' . "\n";
            $methods .= ' * @param AMQPReader $reader' . "\n";
            $methods .= ' * @return array' . "\n";
            $methods .= ' */' . "\n";
            $methods .= 'public static function ' . method_name($c['name'], $m['name'])
                . '(AMQPReader $reader)' . "\n{\n";
            $methods .= indent($methodBody) . "\n";
            $methods .= "}\n";
        }
    }
}

$out .= indent(rtrim($methods)) . "\n";
$out .= "}\n";

file_put_contents(__DIR__ . '/../PhpAmqpLib/Helper/Protocol/Protocol' . protocol_version($json_spec) . '.php', $out);

function export_property($ret)
{
    if (!is_array($ret)) {
        return var_export($ret, true);
    }

    $code = '';
    foreach ($ret as $key => $value) {
        $code .= var_export($key, true) . ' => ' . export_property($value) . ",\n";
    }

    return "array(\n" . indent($code) . ")";
}

function frame_types($json_spec)
{
    $ret = array();
    foreach ($json_spec['constants'] as $c) {
        if (mb_substr($c['name'], 0, 5, 'ASCII') == "FRAME") {
            $ret[$c['value']] = $c['name'];
        }
    }

    return export_property($ret);
}

function content_methods($json_spec)
{
    $ret = array();
    foreach ($json_spec['classes'] as $c) {
        foreach ($c['methods'] as $m) {
            if (isset($m['content']) && $m['content']) {
                $ret[] = $c['id'] . "," . $m['id'];
            }
        }
    }

    return export_property($ret);
}

function close_methods($json_spec)
{
    $ret = array();
    foreach ($json_spec['classes'] as $c) {
        foreach ($c['methods'] as $m) {
            if ($m['name'] == 'close') {
                $ret[] = $c['id'] . "," . $m['id'];
            }
        }
    }

    return export_property($ret);
}

function global_method_names($json_spec)
{
    $ret = array();
    foreach ($json_spec['classes'] as $c) {
        foreach ($c['methods'] as $m) {
            $ret[$c['id'] . "," . $m['id']] = ucfirst($c['name']) . '.' . to_snake_case($m['name']);
        }
    }

    return export_property($ret);
}

/**
 * @param string $type
 * @param string $variableName
 * @param string $returnType (optional)
 * @return string
 */
function get_type_phpdoc($type, $variableName = null, $returnType = null)
{
    $properties = "/**\n";
    $properties .= " * @var " . $type;

    if ($variableName != null) {
        $properties .= " " . $variableName;
    }
    $properties .= "\n";

    if ($returnType != null) {
        $properties .= " * @return " . $returnType . "\n";
    }

    $properties .= " */\n";

    return $properties;
}

$properties = get_type_phpdoc('string');
$properties .= 'public static $AMQP_PROTOCOL_HEADER = ' . protocol_header($json_spec) . ";\n\n";
$properties .= get_type_phpdoc('array');
$properties .= 'public static $FRAME_TYPES = ' . frame_types($json_spec) . ";\n\n";
$properties .= get_type_phpdoc('array');
$properties .= 'public static $CONTENT_METHODS = ' . content_methods($json_spec) . ";\n\n";
$properties .= get_type_phpdoc('array');
$properties .= 'public static $CLOSE_METHODS = ' . close_methods($json_spec) . ";\n\n";
$properties .= get_type_phpdoc('array');
$properties .= 'public static $GLOBAL_METHOD_NAMES = ' . global_method_names($json_spec) . ";\n";

$out = '<?php' . "\n\n";
$out .= '/* This file was autogenerated by spec/parser.php - Do not modify */' . "\n\n";
$out .= 'namespace PhpAmqpLib\Wire;' . "\n\n";
$out .= "class Constants" . protocol_version($json_spec) . "\n";
$out .= "{\n";
$out .= indent($properties);
$out .= "}\n";

file_put_contents(__DIR__ . '/../PhpAmqpLib/Wire/Constants' . protocol_version($json_spec) . '.php', $out);

function method_waits($json_spec)
{
    $ret = array();
    foreach ($json_spec['classes'] as $c) {
        foreach ($c['methods'] as $m) {
            $ret[$c['name'] . '.' . to_snake_case($m['name'])] = $c['id'] . "," . $m['id'];
        }
    }

    return export_property($ret);
}

$classBody = '';
$classBody .= get_type_phpdoc('array');
$classBody .= "protected \$wait = " . method_waits($json_spec) . ";\n\n";
$classBody .= get_type_phpdoc('string', '$method', 'string');
$classBody .= 'public function get_wait($method)' . "\n{\n";
$classBody .= indent('return $this->wait[$method];') . "\n";
$classBody .= "}";

$out = '<?php' . "\n\n";
$out .= '/* This file was autogenerated by spec/parser.php - Do not modify */' . "\n\n";
$out .= 'namespace PhpAmqpLib\Helper\Protocol;' . "\n\n";
$out .= "class Wait" . protocol_version($json_spec) . "\n";
$out .= "{\n";
$out .= indent($classBody) . "\n";
$out .= "}\n";

file_put_contents(__DIR__ . '/../PhpAmqpLib/Helper/Protocol/Wait' . protocol_version($json_spec) . '.php', $out);

function method_map($json_spec)
{
    $ret = array();

    $special_map = array(
        '60,30' => 'basic_cancel_from_server',
        '60,80' => 'basic_ack_from_server',
        '60,120' => 'basic_nack_from_server'
    );

    foreach ($json_spec['classes'] as $c) {
        foreach ($c['methods'] as $m) {
            if (isset($special_map[$c['id'] . ',' . $m['id']]) && protocol_version($json_spec) == '091') {
                $ret[$c['id'] . "," . $m['id']] = $special_map[$c['id'] . ',' . $m['id']];
            } else {
                $ret[$c['id'] . "," . $m['id']] = $c['name'] . '_' . to_snake_case($m['name']);
            }
        }
    }

    return export_property($ret);
}

$classBody = '';
$classBody .= get_type_phpdoc('array');
$classBody .= 'protected $method_map = ' . method_map($json_spec) . ";\n\n";
$classBody .= get_type_phpdoc('string', '$method_sig', 'string');
$classBody .= 'public function get_method($method_sig)' . "\n{\n";
$classBody .= indent('return $this->method_map[$method_sig];') . "\n";
$classBody .= "}\n\n";
$classBody .= get_type_phpdoc('string', '$method_sig', 'bool');
$classBody .= 'public function valid_method($method_sig)' . "\n{\n";
$classBody .= indent('return array_key_exists($method_sig, $this->method_map);') . "\n";
$classBody .= "}";

$out = '<?php' . "\n\n";
$out .= '/* This file was autogenerated by spec/parser.php - Do not modify */' . "\n\n";
$out .= 'namespace PhpAmqpLib\Helper\Protocol;' . "\n\n";
$out .= "class MethodMap" . protocol_version($json_spec) . "\n";
$out .= "{\n";
$out .= indent($classBody) . "\n";
$out .= "}\n";

file_put_contents(__DIR__ . '/../PhpAmqpLib/Helper/Protocol/MethodMap' . protocol_version($json_spec) . '.php', $out);
{
    "name": "AMQP",
    "major-version": 8,
    "minor-version": 0,
    "port": 5672,
    "copyright": [
        "Copyright (C) 2008-2012 VMware, Inc.\n",
        "\n",
        "Permission is hereby granted, free of charge, to any person\n",
        "obtaining a copy of this file (the \"Software\"), to deal in the\n",
        "Software without restriction, including without limitation the \n",
        "rights to use, copy, modify, merge, publish, distribute, \n",
        "sublicense, and/or sell copies of the Software, and to permit \n",
        "persons to whom the Software is furnished to do so, subject to \n",
        "the following conditions:\n",
        "\n",
        "The above copyright notice and this permission notice shall be\n",
        "included in all copies or substantial portions of the Software.\n",
        "\n",
        "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n",
        "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n",
        "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n",
        "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n",
        "HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n",
        "WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n",
        "OTHER DEALINGS IN THE SOFTWARE.\n",
        "\n",
        "Class information entered from amqp_xml0-8.pdf and domain types from amqp-xml-doc0-9.pdf\n",
        "\n",
        "b3cb053f15e7b98808c0ccc67f23cb3e  amqp_xml0-8.pdf\n",
        "http://www.twiststandards.org/index.php?option=com_docman&task=cat_view&gid=28&&Itemid=90\n",
        "8444db91e2949dbecfb2585e9eef6d64  amqp-xml-doc0-9.pdf\n",
        "https://jira.amqp.org/confluence/download/attachments/720900/amqp-xml-doc0-9.pdf?version=1\n"],

    "domains": [
        ["access-ticket", "short"],
        ["bit", "bit"],
        ["channel-id", "longstr"],
        ["class-id", "short"],
        ["consumer-tag", "shortstr"],
        ["delivery-tag", "longlong"],
        ["destination", "shortstr"],
        ["duration", "longlong"],
        ["exchange-name", "shortstr"],
        ["known-hosts", "shortstr"],
        ["long", "long"],
        ["longlong", "longlong"],
        ["longstr", "longstr"],
        ["method-id", "short"],
        ["no-ack", "bit"],
        ["no-local", "bit"],
        ["octet", "octet"],
        ["offset", "longlong"],
        ["path", "shortstr"],
        ["peer-properties", "table"],
        ["queue-name", "shortstr"],
        ["redelivered", "bit"],
        ["reference", "longstr"],
        ["reject-code", "short"],
        ["reject-text", "shortstr"],
        ["reply-code", "short"],
        ["reply-text", "shortstr"],
        ["security-token", "longstr"],
        ["short", "short"],
        ["shortstr", "shortstr"],
        ["table", "table"],
        ["timestamp", "timestamp"]
    ],

    "constants": [
        {"name": "FRAME-METHOD", "value": 1},
        {"name": "FRAME-HEADER", "value": 2},
        {"name": "FRAME-BODY", "value": 3},
        {"name": "FRAME-OOB-METHOD", "value": 4},
        {"name": "FRAME-OOB-HEADER", "value": 5},
        {"name": "FRAME-OOB-BODY", "value": 6},
        {"name": "FRAME-TRACE", "value": 7},
        {"name": "FRAME-HEARTBEAT", "value": 8},
        {"name": "FRAME-MIN-SIZE", "value": 4096},
        {"name": "FRAME-END", "value": 206},
        {"name": "REPLY-SUCCESS", "value": 200},
        {"name": "NOT-DELIVERED", "value": 310, "class": "soft-error"},
        {"name": "CONTENT-TOO-LARGE", "value": 311, "class": "soft-error"},
        {"name": "NO-ROUTE", "value": 312, "class": "soft-error"},
        {"name": "NO-CONSUMERS", "value": 313, "class": "soft-error"},
        {"name": "ACCESS-REFUSED", "value": 403, "class": "soft-error"},
        {"name": "NOT-FOUND", "value": 404, "class": "soft-error"},
        {"name": "RESOURCE-LOCKED", "value": 405, "class": "soft-error"},
        {"name": "PRECONDITION-FAILED", "value": 406, "class": "soft-error"},
        {"name": "CONNECTION-FORCED", "value": 320, "class": "hard-error"},
        {"name": "INVALID-PATH", "value": 402, "class": "hard-error"},
        {"name": "FRAME-ERROR", "value": 501, "class": "hard-error"},
        {"name": "SYNTAX-ERROR", "value": 502, "class": "hard-error"},
        {"name": "COMMAND-INVALID", "value": 503, "class": "hard-error"},
        {"name": "CHANNEL-ERROR", "value": 504, "class": "hard-error"},
        {"name": "UNEXPECTED-FRAME", "value": 505, "class": "hard-error"},
        {"name": "RESOURCE-ERROR", "value": 506, "class": "hard-error"},
        {"name": "NOT-ALLOWED", "value": 530, "class": "hard-error"},
        {"name": "NOT-IMPLEMENTED", "value": 540, "class": "hard-error"},
        {"name": "INTERNAL-ERROR", "value": 541, "class": "hard-error"}
    ],

    "classes": [
        {
            "id": 10,
            "methods": [{"id": 10,
                         "arguments": [{"type": "octet", "name": "version-major", "default-value": 0},
                                       {"type": "octet", "name": "version-minor", "default-value": 8},
                                       {"domain": "peer-properties", "name": "server-properties"},
                                       {"type": "longstr", "name": "mechanisms", "default-value": "PLAIN"},
                                       {"type": "longstr", "name": "locales", "default-value": "en_US"}],
                         "name": "start",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [{"domain": "peer-properties", "name": "client-properties"},
                                       {"type": "shortstr", "name": "mechanism", "default-value": "PLAIN"},
                                       {"type": "longstr", "name": "response"},
                                       {"type": "shortstr", "name": "locale", "default-value": "en_US"}],
                         "name": "start-ok"},
                        {"id": 20,
                         "arguments": [{"type": "longstr", "name": "challenge"}],
                         "name": "secure",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "longstr", "name": "response"}],
                         "name": "secure-ok"},
                        {"id": 30,
                         "arguments": [{"type": "short", "name": "channel-max", "default-value": 0},
                                       {"type": "long", "name": "frame-max", "default-value": 0},
                                       {"type": "short", "name": "heartbeat", "default-value": 0}],
                         "name": "tune",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "short", "name": "channel-max", "default-value": 0},
                                       {"type": "long", "name": "frame-max", "default-value": 0},
                                       {"type": "short", "name": "heartbeat", "default-value": 0}],
                         "name": "tune-ok"},
                        {"id": 40,
                         "arguments": [{"type": "shortstr", "name": "virtual-host", "default-value": "/"},
                                       {"type": "shortstr", "name": "capabilities", "default-value": ""},
                                       {"type": "bit", "name": "insist", "default-value": false}],
                         "name": "open",
                         "synchronous" : true},
                        {"id": 41,
                         "arguments": [{"type": "shortstr", "name": "known-hosts", "default-value": ""}],
                         "name": "open-ok"},
                        {"id": 50,
                         "arguments": [{"type": "shortstr", "name": "host"},
                                       {"type": "shortstr", "name": "known-hosts", "default-value": ""}],
                         "name": "redirect"},
                        {"id": 60,
                         "arguments": [{"type": "short", "name": "reply-code"},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "short", "name": "class-id"},
                                       {"type": "short", "name": "method-id"}],
                         "name": "close",
                         "synchronous" : true},
                        {"id": 61,
                         "arguments": [],
                         "name": "close-ok"}],
            "name": "connection",
            "properties": []
        },
        {
            "id": 20,
            "methods": [{"id": 10,
                         "arguments": [{"type": "shortstr", "name": "out-of-band", "default-value": ""}],
                         "name": "open",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "open-ok"},
                        {"id": 20,
                         "arguments": [{"type": "bit", "name": "active"}],
                         "name": "flow",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "bit", "name": "active"}],
                         "name": "flow-ok"},
                        {"id": 30,
                         "arguments": [{"type": "short", "name": "reply-code"},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "table", "name": "details", "default-value": {}}],
                         "name": "alert"},
                        {"id": 40,
                         "arguments": [{"type": "short", "name": "reply-code"},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "short", "name": "class-id"},
                                       {"type": "short", "name": "method-id"}],
                         "name": "close",
                         "synchronous" : true},
                        {"id": 41,
                         "arguments": [],
                         "name": "close-ok"}],
            "name": "channel"
        },
        {
            "id": 30,
            "methods": [{"id": 10,
                         "arguments": [{"type": "shortstr", "name": "realm", "default-value": "/data"},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "passive", "default-value": true},
                                       {"type": "bit", "name": "active", "default-value": true},
                                       {"type": "bit", "name": "write", "default-value": true},
                                       {"type": "bit", "name": "read", "default-value": true}],
                         "name": "request",
                         "synchronous" : true},
                        {"id": 11,
                    "arguments": [{"type": "short", "name": "ticket", "default-value": 1}],
                         "name": "request-ok"}],
            "name": "access"
        },
        {
            "id": 40,
            "methods": [{"id": 10,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "type", "default-value": "direct"},
                                       {"type": "bit", "name": "passive", "default-value": false},
                                       {"type": "bit", "name": "durable", "default-value": false},
                                       {"type": "bit", "name": "auto-delete", "default-value": false},
                                       {"type": "bit", "name": "internal", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "declare",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "declare-ok"},
                        {"id": 20,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "bit", "name": "if-unused", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "delete",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [],
                         "name": "delete-ok"}],
            "name": "exchange"
        },
        {
            "id": 50,
            "methods": [{"id": 10,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                           {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "passive", "default-value": false},
                                       {"type": "bit", "name": "durable", "default-value": false},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "auto-delete", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "declare",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [{"type": "shortstr", "name": "queue"},
                                       {"type": "long", "name": "message-count"},
                                       {"type": "long", "name": "consumer-count"}],
                         "name": "declare-ok"},
                        {"id": 20,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "bind",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [],
                         "name": "bind-ok"},
                        {"id": 30,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "purge",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "long", "name": "message-count"}],
                         "name": "purge-ok"},
                        {"id": 40,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "if-unused", "default-value": false},
                                       {"type": "bit", "name": "if-empty", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "delete",
                         "synchronous" : true},
                        {"id": 41,
                         "arguments": [{"type": "long", "name": "message-count"}],
                         "name": "delete-ok"},
            {"id": 50,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "unbind",
                         "synchronous" : true},
                        {"id": 51,
                         "arguments": [],
                         "name": "unbind-ok"}
                        ],
            "name": "queue"
        },
        {
            "id": 60,
            "methods": [{"id": 10,
                         "arguments": [{"type": "long", "name": "prefetch-size", "default-value": 0},
                                       {"type": "short", "name": "prefetch-count", "default-value": 0},
                                       {"type": "bit", "name": "global", "default-value": false}],
                         "name": "qos",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "qos-ok"},
                        {"id": 20,
                         "arguments": [{"domain": "access-ticket", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "consumer-tag", "default-value": ""},
                                       {"type": "bit", "name": "no-local", "default-value": false},
                                       {"type": "bit", "name": "no-ack", "default-value": false},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "consume",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "consume-ok"},
                        {"id": 30,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "cancel",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "cancel-ok"},
                        {"content": true,
                         "id": 40,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "exchange", "default-value": ""},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "mandatory", "default-value": false},
                                       {"type": "bit", "name": "immediate", "default-value": false}],
                         "name": "publish"},
                        {"content": true,
                         "id": 50,
                         "arguments": [{"type": "short", "name": "reply-code"},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"}],
                         "name": "return"},
                        {"content": true,
                         "id": 60,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "redelivered", "default-value": false},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"}],
                         "name": "deliver"},
                        {"id": 70,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "no-ack", "default-value": false}],
                         "name": "get",
                         "synchronous" : true},
                        {"content": true,
                         "id": 71,
                         "arguments": [{"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "redelivered", "default-value": false},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"},
                                       {"type": "long", "name": "message-count"}],
                         "name": "get-ok"},
                        {"id": 72,
                         "arguments": [{"type": "shortstr", "name": "cluster-id", "default-value": ""}],
                         "name": "get-empty"},
                        {"id": 80,
                         "arguments": [{"type": "longlong", "name": "delivery-tag", "default-value": 0},
                                       {"type": "bit", "name": "multiple", "default-value": false}],
                         "name": "ack"},
                        {"id": 90,
                         "arguments": [{"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "requeue", "default-value": true}],
                         "name": "reject"},
                        {"id": 100,
                         "arguments": [{"type": "bit", "name": "requeue", "default-value": false}],
                         "name": "recover-async"},
            {"id": 110,
                         "arguments": [{"type": "bit", "name": "requeue", "default-value": false}],
                         "name": "recover",
                         "synchronous" : true},
                        {"id": 111,
                         "arguments": [],
                         "name": "recover-ok"}],
            "name": "basic",
            "properties": [{"type": "shortstr", "name": "content-type"},
                           {"type": "shortstr", "name": "content-encoding"},
                           {"type": "table", "name": "headers"},
                           {"type": "octet", "name": "delivery-mode"},
                           {"type": "octet", "name": "priority"},
                           {"type": "shortstr", "name": "correlation-id"},
                           {"type": "shortstr", "name": "reply-to"},
                           {"type": "shortstr", "name": "expiration"},
                           {"type": "shortstr", "name": "message-id"},
                           {"type": "timestamp", "name": "timestamp"},
                           {"type": "shortstr", "name": "type"},
                           {"type": "shortstr", "name": "user-id"},
                           {"type": "shortstr", "name": "app-id"},
                           {"type": "shortstr", "name": "cluster-id"}]
        },
        {
            "id": 70,
            "methods": [{"id": 10,
                         "arguments": [{"type": "long", "name": "prefetch-size", "default-value": 0},
                                       {"type": "short", "name": "prefetch-count", "default-value": 0},
                                       {"type": "bit", "name": "global", "default-value": false}],
                         "name": "qos",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "qos-ok"},
                        {"id": 20,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "consumer-tag", "default-value": ""},
                                       {"type": "bit", "name": "no-local", "default-value": false},
                                       {"type": "bit", "name": "no-ack", "default-value": false},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "consume",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "consume-ok"},
                        {"id": 30,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "cancel",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "cancel-ok"},
                        {"id": 40,
                         "arguments": [{"type": "shortstr", "name": "identifier"},
                                       {"type": "longlong", "name": "content-size"}],
                         "name": "open",
                         "synchronous" : true},
                        {"id": 41,
                         "arguments": [{"type": "longlong", "name": "staged-size"}],
                         "name": "open-ok"},
                        {"content": true,
                         "id": 50,
                         "arguments": [],
                         "name": "stage"},
                        {"id": 60,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "exchange", "default-value": ""},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "mandatory", "default-value": false},
                                       {"type": "bit", "name": "immediate", "default-value": false},
                                       {"type": "shortstr", "name": "identifier"}],
                         "name": "publish"},
                        {"content": true,
                         "id": 70,
                         "arguments": [{"type": "short", "name": "reply-code", "default-value": 200},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"}],
                         "name": "return"},
                        {"id": 80,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "redelivered", "default-value": false},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"},
                                       {"type": "shortstr", "name": "identifier"}],
                         "name": "deliver"},
                        {"id": 90,
                         "arguments": [{"type": "longlong", "name": "delivery-tag", "default-value": 0},
                           {"type": "bit", "name": "multiple", "default-value": false}],
                         "name": "ack"},
                        {"id": 100,
                         "arguments": [{"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "requeue", "default-value": true}],
                         "name": "reject"}],
            "name": "file",
            "properties": [{"type": "shortstr", "name": "content-type"},
                           {"type": "shortstr", "name": "content-encoding"},
                           {"type": "table", "name": "headers"},
                           {"type": "octet", "name": "priority"},
                           {"type": "shortstr", "name": "reply-to"},
                           {"type": "shortstr", "name": "message-id"},
                           {"type": "shortstr", "name": "filename"},
                           {"type": "timestamp", "name": "timestamp"},
                           {"type": "shortstr", "name": "cluster-id"}]
        },
        {
            "id": 80,
            "methods": [{"id": 10,
                         "arguments": [{"type": "long", "name": "prefetch-size", "default-value": 0},
                                       {"type": "short", "name": "prefetch-count", "default-value": 0},
                                       {"type": "long", "name": "consume-rate", "default-value": 0},
                                       {"type": "bit", "name": "global", "default-value": false}],
                         "name": "qos",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "qos-ok"},
                        {"id": 20,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "consumer-tag", "default-value": ""},
                                       {"type": "bit", "name": "no-local", "default-value": false},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "consume",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "consume-ok"},
                        {"id": 30,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "cancel",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "cancel-ok"},
                        {"content": true,
                         "id": 40,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 1},
                                       {"type": "shortstr", "name": "exchange", "default-value": ""},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "mandatory", "default-value": false},
                                       {"type": "bit", "name": "immediate", "default-value": false}],
                         "name": "publish"},
                        {"content": true,
                         "id": 50,
                         "arguments": [{"type": "short", "name": "reply-code", "default-value": 200},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"}],
                         "name": "return"},
                        {"content": true,
                         "id": 60,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "longlong", "name": "delivery-tag"},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "queue"}],
                         "name": "deliver"}],
            "name": "stream",
            "properties": [{"type": "shortstr", "name": "content-type"},
                           {"type": "shortstr", "name": "content-encoding"},
                           {"type": "table", "name": "headers"},
                           {"type": "octet", "name": "priority"},
                           {"type": "timestamp", "name": "timestamp"}]
        },
        {
            "id": 90,
            "methods": [{"id": 10,
                         "arguments": [],
                         "name": "select",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "select-ok"},
                        {"id": 20,
                         "arguments": [],
                         "name": "commit",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [],
                         "name": "commit-ok"},
                        {"id": 30,
                         "arguments": [],
                         "name": "rollback",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [],
                         "name": "rollback-ok"}],
            "name": "tx"
        },
        {
            "id": 100,
            "methods": [{"id": 10,
                         "arguments": [],
                         "name": "select",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "select-ok"},
                        {"id": 20,
                         "arguments": [{"type": "shortstr", "name": "dtx-identifier"}],
                         "name": "start",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [], "name": "start-ok"}],
            "name": "dtx"
        },
        {
            "id": 110,
            "methods": [{"content": true,
                         "id": 10,
                         "arguments": [{"type": "table", "name": "meta-data"}],
                         "name": "request"}],
            "name": "tunnel",
            "properties": [{"type": "table", "name": "headers"},
                           {"type": "shortstr", "name": "proxy-name"},
                           {"type": "shortstr", "name": "data-name"},
                           {"type": "octet", "name": "durable"},
                           {"type": "octet", "name": "broadcast"}]
        },
        {
            "id": 120,
            "methods": [{"id": 10,
                         "arguments": [{"type": "octet", "name": "integer-1"},
                                       {"type": "short", "name": "integer-2"},
                                       {"type": "long", "name": "integer-3"},
                                       {"type": "longlong", "name": "integer-4"},
                                       {"type": "octet", "name": "operation"}],
                         "name": "integer",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [{"type": "longlong", "name": "result"}],
                         "name": "integer-ok"},
                        {"id": 20,
                         "arguments": [{"type": "shortstr", "name": "string-1"},
                                       {"type": "longstr", "name": "string-2"},
                                       {"type": "octet", "name": "operation"}],
                         "name": "string",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "longstr", "name": "result"}],
                         "name": "string-ok"},
                        {"id": 30,
                         "arguments": [{"type": "table", "name": "table"},
                                       {"type": "octet", "name": "integer-op"},
                                       {"type": "octet", "name": "string-op"}],
                         "name": "table",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "longlong", "name": "integer-result"},
                                       {"type": "longstr", "name": "string-result"}],
                         "name": "table-ok"},
                        {"content": true,
                         "id": 40,
                         "arguments": [],
                         "name": "content",
                         "synchronous" : true},
                        {"content": true,
                         "id": 41,
                         "arguments": [{"type": "long", "name": "content-checksum"}],
                         "name": "content-ok"}],
            "name": "test"
        }
    ]
}
{
    "name": "AMQP",
    "major-version": 0,
    "minor-version": 9,
    "revision": 1,
    "port": 5672,
    "copyright": [
        "Copyright (C) 2008-2012 VMware, Inc.\n",
        "\n",
        "Permission is hereby granted, free of charge, to any person\n",
        "obtaining a copy of this file (the \"Software\"), to deal in the\n",
        "Software without restriction, including without limitation the \n",
        "rights to use, copy, modify, merge, publish, distribute, \n",
        "sublicense, and/or sell copies of the Software, and to permit \n",
        "persons to whom the Software is furnished to do so, subject to \n",
        "the following conditions:\n",
        "\n",
        "The above copyright notice and this permission notice shall be\n",
        "included in all copies or substantial portions of the Software.\n",
        "\n",
        "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n",
        "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n",
        "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n",
        "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n",
        "HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n",
        "WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n",
        "OTHER DEALINGS IN THE SOFTWARE.\n",
        "\n",
        "Class information entered from amqp_xml0-8.pdf and domain types from amqp-xml-doc0-9.pdf\n",
        "Updated for 0-9-1 by Tony Garnock-Jones\n",
        "\n",
        "b3cb053f15e7b98808c0ccc67f23cb3e  amqp_xml0-8.pdf\n",
        "http://www.twiststandards.org/index.php?option=com_docman&task=cat_view&gid=28&&Itemid=90\n",
        "8444db91e2949dbecfb2585e9eef6d64  amqp-xml-doc0-9.pdf\n",
        "https://jira.amqp.org/confluence/download/attachments/720900/amqp-xml-doc0-9.pdf?version=1\n"],

    "domains": [
        ["bit", "bit"],
        ["channel-id", "longstr"],
        ["class-id", "short"],
        ["consumer-tag", "shortstr"],
        ["delivery-tag", "longlong"],
        ["destination", "shortstr"],
        ["duration", "longlong"],
        ["exchange-name", "shortstr"],
        ["long", "long"],
        ["longlong", "longlong"],
        ["longstr", "longstr"],
        ["method-id", "short"],
        ["no-ack", "bit"],
        ["no-local", "bit"],
        ["octet", "octet"],
        ["offset", "longlong"],
        ["path", "shortstr"],
        ["peer-properties", "table"],
        ["queue-name", "shortstr"],
        ["redelivered", "bit"],
        ["reference", "longstr"],
        ["reject-code", "short"],
        ["reject-text", "shortstr"],
        ["reply-code", "short"],
        ["reply-text", "shortstr"],
        ["security-token", "longstr"],
        ["short", "short"],
        ["shortstr", "shortstr"],
        ["table", "table"],
        ["timestamp", "timestamp"]
    ],

    "constants": [
        {"name": "FRAME-METHOD", "value": 1},
        {"name": "FRAME-HEADER", "value": 2},
        {"name": "FRAME-BODY", "value": 3},
        {"name": "FRAME-HEARTBEAT", "value": 8},
        {"name": "FRAME-MIN-SIZE", "value": 4096},
        {"name": "FRAME-END", "value": 206},
        {"name": "REPLY-SUCCESS", "value": 200},
        {"name": "CONTENT-TOO-LARGE", "value": 311, "class": "soft-error"},
        {"name": "NO-ROUTE", "value": 312, "class": "soft-error"},
        {"name": "NO-CONSUMERS", "value": 313, "class": "soft-error"},
        {"name": "ACCESS-REFUSED", "value": 403, "class": "soft-error"},
        {"name": "NOT-FOUND", "value": 404, "class": "soft-error"},
        {"name": "RESOURCE-LOCKED", "value": 405, "class": "soft-error"},
        {"name": "PRECONDITION-FAILED", "value": 406, "class": "soft-error"},
        {"name": "CONNECTION-FORCED", "value": 320, "class": "hard-error"},
        {"name": "INVALID-PATH", "value": 402, "class": "hard-error"},
        {"name": "FRAME-ERROR", "value": 501, "class": "hard-error"},
        {"name": "SYNTAX-ERROR", "value": 502, "class": "hard-error"},
        {"name": "COMMAND-INVALID", "value": 503, "class": "hard-error"},
        {"name": "CHANNEL-ERROR", "value": 504, "class": "hard-error"},
        {"name": "UNEXPECTED-FRAME", "value": 505, "class": "hard-error"},
        {"name": "RESOURCE-ERROR", "value": 506, "class": "hard-error"},
        {"name": "NOT-ALLOWED", "value": 530, "class": "hard-error"},
        {"name": "NOT-IMPLEMENTED", "value": 540, "class": "hard-error"},
        {"name": "INTERNAL-ERROR", "value": 541, "class": "hard-error"}
    ],

    "classes": [
        {
            "id": 10,
            "methods": [{"id": 10,
                         "arguments": [{"type": "octet", "name": "version-major", "default-value": 0},
                                       {"type": "octet", "name": "version-minor", "default-value": 9},
                                       {"domain": "peer-properties", "name": "server-properties"},
                                       {"type": "longstr", "name": "mechanisms", "default-value": "PLAIN"},
                                       {"type": "longstr", "name": "locales", "default-value": "en_US"}],
                         "name": "start",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [{"domain": "peer-properties", "name": "client-properties"},
                                       {"type": "shortstr", "name": "mechanism", "default-value": "PLAIN"},
                                       {"type": "longstr", "name": "response"},
                                       {"type": "shortstr", "name": "locale", "default-value": "en_US"}],
                         "name": "start-ok"},
                        {"id": 20,
                         "arguments": [{"type": "longstr", "name": "challenge"}],
                         "name": "secure",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "longstr", "name": "response"}],
                         "name": "secure-ok"},
                        {"id": 30,
                         "arguments": [{"type": "short", "name": "channel-max", "default-value": 0},
                                       {"type": "long", "name": "frame-max", "default-value": 0},
                                       {"type": "short", "name": "heartbeat", "default-value": 0}],
                         "name": "tune",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "short", "name": "channel-max", "default-value": 0},
                                       {"type": "long", "name": "frame-max", "default-value": 0},
                                       {"type": "short", "name": "heartbeat", "default-value": 0}],
                         "name": "tune-ok"},
                        {"id": 40,
                         "arguments": [{"type": "shortstr", "name": "virtual-host", "default-value": "/"},
                                       {"type": "shortstr", "name": "capabilities", "default-value": ""},
                                       {"type": "bit", "name": "insist", "default-value": false}],
                         "name": "open",
                         "synchronous" : true},
                        {"id": 41,
                         "arguments": [{"type": "shortstr", "name": "known-hosts", "default-value": ""}],
                         "name": "open-ok"},
                        {"id": 50,
                         "arguments": [{"type": "short", "name": "reply-code"},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "short", "name": "class-id"},
                                       {"type": "short", "name": "method-id"}],
                         "name": "close",
                         "synchronous" : true},
                        {"id": 51,
                         "arguments": [],
                         "name": "close-ok"},
                        {"id": 60,
                         "arguments": [{"type": "shortstr", "name": "reason", "default-value": ""}],
                         "name": "blocked"},
                        {"id": 61,
                         "arguments": [],
                         "name": "unblocked"}],
            "name": "connection",
            "properties": []
        },
        {
            "id": 20,
            "methods": [{"id": 10,
                         "arguments": [{"type": "shortstr", "name": "out-of-band", "default-value": ""}],
                         "name": "open",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [{"type": "longstr", "name": "channel-id", "default-value": ""}],
                         "name": "open-ok"},
                        {"id": 20,
                         "arguments": [{"type": "bit", "name": "active"}],
                         "name": "flow",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "bit", "name": "active"}],
                         "name": "flow-ok"},
                        {"id": 40,
                         "arguments": [{"type": "short", "name": "reply-code"},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "short", "name": "class-id"},
                                       {"type": "short", "name": "method-id"}],
                         "name": "close",
                         "synchronous" : true},
                        {"id": 41,
                         "arguments": [],
                         "name": "close-ok"}],
            "name": "channel"
        },
        {
            "id": 30,
            "methods": [{"id": 10,
                         "arguments": [{"type": "shortstr", "name": "realm", "default-value": "/data"},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "passive", "default-value": true},
                                       {"type": "bit", "name": "active", "default-value": true},
                                       {"type": "bit", "name": "write", "default-value": true},
                                       {"type": "bit", "name": "read", "default-value": true}],
                         "name": "request",
                         "synchronous" : true},
                        {"id": 11,
                    "arguments": [{"type": "short", "name": "ticket", "default-value": 1}],
                         "name": "request-ok"}],
            "name": "access"
        },
        {
            "id": 40,
            "methods": [{"id": 10,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "type", "default-value": "direct"},
                                       {"type": "bit", "name": "passive", "default-value": false},
                                       {"type": "bit", "name": "durable", "default-value": false},
                                       {"type": "bit", "name": "auto-delete", "default-value": false},
                                       {"type": "bit", "name": "internal", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "declare",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "declare-ok"},
                        {"id": 20,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "bit", "name": "if-unused", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "delete",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [],
                         "name": "delete-ok"},
                        {"id": 30,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "destination"},
                                       {"type": "shortstr", "name": "source"},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "bind",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [],
                         "name": "bind-ok"},
                        {"id": 40,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "destination"},
                                       {"type": "shortstr", "name": "source"},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "unbind",
                         "synchronous" : true},
                        {"id": 51,
                         "arguments": [],
                         "name": "unbind-ok"}],
            "name": "exchange"
        },
        {
            "id": 50,
            "methods": [{"id": 10,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                           {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "passive", "default-value": false},
                                       {"type": "bit", "name": "durable", "default-value": false},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "auto-delete", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "declare",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [{"type": "shortstr", "name": "queue"},
                                       {"type": "long", "name": "message-count"},
                                       {"type": "long", "name": "consumer-count"}],
                         "name": "declare-ok"},
                        {"id": 20,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "bind",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [],
                         "name": "bind-ok"},
                        {"id": 30,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "purge",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "long", "name": "message-count"}],
                         "name": "purge-ok"},
                        {"id": 40,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "if-unused", "default-value": false},
                                       {"type": "bit", "name": "if-empty", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "delete",
                         "synchronous" : true},
                        {"id": 41,
                         "arguments": [{"type": "long", "name": "message-count"}],
                         "name": "delete-ok"},
                        {"id": 50,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "unbind",
                         "synchronous" : true},
                        {"id": 51,
                         "arguments": [],
                         "name": "unbind-ok"}
                        ],
            "name": "queue"
        },
        {
            "id": 60,
            "methods": [{"id": 10,
                         "arguments": [{"type": "long", "name": "prefetch-size", "default-value": 0},
                                       {"type": "short", "name": "prefetch-count", "default-value": 0},
                                       {"type": "bit", "name": "global", "default-value": false}],
                         "name": "qos",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "qos-ok"},
                        {"id": 20,
                         "arguments": [{"domain": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "shortstr", "name": "consumer-tag", "default-value": ""},
                                       {"type": "bit", "name": "no-local", "default-value": false},
                                       {"type": "bit", "name": "no-ack", "default-value": false},
                                       {"type": "bit", "name": "exclusive", "default-value": false},
                                       {"type": "bit", "name": "nowait", "default-value": false},
                                       {"type": "table", "name": "arguments", "default-value": {}}],
                         "name": "consume",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "consume-ok"},
                        {"id": 30,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "cancel",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
                         "name": "cancel-ok"},
                        {"content": true,
                         "id": 40,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "exchange", "default-value": ""},
                                       {"type": "shortstr", "name": "routing-key", "default-value": ""},
                                       {"type": "bit", "name": "mandatory", "default-value": false},
                                       {"type": "bit", "name": "immediate", "default-value": false}],
                         "name": "publish"},
                        {"content": true,
                         "id": 50,
                         "arguments": [{"type": "short", "name": "reply-code"},
                                       {"type": "shortstr", "name": "reply-text", "default-value": ""},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"}],
                         "name": "return"},
                        {"content": true,
                         "id": 60,
                         "arguments": [{"type": "shortstr", "name": "consumer-tag"},
                                       {"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "redelivered", "default-value": false},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"}],
                         "name": "deliver"},
                        {"id": 70,
                         "arguments": [{"type": "short", "name": "ticket", "default-value": 0},
                                       {"type": "shortstr", "name": "queue", "default-value": ""},
                                       {"type": "bit", "name": "no-ack", "default-value": false}],
                         "name": "get",
                         "synchronous" : true},
                        {"content": true,
                         "id": 71,
                         "arguments": [{"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "redelivered", "default-value": false},
                                       {"type": "shortstr", "name": "exchange"},
                                       {"type": "shortstr", "name": "routing-key"},
                                       {"type": "long", "name": "message-count"}],
                         "name": "get-ok"},
                        {"id": 72,
                         "arguments": [{"type": "shortstr", "name": "cluster-id", "default-value": ""}],
                         "name": "get-empty"},
                        {"id": 80,
                         "arguments": [{"type": "longlong", "name": "delivery-tag", "default-value": 0},
                                       {"type": "bit", "name": "multiple", "default-value": false}],
                         "name": "ack"},
                        {"id": 90,
                         "arguments": [{"type": "longlong", "name": "delivery-tag"},
                                       {"type": "bit", "name": "requeue", "default-value": true}],
                         "name": "reject"},
                        {"id": 100,
                         "arguments": [{"type": "bit", "name": "requeue", "default-value": false}],
                         "name": "recover-async"},
                        {"id": 110,
                         "arguments": [{"type": "bit", "name": "requeue", "default-value": false}],
                         "name": "recover",
                         "synchronous" : true},
                        {"id": 111,
                         "arguments": [],
                         "name": "recover-ok"},
                        {"id": 120,
                         "arguments": [{"type": "longlong", "name": "delivery-tag", "default-value": 0},
                                       {"type": "bit", "name": "multiple", "default-value": false},
                                       {"type": "bit", "name": "requeue", "default-value": true}],
                         "name": "nack"}],
            "name": "basic",
            "properties": [{"type": "shortstr", "name": "content-type"},
                           {"type": "shortstr", "name": "content-encoding"},
                           {"type": "table", "name": "headers"},
                           {"type": "octet", "name": "delivery-mode"},
                           {"type": "octet", "name": "priority"},
                           {"type": "shortstr", "name": "correlation-id"},
                           {"type": "shortstr", "name": "reply-to"},
                           {"type": "shortstr", "name": "expiration"},
                           {"type": "shortstr", "name": "message-id"},
                           {"type": "timestamp", "name": "timestamp"},
                           {"type": "shortstr", "name": "type"},
                           {"type": "shortstr", "name": "user-id"},
                           {"type": "shortstr", "name": "app-id"},
                           {"type": "shortstr", "name": "cluster-id"}]
        },
        {
            "id": 90,
            "methods": [{"id": 10,
                         "arguments": [],
                         "name": "select",
                         "synchronous" : true},
                        {"id": 11,
                         "arguments": [],
                         "name": "select-ok"},
                        {"id": 20,
                         "arguments": [],
                         "name": "commit",
                         "synchronous" : true},
                        {"id": 21,
                         "arguments": [],
                         "name": "commit-ok"},
                        {"id": 30,
                         "arguments": [],
                         "name": "rollback",
                         "synchronous" : true},
                        {"id": 31,
                         "arguments": [],
                         "name": "rollback-ok"}],
            "name": "tx"
        },
        {
            "id": 85,
            "methods": [{"id": 10,
                         "arguments": [
                             {"type": "bit", "name": "nowait", "default-value": false}],
                         "name": "select",
                         "synchronous": true},
                        {"id": 11,
                         "arguments": [],
                         "name": "select-ok"}],
            "name": "confirm"
        }
    ]
}
<?php
namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception\AMQPOutOfBoundsException;

/**
 * AMQP protocol decimal value.
 *
 * Values are represented as (n,e) pairs. The actual value
 * is n * 10^(-e).
 *
 * From 0.8 spec: Decimal values are
 * not intended to support floating point values, but rather
 * business values such as currency rates and amounts. The
 * 'decimals' octet is not signed.
 */
class AMQPDecimal
{
    /** @var int */
    protected $n;

    /** @var int */
    protected $e;

    /**
     * @param int $n
     * @param int $e
     * @throws \PhpAmqpLib\Exception\AMQPOutOfBoundsException
     */
    public function __construct($n, $e)
    {
        if ($e < 0) {
            throw new AMQPOutOfBoundsException('Decimal exponent value must be unsigned!');
        }

        $this->n = $n;
        $this->e = $e;
    }

    /**
     * @return string
     */
    public function asBCvalue()
    {
        return bcdiv($this->n, bcpow(10, $this->e));
    }

    /**
     * @return int
     */
    public function getE()
    {
        return $this->e;
    }

    /**
     * @return int
     */
    public function getN()
    {
        return $this->n;
    }
}
<?php
namespace PhpAmqpLib\Wire;


class AbstractClient
{

    /**
     * @var bool
     */
    protected $is64bits;

    /**
     * @var bool
     */
    protected $isLittleEndian;

    public function __construct()
    {
        $this->is64bits = PHP_INT_SIZE == 8;

        $tmp = unpack('S', "\x01\x00"); // to maintain 5.3 compatibility
        $this->isLittleEndian = $tmp[1] == 1;
    }

    /**
     * Converts byte-string between native and network byte order, in both directions
     *
     * @param string $byteStr
     * @return string
     */
    protected function correctEndianness($byteStr)
    {
        return $this->isLittleEndian ? strrev($byteStr) : $byteStr;
    }
}
<?php
namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception;


class AMQPTable extends AMQPAbstractCollection
{

    /**
     * @return int
     */
    final public function getType()
    {
        return self::T_TABLE;
    }

    /**
     * @param string $key
     * @param mixed $val
     * @param int|null $type
     */
    public function set($key, $val, $type = null)
    {
        //https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf, https://www.rabbitmq.com/resources/specs/amqp0-8.pdf
        //Field names MUST start with a letter, '$' or '#' and may continue with letters, '$' or '#', digits, or underlines, to a maximum length of 128 characters
        //The server SHOULD validate field names and upon receiving an invalid field name, it SHOULD signal a connection exception with reply code 503 (syntax error)

        //validating length only and delegating other stuff to server, as rabbit seems to currently support numeric keys
        if (!($len = strlen($key)) || ($len > 128)) {
            throw new Exception\AMQPInvalidArgumentException('Table key must be non-empty string up to 128 chars in length');
        }
        $this->setValue($val, $type, $key);
    }
}
<?php
namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Exception\AMQPIOWaitException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Wire\IO\AbstractIO;

/**
 * This class can read from a string or from a stream
 *
 * TODO : split this class: AMQPStreamReader and a AMQPBufferReader
 */
class AMQPReader extends AbstractClient
{
    const BIT = 1;
    const OCTET = 1;
    const SHORTSTR = 1;
    const SHORT = 2;
    const LONG = 4;
    const SIGNED_LONG = 4;
    const READ_PHP_INT = 4; // use READ_ to avoid possible clashes with PHP
    const LONGLONG = 8;
    const TIMESTAMP = 8;

    /** @var string */
    protected $str;

    /** @var int */
    protected $str_length;

    /** @var int */
    protected $offset;

    /** @var int */
    protected $bitcount;

    /** @var bool */
    protected $is64bits;

    /** @var int */
    protected $timeout;

    /** @var int */
    protected $bits;

    /** @var \PhpAmqpLib\Wire\IO\AbstractIO */
    protected $io;

    /**
     * @param string $str
     * @param AbstractIO $io
     * @param int $timeout
     */
    public function __construct($str, AbstractIO $io = null, $timeout = 0)
    {
        parent::__construct();

        $this->str = is_string($str) ? $str : '';
        $this->str_length = mb_strlen($this->str, 'ASCII');
        $this->io = $io;
        $this->offset = 0;
        $this->bitcount = $this->bits = 0;
        $this->timeout = $timeout;
    }

    /**
     * Resets the object from the injected param
     *
     * Used to not need to create a new AMQPReader instance every time.
     * when we can just pass a string and reset the object state.
     * NOTE: since we are working with strings we don't need to pass an AbstractIO
     *       or a timeout.
     *
     * @param string $str
     */
    public function reuse($str)
    {
        $this->str = $str;
        $this->str_length = mb_strlen($this->str, 'ASCII');
        $this->offset = 0;
        $this->bitcount = $this->bits = 0;
    }

    /**
     * Closes the stream
     */
    public function close()
    {
        if ($this->io) {
            $this->io->close();
        }
    }

    /**
     * @param int $n
     * @return string
     */
    public function read($n)
    {
        $this->bitcount = $this->bits = 0;

        return $this->rawread($n);
    }

    /**
     * Waits until some data is retrieved from the socket.
     *
     * AMQPTimeoutException can be raised if the timeout is set
     *
     * @throws \PhpAmqpLib\Exception\AMQPIOWaitException
     * @throws \PhpAmqpLib\Exception\AMQPTimeoutException
     */
    protected function wait()
    {
        if ($this->getTimeout() == 0) {
            return null;
        }

        // wait ..
        list($sec, $usec) = MiscHelper::splitSecondsMicroseconds($this->getTimeout());
        $result = $this->io->select($sec, $usec);

        if ($result === false) {
            throw new AMQPIOWaitException('A network error occured while awaiting for incoming data');
        }

        if ($result === 0) {
            throw new AMQPTimeoutException(sprintf(
                'The connection timed out after %s sec while awaiting incoming data',
                $this->getTimeout()
            ));
        }
    }

    /**
     * @param int $n
     * @return string
     * @throws \RuntimeException
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    protected function rawread($n)
    {
        if ($this->io) {
            $this->wait();
            $res = $this->io->read($n);
            $this->offset += $n;

            return $res;
        }

        if ($this->str_length < $n) {
            throw new AMQPRuntimeException(sprintf(
                'Error reading data. Requested %s bytes while string buffer has only %s',
                $n,
                $this->str_length
            ));
        }

        $res = mb_substr($this->str, 0, $n, 'ASCII');
        $this->str = mb_substr($this->str, $n, mb_strlen($this->str, 'ASCII') - $n, 'ASCII');
        $this->str_length -= $n;
        $this->offset += $n;

        return $res;
    }

    /**
     * @return bool
     */
    public function read_bit()
    {
        if (empty($this->bitcount)) {
            $this->bits = ord($this->rawread(1));
            $this->bitcount = 8;
        }

        $result = ($this->bits & 1) == 1;
        $this->bits >>= 1;
        $this->bitcount -= 1;

        return $result;
    }

    /**
     * @return mixed
     */
    public function read_octet()
    {
        $this->bitcount = $this->bits = 0;
        list(, $res) = unpack('C', $this->rawread(1));

        return $res;
    }

    /**
     * @return mixed
     */
    public function read_signed_octet()
    {
        $this->bitcount = $this->bits = 0;
        list(, $res) = unpack('c', $this->rawread(1));

        return $res;
    }

    /**
     * @return mixed
     */
    public function read_short()
    {
        $this->bitcount = $this->bits = 0;
        list(, $res) = unpack('n', $this->rawread(2));

        return $res;
    }

    /**
     * @return mixed
     */
    public function read_signed_short()
    {
        $this->bitcount = $this->bits = 0;
        list(, $res) = unpack('s', $this->correctEndianness($this->rawread(2)));

        return $res;
    }

    /**
     * Reads 32 bit integer in big-endian byte order.
     *
     * On 64 bit systems it will return always unsigned int
     * value in 0..2^32 range.
     *
     * On 32 bit systems it will return signed int value in
     * -2^31...+2^31 range.
     *
     * Use with caution!
     */
    public function read_php_int()
    {
        list(, $res) = unpack('N', $this->rawread(4));

        if ($this->is64bits) {
            return (int) sprintf('%u', $res);
        }

        return $res;
    }

    /**
     * PHP does not have unsigned 32 bit int,
     * so we return it as a string
     *
     * @return string
     */
    public function read_long()
    {
        $this->bitcount = $this->bits = 0;
        list(, $res) = unpack('N', $this->rawread(4));

        return empty($this->is64bits) && self::getLongMSB($res) ? sprintf('%u', $res) : $res;
    }

    /**
     * @return integer
     */
    private function read_signed_long()
    {
        $this->bitcount = $this->bits = 0;
        list(, $res) = unpack('l', $this->correctEndianness($this->rawread(4)));

        return $res;
    }

    /**
     * Even on 64 bit systems PHP integers are singed.
     * Since we need an unsigned value here we return it
     * as a string.
     *
     * @return string
     */
    public function read_longlong()
    {
        $this->bitcount = $this->bits = 0;

        list(, $hi, $lo) = unpack('N2', $this->rawread(8));
        $msb = self::getLongMSB($hi);

        if (empty($this->is64bits)) {
            if ($msb) {
                $hi = sprintf('%u', $hi);
            }
            if (self::getLongMSB($lo)) {
                $lo = sprintf('%u', $lo);
            }
        }

        return bcadd($this->is64bits && !$msb ? $hi << 32 : bcmul($hi, '4294967296', 0), $lo, 0);
    }

    /**
     * @return string
     */
    public function read_signed_longlong()
    {
        $this->bitcount = $this->bits = 0;

        list(, $hi, $lo) = unpack('N2', $this->rawread(8));

        if ($this->is64bits) {
            return bcadd($hi << 32, $lo, 0);
        } else {
            return bcadd(bcmul($hi, '4294967296', 0), self::getLongMSB($lo) ? sprintf('%u', $lo) : $lo, 0);
        }
    }

    /**
     * @param int $longInt
     * @return bool
     */
    private static function getLongMSB($longInt)
    {
        return (bool) ($longInt & 0x80000000);
    }

    /**
     * Read a utf-8 encoded string that's stored in up to
     * 255 bytes.  Return it decoded as a PHP unicode object.
     */
    public function read_shortstr()
    {
        $this->bitcount = $this->bits = 0;
        list(, $slen) = unpack('C', $this->rawread(1));

        return $this->rawread($slen);
    }

    /**
     * Read a string that's up to 2**32 bytes, the encoding
     * isn't specified in the AMQP spec, so just return it as
     * a plain PHP string.
     */
    public function read_longstr()
    {
        $this->bitcount = $this->bits = 0;
        $slen = $this->read_php_int();

        if ($slen < 0) {
            throw new AMQPOutOfBoundsException('Strings longer than supported on this platform');
        }

        return $this->rawread($slen);
    }

    /**
     * Read and AMQP timestamp, which is a 64-bit integer representing
     * seconds since the Unix epoch in 1-second resolution.
     */
    public function read_timestamp()
    {
        return $this->read_longlong();
    }

    /**
     * Read an AMQP table, and return as a PHP array. keys are strings,
     * values are (type,value) tuples.
     *
     * @param bool $returnObject Whether to return AMQPArray instance instead of plain array
     * @return array|AMQPTable
     */
    public function read_table($returnObject = false)
    {
        $this->bitcount = $this->bits = 0;
        $tlen = $this->read_php_int();

        if ($tlen < 0) {
            throw new AMQPOutOfBoundsException('Table is longer than supported');
        }

        $table_data = new AMQPReader($this->rawread($tlen), null);
        $result = $returnObject ? new AMQPTable() : array();

        while ($table_data->tell() < $tlen) {
            $name = $table_data->read_shortstr();
            $ftype = AMQPAbstractCollection::getDataTypeForSymbol($ftypeSym = $table_data->rawread(1));
            $val = $table_data->read_value($ftype, $returnObject);
            $returnObject ? $result->set($name, $val, $ftype) : $result[$name] = array($ftypeSym, $val);
        }

        return $result;
    }

    /**
     * @return array|AMQPTable
     */
    public function read_table_object()
    {
        return $this->read_table(true);
    }

    /**
     * Reads the array in the next value.
     *
     * @param bool $returnObject Whether to return AMQPArray instance instead of plain array
     * @return array|AMQPArray
     */
    public function read_array($returnObject = false)
    {
        $this->bitcount = $this->bits = 0;

        // Determine array length and its end position
        $arrayLength = $this->read_php_int();
        $endOffset = $this->offset + $arrayLength;

        $result = $returnObject ? new AMQPArray() : array();

        // Read values until we reach the end of the array
        while ($this->offset < $endOffset) {
            $fieldType = AMQPAbstractCollection::getDataTypeForSymbol($this->rawread(1));
            $fieldValue = $this->read_value($fieldType, $returnObject);
            $returnObject ? $result->push($fieldValue, $fieldType) : $result[] = $fieldValue;
        }

        return $result;
    }

    /**
     * @return array|AMQPArray
     */
    public function read_array_object()
    {
        return $this->read_array(true);
    }

    /**
     * Reads the next value as the provided field type.
     *
     * @param int $fieldType One of AMQPAbstractCollection::T_* constants
     * @param bool $collectionsAsObjects Description
     * @return mixed
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    public function read_value($fieldType, $collectionsAsObjects = false)
    {
        $this->bitcount = $this->bits = 0;

        switch ($fieldType) {
            case AMQPAbstractCollection::T_INT_SHORTSHORT:
                //according to AMQP091 spec, 'b' is not bit, it is short-short-int, also valid for rabbit/qpid
                //$val=$this->read_bit();
                $val = $this->read_signed_octet();
                break;
            case AMQPAbstractCollection::T_INT_SHORTSHORT_U:
                $val = $this->read_octet();
                break;
            case AMQPAbstractCollection::T_INT_SHORT:
                $val = $this->read_signed_short();
                break;
            case AMQPAbstractCollection::T_INT_SHORT_U:
                $val = $this->read_short();
                break;
            case AMQPAbstractCollection::T_INT_LONG:
                $val = $this->read_signed_long();
                break;
            case AMQPAbstractCollection::T_INT_LONG_U:
                $val = $this->read_long();
                break;
            case AMQPAbstractCollection::T_INT_LONGLONG:
                $val = $this->read_signed_longlong();
                break;
            case AMQPAbstractCollection::T_INT_LONGLONG_U:
                $val = $this->read_longlong();
                break;
            case AMQPAbstractCollection::T_DECIMAL:
                $e = $this->read_octet();
                $n = $this->read_signed_long();
                $val = new AMQPDecimal($n, $e);
                break;
            case AMQPAbstractCollection::T_TIMESTAMP:
                $val = $this->read_timestamp();
                break;
            case AMQPAbstractCollection::T_BOOL:
                $val = $this->read_octet();
                break;
            case AMQPAbstractCollection::T_STRING_SHORT:
                $val = $this->read_shortstr();
                break;
            case AMQPAbstractCollection::T_STRING_LONG:
                $val = $this->read_longstr();
                break;
            case AMQPAbstractCollection::T_ARRAY:
                $val = $this->read_array($collectionsAsObjects);
                break;
            case AMQPAbstractCollection::T_TABLE:
                $val = $this->read_table($collectionsAsObjects);
                break;
            case AMQPAbstractCollection::T_VOID:
                $val = null;
                break;
            default:
                throw new AMQPInvalidArgumentException(sprintf(
                    'Unsupported type "%s"',
                    $fieldType
                ));
        }

        return isset($val) ? $val : null;
    }

    /**
     * @return int
     */
    protected function tell()
    {
        return $this->offset;
    }

    /**
     * Sets the timeout (second)
     *
     * @param int $timeout
     */
    public function setTimeout($timeout)
    {
        $this->timeout = $timeout;
    }

    /**
     * @return int
     */
    public function getTimeout()
    {
        return $this->timeout;
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Wire;

class Constants080
{
    /**
     * @var string
     */
    public static $AMQP_PROTOCOL_HEADER = "AMQP\x01\x01\x08\x00";

    /**
     * @var array
     */
    public static $FRAME_TYPES = array(
        1 => 'FRAME-METHOD',
        2 => 'FRAME-HEADER',
        3 => 'FRAME-BODY',
        4 => 'FRAME-OOB-METHOD',
        5 => 'FRAME-OOB-HEADER',
        6 => 'FRAME-OOB-BODY',
        7 => 'FRAME-TRACE',
        8 => 'FRAME-HEARTBEAT',
        4096 => 'FRAME-MIN-SIZE',
        206 => 'FRAME-END',
        501 => 'FRAME-ERROR',
    );

    /**
     * @var array
     */
    public static $CONTENT_METHODS = array(
        0 => '60,40',
        1 => '60,50',
        2 => '60,60',
        3 => '60,71',
        4 => '70,50',
        5 => '70,70',
        6 => '80,40',
        7 => '80,50',
        8 => '80,60',
        9 => '110,10',
        10 => '120,40',
        11 => '120,41',
    );

    /**
     * @var array
     */
    public static $CLOSE_METHODS = array(
        0 => '10,60',
        1 => '20,40',
    );

    /**
     * @var array
     */
    public static $GLOBAL_METHOD_NAMES = array(
        '10,10' => 'Connection.start',
        '10,11' => 'Connection.start_ok',
        '10,20' => 'Connection.secure',
        '10,21' => 'Connection.secure_ok',
        '10,30' => 'Connection.tune',
        '10,31' => 'Connection.tune_ok',
        '10,40' => 'Connection.open',
        '10,41' => 'Connection.open_ok',
        '10,50' => 'Connection.redirect',
        '10,60' => 'Connection.close',
        '10,61' => 'Connection.close_ok',
        '20,10' => 'Channel.open',
        '20,11' => 'Channel.open_ok',
        '20,20' => 'Channel.flow',
        '20,21' => 'Channel.flow_ok',
        '20,30' => 'Channel.alert',
        '20,40' => 'Channel.close',
        '20,41' => 'Channel.close_ok',
        '30,10' => 'Access.request',
        '30,11' => 'Access.request_ok',
        '40,10' => 'Exchange.declare',
        '40,11' => 'Exchange.declare_ok',
        '40,20' => 'Exchange.delete',
        '40,21' => 'Exchange.delete_ok',
        '50,10' => 'Queue.declare',
        '50,11' => 'Queue.declare_ok',
        '50,20' => 'Queue.bind',
        '50,21' => 'Queue.bind_ok',
        '50,30' => 'Queue.purge',
        '50,31' => 'Queue.purge_ok',
        '50,40' => 'Queue.delete',
        '50,41' => 'Queue.delete_ok',
        '50,50' => 'Queue.unbind',
        '50,51' => 'Queue.unbind_ok',
        '60,10' => 'Basic.qos',
        '60,11' => 'Basic.qos_ok',
        '60,20' => 'Basic.consume',
        '60,21' => 'Basic.consume_ok',
        '60,30' => 'Basic.cancel',
        '60,31' => 'Basic.cancel_ok',
        '60,40' => 'Basic.publish',
        '60,50' => 'Basic.return',
        '60,60' => 'Basic.deliver',
        '60,70' => 'Basic.get',
        '60,71' => 'Basic.get_ok',
        '60,72' => 'Basic.get_empty',
        '60,80' => 'Basic.ack',
        '60,90' => 'Basic.reject',
        '60,100' => 'Basic.recover_async',
        '60,110' => 'Basic.recover',
        '60,111' => 'Basic.recover_ok',
        '70,10' => 'File.qos',
        '70,11' => 'File.qos_ok',
        '70,20' => 'File.consume',
        '70,21' => 'File.consume_ok',
        '70,30' => 'File.cancel',
        '70,31' => 'File.cancel_ok',
        '70,40' => 'File.open',
        '70,41' => 'File.open_ok',
        '70,50' => 'File.stage',
        '70,60' => 'File.publish',
        '70,70' => 'File.return',
        '70,80' => 'File.deliver',
        '70,90' => 'File.ack',
        '70,100' => 'File.reject',
        '80,10' => 'Stream.qos',
        '80,11' => 'Stream.qos_ok',
        '80,20' => 'Stream.consume',
        '80,21' => 'Stream.consume_ok',
        '80,30' => 'Stream.cancel',
        '80,31' => 'Stream.cancel_ok',
        '80,40' => 'Stream.publish',
        '80,50' => 'Stream.return',
        '80,60' => 'Stream.deliver',
        '90,10' => 'Tx.select',
        '90,11' => 'Tx.select_ok',
        '90,20' => 'Tx.commit',
        '90,21' => 'Tx.commit_ok',
        '90,30' => 'Tx.rollback',
        '90,31' => 'Tx.rollback_ok',
        '100,10' => 'Dtx.select',
        '100,11' => 'Dtx.select_ok',
        '100,20' => 'Dtx.start',
        '100,21' => 'Dtx.start_ok',
        '110,10' => 'Tunnel.request',
        '120,10' => 'Test.integer',
        '120,11' => 'Test.integer_ok',
        '120,20' => 'Test.string',
        '120,21' => 'Test.string_ok',
        '120,30' => 'Test.table',
        '120,31' => 'Test.table_ok',
        '120,40' => 'Test.content',
        '120,41' => 'Test.content_ok',
    );
}
<?php
namespace PhpAmqpLib\Wire\IO;

use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Wire\AMQPWriter;

class StreamIO extends AbstractIO
{
    const READ_BUFFER_WAIT_INTERVAL = 100000;

    /** @var string */
    protected $protocol;

    /** @var string */
    protected $host;

    /** @var int */
    protected $port;

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

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

    /** @var resource */
    protected $context;

    /** @var bool */
    protected $keepalive;

    /** @var int */
    protected $heartbeat;

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

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

    /** @var array */
    protected $last_error;

    /** @var int */
    private $initial_heartbeat;

    /** @var resource */
    private $sock;

    /** @var bool */
    private $canDispatchPcntlSignal;

    /**
     * @param string $host
     * @param int $port
     * @param float $connection_timeout
     * @param float $read_write_timeout
     * @param null $context
     * @param bool $keepalive
     * @param int $heartbeat
     */
    public function __construct(
        $host,
        $port,
        $connection_timeout,
        $read_write_timeout,
        $context = null,
        $keepalive = false,
        $heartbeat = 0
    ) {
        if ($heartbeat !== 0 && ($read_write_timeout < ($heartbeat * 2))) {
            throw new \InvalidArgumentException('read_write_timeout must be at least 2x the heartbeat');
        }

        $this->protocol = 'tcp';
        $this->host = $host;
        $this->port = $port;
        $this->connection_timeout = $connection_timeout;
        $this->read_write_timeout = $read_write_timeout;
        $this->context = $context;
        $this->keepalive = $keepalive;
        $this->heartbeat = $heartbeat;
        $this->initial_heartbeat = $heartbeat;
        $this->canDispatchPcntlSignal = $this->isPcntlSignalEnabled();

        if (is_null($this->context)) {
            // tcp_nodelay was added in 7.1.0
            if (PHP_VERSION_ID >= 70100) {
                $this->context = stream_context_create(array(
                    "socket" => array(
                        "tcp_nodelay" => true
                    )
                ));
            } else {
                $this->context = stream_context_create();
            }
        } else {
            $this->protocol = 'ssl';
        }
    }

    /**
     * @return bool
     */
    private function isPcntlSignalEnabled()
    {
        return extension_loaded('pcntl')
            && function_exists('pcntl_signal_dispatch')
            && (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true);
    }

    /**
     * Sets up the stream connection
     *
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     * @throws \Exception
     */
    public function connect()
    {
        $errstr = $errno = null;

        $remote = sprintf(
            '%s://%s:%s',
            $this->protocol,
            $this->host,
            $this->port
        );

        set_error_handler(array($this, 'error_handler'));

        try {
            $this->sock = stream_socket_client(
                $remote,
                $errno,
                $errstr,
                $this->connection_timeout,
                STREAM_CLIENT_CONNECT,
                $this->context
            );
        } catch (\ErrorException $e) {
            restore_error_handler();
            throw $e;
        }

        restore_error_handler();

        if (false === $this->sock) {
            throw new AMQPRuntimeException(
                sprintf(
                    'Error Connecting to server(%s): %s ',
                    $errno,
                    $errstr
                ),
                $errno
            );
        }

        if (false === stream_socket_get_name($this->sock, true)) {
            throw new AMQPRuntimeException(
                sprintf(
                    'Connection refused: %s ',
                    $remote
                )
            );
        }

        list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->read_write_timeout);
        if (!stream_set_timeout($this->sock, $sec, $uSec)) {
            throw new AMQPIOException('Timeout could not be set');
        }

        // php cannot capture signals while streams are blocking
        if ($this->canDispatchPcntlSignal) {
            stream_set_blocking($this->sock, 0);
            stream_set_write_buffer($this->sock, 0);
            if (function_exists('stream_set_read_buffer')) {
                stream_set_read_buffer($this->sock, 0);
            }
        } else {
            stream_set_blocking($this->sock, 1);
        }

        if ($this->keepalive) {
            $this->enable_keepalive();
        }
    }

    /**
     * Reconnects the socket
     */
    public function reconnect()
    {
        $this->close();
        $this->connect();
    }

    /**
     * @param int $len
     * @throws \PhpAmqpLib\Exception\AMQPIOException
     * @return mixed|string
     */
    public function read($len)
    {
        $this->check_heartbeat();

        $read = 0;
        $data = '';

        while ($read < $len) {
            if (!is_resource($this->sock) || feof($this->sock)) {
                throw new AMQPRuntimeException('Broken pipe or closed connection');
            }

            set_error_handler(array($this, 'error_handler'));
            try {
                $buffer = fread($this->sock, ($len - $read));
            } catch (\ErrorException $e) {
                restore_error_handler();
                throw $e;
            }
            restore_error_handler();

            if ($buffer === false) {
                throw new AMQPRuntimeException('Error receiving data');
            }

            if ($buffer === '') {
                if ($this->canDispatchPcntlSignal) {
                    $this->select(0, self::READ_BUFFER_WAIT_INTERVAL);
                    pcntl_signal_dispatch();
                }
                continue;
            }

            $read += mb_strlen($buffer, 'ASCII');
            $data .= $buffer;
        }

        if (mb_strlen($data, 'ASCII') !== $len) {
            throw new AMQPRuntimeException(
                sprintf(
                    'Error reading data. Received %s instead of expected %s bytes',
                    mb_strlen($data, 'ASCII'),
                    $len
                )
            );
        }

        $this->last_read = microtime(true);
        return $data;
    }

    /**
     * @param string $data
     * @return mixed|void
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     * @throws \PhpAmqpLib\Exception\AMQPTimeoutException
     */
    public function write($data)
    {
        $written = 0;
        $len = mb_strlen($data, 'ASCII');

        while ($written < $len) {

            if (!is_resource($this->sock)) {
                throw new AMQPRuntimeException('Broken pipe or closed connection');
            }

            set_error_handler(array($this, 'error_handler'));
            // OpenSSL's C library function SSL_write() can balk on buffers > 8192
            // bytes in length, so we're limiting the write size here. On both TLS
            // and plaintext connections, the write loop will continue until the
            // buffer has been fully written.
            // This behavior has been observed in OpenSSL dating back to at least
            // September 2002:
            // http://comments.gmane.org/gmane.comp.encryption.openssl.user/4361
            try {
                $buffer = fwrite($this->sock, mb_substr($data, $written, 8192, 'ASCII'), 8192);
            } catch (\ErrorException $e) {
                restore_error_handler();
                throw $e;
            }
            restore_error_handler();

            if ($buffer === false) {
                throw new AMQPRuntimeException('Error sending data');
            }

            if ($buffer === 0 && feof($this->sock)) {
                throw new AMQPRuntimeException('Broken pipe or closed connection');
            }

            if ($this->timed_out()) {
                throw new AMQPTimeoutException('Error sending data. Socket connection timed out');
            }

            $written += $buffer;
        }

        $this->last_write = microtime(true);
    }

    /**
     * Internal error handler to deal with stream and socket errors that need to be ignored
     *
     * @param  int $errno
     * @param  string $errstr
     * @param  string $errfile
     * @param  int $errline
     * @param  array $errcontext
     * @return null
     * @throws \ErrorException
     */
    public function error_handler($errno, $errstr, $errfile, $errline, $errcontext = null)
    {
        $this->last_error = compact('errno', 'errstr', 'errfile', 'errline', 'errcontext');

        // fwrite notice that the stream isn't ready
        if (strstr($errstr, 'Resource temporarily unavailable')) {
             // it's allowed to retry
            return null;
        }

        // stream_select warning that it has been interrupted by a signal
        if (strstr($errstr, 'Interrupted system call')) {
             // it's allowed while processing signals
            return null;
        }

        // raise all other issues to exceptions
        throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
    }

    /**
     * Heartbeat logic: check connection health here
     */
    public function check_heartbeat()
    {
        // ignore unless heartbeat interval is set
        if ($this->heartbeat !== 0 && $this->last_read && $this->last_write) {
            $t = microtime(true);
            $t_read = round($t - $this->last_read);
            $t_write = round($t - $this->last_write);

            // server has gone away
            if (($this->heartbeat * 2) < $t_read) {
                $this->reconnect();
            }

            // time for client to send a heartbeat
            if (($this->heartbeat / 2) < $t_write) {
                $this->write_heartbeat();
            }
        }
    }

    /**
     * Sends a heartbeat message
     */
    protected function write_heartbeat()
    {
        $pkt = new AMQPWriter();
        $pkt->write_octet(8);
        $pkt->write_short(0);
        $pkt->write_long(0);
        $pkt->write_octet(0xCE);
        $this->write($pkt->getvalue());
    }

    public function close()
    {
        if (is_resource($this->sock)) {
            fclose($this->sock);
        }
        $this->sock = null;
        $this->last_read = null;
        $this->last_write = null;
    }

    /**
     * @return resource
     */
    public function get_socket()
    {
        return $this->sock;
    }

    /**
     * @return resource
     */
    public function getSocket()
    {
        return $this->get_socket();
    }

    /**
     * @param int $sec
     * @param int $usec
     * @return int|mixed
     */
    public function select($sec, $usec)
    {
        $this->check_heartbeat();

        $read = array($this->sock);
        $write = null;
        $except = null;
        $result = false;

        if (defined('HHVM_VERSION')) {
            $usec = is_int($usec) ? $usec : 0;
        }

        set_error_handler(array($this, 'error_handler'));
        try {
            $result = stream_select($read, $write, $except, $sec, $usec);
        } catch (\ErrorException $e) {
            restore_error_handler();
            throw $e;
        }
        restore_error_handler();

        return $result;
    }

    /**
     * @return mixed
     */
    protected function timed_out()
    {
        // get status of socket to determine whether or not it has timed out
        $info = stream_get_meta_data($this->sock);

        return $info['timed_out'];
    }

    /**
     * @throws \PhpAmqpLib\Exception\AMQPIOException
     */
    protected function enable_keepalive()
    {
        if (!function_exists('socket_import_stream')) {
            throw new AMQPIOException('Can not enable keepalive: function socket_import_stream does not exist');
        }

        if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) {
            throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined');
        }

        $socket = socket_import_stream($this->sock);
        socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1);
    }

    /**
     * @return $this
     */
    public function disableHeartbeat()
    {
        $this->heartbeat = 0;

        return $this;
    }

    /**
     * @return $this
     */
    public function reenableHeartbeat()
    {
        $this->heartbeat = $this->initial_heartbeat;

        return $this;
    }
}
<?php
namespace PhpAmqpLib\Wire\IO;

abstract class AbstractIO
{
    /**
     * @param int $n
     * @return mixed
     */
    abstract public function read($n);

    /**
     * @param string $data
     * @return mixed
     */
    abstract public function write($data);

    /**
     * @return mixed
     */
    abstract public function close();

    /**
     * @param int $sec
     * @param int $usec
     * @return mixed
     */
    abstract public function select($sec, $usec);

    /**
     * @return mixed
     */
    abstract public function connect();

    /**
     * @return mixed
     */
    abstract public function reconnect();

    /**
     * @return mixed
     */
    abstract public function getSocket();

    /**
     * @return mixed
     */
    abstract public function check_heartbeat();
}
<?php
namespace PhpAmqpLib\Wire\IO;

use PhpAmqpLib\Exception\AMQPIOException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Helper\MiscHelper;
use PhpAmqpLib\Wire\AMQPWriter;

class SocketIO extends AbstractIO
{
    /** @var string */
    protected $host;

    /** @var int */
    protected $port;

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

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

    /** @var int */
    protected $heartbeat;

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

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

    /** @var resource */
    private $sock;

    /** @var bool */
    private $keepalive;

    /**
     * @param string $host
     * @param int $port
     * @param float $read_timeout
     * @param bool $keepalive
     * @param float|null $write_timeout if null defaults to read timeout
     * @param int $heartbeat how often to send heartbeat. 0 means off
     */
    public function __construct($host, $port, $read_timeout, $keepalive = false, $write_timeout = null, $heartbeat = 0)
    {
        $this->host = $host;
        $this->port = $port;
        $this->read_timeout = $read_timeout;
        $this->send_timeout = $write_timeout ?: $read_timeout;
        $this->heartbeat = $heartbeat;
        $this->keepalive = $keepalive;
    }

    /**
     * Sets up the socket connection
     *
     * @throws \Exception
     */
    public function connect()
    {
        $this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

        list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->send_timeout);
        socket_set_option($this->sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => $sec, 'usec' => $uSec));
        list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->read_timeout);
        socket_set_option($this->sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $sec, 'usec' => $uSec));

        if (!socket_connect($this->sock, $this->host, $this->port)) {
            $errno = socket_last_error($this->sock);
            $errstr = socket_strerror($errno);
            throw new AMQPIOException(sprintf(
                'Error Connecting to server (%s): %s',
                $errno,
                $errstr
            ), $errno);
        }

        socket_set_block($this->sock);
        socket_set_option($this->sock, SOL_TCP, TCP_NODELAY, 1);

        if ($this->keepalive) {
            $this->enable_keepalive();
        }
    }

    /**
     * @return resource
     */
    public function getSocket()
    {
        return $this->sock;
    }

    /**
     * Reconnects the socket
     */
    public function reconnect()
    {
        $this->close();
        $this->connect();
    }

    /**
     * @param int $n
     * @return mixed|string
     * @throws \PhpAmqpLib\Exception\AMQPIOException
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    public function read($n)
    {
        if (is_null($this->sock)) {
            throw new AMQPRuntimeException(sprintf(
                'Socket was null! Last SocketError was: %s',
                socket_strerror(socket_last_error())
            ));
        }
        $res = '';
        $read = 0;
        $buf = socket_read($this->sock, $n);
        while ($read < $n && $buf !== '' && $buf !== false) {
            $this->check_heartbeat();

            $read += mb_strlen($buf, 'ASCII');
            $res .= $buf;
            $buf = socket_read($this->sock, $n - $read);
        }

        if (mb_strlen($res, 'ASCII') != $n) {
            throw new AMQPIOException(sprintf(
                'Error reading data. Received %s instead of expected %s bytes',
                mb_strlen($res, 'ASCII'),
                $n
            ));
        }

        $this->last_read = microtime(true);

        return $res;
    }

    /**
     * @param string $data
     * @return void
     *
     * @throws \PhpAmqpLib\Exception\AMQPIOException
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    public function write($data)
    {
        $len = mb_strlen($data, 'ASCII');

        while (true) {
            // Null sockets are invalid, throw exception
            if (is_null($this->sock)) {
                throw new AMQPRuntimeException(sprintf(
                    'Socket was null! Last SocketError was: %s',
                    socket_strerror(socket_last_error())
                ));
            }

            $sent = socket_write($this->sock, $data, $len);
            if ($sent === false) {
                throw new AMQPIOException(sprintf(
                    'Error sending data. Last SocketError: %s',
                    socket_strerror(socket_last_error())
                ));
            }

            // Check if the entire message has been sent
            if ($sent < $len) {
                // If not sent the entire message.
                // Get the part of the message that has not yet been sent as message
                $data = mb_substr($data, $sent, mb_strlen($data, 'ASCII') - $sent, 'ASCII');
                // Get the length of the not sent part
                $len -= $sent;
            } else {
                break;
            }
        }

        $this->last_write = microtime(true);
    }

    public function close()
    {
        if (is_resource($this->sock)) {
            socket_close($this->sock);
        }
        $this->sock = null;
        $this->last_read = null;
        $this->last_write = null;
    }

    /**
     * @param int $sec
     * @param int $usec
     * @return int|mixed
     */
    public function select($sec, $usec)
    {
        $read = array($this->sock);
        $write = null;
        $except = null;

        return socket_select($read, $write, $except, $sec, $usec);
    }

    /**
     * @throws \PhpAmqpLib\Exception\AMQPIOException
     */
    protected function enable_keepalive()
    {
        if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) {
            throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined');
        }

        socket_set_option($this->sock, SOL_SOCKET, SO_KEEPALIVE, 1);
    }

    /**
     * Heartbeat logic: check connection health here
     */
    public function check_heartbeat()
    {
        // ignore unless heartbeat interval is set
        if ($this->heartbeat !== 0 && $this->last_read && $this->last_write) {
            $t = microtime(true);
            $t_read = round($t - $this->last_read);
            $t_write = round($t - $this->last_write);

            // server has gone away
            if (($this->heartbeat * 2) < $t_read) {
                $this->reconnect();
            }

            // time for client to send a heartbeat
            if (($this->heartbeat / 2) < $t_write) {
                $this->write_heartbeat();
            }
        }
    }

    /**
     * Sends a heartbeat message
     */
    protected function write_heartbeat()
    {
        $pkt = new AMQPWriter();
        $pkt->write_octet(8);
        $pkt->write_short(0);
        $pkt->write_long(0);
        $pkt->write_octet(0xCE);
        $this->write($pkt->getvalue());
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Wire;

class Constants091
{
    /**
     * @var string
     */
    public static $AMQP_PROTOCOL_HEADER = "AMQP\x00\x00\x09\x01";

    /**
     * @var array
     */
    public static $FRAME_TYPES = array(
        1 => 'FRAME-METHOD',
        2 => 'FRAME-HEADER',
        3 => 'FRAME-BODY',
        8 => 'FRAME-HEARTBEAT',
        4096 => 'FRAME-MIN-SIZE',
        206 => 'FRAME-END',
        501 => 'FRAME-ERROR',
    );

    /**
     * @var array
     */
    public static $CONTENT_METHODS = array(
        0 => '60,40',
        1 => '60,50',
        2 => '60,60',
        3 => '60,71',
    );

    /**
     * @var array
     */
    public static $CLOSE_METHODS = array(
        0 => '10,50',
        1 => '20,40',
    );

    /**
     * @var array
     */
    public static $GLOBAL_METHOD_NAMES = array(
        '10,10' => 'Connection.start',
        '10,11' => 'Connection.start_ok',
        '10,20' => 'Connection.secure',
        '10,21' => 'Connection.secure_ok',
        '10,30' => 'Connection.tune',
        '10,31' => 'Connection.tune_ok',
        '10,40' => 'Connection.open',
        '10,41' => 'Connection.open_ok',
        '10,50' => 'Connection.close',
        '10,51' => 'Connection.close_ok',
        '10,60' => 'Connection.blocked',
        '10,61' => 'Connection.unblocked',
        '20,10' => 'Channel.open',
        '20,11' => 'Channel.open_ok',
        '20,20' => 'Channel.flow',
        '20,21' => 'Channel.flow_ok',
        '20,40' => 'Channel.close',
        '20,41' => 'Channel.close_ok',
        '30,10' => 'Access.request',
        '30,11' => 'Access.request_ok',
        '40,10' => 'Exchange.declare',
        '40,11' => 'Exchange.declare_ok',
        '40,20' => 'Exchange.delete',
        '40,21' => 'Exchange.delete_ok',
        '40,30' => 'Exchange.bind',
        '40,31' => 'Exchange.bind_ok',
        '40,40' => 'Exchange.unbind',
        '40,51' => 'Exchange.unbind_ok',
        '50,10' => 'Queue.declare',
        '50,11' => 'Queue.declare_ok',
        '50,20' => 'Queue.bind',
        '50,21' => 'Queue.bind_ok',
        '50,30' => 'Queue.purge',
        '50,31' => 'Queue.purge_ok',
        '50,40' => 'Queue.delete',
        '50,41' => 'Queue.delete_ok',
        '50,50' => 'Queue.unbind',
        '50,51' => 'Queue.unbind_ok',
        '60,10' => 'Basic.qos',
        '60,11' => 'Basic.qos_ok',
        '60,20' => 'Basic.consume',
        '60,21' => 'Basic.consume_ok',
        '60,30' => 'Basic.cancel',
        '60,31' => 'Basic.cancel_ok',
        '60,40' => 'Basic.publish',
        '60,50' => 'Basic.return',
        '60,60' => 'Basic.deliver',
        '60,70' => 'Basic.get',
        '60,71' => 'Basic.get_ok',
        '60,72' => 'Basic.get_empty',
        '60,80' => 'Basic.ack',
        '60,90' => 'Basic.reject',
        '60,100' => 'Basic.recover_async',
        '60,110' => 'Basic.recover',
        '60,111' => 'Basic.recover_ok',
        '60,120' => 'Basic.nack',
        '90,10' => 'Tx.select',
        '90,11' => 'Tx.select_ok',
        '90,20' => 'Tx.commit',
        '90,21' => 'Tx.commit_ok',
        '90,30' => 'Tx.rollback',
        '90,31' => 'Tx.rollback_ok',
        '85,10' => 'Confirm.select',
        '85,11' => 'Confirm.select_ok',
    );
}
<?php
namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Exception;


/**
 * Iterator implemented for transparent integration with AMQPWriter::write_[array|table]()
 */
abstract class AMQPAbstractCollection implements \Iterator
{

    //protocol defines available field types and their corresponding symbols
    const PROTOCOL_080 = AbstractChannel::PROTOCOL_080;
    const PROTOCOL_091 = AbstractChannel::PROTOCOL_091;
    const PROTOCOL_RBT = 'rabbit'; //pseudo proto

    //Abstract data types
    const T_INT_SHORTSHORT = 1;
    const T_INT_SHORTSHORT_U = 2;
    const T_INT_SHORT = 3;
    const T_INT_SHORT_U = 4;
    const T_INT_LONG = 5;
    const T_INT_LONG_U = 6;
    const T_INT_LONGLONG = 7;
    const T_INT_LONGLONG_U = 8;

    const T_DECIMAL = 9;
    const T_TIMESTAMP = 10;
    const T_VOID = 11;

    const T_BOOL = 12;

    const T_STRING_SHORT = 13;
    const T_STRING_LONG = 14;

    const T_ARRAY = 15;
    const T_TABLE = 16;

    /**
     * @var string
     */
    private static $_protocol = null;

    /*
     * Field types messy mess http://www.rabbitmq.com/amqp-0-9-1-errata.html#section_3
     * Default behaviour is to use rabbitMQ compatible field-set
     * Define AMQP_STRICT_FLD_TYPES=true to use strict AMQP instead
     */
    private static $_types_080 = array(
        self::T_INT_LONG => 'I',
        self::T_DECIMAL => 'D',
        self::T_TIMESTAMP => 'T',
        self::T_STRING_LONG => 'S',
        self::T_TABLE => 'F'
    );

    /**
     * @var array
     */
    private static $_types_091 = array(
        self::T_INT_SHORTSHORT => 'b',
        self::T_INT_SHORTSHORT_U => 'B',
        self::T_INT_SHORT => 'U',
        self::T_INT_SHORT_U => 'u',
        self::T_INT_LONG => 'I',
        self::T_INT_LONG_U => 'i',
        self::T_INT_LONGLONG => 'L',
        self::T_INT_LONGLONG_U => 'l',
        self::T_DECIMAL => 'D',
        self::T_TIMESTAMP => 'T',
        self::T_VOID => 'V',
        self::T_BOOL => 't',
        self::T_STRING_SHORT => 's',
        self::T_STRING_LONG => 'S',
        self::T_ARRAY => 'A',
        self::T_TABLE => 'F'
    );

    /**
     * @var array
     */
    private static $_types_rabbit = array(
        self::T_INT_SHORTSHORT => 'b',
        self::T_INT_SHORT => 's',
        self::T_INT_LONG => 'I',
        self::T_INT_LONGLONG => 'l',
        self::T_DECIMAL => 'D',
        self::T_TIMESTAMP => 'T',
        self::T_VOID => 'V',
        self::T_BOOL => 't',
        self::T_STRING_LONG => 'S',
        self::T_ARRAY => 'A',
        self::T_TABLE => 'F'
    );

    /**
     * @var array
     */
    protected $data = array();

    public function __construct(array $data = null)
    {
        if (!empty($data)) {
            $this->data = $this->encodeCollection($data);
        }
    }

    /**
     * @return int
     */
    abstract public function getType();

    /**
     * @param mixed $val
     * @param int $type
     * @param string $key
     */
    final protected function setValue($val, $type = null, $key = null)
    {
        if ($val instanceof self) {
            if ($type && ($type != $val->getType())) {
                throw new Exception\AMQPInvalidArgumentException(
                    'Attempted to add instance of ' . get_class($val) . ' representing type [' . $val->getType() . '] as mismatching type [' . $type . ']'
                );
            }
            $type = $val->getType();
        } elseif ($type) { //ensuring data integrity and that all members are properly validated
            switch ($type) {
                case self::T_ARRAY:
                    throw new Exception\AMQPInvalidArgumentException('Arrays must be passed as AMQPArray instance');
                    break;
                case self::T_TABLE:
                    throw new Exception\AMQPInvalidArgumentException('Tables must be passed as AMQPTable instance');
                    break;
                case self::T_DECIMAL:
                    if (!($val instanceof AMQPDecimal)) {
                        throw new Exception\AMQPInvalidArgumentException('Decimal values must be instance of AMQPDecimal');
                    }
                    break;
            }
        }

        if ($type) {
            self::checkDataTypeIsSupported($type, false);
            $val = array($type, $val);
        } else {
            $val = $this->encodeValue($val);
        }

        if ($key === null) {
            $this->data[] = $val;
        } else {
            $this->data[$key] = $val;
        }
    }

    /**
     * @return array
     */
    final public function getNativeData()
    {
        return $this->decodeCollection($this->data);
    }

    /**
     * @param array $val
     * @return array
     */
    final protected function encodeCollection(array $val)
    {
        foreach ($val as $k=>$v) {
            $val[$k] = $this->encodeValue($v);
        }

        return $val;
    }

    /**
     * @param array $val
     * @return array
     */
    final protected function decodeCollection(array $val)
    {
        foreach ($val as $k=>$v) {
            $val[$k] = $this->decodeValue($v[1], $v[0]);
        }

        return $val;
    }

    /**
     * @param mixed $val
     * @return mixed
     * @throws Exception\AMQPOutOfBoundsException
     */
    protected function encodeValue($val)
    {
        if (is_string($val)) {
            $val = $this->encodeString($val);
        } elseif (is_float($val)) {
            $val = $this->encodeFloat($val);
        } elseif (is_int($val)) {
            $val = $this->encodeInt($val);
        } elseif (is_bool($val)) {
            $val = $this->encodeBool($val);
        } elseif (is_null($val)) {
            $val = $this->encodeVoid();
        } elseif ($val instanceof \DateTimeInterface) {
            $val = array(self::T_TIMESTAMP, $val->getTimestamp());
        } elseif ($val instanceof \DateTime) {
            // PHP <= 5.4 has no DateTimeInterface
            $val = array(self::T_TIMESTAMP, $val->getTimestamp());
        } elseif ($val instanceof AMQPDecimal) {
            $val = array(self::T_DECIMAL, $val);
        } elseif ($val instanceof self) {
            //avoid silent type correction of strictly typed values
            self::checkDataTypeIsSupported($val->getType(), false);
            $val = array($val->getType(), $val);
        } elseif (is_array($val)) {
            //AMQP specs says "Field names MUST start with a letter, '$' or '#'"
            //so beware, some servers may raise an exception with 503 code in cases when indexed array is encoded as table
            if (self::isProtocol(self::PROTOCOL_080)) {
                //080 doesn't support arrays, forcing table
                $val = array(self::T_TABLE, new AMQPTable($val));
            } elseif (empty($val) || (array_keys($val) === range(0, count($val) - 1))) {
                $val = array(self::T_ARRAY, new AMQPArray($val));
            } else {
                $val = array(self::T_TABLE, new AMQPTable($val));
            }
        } else {
            throw new Exception\AMQPOutOfBoundsException(sprintf('Encountered value of unsupported type: %s', gettype($val)));
        }

        return $val;
    }

    /**
     * @param mixed $val
     * @param int $type
     * @return array|bool|\DateTime|null
     */
    protected function decodeValue($val, $type)
    {
        if ($val instanceof self) {
            //covering arrays and tables
            $val = $val->getNativeData();
        } else {
            switch ($type) {
                case self::T_BOOL:
                    $val = (bool) $val;
                    break;
                case self::T_TIMESTAMP:
                    $val = \DateTime::createFromFormat('U', $val);
                    break;
                case self::T_VOID:
                    $val = null;
                    break;
                case self::T_ARRAY:
                case self::T_TABLE:
                    throw new Exception\AMQPLogicException(
                        'Encountered an array/table struct which is not an instance of AMQPCollection. ' .
                        'This is considered a bug and should be fixed, please report'
                    );
            }
        }

        return $val;
    }

    /**
     * @param string $val
     * @return array
     */
    protected function encodeString($val)
    {
        return array(self::T_STRING_LONG, $val);
    }

    /**
     * @param int $val
     * @return array
     */
    protected function encodeInt($val)
    {
        if (($val >= -2147483648) && ($val <= 2147483647)) {
            $ev = array(self::T_INT_LONG, $val);
        } elseif (self::isProtocol(self::PROTOCOL_080)) {
            //080 doesn't support longlong
            $ev = $this->encodeString((string) $val);
        } else {
            $ev = array(self::T_INT_LONGLONG, $val);
        }

        return $ev;
    }

    /**
     * @param float $val
     * @return array
     */
    protected function encodeFloat($val)
    {
        return static::encodeString((string) $val);
    }

    /**
     * @param bool $val
     * @return array
     */
    protected function encodeBool($val)
    {
        $val = (bool) $val;

        return self::isProtocol(self::PROTOCOL_080) ? array(self::T_INT_LONG, (int) $val) : array(self::T_BOOL, $val);
    }

    /**
     * @return array
     */
    protected function encodeVoid()
    {
        return self::isProtocol(self::PROTOCOL_080) ? $this->encodeString('') : array(self::T_VOID, null);
    }

    /**
     * @return string
     */
    final public static function getProtocol()
    {
        if (self::$_protocol === null) {
            self::$_protocol = defined('AMQP_STRICT_FLD_TYPES') && AMQP_STRICT_FLD_TYPES ?
                AbstractChannel::getProtocolVersion() :
                self::PROTOCOL_RBT;
        }

        return self::$_protocol;
    }

    /**
     * @param string $proto
     * @return bool
     */
    final public static function isProtocol($proto)
    {
        return self::getProtocol() == $proto;
    }

    /**
     * @return array  [dataTypeConstant => dataTypeSymbol]
     */
    final public static function getSupportedDataTypes()
    {
        switch ($proto = self::getProtocol()) {
            case self::PROTOCOL_080:
                $types = self::$_types_080;
                break;
            case self::PROTOCOL_091:
                $types = self::$_types_091;
                break;
            case self::PROTOCOL_RBT:
                $types = self::$_types_rabbit;
                break;
            default:
                throw new Exception\AMQPOutOfRangeException(sprintf('Unknown protocol: %s', $proto));
        }

        return $types;
    }

    /**
     * @param string $type
     * @param bool $return Whether to return or raise AMQPOutOfRangeException
     * @return boolean
     */
    final public static function checkDataTypeIsSupported($type, $return = true)
    {
        try {
            $supported = self::getSupportedDataTypes();
            if (!isset($supported[$type])) {
                throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type));
            }
            return true;

        } catch (Exception\AMQPOutOfRangeException $ex) {
            if (!$return) {
                throw $ex;
            }

            return false;
        }
    }

    /**
     * @param int $type
     * @return string
     */
    final public static function getSymbolForDataType($type)
    {
        $types = self::getSupportedDataTypes();
        if (!isset($types[$type])) {
            throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type));
        }

        return $types[$type];
    }

    /**
     * @param string $symbol
     * @return integer
     */
    final public static function getDataTypeForSymbol($symbol)
    {
        $symbols = array_flip(self::getSupportedDataTypes());
        if (!isset($symbols[$symbol])) {
            throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t define data of type [%s]', self::getProtocol(), $symbol));
        }

        return $symbols[$symbol];
    }

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

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

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

    public function rewind()
    {
        reset($this->data);
    }

    public function valid()
    {
        return key($this->data) !== null;
    }
}
<?php
namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Exception\AMQPInvalidArgumentException;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;

class AMQPWriter extends AbstractClient
{
    /** @var string */
    protected $out;

    /** @var array */
    protected $bits;

    /** @var int */
    protected $bitcount;

    public function __construct()
    {
        parent::__construct();

        $this->out = '';
        $this->bits = array();
        $this->bitcount = 0;
    }

    /**
     * Packs integer into raw byte string in big-endian order
     * Supports positive and negative ints represented as PHP int or string (except scientific notation)
     *
     * Floats has some precision issues and so intentionally not supported.
     * Beware that floats out of PHP_INT_MAX range will be represented in scientific (exponential) notation when casted to string
     *
     * @param int|string $x Value to pack
     * @param int $bytes Must be multiply of 2
     * @return string
     */
    private static function packBigEndian($x, $bytes)
    {
        if (($bytes <= 0) || ($bytes % 2)) {
            throw new AMQPInvalidArgumentException(sprintf('Expected bytes count must be multiply of 2, %s given', $bytes));
        }

        $ox = $x; //purely for dbg purposes (overflow exception)
        $isNeg = false;

        if (is_int($x)) {
            if ($x < 0) {
                $isNeg = true;
                $x = abs($x);
            }
        } elseif (is_string($x)) {
            if (!is_numeric($x)) {
                throw new AMQPInvalidArgumentException(sprintf('Unknown numeric string format: %s', $x));
            }
            $x = preg_replace('/^-/', '', $x, 1, $isNeg);
        } else {
            throw new AMQPInvalidArgumentException('Only integer and numeric string values are supported');
        }

        if ($isNeg) {
            $x = bcadd($x, -1, 0);
        } //in negative domain starting point is -1, not 0

        $res = array();
        for ($b = 0; $b < $bytes; $b += 2) {
            $chnk = (int) bcmod($x, 65536);
            $x = bcdiv($x, 65536, 0);
            $res[] = pack('n', $isNeg ? ~$chnk : $chnk);
        }

        if ($x || ($isNeg && ($chnk & 0x8000))) {
            throw new AMQPOutOfBoundsException(sprintf('Overflow detected while attempting to pack %s into %s bytes', $ox, $bytes));
        }

        return implode(array_reverse($res));
    }

    private function flushbits()
    {
        if (!empty($this->bits)) {
            $this->out .= implode('', array_map('chr', $this->bits));
            $this->bits = array();
            $this->bitcount = 0;
        }
    }

    /**
     * Get what's been encoded so far.
     *
     * @return string
     */
    public function getvalue()
    {
        /* temporarily needed for compatibility with write_bit unit tests */
        if ($this->bitcount) {
            $this->flushbits();
        }

        return $this->out;
    }

    /**
     * Write a plain PHP string, with no special encoding.
     *
     * @param string $s
     *
     * @return $this
     */
    public function write($s)
    {
        $this->out .= $s;

        return $this;
    }

    /**
     * Write a boolean value.
     * (deprecated, use write_bits instead)
     *
     * @deprecated
     * @param bool $b
     * @return $this
     */
    public function write_bit($b)
    {
        $b = $b ? 1 : 0;
        $shift = $this->bitcount % 8;
        $last = $shift === 0 ? 0 : array_pop($this->bits);
        $last |= ($b << $shift);
        array_push($this->bits, $last);
        $this->bitcount += 1;

        return $this;
    }

    /**
     * Write multiple bits as an octet
     *
     * @param bool[] $bits
     * @return $this
     */
    public function write_bits($bits)
    {
        $value = 0;

        foreach ($bits as $n => $bit) {
            $bit = $bit ? 1 : 0;
            $value |= ($bit << $n);
        }

        $this->out .= chr($value);

        return $this;
    }

    /**
     * Write an integer as an unsigned 8-bit value
     *
     * @param int $n
     * @return $this
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
     */
    public function write_octet($n)
    {
        if ($n < 0 || $n > 255) {
            throw new AMQPInvalidArgumentException('Octet out of range: ' . $n);
        }

        $this->out .= chr($n);

        return $this;
    }

    /**
     * @param int $n
     * @return $this
     */
    public function write_signed_octet($n)
    {
        if (($n < -128) || ($n > 127)) {
            throw new AMQPInvalidArgumentException('Signed octet out of range: ' . $n);
        }

        $this->out .= pack('c', $n);

        return $this;
    }

    /**
     * Write an integer as an unsigned 16-bit value
     *
     * @param int $n
     * @return $this
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
     */
    public function write_short($n)
    {
        if ($n < 0 || $n > 65535) {
            throw new AMQPInvalidArgumentException('Short out of range: ' . $n);
        }

        $this->out .= pack('n', $n);

        return $this;
    }

    /**
     * @param int $n
     * @return $this
     */
    public function write_signed_short($n)
    {
        if (($n < -32768) || ($n > 32767)) {
            throw new AMQPInvalidArgumentException('Signed short out of range: ' . $n);
        }

        $this->out .= $this->correctEndianness(pack('s', $n));

        return $this;
    }

    /**
     * Write an integer as an unsigned 32-bit value
     *
     * @param int $n
     * @return $this
     */
    public function write_long($n)
    {
        if (($n < 0) || ($n > 4294967295)) {
            throw new AMQPInvalidArgumentException('Long out of range: ' . $n);
        }

        //Numeric strings >PHP_INT_MAX on 32bit are casted to PHP_INT_MAX, damn PHP
        if (empty($this->is64bits) && is_string($n)) {
            $n = (float) $n;
        }
        $this->out .= pack('N', $n);

        return $this;
    }

    /**
     * @param int $n
     * @return $this
     */
    private function write_signed_long($n)
    {
        if (($n < -2147483648) || ($n > 2147483647)) {
            throw new AMQPInvalidArgumentException('Signed long out of range: ' . $n);
        }

        //on my 64bit debian this approach is slightly faster than splitIntoQuads()
        $this->out .= $this->correctEndianness(pack('l', $n));

        return $this;
    }

    /**
     * Write an integer as an unsigned 64-bit value
     *
     * @param int $n
     * @return $this
     */
    public function write_longlong($n)
    {
        if ($n < 0) {
            throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n);
        }

        // if PHP_INT_MAX is big enough for that
        // direct $n<=PHP_INT_MAX check is unreliable on 64bit (values close to max) due to limited float precision
        if (bcadd($n, -PHP_INT_MAX, 0) <= 0) {
            // trick explained in http://www.php.net/manual/fr/function.pack.php#109328
            if ($this->is64bits) {
                list($hi, $lo) = $this->splitIntoQuads($n);
            } else {
                $hi = 0;
                $lo = $n;
            } //on 32bits hi quad is 0 a priori
            $this->out .= pack('NN', $hi, $lo);
        } else {
            try {
                $this->out .= self::packBigEndian($n, 8);
            } catch (AMQPOutOfBoundsException $ex) {
                throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n, 0, $ex);
            }
        }

        return $this;
    }

    /**
     * @param int $n
     * @return $this
     */
    public function write_signed_longlong($n)
    {
        if ((bcadd($n, PHP_INT_MAX, 0) >= -1) && (bcadd($n, -PHP_INT_MAX, 0) <= 0)) {
            if ($this->is64bits) {
                list($hi, $lo) = $this->splitIntoQuads($n);
            } else {
                $hi = $n < 0 ? -1 : 0;
                $lo = $n;
            } //0xffffffff for negatives
            $this->out .= pack('NN', $hi, $lo);
        } elseif ($this->is64bits) {
            throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n);
        } else {
            if (bcadd($n, '-9223372036854775807', 0) > 0) {
                throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n);
            }
            try {
                //will catch only negative overflow, as values >9223372036854775807 are valid for 8bytes range (unsigned)
                $this->out .= self::packBigEndian($n, 8);
            } catch (AMQPOutOfBoundsException $ex) {
                throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n, 0, $ex);
            }
        }

        return $this;
    }

    /**
     * @param int|string $n
     * @return integer[]
     */
    private function splitIntoQuads($n)
    {
        $n = (int) $n;

        return array($n >> 32, $n & 0x00000000ffffffff);
    }

    /**
     * Write a string up to 255 bytes long after encoding.
     * Assume UTF-8 encoding
     *
     * @param string $s
     * @return $this
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
     */
    public function write_shortstr($s)
    {
        $len = mb_strlen($s, 'ASCII');
        if ($len > 255) {
            throw new AMQPInvalidArgumentException('String too long');
        }

        $this->write_octet($len);
        $this->out .= $s;

        return $this;
    }

    /**
     * Write a string up to 2**32 bytes long.  Assume UTF-8 encoding
     *
     * @param string $s
     * @return $this
     */
    public function write_longstr($s)
    {
        $this->write_long(mb_strlen($s, 'ASCII'));
        $this->out .= $s;

        return $this;
    }

    /**
     * Supports the writing of Array types, so that you can implement
     * array methods, like Rabbitmq's HA parameters
     *
     * @param AMQPArray|array $a Instance of AMQPArray or PHP array WITHOUT format hints (unlike write_table())
     * @return self
     */
    public function write_array($a)
    {
        if (!($a instanceof AMQPArray)) {
            $a = new AMQPArray($a);
        }
        $data = new self();

        foreach ($a as $v) {
            $data->write_value($v[0], $v[1]);
        }

        $data = $data->getvalue();
        $this->write_long(mb_strlen($data, 'ASCII'));
        $this->write($data);

        return $this;
    }

    /**
     * Write unix time_t value as 64 bit timestamp
     *
     * @param int $v
     * @return $this
     */
    public function write_timestamp($v)
    {
        $this->write_longlong($v);

        return $this;
    }

    /**
     * Write PHP array, as table. Input array format: keys are strings,
     * values are (type,value) tuples.
     *
     * @param AMQPTable|array $d Instance of AMQPTable or PHP array WITH format hints (unlike write_array())
     * @return $this
     * @throws \PhpAmqpLib\Exception\AMQPInvalidArgumentException
     */
    public function write_table($d)
    {
        $typeIsSym = !($d instanceof AMQPTable); //purely for back-compat purposes

        $table_data = new AMQPWriter();
        foreach ($d as $k => $va) {
            list($ftype, $v) = $va;
            $table_data->write_shortstr($k);
            $table_data->write_value($typeIsSym ? AMQPAbstractCollection::getDataTypeForSymbol($ftype) : $ftype, $v);
        }

        $table_data = $table_data->getvalue();
        $this->write_long(mb_strlen($table_data, 'ASCII'));
        $this->write($table_data);

        return $this;
    }

    /**
     * for compat with method mapping used by AMQPMessage
     *
     * @param AMQPTable|array
     * @return $this
     */
    public function write_table_object($d)
    {
        return $this->write_table($d);
    }

    /**
     * @param int $type One of AMQPAbstractCollection::T_* constants
     * @param mixed $val
     */
    private function write_value($type, $val)
    {
        //This will find appropriate symbol for given data type for currently selected protocol
        //Also will raise an exception on unknown type
        $this->write(AMQPAbstractCollection::getSymbolForDataType($type));

        switch ($type) {
            case AMQPAbstractCollection::T_INT_SHORTSHORT:
                $this->write_signed_octet($val);
                break;
            case AMQPAbstractCollection::T_INT_SHORTSHORT_U:
                $this->write_octet($val);
                break;
            case AMQPAbstractCollection::T_INT_SHORT:
                $this->write_signed_short($val);
                break;
            case AMQPAbstractCollection::T_INT_SHORT_U:
                $this->write_short($val);
                break;
            case AMQPAbstractCollection::T_INT_LONG:
                $this->write_signed_long($val);
                break;
            case AMQPAbstractCollection::T_INT_LONG_U:
                $this->write_long($val);
                break;
            case AMQPAbstractCollection::T_INT_LONGLONG:
                $this->write_signed_longlong($val);
                break;
            case AMQPAbstractCollection::T_INT_LONGLONG_U:
                $this->write_longlong($val);
                break;
            case AMQPAbstractCollection::T_DECIMAL:
                $this->write_octet($val->getE());
                $this->write_signed_long($val->getN());
                break;
            case AMQPAbstractCollection::T_TIMESTAMP:
                $this->write_timestamp($val);
                break;
            case AMQPAbstractCollection::T_BOOL:
                $this->write_octet($val ? 1 : 0);
                break;
            case AMQPAbstractCollection::T_STRING_SHORT:
                $this->write_shortstr($val);
                break;
            case AMQPAbstractCollection::T_STRING_LONG:
                $this->write_longstr($val);
                break;
            case AMQPAbstractCollection::T_ARRAY:
                $this->write_array($val);
                break;
            case AMQPAbstractCollection::T_TABLE:
                $this->write_table($val);
                break;
            case AMQPAbstractCollection::T_VOID:
                break;
            default:
                throw new AMQPInvalidArgumentException(sprintf(
                    'Unsupported type "%s"',
                    $type
                ));
        }
    }
}
<?php
namespace PhpAmqpLib\Wire;

use PhpAmqpLib\Channel\AMQPChannel;

/**
 * Abstract base class for AMQP content.  Subclasses should override
 * the propertyDefinitions attribute.
 */
abstract class GenericContent
{
    /** @var array */
    public $delivery_info = array();

    /** @var array Final property definitions */
    protected $prop_types;

    /** @var array Properties content */
    private $properties = array();

    /** @var string Compiled properties */
    private $serialized_properties;

    /**
     * @var array
     */
    protected static $propertyDefinitions = array(
        'dummy' => 'shortstr'
    );

    /**
     * @param array $properties Message property content
     * @param array $propertyTypes Message property definitions
     */
    public function __construct($properties, $propertyTypes = null)
    {
        $this->prop_types = !empty($propertyTypes) ? $propertyTypes : self::$propertyDefinitions;

        if (!empty($properties)) {
            $this->properties = array_intersect_key($properties, $this->prop_types);
        }
    }

    /**
     * Check whether a property exists in the 'properties' dictionary
     * or if present - in the 'delivery_info' dictionary.
     *
     * @param string $name
     * @return bool
     */
    public function has($name)
    {
        return isset($this->properties[$name]) || isset($this->delivery_info[$name]);
    }

    /**
     * Look for additional properties in the 'properties' dictionary,
     * and if present - the 'delivery_info' dictionary.
     *
     * @param string $name
     * @throws \OutOfBoundsException
     * @return mixed|AMQPChannel
     */
    public function get($name)
    {
        if (isset($this->properties[$name])) {
            return $this->properties[$name];
        }

        if (isset($this->delivery_info[$name])) {
            return $this->delivery_info[$name];
        }

        throw new \OutOfBoundsException(sprintf(
            'No "%s" property',
            $name
        ));
    }

    /**
     * Returns the properties content
     *
     * @return array
     */
    public function get_properties()
    {
        return $this->properties;
    }

    /**
     * Sets a property value
     *
     * @param string $name The property name (one of the property definition)
     * @param mixed $value The property value
     * @throws \OutOfBoundsException
     */
    public function set($name, $value)
    {
        if (!array_key_exists($name, $this->prop_types)) {
            throw new \OutOfBoundsException(sprintf(
                'No "%s" property',
                $name
            ));
        }

        $this->properties[$name] = $value;
    }

    /**
     * Given the raw bytes containing the property-flags and
     * property-list from a content-frame-header, parse and insert
     * into a dictionary stored in this object as an attribute named
     * 'properties'.
     *
     * @param AMQPReader $reader
     * NOTE: do not mutate $reader
     * @return $this
     */
    public function load_properties(AMQPReader $reader)
    {
        // Read 16-bit shorts until we get one with a low bit set to zero
        $flags = array();

        while (true) {
            $flag_bits = $reader->read_short();
            $flags[] = $flag_bits;

            if (($flag_bits & 1) === 0) {
                break;
            }
        }

        $shift = 0;
        $data = array();

        foreach ($this->prop_types as $key => $proptype) {
            if ($shift === 0) {
                if (!$flags) {
                    break;
                }
                $flag_bits = array_shift($flags);
                $shift = 15;
            }

            if ($flag_bits & (1 << $shift)) {
                $data[$key] = $reader->{'read_' . $proptype}();
            }

            $shift -= 1;
        }

        $this->properties = $data;

        return $this;
    }


    /**
     * Serializes the 'properties' attribute (a dictionary) into the
     * raw bytes making up a set of property flags and a property
     * list, suitable for putting into a content frame header.
     *
     * @return string
     * @todo Inject the AMQPWriter to make the method easier to test
     */
    public function serialize_properties()
    {
        if (!empty($this->serialized_properties)) {
            return $this->serialized_properties;
        }

        $shift = 15;
        $flag_bits = 0;
        $flags = array();
        $raw_bytes = new AMQPWriter();

        foreach ($this->prop_types as $key => $prototype) {
            $val = isset($this->properties[$key]) ? $this->properties[$key] : null;

            // Very important: PHP type eval is weak, use the === to test the
            // value content. Zero or false value should not be removed
            if ($val === null) {
                $shift -= 1;
                continue;
            }

            if ($shift === 0) {
                $flags[] = $flag_bits;
                $flag_bits = 0;
                $shift = 15;
            }

            $flag_bits |= (1 << $shift);
            if ($prototype != 'bit') {
                $raw_bytes->{'write_' . $prototype}($val);
            }

            $shift -= 1;
        }

        $flags[] = $flag_bits;
        $result = new AMQPWriter();
        foreach ($flags as $flag_bits) {
            $result->write_short($flag_bits);
        }

        $result->write($raw_bytes->getvalue());

        $this->serialized_properties = $result->getvalue();

        return $this->serialized_properties;
    }
}
<?php
namespace PhpAmqpLib\Wire;


class AMQPArray extends AMQPAbstractCollection
{

    /**
     * @param array|null $data
     */
    public function __construct(array $data = null)
    {
        parent::__construct(empty($data) ? null : array_values($data));
    }

    /**
     * @return int
     */
    final public function getType()
    {
        return self::T_ARRAY;
    }

    /**
     * @param mixed $val
     * @param null $type
     * @return $this
     */
    public function push($val, $type = null)
    {
        $this->setValue($val, $type);

        return $this;
    }
}
<?php
namespace PhpAmqpLib\Connection;

use PhpAmqpLib\Wire\IO\SocketIO;

class AMQPSocketConnection extends AbstractConnection
{
    /**
     * @param string $host
     * @param int $port
     * @param string $user
     * @param string $password
     * @param string $vhost
     * @param bool $insist
     * @param string $login_method
     * @param null $login_response
     * @param string $locale
     * @param float $read_timeout
     * @param bool $keepalive
     * @param int $write_timeout
     * @param int $heartbeat
     */
    public function __construct(
        $host,
        $port,
        $user,
        $password,
        $vhost = '/',
        $insist = false,
        $login_method = 'AMQPLAIN',
        $login_response = null,
        $locale = 'en_US',
        $read_timeout = 3,
        $keepalive = false,
        $write_timeout = 3,
        $heartbeat = 0
    ) {
        $io = new SocketIO($host, $port, $read_timeout, $keepalive, $write_timeout, $heartbeat);

        parent::__construct(
            $user,
            $password,
            $vhost,
            $insist,
            $login_method,
            $login_response,
            $locale,
            $io,
            $heartbeat
        );
    }
}
<?php
namespace PhpAmqpLib\Connection;

class AMQPSSLConnection extends AMQPStreamConnection
{
    /**
     * @param string $host
     * @param int $port
     * @param string $user
     * @param string $password
     * @param string $vhost
     * @param array $ssl_options
     * @param array $options
     */
    public function __construct(
        $host,
        $port,
        $user,
        $password,
        $vhost = '/',
        $ssl_options = array(),
        $options = array()
    ) {
        $ssl_context = empty($ssl_options) ? null : $this->create_ssl_context($ssl_options);
        parent::__construct(
            $host,
            $port,
            $user,
            $password,
            $vhost,
            isset($options['insist']) ? $options['insist'] : false,
            isset($options['login_method']) ? $options['login_method'] : 'AMQPLAIN',
            isset($options['login_response']) ? $options['login_response'] : null,
            isset($options['locale']) ? $options['locale'] : 'en_US',
            isset($options['connection_timeout']) ? $options['connection_timeout'] : 3,
            isset($options['read_write_timeout']) ? $options['read_write_timeout'] : 3,
            $ssl_context,
            isset($options['keepalive']) ? $options['keepalive'] : false,
            isset($options['heartbeat']) ? $options['heartbeat'] : 0
        );
    }

    /**
     * @param array $options
     * @return resource
     */
    private function create_ssl_context($options)
    {
        $ssl_context = stream_context_create();
        foreach ($options as $k => $v) {
            stream_context_set_option($ssl_context, 'ssl', $k, $v);
        }

        return $ssl_context;
    }
}
<?php
namespace PhpAmqpLib\Connection;

class AMQPLazyConnection extends AMQPStreamConnection
{
    /**
     * Gets socket from current connection
     *
     * @deprecated
     */
    public function getSocket()
    {
        $this->connect();

        return parent::getSocket();
    }

    /**
     * {@inheritdoc}
     */
    public function channel($channel_id = null)
    {
        $this->connect();

        return parent::channel($channel_id);
    }

    /**
     * @return null|\PhpAmqpLib\Wire\IO\AbstractIO
     */
    public function getIO()
    {
        if (empty($this->io)) {
            $this->connect();
        }

        return $this->io;
    }

    /**
     * Should the connection be attempted during construction?
     *
     * @return bool
     */
    public function connectOnConstruct()
    {
        return false;
    }
}
<?php

namespace PhpAmqpLib\Connection;

/**
 * Yet another lazy connection. This time using sockets. Current architecture doesn't allow to wrap existing connections
 */
class AMQPLazySocketConnection extends AMQPSocketConnection
{
    /**
     * Gets socket from current connection
     *
     * @deprecated
     */
    public function getSocket()
    {
        $this->connect();

        return parent::getSocket();
    }

    /**
     * {@inheritdoc}
     */
    public function channel($channel_id = null)
    {
        $this->connect();

        return parent::channel($channel_id);
    }

    /**
     * @return null|\PhpAmqpLib\Wire\IO\AbstractIO
     */
    public function getIO()
    {
        if (empty($this->io)) {
            $this->connect();
        }

        return $this->io;
    }

    /**
     * Should the connection be attempted during construction?
     *
     * @return bool
     */
    public function connectOnConstruct()
    {
        return false;
    }
}
<?php
namespace PhpAmqpLib\Connection;

use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Exception\AMQPProtocolConnectionException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Exception\AMQPTimeoutException;
use PhpAmqpLib\Wire\AMQPReader;
use PhpAmqpLib\Wire\AMQPTable;
use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\IO\AbstractIO;
use PhpAmqpLib\Wire\IO\SocketIO;
use PhpAmqpLib\Wire\IO\StreamIO;

class AbstractConnection extends AbstractChannel
{
    /** @var array */
    public static $LIBRARY_PROPERTIES = array(
        'product' => array('S', 'AMQPLib'),
        'platform' => array('S', 'PHP'),
        'version' => array('S', '2.6'),
        'information' => array('S', ''),
        'copyright' => array('S', ''),
        'capabilities' => array(
            'F',
            array(
                'authentication_failure_close' => array('t', true),
                'publisher_confirms' => array('t', true),
                'consumer_cancel_notify' => array('t', true),
                'exchange_exchange_bindings' => array('t', true),
                'basic.nack' => array('t', true),
                'connection.blocked' => array('t', true)
            )
        )
    );

    /** @var AMQPChannel[] */
    public $channels = array();

    /** @var int */
    protected $version_major;

    /** @var int */
    protected $version_minor;

    /** @var array */
    protected $server_properties;

    /** @var array */
    protected $mechanisms;

    /** @var array */
    protected $locales;

    /** @var bool */
    protected $wait_tune_ok;

    /** @var string */
    protected $known_hosts;

    /** @var AMQPReader */
    protected $input;

    /** @var string */
    protected $vhost;

    /** @var bool */
    protected $insist;

    /** @var string */
    protected $login_method;

    /** @var string */
    protected $login_response;

    /** @var string */
    protected $locale;

    /** @var int */
    protected $heartbeat;

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

    /** @var SocketIO */
    protected $sock;

    /** @var int */
    protected $channel_max = 65535;

    /** @var int */
    protected $frame_max = 131072;

     /** @var array Constructor parameters for clone */
    protected $construct_params;

    /** @var bool Close the connection in destructor */
    protected $close_on_destruct = true;

    /** @var bool Maintain connection status */
    protected $is_connected = false;

    /** @var \PhpAmqpLib\Wire\IO\AbstractIO */
    protected $io;

    /** @var \PhpAmqpLib\Wire\AMQPReader */
    protected $wait_frame_reader;

    /** @var callable Handles connection blocking from the server */
    private $connection_block_handler;

    /** @var callable Handles connection unblocking from the server */
    private $connection_unblock_handler;

    /** @var int Connection timeout value*/
    protected $connection_timeout ;

    /**
     * Circular buffer to speed up prepare_content().
     * Max size limited by $prepare_content_cache_max_size.
     *
     * @var array
     * @see prepare_content()
     */
    private $prepare_content_cache;

    /** @var int Maximal size of $prepare_content_cache */
    private $prepare_content_cache_max_size;

    /**
     * @param string $user
     * @param string $password
     * @param string $vhost
     * @param bool $insist
     * @param string $login_method
     * @param null $login_response
     * @param string $locale
     * @param AbstractIO $io
     * @param int $heartbeat
     * @param int $connection_timeout
     * @throws \Exception
     */
    public function __construct(
        $user,
        $password,
        $vhost = '/',
        $insist = false,
        $login_method = 'AMQPLAIN',
        $login_response = null,
        $locale = 'en_US',
        AbstractIO $io,
        $heartbeat = 0,
        $connection_timeout = 0
    ) {
        // save the params for the use of __clone
        $this->construct_params = func_get_args();

        $this->wait_frame_reader = new AMQPReader(null);
        $this->vhost = $vhost;
        $this->insist = $insist;
        $this->login_method = $login_method;
        $this->login_response = $login_response;
        $this->locale = $locale;
        $this->io = $io;
        $this->heartbeat = $heartbeat;
        $this->connection_timeout = $connection_timeout;

        if ($user && $password) {
            $this->login_response = new AMQPWriter();
            $this->login_response->write_table(array(
                'LOGIN' => array('S', $user),
                'PASSWORD' => array('S', $password)
            ));

            // Skip the length
            $responseValue = $this->login_response->getvalue();
            $this->login_response = mb_substr($responseValue, 4, mb_strlen($responseValue, 'ASCII') - 4, 'ASCII');

        } else {
            $this->login_response = null;
        }

        $this->prepare_content_cache = array();
        $this->prepare_content_cache_max_size = 100;

        // Lazy Connection waits on connecting
        if ($this->connectOnConstruct()) {
            $this->connect();
        }
    }

    /**
     * Connects to the AMQP server
     */
    protected function connect()
    {
        try {
            // Loop until we connect
            while (!$this->isConnected()) {
                // Assume we will connect, until we dont
                $this->setIsConnected(true);

                // Connect the socket
                $this->getIO()->connect();

                $this->channels = array();
                // The connection object itself is treated as channel 0
                parent::__construct($this, 0);

                $this->input = new AMQPReader(null, $this->getIO());

                $this->write($this->amqp_protocol_header);
                $this->wait(array($this->waitHelper->get_wait('connection.start')),false,$this->connection_timeout);
                $this->x_start_ok(self::$LIBRARY_PROPERTIES, $this->login_method, $this->login_response, $this->locale);

                $this->wait_tune_ok = true;
                while ($this->wait_tune_ok) {
                    $this->wait(array(
                        $this->waitHelper->get_wait('connection.secure'),
                        $this->waitHelper->get_wait('connection.tune')
                    ));
                }

                $host = $this->x_open($this->vhost, '', $this->insist);
                if (!$host) {
                    //Reconnected
                    if ($this->io instanceof StreamIO)
                    {
                        $this->getIO()->reenableHeartbeat();
                    }
                    return null; // we weren't redirected
                }

                $this->setIsConnected(false);
                $this->closeChannels();

                // we were redirected, close the socket, loop and try again
                $this->close_socket();
            }

        } catch (\Exception $e) {
            // Something went wrong, set the connection status
            $this->setIsConnected(false);
            $this->closeChannels();
            throw $e; // Rethrow exception
        }
    }

    /**
     * Reconnects using the original connection settings.
     * This will not recreate any channels that were established previously
     */
    public function reconnect()
    {
        // Try to close the AMQP connection
        $this->safeClose();
        // Reconnect the socket/stream then AMQP
        $this->getIO()->reconnect();
        $this->setIsConnected(false); // getIO can initiate the connection setting via LazyConnection, set it here to be sure
        $this->connect();
    }

    /**
     * Cloning will use the old properties to make a new connection to the same server
     */
    public function __clone()
    {
        call_user_func_array(array($this, '__construct'), $this->construct_params);
    }

    public function __destruct()
    {
        if ($this->close_on_destruct) {
            $this->safeClose();
        }
    }

    /**
     * Attempts to close the connection safely
     */
    protected function safeClose()
    {
        try {
            if (isset($this->input) && $this->input) {
                $this->close();
            }
        } catch (\Exception $e) {
            // Nothing here
        }
    }

    /**
     * @param int $sec
     * @param int $usec
     * @return mixed
     */
    public function select($sec, $usec = 0)
    {
        return $this->getIO()->select($sec, $usec);
    }

    /**
     * Allows to not close the connection
     * it's useful after the fork when you don't want to close parent process connection
     *
     * @param bool $close
     */
    public function set_close_on_destruct($close = true)
    {
        $this->close_on_destruct = (bool) $close;
    }

    protected function close_input()
    {
        $this->debug->debug_msg('closing input');

        if (!is_null($this->input)) {
            $this->input->close();
            $this->input = null;
        }
    }

    protected function close_socket()
    {
        $this->debug->debug_msg('closing socket');

        if (!is_null($this->getIO())) {
            $this->getIO()->close();
        }
    }

    /**
     * @param $data
     */
    public function write($data)
    {
        $this->debug->debug_hexdump($data);

        try {
            $this->getIO()->write($data);
        } catch (AMQPRuntimeException $e) {
            $this->setIsConnected(false);
            throw $e;
        }
    }

    protected function do_close()
    {
        $this->setIsConnected(false);
        $this->close_input();
        $this->close_socket();
    }

    /**
     * @return int
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    public function get_free_channel_id()
    {
        for ($i = 1; $i <= $this->channel_max; $i++) {
            if (!isset($this->channels[$i])) {
                return $i;
            }
        }

        throw new AMQPRuntimeException('No free channel ids');
    }

    /**
     * @param string $channel
     * @param int $class_id
     * @param int $weight
     * @param int $body_size
     * @param string $packed_properties
     * @param string $body
     * @param AMQPWriter $pkt
     */
    public function send_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt = null)
    {
        $this->prepare_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt);
        $this->write($pkt->getvalue());
    }

    /**
     * Returns a new AMQPWriter or mutates the provided $pkt
     *
     * @param string $channel
     * @param int $class_id
     * @param int $weight
     * @param int $body_size
     * @param string $packed_properties
     * @param string $body
     * @param AMQPWriter $pkt
     * @return AMQPWriter
     */
    public function prepare_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt = null)
    {
        $pkt = $pkt ?: new AMQPWriter();

        // Content already prepared ?
        $key_cache = sprintf(
            '%s|%s|%s|%s',
            $channel,
            $packed_properties,
            $class_id,
            $weight
        );

        if (!isset($this->prepare_content_cache[$key_cache])) {
            $w = new AMQPWriter();
            $w->write_octet(2);
            $w->write_short($channel);
            $w->write_long(mb_strlen($packed_properties, 'ASCII') + 12);
            $w->write_short($class_id);
            $w->write_short($weight);
            $this->prepare_content_cache[$key_cache] = $w->getvalue();
            if (count($this->prepare_content_cache) > $this->prepare_content_cache_max_size) {
                reset($this->prepare_content_cache);
                $old_key = key($this->prepare_content_cache);
                unset($this->prepare_content_cache[$old_key]);
            }
        }
        $pkt->write($this->prepare_content_cache[$key_cache]);

        $pkt->write_longlong($body_size);
        $pkt->write($packed_properties);

        $pkt->write_octet(0xCE);


        // memory efficiency: walk the string instead of biting
        // it. good for very large packets (close in size to
        // memory_limit setting)
        $position = 0;
        $bodyLength = mb_strlen($body,'ASCII');
        while ($position < $bodyLength) {
            $payload = mb_substr($body, $position, $this->frame_max - 8, 'ASCII');
            $position += $this->frame_max - 8;

            $pkt->write_octet(3);
            $pkt->write_short($channel);
            $pkt->write_long(mb_strlen($payload, 'ASCII'));

            $pkt->write($payload);

            $pkt->write_octet(0xCE);
        }

        return $pkt;
    }

    /**
     * @param string $channel
     * @param array $method_sig
     * @param AMQPWriter|string $args
     * @param null $pkt
     */
    protected function send_channel_method_frame($channel, $method_sig, $args = '', $pkt = null)
    {
        $pkt = $this->prepare_channel_method_frame($channel, $method_sig, $args, $pkt);
        $this->write($pkt->getvalue());
        $this->debug->debug_method_signature1($method_sig);
    }

    /**
     * Returns a new AMQPWriter or mutates the provided $pkt
     *
     * @param string $channel
     * @param array $method_sig
     * @param AMQPWriter|string $args
     * @param AMQPWriter $pkt
     * @return AMQPWriter
     */
    protected function prepare_channel_method_frame($channel, $method_sig, $args = '', $pkt = null)
    {
        if ($args instanceof AMQPWriter) {
            $args = $args->getvalue();
        }

        $pkt = $pkt ?: new AMQPWriter();

        $pkt->write_octet(1);
        $pkt->write_short($channel);
        $pkt->write_long(mb_strlen($args, 'ASCII') + 4); // 4 = length of class_id and method_id
        // in payload

        $pkt->write_short($method_sig[0]); // class_id
        $pkt->write_short($method_sig[1]); // method_id
        $pkt->write($args);

        $pkt->write_octet(0xCE);

        $this->debug->debug_method_signature1($method_sig);

        return $pkt;
    }

    /**
     * Waits for a frame from the server
     *
     * @param int $timeout
     * @return array
     * @throws \Exception
     * @throws \PhpAmqpLib\Exception\AMQPTimeoutException
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    protected function wait_frame($timeout = 0)
    {
        if (is_null($this->input))
        {
            $this->setIsConnected(false);
            throw new AMQPRuntimeException('Broken pipe or closed connection');
        }

        $currentTimeout = $this->input->getTimeout();
        $this->input->setTimeout($timeout);

        try {
            // frame_type + channel_id + size
            $this->wait_frame_reader->reuse(
                $this->input->read(AMQPReader::OCTET + AMQPReader::SHORT + AMQPReader::LONG)
            );

            $frame_type = $this->wait_frame_reader->read_octet();
            $class = self::$PROTOCOL_CONSTANTS_CLASS;
            if (!array_key_exists($frame_type, $class::$FRAME_TYPES)) {
                throw new AMQPRuntimeException('Invalid frame type ' . $frame_type);
            }
            $channel = $this->wait_frame_reader->read_short();
            $size = $this->wait_frame_reader->read_long();

            // payload + ch
            $this->wait_frame_reader->reuse($this->input->read(AMQPReader::OCTET + (int) $size));

            $payload = $this->wait_frame_reader->read($size);
            $ch = $this->wait_frame_reader->read_octet();

        } catch (AMQPTimeoutException $e) {
            $this->input->setTimeout($currentTimeout);
            throw $e;
        }

        $this->input->setTimeout($currentTimeout);

        if ($ch != 0xCE) {
            throw new AMQPRuntimeException(sprintf(
                'Framing error, unexpected byte: %x',
                $ch
            ));
        }

        return array($frame_type, $channel, $payload);
    }

    /**
     * Waits for a frame from the server destined for a particular channel.
     *
     * @param string $channel_id
     * @param int $timeout
     * @return array
     */
    protected function wait_channel($channel_id, $timeout = 0)
    {
        // Keeping the original timeout unchanged.
        $_timeout = $timeout;
        while (true) {
            $now = time();
            try {
                list($frame_type, $frame_channel, $payload) = $this->wait_frame($_timeout);
            }
            catch ( AMQPTimeoutException $e ) {
                if ( $this->heartbeat && microtime(true) - ($this->heartbeat*2) > $this->last_frame ) {
                    $this->debug->debug_msg("missed server heartbeat (at threshold * 2)");
                    $this->setIsConnected(false);
                    throw new AMQPRuntimeException("Missed server heartbeat");
                }

                throw $e;
            }

            $this->last_frame = microtime(true);

            if ($frame_channel === 0 && $frame_type === 8) {
                // skip heartbeat frames and reduce the timeout by the time passed
                $this->debug->debug_msg("received server heartbeat");
                if($_timeout > 0) {
                    $_timeout -= time() - $now;
                    if($_timeout <= 0) {
                        // If timeout has been reached, throw the exception without calling wait_frame
                        throw new AMQPTimeoutException("Timeout waiting on channel");
                    }
                }
                continue;

            } else {

                if ($frame_channel == $channel_id) {
                    return array($frame_type, $payload);
                }

                // Not the channel we were looking for.  Queue this frame
                //for later, when the other channel is looking for frames.
                // Make sure the channel still exists, it could have been
                // closed by a previous Exception.
                if (isset($this->channels[$frame_channel])) {
                    array_push($this->channels[$frame_channel]->frame_queue, array($frame_type, $payload));
                }

                // If we just queued up a method for channel 0 (the Connection
                // itself) it's probably a close method in reaction to some
                // error, so deal with it right away.
                if (($frame_type == 1) && ($frame_channel == 0)) {
                    $this->wait();
                }
            }
        }
    }

    /**
     * Fetches a channel object identified by the numeric channel_id, or
     * create that object if it doesn't already exist.
     *
     * @param int $channel_id
     * @return AMQPChannel
     */
    public function channel($channel_id = null)
    {
        if (isset($this->channels[$channel_id])) {
            return $this->channels[$channel_id];
        }

        $channel_id = $channel_id ? $channel_id : $this->get_free_channel_id();
        $ch = new AMQPChannel($this->connection, $channel_id);
        $this->channels[$channel_id] = $ch;

        return $ch;
    }

    /**
     * Requests a connection close
     *
     * @param int $reply_code
     * @param string $reply_text
     * @param array $method_sig
     * @return mixed|null
     */
    public function close($reply_code = 0, $reply_text = '', $method_sig = array(0, 0))
    {
        if ($this->io instanceof StreamIO)
        {
            $this->io->disableHeartbeat();
        }

        if (empty($this->protocolWriter) || !$this->isConnected()) {
            return null;
        }

        $this->closeChannels();

        list($class_id, $method_id, $args) = $this->protocolWriter->connectionClose(
            $reply_code,
            $reply_text,
            $method_sig[0],
            $method_sig[1]
        );
        $this->send_method_frame(array($class_id, $method_id), $args);

        $this->setIsConnected(false);

        return $this->wait(array(
            $this->waitHelper->get_wait('connection.close_ok')
        ),false,$this->connection_timeout);
    }

    /**
     * @param AMQPReader $reader
     * @throws \PhpAmqpLib\Exception\AMQPProtocolConnectionException
     */
    protected function connection_close(AMQPReader $reader)
    {
        $reply_code = $reader->read_short();
        $reply_text = $reader->read_shortstr();
        $class_id = $reader->read_short();
        $method_id = $reader->read_short();

        $this->x_close_ok();

        throw new AMQPProtocolConnectionException($reply_code, $reply_text, array($class_id, $method_id));
    }

    /**
     * Confirms a connection close
     */
    protected function x_close_ok()
    {
        $this->send_method_frame(
            explode(',', $this->waitHelper->get_wait('connection.close_ok'))
        );
        $this->do_close();
    }

    /**
     * Confirm a connection close
     *
     * @param AMQPReader $args
     */
    protected function connection_close_ok($args)
    {
        $this->do_close();
    }

    /**
     * @param string $virtual_host
     * @param string $capabilities
     * @param bool $insist
     * @return mixed
     */
    protected function x_open($virtual_host, $capabilities = '', $insist = false)
    {
        $args = new AMQPWriter();
        $args->write_shortstr($virtual_host);
        $args->write_shortstr($capabilities);
        $args->write_bits(array($insist));
        $this->send_method_frame(array(10, 40), $args);

        $wait = array(
            $this->waitHelper->get_wait('connection.open_ok')
        );

        if ($this->protocolVersion == '0.8') {
            $wait[] = $this->waitHelper->get_wait('connection.redirect');
        }

        return $this->wait($wait);
    }

    /**
     * Signals that the connection is ready
     *
     * @param AMQPReader $args
     */
    protected function connection_open_ok($args)
    {
        $this->known_hosts = $args->read_shortstr();
        $this->debug->debug_msg('Open OK! known_hosts: ' . $this->known_hosts);
    }

    /**
     * Asks the client to use a different server
     *
     * @param AMQPReader $args
     * @return string
     */
    protected function connection_redirect($args)
    {
        $host = $args->read_shortstr();
        $this->known_hosts = $args->read_shortstr();
        $this->debug->debug_msg(sprintf(
                'Redirected to [%s], known_hosts [%s]',
                $host,
                $this->known_hosts
            ));

        return $host;
    }

    /**
     * Security mechanism challenge
     *
     * @param AMQPReader $args
     */
    protected function connection_secure($args)
    {
        $challenge = $args->read_longstr();
    }

    /**
     * Security mechanism response
     *
     * @param string $response
     */
    protected function x_secure_ok($response)
    {
        $args = new AMQPWriter();
        $args->write_longstr($response);
        $this->send_method_frame(array(10, 21), $args);
    }

    /**
     * Starts connection negotiation
     *
     * @param AMQPReader $args
     */
    protected function connection_start($args)
    {
        $this->version_major = $args->read_octet();
        $this->version_minor = $args->read_octet();
        $this->server_properties = $args->read_table();
        $this->mechanisms = explode(' ', $args->read_longstr());
        $this->locales = explode(' ', $args->read_longstr());

        $this->debug->debug_connection_start(
            $this->version_major,
            $this->version_minor,
            $this->server_properties,
            $this->mechanisms,
            $this->locales
        );
    }

    /**
     * @param AMQPTable|array $clientProperties
     * @param string $mechanism
     * @param string $response
     * @param string $locale
     */
    protected function x_start_ok($clientProperties, $mechanism, $response, $locale)
    {
        $args = new AMQPWriter();
        $args->write_table($clientProperties);
        $args->write_shortstr($mechanism);
        $args->write_longstr($response);
        $args->write_shortstr($locale);
        $this->send_method_frame(array(10, 11), $args);
    }

    /**
     * Proposes connection tuning parameters
     *
     * @param AMQPReader $args
     */
    protected function connection_tune($args)
    {
        $v = $args->read_short();
        if ($v) {
            $this->channel_max = $v;
        }

        $v = $args->read_long();
        if ($v) {
            $this->frame_max = $v;
        }

        // use server proposed value if not set
        if ($this->heartbeat === null) {
            $this->heartbeat = $args->read_short();
        }

        $this->x_tune_ok($this->channel_max, $this->frame_max, $this->heartbeat);
    }

    /**
     * Negotiates connection tuning parameters
     *
     * @param int $channel_max
     * @param int $frame_max
     * @param int $heartbeat
     */
    protected function x_tune_ok($channel_max, $frame_max, $heartbeat)
    {
        $args = new AMQPWriter();
        $args->write_short($channel_max);
        $args->write_long($frame_max);
        $args->write_short($heartbeat);
        $this->send_method_frame(array(10, 31), $args);
        $this->wait_tune_ok = false;
    }

    /**
     * @return SocketIO
     */
    public function getSocket()
    {
        return $this->io->getSocket();
    }

    /**
     * @return \PhpAmqpLib\Wire\IO\AbstractIO
     */
    public function getIO()
    {
        return $this->io;
    }

    /**
     * Handles connection blocked notifications
     *
     * @param AMQPReader $args
     */
    protected function connection_blocked(AMQPReader $args)
    {
        // Call the block handler and pass in the reason
        $this->dispatch_to_handler($this->connection_block_handler, array($args->read_shortstr()));
    }

    /**
     * Handles connection unblocked notifications
     *
     * @param AMQPReader $args
     */
    protected function connection_unblocked(AMQPReader $args)
    {
        // No args to an unblock event
        $this->dispatch_to_handler($this->connection_unblock_handler, array());
    }

    /**
     * Sets a handler which is called whenever a connection.block is sent from the server
     *
     * @param callable $callback
     */
    public function set_connection_block_handler($callback)
    {
        $this->connection_block_handler = $callback;
    }

    /**
     * Sets a handler which is called whenever a connection.block is sent from the server
     *
     * @param callable $callback
     */
    public function set_connection_unblock_handler($callback)
    {
        $this->connection_unblock_handler = $callback;
    }

    /**
     * Gets the connection status
     *
     * @return bool
     */
    public function isConnected()
    {
        return (bool) $this->is_connected;
    }

    /**
     * Set the connection status
     *
     * @param bool $is_connected
     */
    protected function setIsConnected($is_connected)
    {
        $this->is_connected = (bool) $is_connected;
    }

    /**
     * Closes all available channels
     */
    protected function closeChannels()
    {
        foreach ($this->channels as $key => $channel) {
            // channels[0] is this connection object, so don't close it yet
            if ($key === 0) {
                continue;
            }
            try {
                $channel->close();
            } catch (\Exception $e) {
                /* Ignore closing errors */
            }
        }
    }

    /**
     * Should the connection be attempted during construction?
     *
     * @return bool
     */
    public function connectOnConstruct()
    {
        return true;
    }

    /**
     * @return array
     */
    public function getServerProperties()
    {
        return $this->server_properties;
    }
}
<?php
namespace PhpAmqpLib\Connection;

/**
 * Class AMQPConnection
 *
 * Kept for BC
 *
 * @deprecated
 */
class AMQPConnection extends AMQPStreamConnection
{
}
<?php
namespace PhpAmqpLib\Connection;

use PhpAmqpLib\Wire\IO\StreamIO;

class AMQPStreamConnection extends AbstractConnection
{
    /**
     * @param string $host
     * @param string $port
     * @param string $user
     * @param string $password
     * @param string $vhost
     * @param bool $insist
     * @param string $login_method
     * @param null $login_response
     * @param string $locale
     * @param float $connection_timeout
     * @param float $read_write_timeout
     * @param null $context
     * @param bool $keepalive
     * @param int $heartbeat
     */
    public function __construct(
        $host,
        $port,
        $user,
        $password,
        $vhost = '/',
        $insist = false,
        $login_method = 'AMQPLAIN',
        $login_response = null,
        $locale = 'en_US',
        $connection_timeout = 3.0,
        $read_write_timeout = 3.0,
        $context = null,
        $keepalive = false,
        $heartbeat = 0
    ) {
        $io = new StreamIO(
            $host,
            $port,
            $connection_timeout,
            $read_write_timeout,
            $context,
            $keepalive,
            $heartbeat
        );

        parent::__construct(
            $user,
            $password,
            $vhost,
            $insist,
            $login_method,
            $login_response,
            $locale,
            $io,
            $heartbeat,
            $connection_timeout
        );

        // save the params for the use of __clone, this will overwrite the parent
        $this->construct_params = func_get_args();
    }
}
<?php
namespace PhpAmqpLib\Message;

use PhpAmqpLib\Wire\GenericContent;

/**
 * A Message for use with the Channnel.basic_* methods.
 */
class AMQPMessage extends GenericContent
{
    const DELIVERY_MODE_NON_PERSISTENT = 1;
    const DELIVERY_MODE_PERSISTENT = 2;

    /** @var string */
    public $body;

    /** @var int */
    public $body_size;

    /** @var bool */
    public $is_truncated = false;

    /** @var string */
    public $content_encoding;

    /** @var array */
    protected static $propertyDefinitions = array(
        'content_type' => 'shortstr',
        'content_encoding' => 'shortstr',
        'application_headers' => 'table_object',
        'delivery_mode' => 'octet',
        'priority' => 'octet',
        'correlation_id' => 'shortstr',
        'reply_to' => 'shortstr',
        'expiration' => 'shortstr',
        'message_id' => 'shortstr',
        'timestamp' => 'timestamp',
        'type' => 'shortstr',
        'user_id' => 'shortstr',
        'app_id' => 'shortstr',
        'cluster_id' => 'shortstr',
    );

    /**
     * @param string $body
     * @param array $properties
     */
    public function __construct($body = '', $properties = array())
    {
        $this->setBody($body);
        parent::__construct($properties, static::$propertyDefinitions);
    }

    /**
     * @return string
     */
    public function getBody()
    {
        return $this->body;
    }

    /**
     * Sets the message payload
     *
     * @param string $body
     * @return $this
     */
    public function setBody($body)
    {
        $this->body = $body;

        return $this;
    }

    /**
     * @return string
     */
    public function getContentEncoding()
    {
        return $this->content_encoding;
    }

    /**
     * @return int
     */
    public function getBodySize()
    {
        return $this->body_size;
    }

    /**
     * @param int $body_size Message body size in byte(s)
     * @return AMQPMessage
     */
    public function setBodySize($body_size)
    {
        $this->body_size = (int) $body_size;

        return $this;
    }

    /**
     * @return boolean
     */
    public function isTruncated()
    {
        return $this->is_truncated;
    }

    /**
     * @param bool $is_truncated
     * @return AMQPMessage
     */
    public function setIsTruncated($is_truncated)
    {
        $this->is_truncated = (bool) $is_truncated;

        return $this;
    }
}
<?php
namespace PhpAmqpLib\Channel;

use PhpAmqpLib\Exception\AMQPBasicCancelException;
use PhpAmqpLib\Exception\AMQPProtocolChannelException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPReader;
use PhpAmqpLib\Wire\AMQPWriter;

class AMQPChannel extends AbstractChannel
{
    /** @var array */
    public $callbacks = array();

    /** @var bool Whether or not the channel has been "opened" */
    protected $is_open = false;

    /** @var int */
    protected $default_ticket;

    /** @var bool */
    protected $active;

    /** @var array */
    protected $alerts;

    /** @var bool */
    protected $auto_decode;

    /**
     * These parameters will be passed to function in case of basic_return:
     *    param int $reply_code
     *    param string $reply_text
     *    param string $exchange
     *    param string $routing_key
     *    param AMQPMessage $msg
     *
     * @var callable
     */
    protected $basic_return_callback;

    /** @var array Used to keep track of the messages that are going to be batch published. */
    protected $batch_messages = array();

    /**
     * If the channel is in confirm_publish mode this array will store all published messages
     * until they get ack'ed or nack'ed
     *
     * @var AMQPMessage[]
     */
    private $published_messages = array();

    /** @var int */
    private $next_delivery_tag = 0;

    /** @var callable */
    private $ack_handler;

    /** @var callable */
    private $nack_handler;

    /**
     * Circular buffer to speed up both basic_publish() and publish_batch().
     * Max size limited by $publish_cache_max_size.
     *
     * @var array
     * @see basic_publish()
     * @see publish_batch()
     */
    private $publish_cache;

    /**
     * Maximal size of $publish_cache
     *
     * @var int
     */
    private $publish_cache_max_size;

    /**
     * @param \PhpAmqpLib\Connection\AbstractConnection $connection
     * @param null $channel_id
     * @param bool $auto_decode
     * @throws \Exception
     */
    public function __construct($connection, $channel_id = null, $auto_decode = true)
    {
        if ($channel_id == null) {
            $channel_id = $connection->get_free_channel_id();
        }

        parent::__construct($connection, $channel_id);

        $this->publish_cache = array();
        $this->publish_cache_max_size = 100;

        $this->debug->debug_msg('using channel_id: ' . $channel_id);

        $this->default_ticket = 0;
        $this->is_open = false;
        $this->active = true; // Flow control
        $this->alerts = array();
        $this->callbacks = array();
        $this->auto_decode = $auto_decode;

        try {
            $this->x_open();
        } catch (\Exception $e) {
            $this->close();
            throw $e;
        }
    }

    /**
     * Tear down this object, after we've agreed to close with the server.
     */
    protected function do_close()
    {
        if ($this->channel_id !== null) {
            unset($this->connection->channels[$this->channel_id]);
        }
        $this->channel_id = $this->connection = null;
        $this->is_open = false;
    }

    /**
     * Only for AMQP0.8.0
     * This method allows the server to send a non-fatal warning to
     * the client.  This is used for methods that are normally
     * asynchronous and thus do not have confirmations, and for which
     * the server may detect errors that need to be reported.  Fatal
     * errors are handled as channel or connection exceptions; non-
     * fatal errors are sent through this method.
     *
     * @param AMQPReader $reader
     */
    protected function channel_alert($reader)
    {
        $reply_code = $reader->read_short();
        $reply_text = $reader->read_shortstr();
        $details = $reader->read_table();
        array_push($this->alerts, array($reply_code, $reply_text, $details));
    }

    /**
     * Request a channel close
     *
     * @param int $reply_code
     * @param string $reply_text
     * @param array $method_sig
     * @return mixed
     */
    public function close($reply_code = 0, $reply_text = '', $method_sig = array(0, 0))
    {
        if ($this->is_open === false || $this->connection === null) {
            $this->do_close();

            return null; // already closed
        }
        list($class_id, $method_id, $args) = $this->protocolWriter->channelClose(
            $reply_code,
            $reply_text,
            $method_sig[0],
            $method_sig[1]
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('channel.close_ok')
        ));
    }

    /**
     * @param AMQPReader $reader
     * @throws \PhpAmqpLib\Exception\AMQPProtocolChannelException
     */
    protected function channel_close($reader)
    {
        $reply_code = $reader->read_short();
        $reply_text = $reader->read_shortstr();
        $class_id = $reader->read_short();
        $method_id = $reader->read_short();

        $this->send_method_frame(array(20, 41));
        $this->do_close();

        throw new AMQPProtocolChannelException($reply_code, $reply_text, array($class_id, $method_id));
    }

    /**
     * Confirm a channel close
     * Alias of AMQPChannel::do_close()
     *
     * @param AMQPReader $reader
     */
    protected function channel_close_ok($reader)
    {
        $this->do_close();
    }

    /**
     * Enables/disables flow from peer
     *
     * @param $active
     * @return mixed
     */
    public function flow($active)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->channelFlow($active);
        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('channel.flow_ok')
        ));
    }

    /**
     * @param AMQPReader $reader
     */
    protected function channel_flow($reader)
    {
        $this->active = $reader->read_bit();
        $this->x_flow_ok($this->active);
    }

    /**
     * @param bool $active
     */
    protected function x_flow_ok($active)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->channelFlow($active);
        $this->send_method_frame(array($class_id, $method_id), $args);
    }

    /**
     * @param AMQPReader $reader
     * @return bool
     */
    protected function channel_flow_ok($reader)
    {
        return $reader->read_bit();
    }

    /**
     * @param string $out_of_band
     * @return mixed
     */
    protected function x_open($out_of_band = '')
    {
        if ($this->is_open) {
            return null;
        }

        list($class_id, $method_id, $args) = $this->protocolWriter->channelOpen($out_of_band);
        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('channel.open_ok')
        ));
    }

    /**
     * @param AMQPReader $reader
     */
    protected function channel_open_ok($reader)
    {
        $this->is_open = true;

        $this->debug->debug_msg('Channel open');
    }

    /**
     * Requests an access ticket
     *
     * @param string $realm
     * @param bool $exclusive
     * @param bool $passive
     * @param bool $active
     * @param bool $write
     * @param bool $read
     * @return mixed
     */
    public function access_request(
        $realm,
        $exclusive = false,
        $passive = false,
        $active = false,
        $write = false,
        $read = false
    ) {
        list($class_id, $method_id, $args) = $this->protocolWriter->accessRequest(
            $realm,
            $exclusive,
            $passive,
            $active,
            $write,
            $read
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('access.request_ok')
        ));
    }

    /**
     * Grants access to server resources
     *
     * @param AMQPReader $reader
     * @return string
     */
    protected function access_request_ok($reader)
    {
        $this->default_ticket = $reader->read_short();

        return $this->default_ticket;
    }

    /**
     * Declares exchange
     *
     * @param string $exchange
     * @param string $type
     * @param bool $passive
     * @param bool $durable
     * @param bool $auto_delete
     * @param bool $internal
     * @param bool $nowait
     * @param array $arguments
     * @param int $ticket
     * @return mixed|null
     */
    public function exchange_declare(
        $exchange,
        $type,
        $passive = false,
        $durable = false,
        $auto_delete = true,
        $internal = false,
        $nowait = false,
        $arguments = array(),
        $ticket = null
    ) {
        $ticket = $this->getTicket($ticket);

        list($class_id, $method_id, $args) = $this->protocolWriter->exchangeDeclare(
            $ticket,
            $exchange,
            $type,
            $passive,
            $durable,
            $auto_delete,
            $internal,
            $nowait,
            $arguments
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('exchange.declare_ok')
        ));
    }

    /**
     * Confirms an exchange declaration
     * @param AMQPReader $reader
     */
    protected function exchange_declare_ok($reader)
    {
    }

    /**
     * Deletes an exchange
     *
     * @param string $exchange
     * @param bool $if_unused
     * @param bool $nowait
     * @param null $ticket
     * @return mixed|null
     */
    public function exchange_delete(
        $exchange,
        $if_unused = false,
        $nowait = false,
        $ticket = null
    ) {
        $ticket = $this->getTicket($ticket);
        list($class_id, $method_id, $args) = $this->protocolWriter->exchangeDelete(
            $ticket,
            $exchange,
            $if_unused,
            $nowait
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('exchange.delete_ok')
        ));
    }

    /**
     * Confirms deletion of an exchange
     *
     * @param AMQPReader $reader
     */
    protected function exchange_delete_ok($reader)
    {
    }

    /**
     * Binds dest exchange to source exchange
     *
     * @param string $destination
     * @param string $source
     * @param string $routing_key
     * @param bool $nowait
     * @param array $arguments
     * @param int $ticket
     * @return mixed|null
     */
    public function exchange_bind(
        $destination,
        $source,
        $routing_key = '',
        $nowait = false,
        $arguments = array(),
        $ticket = null
    ) {
        $ticket = $this->getTicket($ticket);

        list($class_id, $method_id, $args) = $this->protocolWriter->exchangeBind(
            $ticket,
            $destination,
            $source,
            $routing_key,
            $nowait,
            $arguments
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('exchange.bind_ok')
        ));
    }

    /**
     * Confirms bind successful
     * @param AMQPReader $reader
     */
    protected function exchange_bind_ok($reader)
    {
    }

    /**
     * Unbinds dest exchange from source exchange
     *
     * @param string $destination
     * @param string $source
     * @param string $routing_key
     * @param bool $nowait
     * @param array $arguments
     * @param int $ticket
     * @return mixed
     */
    public function exchange_unbind(
        $destination,
        $source,
        $routing_key = '',
        $nowait = false,
        $arguments = array(),
        $ticket = null
    ) {
        $ticket = $this->getTicket($ticket);

        list($class_id, $method_id, $args) = $this->protocolWriter->exchangeUnbind(
            $ticket,
            $destination,
            $source,
            $routing_key,
            $nowait,
            $arguments
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('exchange.unbind_ok')
        ));
    }

    /**
     * Confirms unbind successful
     *
     * @param AMQPReader $reader
     */
    protected function exchange_unbind_ok($reader)
    {
    }

    /**
     * Binds queue to an exchange
     *
     * @param string $queue
     * @param string $exchange
     * @param string $routing_key
     * @param bool $nowait
     * @param array $arguments
     * @param int $ticket
     * @return mixed|null
     */
    public function queue_bind(
        $queue,
        $exchange,
        $routing_key = '',
        $nowait = false,
        $arguments = array(),
        $ticket = null
    ) {
        $ticket = $this->getTicket($ticket);

        list($class_id, $method_id, $args) = $this->protocolWriter->queueBind(
            $ticket,
            $queue,
            $exchange,
            $routing_key,
            $nowait,
            $arguments
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('queue.bind_ok')
        ));
    }

    /**
     * Confirms bind successful
     *
     * @param AMQPReader $reader
     */
    protected function queue_bind_ok($reader)
    {
    }

    /**
     * Unbind queue from an exchange
     *
     * @param string $queue
     * @param string $exchange
     * @param string $routing_key
     * @param array $arguments
     * @param int $ticket
     * @return mixed
     */
    public function queue_unbind(
        $queue,
        $exchange,
        $routing_key = '',
        $arguments = array(),
        $ticket = null
    ) {
        $ticket = $this->getTicket($ticket);

        list($class_id, $method_id, $args) = $this->protocolWriter->queueUnbind(
            $ticket,
            $queue,
            $exchange,
            $routing_key,
            $arguments
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('queue.unbind_ok')
        ));
    }

    /**
     * Confirms unbind successful
     * @param AMQPReader $reader
     */
    protected function queue_unbind_ok($reader)
    {
    }

    /**
     * Declares queue, creates if needed
     *
     * @param string $queue
     * @param bool $passive
     * @param bool $durable
     * @param bool $exclusive
     * @param bool $auto_delete
     * @param bool $nowait
     * @param array $arguments
     * @param int $ticket
     * @return mixed|null
     */
    public function queue_declare(
        $queue = '',
        $passive = false,
        $durable = false,
        $exclusive = false,
        $auto_delete = true,
        $nowait = false,
        $arguments = array(),
        $ticket = null
    ) {
        $ticket = $this->getTicket($ticket);

        list($class_id, $method_id, $args) = $this->protocolWriter->queueDeclare(
            $ticket,
            $queue,
            $passive,
            $durable,
            $exclusive,
            $auto_delete,
            $nowait,
            $arguments
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('queue.declare_ok')
        ));
    }

    /**
     * Confirms a queue definition
     *
     * @param AMQPReader $reader
     * @return string[]
     */
    protected function queue_declare_ok($reader)
    {
        $queue = $reader->read_shortstr();
        $message_count = $reader->read_long();
        $consumer_count = $reader->read_long();

        return array($queue, $message_count, $consumer_count);
    }

    /**
     * Deletes a queue
     *
     * @param string $queue
     * @param bool $if_unused
     * @param bool $if_empty
     * @param bool $nowait
     * @param int $ticket
     * @return mixed|null
     */
    public function queue_delete($queue = '', $if_unused = false, $if_empty = false, $nowait = false, $ticket = null)
    {
        $ticket = $this->getTicket($ticket);

        list($class_id, $method_id, $args) = $this->protocolWriter->queueDelete(
            $ticket,
            $queue,
            $if_unused,
            $if_empty,
            $nowait
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('queue.delete_ok')
        ));
    }

    /**
     * Confirms deletion of a queue
     *
     * @param AMQPReader $reader
     * @return string
     */
    protected function queue_delete_ok($reader)
    {
        return $reader->read_long();
    }

    /**
     * Purges a queue
     *
     * @param string $queue
     * @param bool $nowait
     * @param int $ticket
     * @return mixed|null
     */
    public function queue_purge($queue = '', $nowait = false, $ticket = null)
    {
        $ticket = $this->getTicket($ticket);
        list($class_id, $method_id, $args) = $this->protocolWriter->queuePurge($ticket, $queue, $nowait);

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('queue.purge_ok')
        ));
    }

    /**
     * Confirms a queue purge
     *
     * @param AMQPReader $reader
     * @return string
     */
    protected function queue_purge_ok($reader)
    {
        return $reader->read_long();
    }

    /**
     * Acknowledges one or more messages
     *
     * @param string $delivery_tag
     * @param bool $multiple
     */
    public function basic_ack($delivery_tag, $multiple = false)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->basicAck($delivery_tag, $multiple);
        $this->send_method_frame(array($class_id, $method_id), $args);
    }

    /**
     * Called when the server sends a basic.ack
     *
     * @param AMQPReader $reader
     * @throws AMQPRuntimeException
     */
    protected function basic_ack_from_server(AMQPReader $reader)
    {
        $delivery_tag = $reader->read_longlong();
        $multiple = (bool) $reader->read_bit();

        if (!isset($this->published_messages[$delivery_tag])) {
            throw new AMQPRuntimeException(sprintf(
                'Server ack\'ed unknown delivery_tag "%s"',
                $delivery_tag
            ));
        }

        $this->internal_ack_handler($delivery_tag, $multiple, $this->ack_handler);
    }

    /**
     * Called when the server sends a basic.nack
     *
     * @param AMQPReader $reader
     * @throws AMQPRuntimeException
     */
    protected function basic_nack_from_server($reader)
    {
        $delivery_tag = $reader->read_longlong();
        $multiple = (bool) $reader->read_bit();

        if (!isset($this->published_messages[$delivery_tag])) {
            throw new AMQPRuntimeException(sprintf(
                'Server nack\'ed unknown delivery_tag "%s"',
                $delivery_tag
            ));
        }

        $this->internal_ack_handler($delivery_tag, $multiple, $this->nack_handler);
    }

    /**
     * Handles the deletion of messages from this->publishedMessages and dispatches them to the $handler
     *
     * @param string $delivery_tag
     * @param bool $multiple
     * @param callable $handler
     */
    protected function internal_ack_handler($delivery_tag, $multiple, $handler)
    {
        if ($multiple) {
            $keys = $this->get_keys_less_or_equal($this->published_messages, $delivery_tag);

            foreach ($keys as $key) {
                $this->internal_ack_handler($key, false, $handler);
            }

        } else {
            $message = $this->get_and_unset_message($delivery_tag);
            $message->delivery_info['delivery_tag'] = $delivery_tag;
            $this->dispatch_to_handler($handler, array($message));
        }
    }

    /**
     * @param AMQPMessage[] $messages
     * @param string $value
     * @return mixed
     */
    protected function get_keys_less_or_equal(array $messages, $value)
    {
        $keys = array_reduce(
            array_keys($messages),

            /**
             * @param string $key
             */
            function ($keys, $key) use ($value) {
                if (bccomp($key, $value, 0) <= 0) {
                    $keys[] = $key;
                }

                return $keys;
            },
            array()
        );

        return $keys;
    }

    /**
     * Rejects one or several received messages
     *
     * @param string $delivery_tag
     * @param bool $multiple
     * @param bool $requeue
     */
    public function basic_nack($delivery_tag, $multiple = false, $requeue = false)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->basicNack($delivery_tag, $multiple, $requeue);
        $this->send_method_frame(array($class_id, $method_id), $args);
    }

    /**
     * Ends a queue consumer
     *
     * @param string $consumer_tag
     * @param bool $nowait
     * @param bool $noreturn
     * @return mixed
     */
    public function basic_cancel($consumer_tag, $nowait = false, $noreturn = false)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->basicCancel($consumer_tag, $nowait);
        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait || $noreturn) {
            unset($this->callbacks[$consumer_tag]);
            return $consumer_tag;
        }

        return $this->wait(array(
            $this->waitHelper->get_wait('basic.cancel_ok')
        ));
    }

    /**
     * @param AMQPReader $reader
     * @throws \PhpAmqpLib\Exception\AMQPBasicCancelException
     */
    protected function basic_cancel_from_server(AMQPReader $reader)
    {
        throw new AMQPBasicCancelException($reader->read_shortstr());
    }

    /**
     * Confirm a cancelled consumer
     *
     * @param AMQPReader $reader
     * @return string
     */
    protected function basic_cancel_ok($reader)
    {
        $consumerTag = $reader->read_shortstr();
        unset($this->callbacks[$consumerTag]);

        return $consumerTag;
    }

    /**
     * Starts a queue consumer
     *
     * @param string $queue
     * @param string $consumer_tag
     * @param bool $no_local
     * @param bool $no_ack
     * @param bool $exclusive
     * @param bool $nowait
     * @param callable|null $callback
     * @param int|null $ticket
     * @param array $arguments
     * @return mixed|string
     */
    public function basic_consume(
        $queue = '',
        $consumer_tag = '',
        $no_local = false,
        $no_ack = false,
        $exclusive = false,
        $nowait = false,
        $callback = null,
        $ticket = null,
        $arguments = array()
    ) {
        $ticket = $this->getTicket($ticket);
        list($class_id, $method_id, $args) = $this->protocolWriter->basicConsume(
            $ticket,
            $queue,
            $consumer_tag,
            $no_local,
            $no_ack,
            $exclusive,
            $nowait,
            $this->protocolVersion == '0.9.1' ? $arguments : null
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        if (false === $nowait) {
            $consumer_tag = $this->wait(array(
                $this->waitHelper->get_wait('basic.consume_ok')
            ));
        }

        $this->callbacks[$consumer_tag] = $callback;

        return $consumer_tag;
    }

    /**
     * Confirms a new consumer
     *
     * @param AMQPReader $reader
     * @return string
     */
    protected function basic_consume_ok($reader)
    {
        return $reader->read_shortstr();
    }

    /**
     * Notifies the client of a consumer message
     *
     * @param AMQPReader $reader
     * @param AMQPMessage $message
     */
    protected function basic_deliver($reader, $message)
    {
        $consumer_tag = $reader->read_shortstr();
        $delivery_tag = $reader->read_longlong();
        $redelivered = $reader->read_bit();
        $exchange = $reader->read_shortstr();
        $routing_key = $reader->read_shortstr();

        $message->delivery_info = array(
            'channel' => $this,
            'consumer_tag' => $consumer_tag,
            'delivery_tag' => $delivery_tag,
            'redelivered' => $redelivered,
            'exchange' => $exchange,
            'routing_key' => $routing_key
        );

        if (isset($this->callbacks[$consumer_tag])) {
            call_user_func($this->callbacks[$consumer_tag], $message);
        }
    }

    /**
     * Direct access to a queue if no message was available in the queue, return null
     *
     * @param string $queue
     * @param bool $no_ack
     * @param int $ticket
     * @return mixed
     */
    public function basic_get($queue = '', $no_ack = false, $ticket = null)
    {
        $ticket = $this->getTicket($ticket);
        list($class_id, $method_id, $args) = $this->protocolWriter->basicGet($ticket, $queue, $no_ack);

        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('basic.get_ok'),
            $this->waitHelper->get_wait('basic.get_empty')
        ));
    }

    /**
     * Indicates no messages available
     *
     * @param AMQPReader $reader
     */
    protected function basic_get_empty($reader)
    {
    }

    /**
     * Provides client with a message
     *
     * @param AMQPReader $reader
     * @param AMQPMessage $message
     * @return AMQPMessage
     */
    protected function basic_get_ok($reader, $message)
    {
        $delivery_tag = $reader->read_longlong();
        $redelivered = $reader->read_bit();
        $exchange = $reader->read_shortstr();
        $routing_key = $reader->read_shortstr();
        $message_count = $reader->read_long();

        $message->delivery_info = array(
            'delivery_tag' => $delivery_tag,
            'redelivered' => $redelivered,
            'exchange' => $exchange,
            'routing_key' => $routing_key,
            'message_count' => $message_count
        );

        return $message;
    }

    /**
     * @param string $exchange
     * @param string $routing_key
     * @param $mandatory
     * @param $immediate
     * @param int $ticket
     * @return mixed
     */
    private function pre_publish($exchange, $routing_key, $mandatory, $immediate, $ticket)
    {
        $cache_key = sprintf(
            '%s|%s|%s|%s|%s',
            $exchange,
            $routing_key,
            $mandatory,
            $immediate,
            $ticket
        );
        if (false === isset($this->publish_cache[$cache_key])) {
            $ticket = $this->getTicket($ticket);
            list($class_id, $method_id, $args) = $this->protocolWriter->basicPublish(
                $ticket,
                $exchange,
                $routing_key,
                $mandatory,
                $immediate
            );

            $pkt = $this->prepare_method_frame(array($class_id, $method_id), $args);
            $this->publish_cache[$cache_key] = $pkt->getvalue();
            if (count($this->publish_cache) > $this->publish_cache_max_size) {
                reset($this->publish_cache);
                $old_key = key($this->publish_cache);
                unset($this->publish_cache[$old_key]);
            }
        }

        return $this->publish_cache[$cache_key];
    }

    /**
     * Publishes a message
     *
     * @param AMQPMessage $msg
     * @param string $exchange
     * @param string $routing_key
     * @param bool $mandatory
     * @param bool $immediate
     * @param int $ticket
     */
    public function basic_publish(
        $msg,
        $exchange = '',
        $routing_key = '',
        $mandatory = false,
        $immediate = false,
        $ticket = null
    ) {
        $pkt = new AMQPWriter();
        $pkt->write($this->pre_publish($exchange, $routing_key, $mandatory, $immediate, $ticket));

        $this->connection->send_content(
            $this->channel_id,
            60,
            0,
            mb_strlen($msg->body, 'ASCII'),
            $msg->serialize_properties(),
            $msg->body,
            $pkt
        );

        if ($this->next_delivery_tag > 0) {
            $this->published_messages[$this->next_delivery_tag] = $msg;
            $this->next_delivery_tag = bcadd($this->next_delivery_tag, '1', 0);
        }
    }

    /**
     * @param AMQPMessage $msg
     * @param string $exchange
     * @param string $routing_key
     * @param bool $mandatory
     * @param bool $immediate
     * @param int $ticket
     */
    public function batch_basic_publish(
        $msg,
        $exchange = '',
        $routing_key = '',
        $mandatory = false,
        $immediate = false,
        $ticket = null
    ) {
        $this->batch_messages[] = func_get_args();
    }

    /**
     * Publish batch
     *
     * @return void
     */
    public function publish_batch()
    {
        if (empty($this->batch_messages)) {
            return null;
        }

        /** @var AMQPWriter $pkt */
        $pkt = new AMQPWriter();

        /** @var AMQPMessage $msg */
        foreach ($this->batch_messages as $m) {
            $msg = $m[0];

            $exchange = isset($m[1]) ? $m[1] : '';
            $routing_key = isset($m[2]) ? $m[2] : '';
            $mandatory = isset($m[3]) ? $m[3] : false;
            $immediate = isset($m[4]) ? $m[4] : false;
            $ticket = isset($m[5]) ? $m[5] : null;
            $pkt->write($this->pre_publish($exchange, $routing_key, $mandatory, $immediate, $ticket));

            $this->connection->prepare_content(
                $this->channel_id,
                60,
                0,
                mb_strlen($msg->body, 'ASCII'),
                $msg->serialize_properties(),
                $msg->body,
                $pkt
            );

            if ($this->next_delivery_tag > 0) {
                $this->published_messages[$this->next_delivery_tag] = $msg;
                $this->next_delivery_tag = bcadd($this->next_delivery_tag, '1', 0);
            }
        }

        //call write here
        $this->connection->write($pkt->getvalue());
        $this->batch_messages = array();
    }

    /**
     * Specifies QoS
     *
     * @param int $prefetch_size
     * @param int $prefetch_count
     * @param bool $a_global
     * @return mixed
     */
    public function basic_qos($prefetch_size, $prefetch_count, $a_global)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->basicQos(
            $prefetch_size,
            $prefetch_count,
            $a_global
        );

        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('basic.qos_ok')
        ));
    }

    /**
     * Confirms QoS request
     * @param AMQPReader $reader
     */
    protected function basic_qos_ok($reader)
    {
    }

    /**
     * Redelivers unacknowledged messages
     *
     * @param bool $requeue
     * @return mixed
     */
    public function basic_recover($requeue = false)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->basicRecover($requeue);
        $this->send_method_frame(array($class_id, $method_id), $args);

        return $this->wait(array(
            $this->waitHelper->get_wait('basic.recover_ok')
        ));
    }

    /**
     * Confirm the requested recover
     * @param AMQPReader $reader
     */
    protected function basic_recover_ok($reader)
    {
    }

    /**
     * Rejects an incoming message
     *
     * @param string $delivery_tag
     * @param bool $requeue
     */
    public function basic_reject($delivery_tag, $requeue)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->basicReject($delivery_tag, $requeue);
        $this->send_method_frame(array($class_id, $method_id), $args);
    }

    /**
     * Returns a failed message
     *
     * @param AMQPReader $reader
     * @param AMQPMessage $message
     * @return null
     */
    protected function basic_return($reader, $message)
    {
        $callback = $this->basic_return_callback;
        if (!is_callable($callback)) {
            $this->debug->debug_msg('Skipping unhandled basic_return message');
            return null;
        }

        $reply_code = $reader->read_short();
        $reply_text = $reader->read_shortstr();
        $exchange = $reader->read_shortstr();
        $routing_key = $reader->read_shortstr();

        call_user_func_array($callback, array(
            $reply_code,
            $reply_text,
            $exchange,
            $routing_key,
            $message,
        ));
    }

    /**
     * @return mixed
     */
    public function tx_commit()
    {
        $this->send_method_frame(array(90, 20));

        return $this->wait(array(
            $this->waitHelper->get_wait('tx.commit_ok')
        ));
    }

    /**
     * Confirms a successful commit
     * @param AMQPReader $reader
     */
    protected function tx_commit_ok($reader)
    {
    }

    /**
     * Rollbacks the current transaction
     *
     * @return mixed
     */
    public function tx_rollback()
    {
        $this->send_method_frame(array(90, 30));

        return $this->wait(array(
            $this->waitHelper->get_wait('tx.rollback_ok')
        ));
    }

    /**
     * Confirms a successful rollback
     *
     * @param AMQPReader $reader
     */
    protected function tx_rollback_ok($reader)
    {
    }

    /**
     * Puts the channel into confirm mode
     * Beware that only non-transactional channels may be put into confirm mode and vice versa
     *
     * @param bool $nowait
     * @return null
     */
    public function confirm_select($nowait = false)
    {
        list($class_id, $method_id, $args) = $this->protocolWriter->confirmSelect($nowait);

        $this->send_method_frame(array($class_id, $method_id), $args);

        if ($nowait) {
            return null;
        }

        $this->wait(array($this->waitHelper->get_wait('confirm.select_ok')));
        $this->next_delivery_tag = 1;
    }

    /**
     * Confirms a selection
     *
     * @param AMQPReader $reader
     */
    public function confirm_select_ok($reader)
    {
    }

    /**
     * Waits for pending acks and nacks from the server.
     * If there are no pending acks, the method returns immediately
     *
     * @param int $timeout Waits until $timeout value is reached
     */
    public function wait_for_pending_acks($timeout = 0)
    {
        $functions = array(
            $this->waitHelper->get_wait('basic.ack'),
            $this->waitHelper->get_wait('basic.nack'),
        );

        while (count($this->published_messages) !== 0) {
            if ($timeout > 0) {
                $this->wait($functions, true, $timeout);
            } else {
                $this->wait($functions);
            }
        }
    }

    /**
     * Waits for pending acks, nacks and returns from the server. If there are no pending acks, the method returns immediately.
     *
     * @param int $timeout If set to value > 0 the method will wait at most $timeout seconds for pending acks.
     */
    public function wait_for_pending_acks_returns($timeout = 0)
    {
        $functions = array(
            $this->waitHelper->get_wait('basic.ack'),
            $this->waitHelper->get_wait('basic.nack'),
            $this->waitHelper->get_wait('basic.return'),
        );

        while (count($this->published_messages) !== 0) {
            if ($timeout > 0) {
                $this->wait($functions, true, $timeout);
            } else {
                $this->wait($functions);
            }
        }
    }

    /**
     * Selects standard transaction mode
     *
     * @return mixed
     */
    public function tx_select()
    {
        $this->send_method_frame(array(90, 10));

        return $this->wait(array(
            $this->waitHelper->get_wait('tx.select_ok')
        ));
    }

    /**
     * Confirms transaction mode
     * @param AMQPReader $reader
     */
    protected function tx_select_ok($reader)
    {
    }

    /**
     * @param array $arguments
     * @return array
     */
    protected function getArguments($arguments)
    {
        @trigger_error(sprintf(
            'Method "%s" is deprecated, please use an array as a default argument instead',
            __METHOD__
        ), E_USER_DEPRECATED);
        return (null === $arguments) ? array() : $arguments;
    }

    /**
     * @param int $ticket
     * @return int
     */
    protected function getTicket($ticket)
    {
        return (null === $ticket) ? $this->default_ticket : $ticket;
    }

    /**
     * Helper method to get a particular method from $this->publishedMessages, removes it from the array and returns it.
     *
     * @param int $index
     * @return AMQPMessage
     */
    protected function get_and_unset_message($index)
    {
        $message = $this->published_messages[$index];
        unset($this->published_messages[$index]);

        return $message;
    }

    /**
     * Sets callback for basic_return
     *
     * @param  callable $callback
     * @throws \InvalidArgumentException if $callback is not callable
     */
    public function set_return_listener($callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException(sprintf(
                'Given callback "%s" should be callable. %s type was given.',
                $callback,
                gettype($callback)
            ));
        }

        $this->basic_return_callback = $callback;
    }

    /**
     * Sets a handler which called for any message nack'ed by the server, with the AMQPMessage as first argument.
     *
     * @param callable $callback
     * @throws \InvalidArgumentException
     */
    public function set_nack_handler($callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException(sprintf(
                'Given callback "%s" should be callable. %s type was given.',
                $callback,
                gettype($callback)
            ));
        }

        $this->nack_handler = $callback;
    }

    /**
     * Sets a handler which called for any message ack'ed by the server, with the AMQPMessage as first argument.
     *
     * @param callable $callback
     * @throws \InvalidArgumentException
     */
    public function set_ack_handler($callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException(sprintf(
                'Given callback "%s" should be callable. %s type was given.',
                $callback,
                gettype($callback)
            ));
        }

        $this->ack_handler = $callback;
    }
}
<?php
namespace PhpAmqpLib\Channel;

use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPOutOfBoundsException;
use PhpAmqpLib\Exception\AMQPOutOfRangeException;
use PhpAmqpLib\Exception\AMQPRuntimeException;
use PhpAmqpLib\Helper\DebugHelper;
use PhpAmqpLib\Helper\Protocol\MethodMap080;
use PhpAmqpLib\Helper\Protocol\MethodMap091;
use PhpAmqpLib\Helper\Protocol\Protocol080;
use PhpAmqpLib\Helper\Protocol\Protocol091;
use PhpAmqpLib\Helper\Protocol\Wait080;
use PhpAmqpLib\Helper\Protocol\Wait091;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPReader;

abstract class AbstractChannel
{
    const PROTOCOL_080 = '0.8';
    const PROTOCOL_091 = '0.9.1';

    public static $PROTOCOL_CONSTANTS_CLASS;

    /** @var array */
    protected $frame_queue;

    /** @var array */
    protected $method_queue;

    /** @var bool */
    protected $auto_decode;

    /** @var string */
    protected $amqp_protocol_header;

    /** @var \PhpAmqpLib\Helper\DebugHelper */
    protected $debug;

    /** @var \PhpAmqpLib\Connection\AbstractConnection */
    protected $connection;

    /** @var string */
    protected $protocolVersion;

    /** @var int */
    protected $maxBodySize;

    /** @var \PhpAmqpLib\Helper\Protocol\Protocol080|\PhpAmqpLib\Helper\Protocol\Protocol091 */
    protected $protocolWriter;

    /** @var \PhpAmqpLib\Helper\Protocol\Wait080|\PhpAmqpLib\Helper\Protocol\Wait091 */
    protected $waitHelper;

    /** @var \PhpAmqpLib\Helper\Protocol\MethodMap080|\PhpAmqpLib\Helper\Protocol\MethodMap091 */
    protected $methodMap;

    /** @var string */
    protected $channel_id;

    /** @var \PhpAmqpLib\Wire\AMQPReader */
    protected $msg_property_reader;

    /** @var \PhpAmqpLib\Wire\AMQPReader */
    protected $wait_content_reader;

    /** @var \PhpAmqpLib\Wire\AMQPReader */
    protected $dispatch_reader;

    /**
     * @param AbstractConnection $connection
     * @param string $channel_id
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    public function __construct(AbstractConnection $connection, $channel_id)
    {
        $this->connection = $connection;
        $this->channel_id = $channel_id;
        $connection->channels[$channel_id] = $this;
        $this->frame_queue = array(); // Lower level queue for frames
        $this->method_queue = array(); // Higher level queue for methods
        $this->auto_decode = false;

        $this->msg_property_reader = new AMQPReader(null);
        $this->wait_content_reader = new AMQPReader(null);
        $this->dispatch_reader = new AMQPReader(null);

        $this->protocolVersion = self::getProtocolVersion();
        switch ($this->protocolVersion) {
            case self::PROTOCOL_091:
                self::$PROTOCOL_CONSTANTS_CLASS = 'PhpAmqpLib\Wire\Constants091';
                $c = self::$PROTOCOL_CONSTANTS_CLASS;
                $this->debug = new DebugHelper($c);
                $this->amqp_protocol_header = $c::$AMQP_PROTOCOL_HEADER;
                $this->protocolWriter = new Protocol091();
                $this->waitHelper = new Wait091();
                $this->methodMap = new MethodMap091();
                break;
            case self::PROTOCOL_080:
                self::$PROTOCOL_CONSTANTS_CLASS = 'PhpAmqpLib\Wire\Constants080';
                $c = self::$PROTOCOL_CONSTANTS_CLASS;
                $this->debug = new DebugHelper($c);
                $this->amqp_protocol_header = $c::$AMQP_PROTOCOL_HEADER;
                $this->protocolWriter = new Protocol080();
                $this->waitHelper = new Wait080();
                $this->methodMap = new MethodMap080();
                break;
            default:
                throw new AMQPRuntimeException(sprintf(
                    'Protocol: %s not implemented.',
                    $this->protocolVersion
                ));
        }
    }

    /**
     * @return string
     * @throws AMQPOutOfRangeException
     */
    public static function getProtocolVersion()
    {
        $protocol = defined('AMQP_PROTOCOL') ? AMQP_PROTOCOL : self::PROTOCOL_091;
        //adding check here to catch unknown protocol ASAP, as this method may be called from the outside
        if (!in_array($protocol, array(self::PROTOCOL_080, self::PROTOCOL_091), TRUE)) {
            throw new AMQPOutOfRangeException(sprintf('Protocol version %s not implemented.', $protocol));
        }

        return $protocol;
    }

    /**
     * @return string
     */
    public function getChannelId()
    {
        return $this->channel_id;
    }

    /**
     * @param int $max_bytes Max message body size for this channel
     * @return $this
     */
    public function setBodySizeLimit($max_bytes)
    {
        $max_bytes = (int) $max_bytes;

        if ($max_bytes > 0) {
            $this->maxBodySize = $max_bytes;
        }

        return $this;
    }

    /**
     * @return AbstractConnection
     */
    public function getConnection()
    {
        return $this->connection;
    }

    /**
     * @return array
     */
    public function getMethodQueue()
    {
        return $this->method_queue;
    }

    /**
     * @return bool
     */
    public function hasPendingMethods()
    {
        return !empty($this->method_queue);
    }

    /**
     * @param string $method_sig
     * @param string $args
     * @param AMQPMessage|null $amqpMessage
     * @return mixed
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    public function dispatch($method_sig, $args, $amqpMessage)
    {
        if (!$this->methodMap->valid_method($method_sig)) {
            throw new AMQPRuntimeException(sprintf(
                'Unknown AMQP method "%s"',
                $method_sig
            ));
        }

        $amqp_method = $this->methodMap->get_method($method_sig);

        if (!method_exists($this, $amqp_method)) {
            throw new AMQPRuntimeException(sprintf(
                'Method: "%s" not implemented by class: %s',
                $amqp_method,
                get_class($this)
            ));
        }

        $this->dispatch_reader->reuse($args);

        if ($amqpMessage == null) {
            return call_user_func(array($this, $amqp_method), $this->dispatch_reader);
        }

        return call_user_func(array($this, $amqp_method), $this->dispatch_reader, $amqpMessage);
    }

    /**
     * @param int $timeout
     * @return array|mixed
     */
    public function next_frame($timeout = 0)
    {
        $this->debug->debug_msg('waiting for a new frame');

        if (!empty($this->frame_queue)) {
            return array_shift($this->frame_queue);
        }

        return $this->connection->wait_channel($this->channel_id, $timeout);
    }

    /**
     * @param array $method_sig
     * @param \PhpAmqpLib\Wire\AMQPWriter|string $args
     */
    protected function send_method_frame($method_sig, $args = '')
    {
        if ($this->connection === null) {
            throw new AMQPRuntimeException('Channel connection is closed.');
        }

        $this->connection->send_channel_method_frame($this->channel_id, $method_sig, $args);
    }

    /**
     * This is here for performance reasons to batch calls to fwrite from basic.publish
     *
     * @param array $method_sig
     * @param \PhpAmqpLib\Wire\AMQPWriter|string $args
     * @param \PhpAmqpLib\Wire\AMQPWriter $pkt
     * @return \PhpAmqpLib\Wire\AMQPWriter
     */
    protected function prepare_method_frame($method_sig, $args = '', $pkt = null)
    {
        return $this->connection->prepare_channel_method_frame($this->channel_id, $method_sig, $args, $pkt);
    }

    /**
     * @return AMQPMessage
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    public function wait_content()
    {
        list($frame_type, $payload) = $this->next_frame();

        $this->validate_header_frame($frame_type);

        $this->wait_content_reader->reuse(mb_substr($payload, 0, 12, 'ASCII'));

        $class_id = $this->wait_content_reader->read_short();
        $weight = $this->wait_content_reader->read_short();

        //hack to avoid creating new instances of AMQPReader;
        $this->msg_property_reader->reuse(mb_substr($payload, 12, mb_strlen($payload, 'ASCII') - 12, 'ASCII'));

        return $this->createMessage(
            $this->msg_property_reader,
            $this->wait_content_reader
        );
    }

    /**
     * @param AMQPReader $propertyReader
     * @param AMQPReader $contentReader
     * @return \PhpAmqpLib\Message\AMQPMessage
     */
    protected function createMessage($propertyReader, $contentReader)
    {
        $bodyChunks = array();
        $bodyReceivedBytes = 0;

        $message = new AMQPMessage();
        $message
            ->load_properties($propertyReader)
            ->setBodySize($contentReader->read_longlong());

        while (bccomp($message->getBodySize(), $bodyReceivedBytes, 0) == 1) {
            list($frame_type, $payload) = $this->next_frame();

            $this->validate_body_frame($frame_type);
            $bodyReceivedBytes = bcadd($bodyReceivedBytes, mb_strlen($payload, 'ASCII'), 0);

            if (is_int($this->maxBodySize) && $bodyReceivedBytes > $this->maxBodySize ) {
                $message->setIsTruncated(true);
                continue;
            }

            $bodyChunks[] = $payload;
        }

        $message->setBody(implode('', $bodyChunks));

        return $message;
    }

    /**
     * Wait for some expected AMQP methods and dispatch to them.
     * Unexpected methods are queued up for later calls to this PHP
     * method.
     *
     * @param array $allowed_methods
     * @param bool $non_blocking
     * @param int $timeout
     * @throws \PhpAmqpLib\Exception\AMQPOutOfBoundsException
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     * @return mixed
     */
    public function wait($allowed_methods = null, $non_blocking = false, $timeout = 0)
    {
        $this->debug->debug_allowed_methods($allowed_methods);

        $deferred = $this->process_deferred_methods($allowed_methods);
        if ($deferred['dispatch'] === true) {
            return $this->dispatch_deferred_method($deferred['queued_method']);
        }

        // No deferred methods?  wait for new ones
        while (true) {
            list($frame_type, $payload) = $this->next_frame($timeout);

            $this->validate_method_frame($frame_type);
            $this->validate_frame_payload($payload);

            $method_sig = $this->build_method_signature($payload);
            $args = $this->extract_args($payload);

            $this->debug->debug_method_signature('> %s', $method_sig);

            $amqpMessage = $this->maybe_wait_for_content($method_sig);

            if ($this->should_dispatch_method($allowed_methods, $method_sig)) {
                return $this->dispatch($method_sig, $args, $amqpMessage);
            }

            // Wasn't what we were looking for? save it for later
            $this->debug->debug_method_signature('Queueing for later: %s', $method_sig);
            $this->method_queue[] = array($method_sig, $args, $amqpMessage);

            if ($non_blocking) {
                break;
            }
        }
    }

    /**
     * @param array $allowed_methods
     * @return array
     */
    protected function process_deferred_methods($allowed_methods)
    {
        $dispatch = false;
        $queued_method = array();

        foreach ($this->method_queue as $qk => $qm) {
            $this->debug->debug_msg('checking queue method ' . $qk);

            $method_sig = $qm[0];

            if ($allowed_methods == null || in_array($method_sig, $allowed_methods)) {
                unset($this->method_queue[$qk]);
                $dispatch = true;
                $queued_method = $qm;
                break;
            }
        }

        return array('dispatch' => $dispatch, 'queued_method' => $queued_method);
    }

    /**
     * @param array $queued_method
     * @return mixed
     */
    protected function dispatch_deferred_method($queued_method)
    {
        $this->debug->debug_method_signature('Executing queued method: %s', $queued_method[0]);

        return $this->dispatch($queued_method[0], $queued_method[1], $queued_method[2]);
    }

    /**
     * @param int $frame_type
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    protected function validate_method_frame($frame_type)
    {
        $this->validate_frame($frame_type, 1, 'AMQP method');
    }

    /**
     * @param int $frame_type
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    protected function validate_header_frame($frame_type)
    {
        $this->validate_frame($frame_type, 2, 'AMQP Content header');
    }

    /**
     * @param int $frame_type
     * @throws \PhpAmqpLib\Exception\AMQPRuntimeException
     */
    protected function validate_body_frame($frame_type)
    {
        $this->validate_frame($frame_type, 3, 'AMQP Content body');
    }

    /**
     * @param int $frameType
     * @param int $expectedType
     * @param string $expectedMessage
     */
    protected function validate_frame($frameType, $expectedType, $expectedMessage)
    {
        if ($frameType != $expectedType) {
            $protocolClass = self::$PROTOCOL_CONSTANTS_CLASS;
            throw new AMQPRuntimeException(sprintf(
                    'Expecting %s, received frame type %s (%s)',
                    $expectedMessage,
                    $frameType,
                    $protocolClass::$FRAME_TYPES[$frameType]
                ));
        }
    }

    /**
     * @param string $payload
     * @throws \PhpAmqpLib\Exception\AMQPOutOfBoundsException
     */
    protected function validate_frame_payload($payload)
    {
        if (mb_strlen($payload, 'ASCII') < 4) {
            throw new AMQPOutOfBoundsException('Method frame too short');
        }
    }

    /**
     * @param string $payload
     * @return string
     */
    protected function build_method_signature($payload)
    {
        $method_sig_array = unpack('n2', mb_substr($payload, 0, 4, 'ASCII'));

        return sprintf('%s,%s', $method_sig_array[1], $method_sig_array[2]);
    }

    /**
     * @param string $payload
     * @return string
     */
    protected function extract_args($payload)
    {
        return mb_substr($payload, 4, mb_strlen($payload, 'ASCII') - 4, 'ASCII');
    }

    /**
     * @param array|null $allowed_methods
     * @param string $method_sig
     * @return bool
     */
    protected function should_dispatch_method($allowed_methods, $method_sig)
    {
        $protocolClass = self::$PROTOCOL_CONSTANTS_CLASS;

        return $allowed_methods == null
            || in_array($method_sig, $allowed_methods)
            || in_array($method_sig, $protocolClass::$CLOSE_METHODS);
    }

    /**
     * @param string $method_sig
     * @return AMQPMessage|null
     */
    protected function maybe_wait_for_content($method_sig)
    {
        $protocolClass = self::$PROTOCOL_CONSTANTS_CLASS;
        $amqpMessage = null;

        if (in_array($method_sig, $protocolClass::$CONTENT_METHODS)) {
            $amqpMessage = $this->wait_content();
        }

        return $amqpMessage;
    }

    /**
     * @param callable $handler
     * @param array $arguments
     */
    protected function dispatch_to_handler($handler, array $arguments)
    {
        if (is_callable($handler)) {
            call_user_func_array($handler, $arguments);
        }
    }
}
<?php
namespace PhpAmqpLib\Helper;

class MiscHelper
{
    /**
     * @param string|array $a
     * @return string
     */
    public static function methodSig($a)
    {
        if (is_string($a)) {
            return $a;
        }

        return sprintf('%d,%d', $a[0], $a[1]);
    }

    /**
     * @param string $bytes
     */
    public static function saveBytes($bytes)
    {
        $fh = fopen('/tmp/bytes', 'wb');
        fwrite($fh, $bytes);
        fclose($fh);
    }

    /**
     * Gets a number (either int or float) and returns an array containing its integer part as first element and its
     * decimal part mutliplied by 10^6. Useful for some PHP stream functions that need seconds and microseconds as
     * different arguments
     *
     * @param int|float $number
     * @return array
     */
    public static function splitSecondsMicroseconds($number)
    {
        return array(floor($number), ($number - floor($number)) * 1000000);
    }

    /**
     * View any string as a hexdump.
     *
     * This is most commonly used to view binary data from streams
     * or sockets while debugging, but can be used to view any string
     * with non-viewable characters.
     *
     * @version     1.3.2
     * @author      Aidan Lister <aidan@php.net>
     * @author      Peter Waller <iridum@php.net>
     * @link        http://aidanlister.com/repos/v/function.hexdump.php
     *
     * @param string $data The string to be dumped
     * @param bool $htmloutput Set to false for non-HTML output
     * @param bool $uppercase Set to true for uppercase hex
     * @param bool $return Set to true to return the dump
     * @return string|null
     */
    public static function hexdump($data, $htmloutput = true, $uppercase = false, $return = false)
    {
        // Init
        $hexi = '';
        $ascii = '';
        $dump = $htmloutput ? '<pre>' : '';
        $offset = 0;
        $len = mb_strlen($data, 'ASCII');

        // Upper or lower case hexidecimal
        $hexFormat = $uppercase ? 'X' : 'x';

        // Iterate string
        for ($i = $j = 0; $i < $len; $i++) {
            // Convert to hexidecimal
            // We must use concatenation here because the $hexFormat value
            // is needed for sprintf() to parse the format
            $hexi .= sprintf('%02' .  $hexFormat . ' ', ord($data[$i]));

            // Replace non-viewable bytes with '.'
            if (ord($data[$i]) >= 32) {
                $ascii .= $htmloutput ? htmlentities($data[$i]) : $data[$i];
            } else {
                $ascii .= '.';
            }

            // Add extra column spacing
            if ($j === 7) {
                $hexi .= ' ';
                $ascii .= ' ';
            }

            // Add row
            if (++$j === 16 || $i === $len - 1) {
                // Join the hexi / ascii output
                // We must use concatenation here because the $hexFormat value
                // is needed for sprintf() to parse the format
                $dump .= sprintf('%04' . $hexFormat . '  %-49s  %s', $offset, $hexi, $ascii);

                // Reset vars
                $hexi = $ascii = '';
                $offset += 16;
                $j = 0;

                // Add newline
                if ($i !== $len - 1) {
                    $dump .= PHP_EOL;
                }
            }
        }

        // Finish dump
        $dump .= $htmloutput ? '</pre>' : '';
        $dump .= PHP_EOL;

        if ($return) {
            return $dump;
        }

        echo $dump;
    }

    /**
     * @param array $table
     * @return string
     */
    public static function dump_table($table)
    {
        $tokens = array();
        foreach ($table as $name => $value) {
            switch ($value[0]) {
                case 'D':
                    $val = $value[1]->n . 'E' . $value[1]->e;
                    break;
                case 'F':
                    $val = '(' . self::dump_table($value[1]) . ')';
                    break;
                case 'T':
                    $val = date('Y-m-d H:i:s', $value[1]);
                    break;
                default:
                    $val = $value[1];
            }
            $tokens[] = $name . '=' . $val;
        }

        return implode(', ', $tokens);
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Helper\Protocol;

use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\AMQPReader;

class Protocol091
{
    /**
     * @param int $version_major
     * @param int $version_minor
     * @param mixed $server_properties
     * @param string $mechanisms
     * @param string $locales
     * @return array
     */
    public function connectionStart($version_major = 0, $version_minor = 9, $server_properties, $mechanisms = 'PLAIN', $locales = 'en_US')
    {
        $writer = new AMQPWriter();
        $writer->write_octet($version_major);
        $writer->write_octet($version_minor);
        $writer->write_table(empty($server_properties) ? array() : $server_properties);
        $writer->write_longstr($mechanisms);
        $writer->write_longstr($locales);
        return array(10, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionStartOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_table();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_longstr();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $challenge
     * @return array
     */
    public function connectionSecure($challenge)
    {
        $writer = new AMQPWriter();
        $writer->write_longstr($challenge);
        return array(10, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionSecureOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longstr();
        return $response;
    }

    /**
     * @param int $channel_max
     * @param int $frame_max
     * @param int $heartbeat
     * @return array
     */
    public function connectionTune($channel_max = 0, $frame_max = 0, $heartbeat = 0)
    {
        $writer = new AMQPWriter();
        $writer->write_short($channel_max);
        $writer->write_long($frame_max);
        $writer->write_short($heartbeat);
        return array(10, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionTuneOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_short();
        $response[] = $reader->read_long();
        $response[] = $reader->read_short();
        return $response;
    }

    /**
     * @param string $virtual_host
     * @param string $capabilities
     * @param bool $insist
     * @return array
     */
    public function connectionOpen($virtual_host = '/', $capabilities = '', $insist = false)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($virtual_host);
        $writer->write_shortstr($capabilities);
        $writer->write_bits(array($insist));
        return array(10, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionOpenOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param int $class_id
     * @param int $method_id
     * @return array
     */
    public function connectionClose($reply_code, $reply_text = '', $class_id, $method_id)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_short($class_id);
        $writer->write_short($method_id);
        return array(10, 50, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionCloseOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param string $reason
     * @return array
     */
    public function connectionBlocked($reason = '')
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($reason);
        return array(10, 60, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionUnblocked(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param string $out_of_band
     * @return array
     */
    public function channelOpen($out_of_band = '')
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($out_of_band);
        return array(20, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function channelOpenOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longstr();
        return $response;
    }

    /**
     * @param bool $active
     * @return array
     */
    public function channelFlow($active)
    {
        $writer = new AMQPWriter();
        $writer->write_bits(array($active));
        return array(20, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function channelFlowOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_bit();
        return $response;
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param int $class_id
     * @param int $method_id
     * @return array
     */
    public function channelClose($reply_code, $reply_text = '', $class_id, $method_id)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_short($class_id);
        $writer->write_short($method_id);
        return array(20, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function channelCloseOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param string $realm
     * @param bool $exclusive
     * @param bool $passive
     * @param bool $active
     * @param bool $write
     * @param bool $read
     * @return array
     */
    public function accessRequest($realm = '/data', $exclusive = false, $passive = true, $active = true, $write = true, $read = true)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($realm);
        $writer->write_bits(array($exclusive, $passive, $active, $write, $read));
        return array(30, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function accessRequestOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_short();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param string $type
     * @param bool $passive
     * @param bool $durable
     * @param bool $auto_delete
     * @param bool $internal
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function exchangeDeclare($ticket = 0, $exchange, $type = 'direct', $passive = false, $durable = false, $auto_delete = false, $internal = false, $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($type);
        $writer->write_bits(array($passive, $durable, $auto_delete, $internal, $nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(40, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function exchangeDeclareOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param bool $if_unused
     * @param bool $nowait
     * @return array
     */
    public function exchangeDelete($ticket = 0, $exchange, $if_unused = false, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_bits(array($if_unused, $nowait));
        return array(40, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function exchangeDeleteOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $destination
     * @param string $source
     * @param string $routing_key
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function exchangeBind($ticket = 0, $destination, $source, $routing_key = '', $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($destination);
        $writer->write_shortstr($source);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(40, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function exchangeBindOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $destination
     * @param string $source
     * @param string $routing_key
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function exchangeUnbind($ticket = 0, $destination, $source, $routing_key = '', $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($destination);
        $writer->write_shortstr($source);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(40, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function exchangeUnbindOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $passive
     * @param bool $durable
     * @param bool $exclusive
     * @param bool $auto_delete
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function queueDeclare($ticket = 0, $queue = '', $passive = false, $durable = false, $exclusive = false, $auto_delete = false, $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($passive, $durable, $exclusive, $auto_delete, $nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(50, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueDeclareOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_long();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $exchange
     * @param string $routing_key
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function queueBind($ticket = 0, $queue = '', $exchange, $routing_key = '', $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(50, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueBindOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $nowait
     * @return array
     */
    public function queuePurge($ticket = 0, $queue = '', $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($nowait));
        return array(50, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queuePurgeOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $if_unused
     * @param bool $if_empty
     * @param bool $nowait
     * @return array
     */
    public function queueDelete($ticket = 0, $queue = '', $if_unused = false, $if_empty = false, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($if_unused, $if_empty, $nowait));
        return array(50, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueDeleteOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $exchange
     * @param string $routing_key
     * @param array $arguments
     * @return array
     */
    public function queueUnbind($ticket = 0, $queue = '', $exchange, $routing_key = '', $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(50, 50, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueUnbindOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $prefetch_size
     * @param int $prefetch_count
     * @param bool $global
     * @return array
     */
    public function basicQos($prefetch_size = 0, $prefetch_count = 0, $global = false)
    {
        $writer = new AMQPWriter();
        $writer->write_long($prefetch_size);
        $writer->write_short($prefetch_count);
        $writer->write_bits(array($global));
        return array(60, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicQosOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $consumer_tag
     * @param bool $no_local
     * @param bool $no_ack
     * @param bool $exclusive
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function basicConsume($ticket = 0, $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($no_local, $no_ack, $exclusive, $nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(60, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicConsumeOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $consumer_tag
     * @param bool $nowait
     * @return array
     */
    public function basicCancel($consumer_tag, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($nowait));
        return array(60, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicCancelOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param string $routing_key
     * @param bool $mandatory
     * @param bool $immediate
     * @return array
     */
    public function basicPublish($ticket = 0, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($mandatory, $immediate));
        return array(60, 40, $writer);
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param string $exchange
     * @param string $routing_key
     * @return array
     */
    public function basicReturn($reply_code, $reply_text = '', $exchange, $routing_key)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        return array(60, 50, $writer);
    }

    /**
     * @param string $consumer_tag
     * @param int $delivery_tag
     * @param bool $redelivered
     * @param string $exchange
     * @param string $routing_key
     * @return array
     */
    public function basicDeliver($consumer_tag, $delivery_tag, $redelivered = false, $exchange, $routing_key)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($redelivered));
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        return array(60, 60, $writer);
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $no_ack
     * @return array
     */
    public function basicGet($ticket = 0, $queue = '', $no_ack = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($no_ack));
        return array(60, 70, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicGetOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longlong();
        $response[] = $reader->read_bit();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicGetEmpty(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param int $delivery_tag
     * @param bool $multiple
     * @return array
     */
    public function basicAck($delivery_tag = 0, $multiple = false)
    {
        $writer = new AMQPWriter();
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($multiple));
        return array(60, 80, $writer);
    }

    /**
     * @param int $delivery_tag
     * @param bool $requeue
     * @return array
     */
    public function basicReject($delivery_tag, $requeue = true)
    {
        $writer = new AMQPWriter();
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($requeue));
        return array(60, 90, $writer);
    }

    /**
     * @param bool $requeue
     * @return array
     */
    public function basicRecoverAsync($requeue = false)
    {
        $writer = new AMQPWriter();
        $writer->write_bits(array($requeue));
        return array(60, 100, $writer);
    }

    /**
     * @param bool $requeue
     * @return array
     */
    public function basicRecover($requeue = false)
    {
        $writer = new AMQPWriter();
        $writer->write_bits(array($requeue));
        return array(60, 110, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicRecoverOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $delivery_tag
     * @param bool $multiple
     * @param bool $requeue
     * @return array
     */
    public function basicNack($delivery_tag = 0, $multiple = false, $requeue = true)
    {
        $writer = new AMQPWriter();
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($multiple, $requeue));
        return array(60, 120, $writer);
    }

    /**

     * @return array
     */
    public function txSelect()
    {
        $writer = new AMQPWriter();
        return array(90, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function txSelectOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**

     * @return array
     */
    public function txCommit()
    {
        $writer = new AMQPWriter();
        return array(90, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function txCommitOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**

     * @return array
     */
    public function txRollback()
    {
        $writer = new AMQPWriter();
        return array(90, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function txRollbackOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param bool $nowait
     * @return array
     */
    public function confirmSelect($nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_bits(array($nowait));
        return array(85, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function confirmSelectOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Helper\Protocol;

use PhpAmqpLib\Wire\AMQPWriter;
use PhpAmqpLib\Wire\AMQPReader;

class Protocol080
{
    /**
     * @param int $version_major
     * @param int $version_minor
     * @param mixed $server_properties
     * @param string $mechanisms
     * @param string $locales
     * @return array
     */
    public function connectionStart($version_major = 0, $version_minor = 8, $server_properties, $mechanisms = 'PLAIN', $locales = 'en_US')
    {
        $writer = new AMQPWriter();
        $writer->write_octet($version_major);
        $writer->write_octet($version_minor);
        $writer->write_table(empty($server_properties) ? array() : $server_properties);
        $writer->write_longstr($mechanisms);
        $writer->write_longstr($locales);
        return array(10, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionStartOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_table();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_longstr();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $challenge
     * @return array
     */
    public function connectionSecure($challenge)
    {
        $writer = new AMQPWriter();
        $writer->write_longstr($challenge);
        return array(10, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionSecureOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longstr();
        return $response;
    }

    /**
     * @param int $channel_max
     * @param int $frame_max
     * @param int $heartbeat
     * @return array
     */
    public function connectionTune($channel_max = 0, $frame_max = 0, $heartbeat = 0)
    {
        $writer = new AMQPWriter();
        $writer->write_short($channel_max);
        $writer->write_long($frame_max);
        $writer->write_short($heartbeat);
        return array(10, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionTuneOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_short();
        $response[] = $reader->read_long();
        $response[] = $reader->read_short();
        return $response;
    }

    /**
     * @param string $virtual_host
     * @param string $capabilities
     * @param bool $insist
     * @return array
     */
    public function connectionOpen($virtual_host = '/', $capabilities = '', $insist = false)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($virtual_host);
        $writer->write_shortstr($capabilities);
        $writer->write_bits(array($insist));
        return array(10, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionOpenOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $host
     * @param string $known_hosts
     * @return array
     */
    public function connectionRedirect($host, $known_hosts = '')
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($host);
        $writer->write_shortstr($known_hosts);
        return array(10, 50, $writer);
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param int $class_id
     * @param int $method_id
     * @return array
     */
    public function connectionClose($reply_code, $reply_text = '', $class_id, $method_id)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_short($class_id);
        $writer->write_short($method_id);
        return array(10, 60, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function connectionCloseOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param string $out_of_band
     * @return array
     */
    public function channelOpen($out_of_band = '')
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($out_of_band);
        return array(20, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function channelOpenOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param bool $active
     * @return array
     */
    public function channelFlow($active)
    {
        $writer = new AMQPWriter();
        $writer->write_bits(array($active));
        return array(20, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function channelFlowOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_bit();
        return $response;
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param array $details
     * @return array
     */
    public function channelAlert($reply_code, $reply_text = '', $details = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_table(empty($details) ? array() : $details);
        return array(20, 30, $writer);
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param int $class_id
     * @param int $method_id
     * @return array
     */
    public function channelClose($reply_code, $reply_text = '', $class_id, $method_id)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_short($class_id);
        $writer->write_short($method_id);
        return array(20, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function channelCloseOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param string $realm
     * @param bool $exclusive
     * @param bool $passive
     * @param bool $active
     * @param bool $write
     * @param bool $read
     * @return array
     */
    public function accessRequest($realm = '/data', $exclusive = false, $passive = true, $active = true, $write = true, $read = true)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($realm);
        $writer->write_bits(array($exclusive, $passive, $active, $write, $read));
        return array(30, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function accessRequestOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_short();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param string $type
     * @param bool $passive
     * @param bool $durable
     * @param bool $auto_delete
     * @param bool $internal
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function exchangeDeclare($ticket = 1, $exchange, $type = 'direct', $passive = false, $durable = false, $auto_delete = false, $internal = false, $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($type);
        $writer->write_bits(array($passive, $durable, $auto_delete, $internal, $nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(40, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function exchangeDeclareOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param bool $if_unused
     * @param bool $nowait
     * @return array
     */
    public function exchangeDelete($ticket = 1, $exchange, $if_unused = false, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_bits(array($if_unused, $nowait));
        return array(40, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function exchangeDeleteOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $passive
     * @param bool $durable
     * @param bool $exclusive
     * @param bool $auto_delete
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function queueDeclare($ticket = 1, $queue = '', $passive = false, $durable = false, $exclusive = false, $auto_delete = false, $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($passive, $durable, $exclusive, $auto_delete, $nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(50, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueDeclareOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_long();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $exchange
     * @param string $routing_key
     * @param bool $nowait
     * @param array $arguments
     * @return array
     */
    public function queueBind($ticket = 1, $queue = '', $exchange, $routing_key = '', $nowait = false, $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($nowait));
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(50, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueBindOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $nowait
     * @return array
     */
    public function queuePurge($ticket = 1, $queue = '', $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($nowait));
        return array(50, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queuePurgeOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $if_unused
     * @param bool $if_empty
     * @param bool $nowait
     * @return array
     */
    public function queueDelete($ticket = 1, $queue = '', $if_unused = false, $if_empty = false, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($if_unused, $if_empty, $nowait));
        return array(50, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueDeleteOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $exchange
     * @param string $routing_key
     * @param array $arguments
     * @return array
     */
    public function queueUnbind($ticket = 1, $queue = '', $exchange, $routing_key = '', $arguments = array())
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_table(empty($arguments) ? array() : $arguments);
        return array(50, 50, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function queueUnbindOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $prefetch_size
     * @param int $prefetch_count
     * @param bool $global
     * @return array
     */
    public function basicQos($prefetch_size = 0, $prefetch_count = 0, $global = false)
    {
        $writer = new AMQPWriter();
        $writer->write_long($prefetch_size);
        $writer->write_short($prefetch_count);
        $writer->write_bits(array($global));
        return array(60, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicQosOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $consumer_tag
     * @param bool $no_local
     * @param bool $no_ack
     * @param bool $exclusive
     * @param bool $nowait
     * @return array
     */
    public function basicConsume($ticket = 1, $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($no_local, $no_ack, $exclusive, $nowait));
        return array(60, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicConsumeOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $consumer_tag
     * @param bool $nowait
     * @return array
     */
    public function basicCancel($consumer_tag, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($nowait));
        return array(60, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicCancelOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param string $routing_key
     * @param bool $mandatory
     * @param bool $immediate
     * @return array
     */
    public function basicPublish($ticket = 1, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($mandatory, $immediate));
        return array(60, 40, $writer);
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param string $exchange
     * @param string $routing_key
     * @return array
     */
    public function basicReturn($reply_code, $reply_text = '', $exchange, $routing_key)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        return array(60, 50, $writer);
    }

    /**
     * @param string $consumer_tag
     * @param int $delivery_tag
     * @param bool $redelivered
     * @param string $exchange
     * @param string $routing_key
     * @return array
     */
    public function basicDeliver($consumer_tag, $delivery_tag, $redelivered = false, $exchange, $routing_key)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($redelivered));
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        return array(60, 60, $writer);
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param bool $no_ack
     * @return array
     */
    public function basicGet($ticket = 1, $queue = '', $no_ack = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_bits(array($no_ack));
        return array(60, 70, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicGetOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longlong();
        $response[] = $reader->read_bit();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_shortstr();
        $response[] = $reader->read_long();
        return $response;
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicGetEmpty(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param int $delivery_tag
     * @param bool $multiple
     * @return array
     */
    public function basicAck($delivery_tag = 0, $multiple = false)
    {
        $writer = new AMQPWriter();
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($multiple));
        return array(60, 80, $writer);
    }

    /**
     * @param int $delivery_tag
     * @param bool $requeue
     * @return array
     */
    public function basicReject($delivery_tag, $requeue = true)
    {
        $writer = new AMQPWriter();
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($requeue));
        return array(60, 90, $writer);
    }

    /**
     * @param bool $requeue
     * @return array
     */
    public function basicRecoverAsync($requeue = false)
    {
        $writer = new AMQPWriter();
        $writer->write_bits(array($requeue));
        return array(60, 100, $writer);
    }

    /**
     * @param bool $requeue
     * @return array
     */
    public function basicRecover($requeue = false)
    {
        $writer = new AMQPWriter();
        $writer->write_bits(array($requeue));
        return array(60, 110, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function basicRecoverOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $prefetch_size
     * @param int $prefetch_count
     * @param bool $global
     * @return array
     */
    public function fileQos($prefetch_size = 0, $prefetch_count = 0, $global = false)
    {
        $writer = new AMQPWriter();
        $writer->write_long($prefetch_size);
        $writer->write_short($prefetch_count);
        $writer->write_bits(array($global));
        return array(70, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function fileQosOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $consumer_tag
     * @param bool $no_local
     * @param bool $no_ack
     * @param bool $exclusive
     * @param bool $nowait
     * @return array
     */
    public function fileConsume($ticket = 1, $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($no_local, $no_ack, $exclusive, $nowait));
        return array(70, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function fileConsumeOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $consumer_tag
     * @param bool $nowait
     * @return array
     */
    public function fileCancel($consumer_tag, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($nowait));
        return array(70, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function fileCancelOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $identifier
     * @param int $content_size
     * @return array
     */
    public function fileOpen($identifier, $content_size)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($identifier);
        $writer->write_longlong($content_size);
        return array(70, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function fileOpenOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longlong();
        return $response;
    }

    /**

     * @return array
     */
    public function fileStage()
    {
        $writer = new AMQPWriter();
        return array(70, 50, $writer);
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param string $routing_key
     * @param bool $mandatory
     * @param bool $immediate
     * @param string $identifier
     * @return array
     */
    public function filePublish($ticket = 1, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false, $identifier)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($mandatory, $immediate));
        $writer->write_shortstr($identifier);
        return array(70, 60, $writer);
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param string $exchange
     * @param string $routing_key
     * @return array
     */
    public function fileReturn($reply_code = 200, $reply_text = '', $exchange, $routing_key)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        return array(70, 70, $writer);
    }

    /**
     * @param string $consumer_tag
     * @param int $delivery_tag
     * @param bool $redelivered
     * @param string $exchange
     * @param string $routing_key
     * @param string $identifier
     * @return array
     */
    public function fileDeliver($consumer_tag, $delivery_tag, $redelivered = false, $exchange, $routing_key, $identifier)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($redelivered));
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_shortstr($identifier);
        return array(70, 80, $writer);
    }

    /**
     * @param int $delivery_tag
     * @param bool $multiple
     * @return array
     */
    public function fileAck($delivery_tag = 0, $multiple = false)
    {
        $writer = new AMQPWriter();
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($multiple));
        return array(70, 90, $writer);
    }

    /**
     * @param int $delivery_tag
     * @param bool $requeue
     * @return array
     */
    public function fileReject($delivery_tag, $requeue = true)
    {
        $writer = new AMQPWriter();
        $writer->write_longlong($delivery_tag);
        $writer->write_bits(array($requeue));
        return array(70, 100, $writer);
    }

    /**
     * @param int $prefetch_size
     * @param int $prefetch_count
     * @param int $consume_rate
     * @param bool $global
     * @return array
     */
    public function streamQos($prefetch_size = 0, $prefetch_count = 0, $consume_rate = 0, $global = false)
    {
        $writer = new AMQPWriter();
        $writer->write_long($prefetch_size);
        $writer->write_short($prefetch_count);
        $writer->write_long($consume_rate);
        $writer->write_bits(array($global));
        return array(80, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function streamQosOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $queue
     * @param string $consumer_tag
     * @param bool $no_local
     * @param bool $exclusive
     * @param bool $nowait
     * @return array
     */
    public function streamConsume($ticket = 1, $queue = '', $consumer_tag = '', $no_local = false, $exclusive = false, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($queue);
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($no_local, $exclusive, $nowait));
        return array(80, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function streamConsumeOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param string $consumer_tag
     * @param bool $nowait
     * @return array
     */
    public function streamCancel($consumer_tag, $nowait = false)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_bits(array($nowait));
        return array(80, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function streamCancelOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_shortstr();
        return $response;
    }

    /**
     * @param int $ticket
     * @param string $exchange
     * @param string $routing_key
     * @param bool $mandatory
     * @param bool $immediate
     * @return array
     */
    public function streamPublish($ticket = 1, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false)
    {
        $writer = new AMQPWriter();
        $writer->write_short($ticket);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        $writer->write_bits(array($mandatory, $immediate));
        return array(80, 40, $writer);
    }

    /**
     * @param int $reply_code
     * @param string $reply_text
     * @param string $exchange
     * @param string $routing_key
     * @return array
     */
    public function streamReturn($reply_code = 200, $reply_text = '', $exchange, $routing_key)
    {
        $writer = new AMQPWriter();
        $writer->write_short($reply_code);
        $writer->write_shortstr($reply_text);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($routing_key);
        return array(80, 50, $writer);
    }

    /**
     * @param string $consumer_tag
     * @param int $delivery_tag
     * @param string $exchange
     * @param string $queue
     * @return array
     */
    public function streamDeliver($consumer_tag, $delivery_tag, $exchange, $queue)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($consumer_tag);
        $writer->write_longlong($delivery_tag);
        $writer->write_shortstr($exchange);
        $writer->write_shortstr($queue);
        return array(80, 60, $writer);
    }

    /**

     * @return array
     */
    public function txSelect()
    {
        $writer = new AMQPWriter();
        return array(90, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function txSelectOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**

     * @return array
     */
    public function txCommit()
    {
        $writer = new AMQPWriter();
        return array(90, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function txCommitOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**

     * @return array
     */
    public function txRollback()
    {
        $writer = new AMQPWriter();
        return array(90, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function txRollbackOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**

     * @return array
     */
    public function dtxSelect()
    {
        $writer = new AMQPWriter();
        return array(100, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function dtxSelectOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param string $dtx_identifier
     * @return array
     */
    public function dtxStart($dtx_identifier)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($dtx_identifier);
        return array(100, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function dtxStartOk(AMQPReader $reader)
    {
        $response = array();
        return $response;
    }

    /**
     * @param mixed $meta_data
     * @return array
     */
    public function tunnelRequest($meta_data)
    {
        $writer = new AMQPWriter();
        $writer->write_table(empty($meta_data) ? array() : $meta_data);
        return array(110, 10, $writer);
    }

    /**
     * @param mixed $integer_1
     * @param int $integer_2
     * @param int $integer_3
     * @param int $integer_4
     * @param mixed $operation
     * @return array
     */
    public function testInteger($integer_1, $integer_2, $integer_3, $integer_4, $operation)
    {
        $writer = new AMQPWriter();
        $writer->write_octet($integer_1);
        $writer->write_short($integer_2);
        $writer->write_long($integer_3);
        $writer->write_longlong($integer_4);
        $writer->write_octet($operation);
        return array(120, 10, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function testIntegerOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longlong();
        return $response;
    }

    /**
     * @param string $string_1
     * @param string $string_2
     * @param mixed $operation
     * @return array
     */
    public function testString($string_1, $string_2, $operation)
    {
        $writer = new AMQPWriter();
        $writer->write_shortstr($string_1);
        $writer->write_longstr($string_2);
        $writer->write_octet($operation);
        return array(120, 20, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function testStringOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longstr();
        return $response;
    }

    /**
     * @param mixed $table
     * @param mixed $integer_op
     * @param mixed $string_op
     * @return array
     */
    public function testTable($table, $integer_op, $string_op)
    {
        $writer = new AMQPWriter();
        $writer->write_table(empty($table) ? array() : $table);
        $writer->write_octet($integer_op);
        $writer->write_octet($string_op);
        return array(120, 30, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function testTableOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_longlong();
        $response[] = $reader->read_longstr();
        return $response;
    }

    /**

     * @return array
     */
    public function testContent()
    {
        $writer = new AMQPWriter();
        return array(120, 40, $writer);
    }

    /**
     * @param AMQPReader $reader
     * @return array
     */
    public static function testContentOk(AMQPReader $reader)
    {
        $response = array();
        $response[] = $reader->read_long();
        return $response;
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Helper\Protocol;

class Wait091
{
    /**
     * @var array
     */
    protected $wait = array(
        'connection.start' => '10,10',
        'connection.start_ok' => '10,11',
        'connection.secure' => '10,20',
        'connection.secure_ok' => '10,21',
        'connection.tune' => '10,30',
        'connection.tune_ok' => '10,31',
        'connection.open' => '10,40',
        'connection.open_ok' => '10,41',
        'connection.close' => '10,50',
        'connection.close_ok' => '10,51',
        'connection.blocked' => '10,60',
        'connection.unblocked' => '10,61',
        'channel.open' => '20,10',
        'channel.open_ok' => '20,11',
        'channel.flow' => '20,20',
        'channel.flow_ok' => '20,21',
        'channel.close' => '20,40',
        'channel.close_ok' => '20,41',
        'access.request' => '30,10',
        'access.request_ok' => '30,11',
        'exchange.declare' => '40,10',
        'exchange.declare_ok' => '40,11',
        'exchange.delete' => '40,20',
        'exchange.delete_ok' => '40,21',
        'exchange.bind' => '40,30',
        'exchange.bind_ok' => '40,31',
        'exchange.unbind' => '40,40',
        'exchange.unbind_ok' => '40,51',
        'queue.declare' => '50,10',
        'queue.declare_ok' => '50,11',
        'queue.bind' => '50,20',
        'queue.bind_ok' => '50,21',
        'queue.purge' => '50,30',
        'queue.purge_ok' => '50,31',
        'queue.delete' => '50,40',
        'queue.delete_ok' => '50,41',
        'queue.unbind' => '50,50',
        'queue.unbind_ok' => '50,51',
        'basic.qos' => '60,10',
        'basic.qos_ok' => '60,11',
        'basic.consume' => '60,20',
        'basic.consume_ok' => '60,21',
        'basic.cancel' => '60,30',
        'basic.cancel_ok' => '60,31',
        'basic.publish' => '60,40',
        'basic.return' => '60,50',
        'basic.deliver' => '60,60',
        'basic.get' => '60,70',
        'basic.get_ok' => '60,71',
        'basic.get_empty' => '60,72',
        'basic.ack' => '60,80',
        'basic.reject' => '60,90',
        'basic.recover_async' => '60,100',
        'basic.recover' => '60,110',
        'basic.recover_ok' => '60,111',
        'basic.nack' => '60,120',
        'tx.select' => '90,10',
        'tx.select_ok' => '90,11',
        'tx.commit' => '90,20',
        'tx.commit_ok' => '90,21',
        'tx.rollback' => '90,30',
        'tx.rollback_ok' => '90,31',
        'confirm.select' => '85,10',
        'confirm.select_ok' => '85,11',
    );

    /**
     * @var string $method
     * @return string
     */
    public function get_wait($method)
    {
        return $this->wait[$method];
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Helper\Protocol;

class MethodMap091
{
    /**
     * @var array
     */
    protected $method_map = array(
        '10,10' => 'connection_start',
        '10,11' => 'connection_start_ok',
        '10,20' => 'connection_secure',
        '10,21' => 'connection_secure_ok',
        '10,30' => 'connection_tune',
        '10,31' => 'connection_tune_ok',
        '10,40' => 'connection_open',
        '10,41' => 'connection_open_ok',
        '10,50' => 'connection_close',
        '10,51' => 'connection_close_ok',
        '10,60' => 'connection_blocked',
        '10,61' => 'connection_unblocked',
        '20,10' => 'channel_open',
        '20,11' => 'channel_open_ok',
        '20,20' => 'channel_flow',
        '20,21' => 'channel_flow_ok',
        '20,40' => 'channel_close',
        '20,41' => 'channel_close_ok',
        '30,10' => 'access_request',
        '30,11' => 'access_request_ok',
        '40,10' => 'exchange_declare',
        '40,11' => 'exchange_declare_ok',
        '40,20' => 'exchange_delete',
        '40,21' => 'exchange_delete_ok',
        '40,30' => 'exchange_bind',
        '40,31' => 'exchange_bind_ok',
        '40,40' => 'exchange_unbind',
        '40,51' => 'exchange_unbind_ok',
        '50,10' => 'queue_declare',
        '50,11' => 'queue_declare_ok',
        '50,20' => 'queue_bind',
        '50,21' => 'queue_bind_ok',
        '50,30' => 'queue_purge',
        '50,31' => 'queue_purge_ok',
        '50,40' => 'queue_delete',
        '50,41' => 'queue_delete_ok',
        '50,50' => 'queue_unbind',
        '50,51' => 'queue_unbind_ok',
        '60,10' => 'basic_qos',
        '60,11' => 'basic_qos_ok',
        '60,20' => 'basic_consume',
        '60,21' => 'basic_consume_ok',
        '60,30' => 'basic_cancel_from_server',
        '60,31' => 'basic_cancel_ok',
        '60,40' => 'basic_publish',
        '60,50' => 'basic_return',
        '60,60' => 'basic_deliver',
        '60,70' => 'basic_get',
        '60,71' => 'basic_get_ok',
        '60,72' => 'basic_get_empty',
        '60,80' => 'basic_ack_from_server',
        '60,90' => 'basic_reject',
        '60,100' => 'basic_recover_async',
        '60,110' => 'basic_recover',
        '60,111' => 'basic_recover_ok',
        '60,120' => 'basic_nack_from_server',
        '90,10' => 'tx_select',
        '90,11' => 'tx_select_ok',
        '90,20' => 'tx_commit',
        '90,21' => 'tx_commit_ok',
        '90,30' => 'tx_rollback',
        '90,31' => 'tx_rollback_ok',
        '85,10' => 'confirm_select',
        '85,11' => 'confirm_select_ok',
    );

    /**
     * @var string $method_sig
     * @return string
     */
    public function get_method($method_sig)
    {
        return $this->method_map[$method_sig];
    }

    /**
     * @var string $method_sig
     * @return bool
     */
    public function valid_method($method_sig)
    {
        return array_key_exists($method_sig, $this->method_map);
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Helper\Protocol;

class MethodMap080
{
    /**
     * @var array
     */
    protected $method_map = array(
        '10,10' => 'connection_start',
        '10,11' => 'connection_start_ok',
        '10,20' => 'connection_secure',
        '10,21' => 'connection_secure_ok',
        '10,30' => 'connection_tune',
        '10,31' => 'connection_tune_ok',
        '10,40' => 'connection_open',
        '10,41' => 'connection_open_ok',
        '10,50' => 'connection_redirect',
        '10,60' => 'connection_close',
        '10,61' => 'connection_close_ok',
        '20,10' => 'channel_open',
        '20,11' => 'channel_open_ok',
        '20,20' => 'channel_flow',
        '20,21' => 'channel_flow_ok',
        '20,30' => 'channel_alert',
        '20,40' => 'channel_close',
        '20,41' => 'channel_close_ok',
        '30,10' => 'access_request',
        '30,11' => 'access_request_ok',
        '40,10' => 'exchange_declare',
        '40,11' => 'exchange_declare_ok',
        '40,20' => 'exchange_delete',
        '40,21' => 'exchange_delete_ok',
        '50,10' => 'queue_declare',
        '50,11' => 'queue_declare_ok',
        '50,20' => 'queue_bind',
        '50,21' => 'queue_bind_ok',
        '50,30' => 'queue_purge',
        '50,31' => 'queue_purge_ok',
        '50,40' => 'queue_delete',
        '50,41' => 'queue_delete_ok',
        '50,50' => 'queue_unbind',
        '50,51' => 'queue_unbind_ok',
        '60,10' => 'basic_qos',
        '60,11' => 'basic_qos_ok',
        '60,20' => 'basic_consume',
        '60,21' => 'basic_consume_ok',
        '60,30' => 'basic_cancel',
        '60,31' => 'basic_cancel_ok',
        '60,40' => 'basic_publish',
        '60,50' => 'basic_return',
        '60,60' => 'basic_deliver',
        '60,70' => 'basic_get',
        '60,71' => 'basic_get_ok',
        '60,72' => 'basic_get_empty',
        '60,80' => 'basic_ack',
        '60,90' => 'basic_reject',
        '60,100' => 'basic_recover_async',
        '60,110' => 'basic_recover',
        '60,111' => 'basic_recover_ok',
        '70,10' => 'file_qos',
        '70,11' => 'file_qos_ok',
        '70,20' => 'file_consume',
        '70,21' => 'file_consume_ok',
        '70,30' => 'file_cancel',
        '70,31' => 'file_cancel_ok',
        '70,40' => 'file_open',
        '70,41' => 'file_open_ok',
        '70,50' => 'file_stage',
        '70,60' => 'file_publish',
        '70,70' => 'file_return',
        '70,80' => 'file_deliver',
        '70,90' => 'file_ack',
        '70,100' => 'file_reject',
        '80,10' => 'stream_qos',
        '80,11' => 'stream_qos_ok',
        '80,20' => 'stream_consume',
        '80,21' => 'stream_consume_ok',
        '80,30' => 'stream_cancel',
        '80,31' => 'stream_cancel_ok',
        '80,40' => 'stream_publish',
        '80,50' => 'stream_return',
        '80,60' => 'stream_deliver',
        '90,10' => 'tx_select',
        '90,11' => 'tx_select_ok',
        '90,20' => 'tx_commit',
        '90,21' => 'tx_commit_ok',
        '90,30' => 'tx_rollback',
        '90,31' => 'tx_rollback_ok',
        '100,10' => 'dtx_select',
        '100,11' => 'dtx_select_ok',
        '100,20' => 'dtx_start',
        '100,21' => 'dtx_start_ok',
        '110,10' => 'tunnel_request',
        '120,10' => 'test_integer',
        '120,11' => 'test_integer_ok',
        '120,20' => 'test_string',
        '120,21' => 'test_string_ok',
        '120,30' => 'test_table',
        '120,31' => 'test_table_ok',
        '120,40' => 'test_content',
        '120,41' => 'test_content_ok',
    );

    /**
     * @var string $method_sig
     * @return string
     */
    public function get_method($method_sig)
    {
        return $this->method_map[$method_sig];
    }

    /**
     * @var string $method_sig
     * @return bool
     */
    public function valid_method($method_sig)
    {
        return array_key_exists($method_sig, $this->method_map);
    }
}
<?php

/* This file was autogenerated by spec/parser.php - Do not modify */

namespace PhpAmqpLib\Helper\Protocol;

class Wait080
{
    /**
     * @var array
     */
    protected $wait = array(
        'connection.start' => '10,10',
        'connection.start_ok' => '10,11',
        'connection.secure' => '10,20',
        'connection.secure_ok' => '10,21',
        'connection.tune' => '10,30',
        'connection.tune_ok' => '10,31',
        'connection.open' => '10,40',
        'connection.open_ok' => '10,41',
        'connection.redirect' => '10,50',
        'connection.close' => '10,60',
        'connection.close_ok' => '10,61',
        'channel.open' => '20,10',
        'channel.open_ok' => '20,11',
        'channel.flow' => '20,20',
        'channel.flow_ok' => '20,21',
        'channel.alert' => '20,30',
        'channel.close' => '20,40',
        'channel.close_ok' => '20,41',
        'access.request' => '30,10',
        'access.request_ok' => '30,11',
        'exchange.declare' => '40,10',
        'exchange.declare_ok' => '40,11',
        'exchange.delete' => '40,20',
        'exchange.delete_ok' => '40,21',
        'queue.declare' => '50,10',
        'queue.declare_ok' => '50,11',
        'queue.bind' => '50,20',
        'queue.bind_ok' => '50,21',
        'queue.purge' => '50,30',
        'queue.purge_ok' => '50,31',
        'queue.delete' => '50,40',
        'queue.delete_ok' => '50,41',
        'queue.unbind' => '50,50',
        'queue.unbind_ok' => '50,51',
        'basic.qos' => '60,10',
        'basic.qos_ok' => '60,11',
        'basic.consume' => '60,20',
        'basic.consume_ok' => '60,21',
        'basic.cancel' => '60,30',
        'basic.cancel_ok' => '60,31',
        'basic.publish' => '60,40',
        'basic.return' => '60,50',
        'basic.deliver' => '60,60',
        'basic.get' => '60,70',
        'basic.get_ok' => '60,71',
        'basic.get_empty' => '60,72',
        'basic.ack' => '60,80',
        'basic.reject' => '60,90',
        'basic.recover_async' => '60,100',
        'basic.recover' => '60,110',
        'basic.recover_ok' => '60,111',
        'file.qos' => '70,10',
        'file.qos_ok' => '70,11',
        'file.consume' => '70,20',
        'file.consume_ok' => '70,21',
        'file.cancel' => '70,30',
        'file.cancel_ok' => '70,31',
        'file.open' => '70,40',
        'file.open_ok' => '70,41',
        'file.stage' => '70,50',
        'file.publish' => '70,60',
        'file.return' => '70,70',
        'file.deliver' => '70,80',
        'file.ack' => '70,90',
        'file.reject' => '70,100',
        'stream.qos' => '80,10',
        'stream.qos_ok' => '80,11',
        'stream.consume' => '80,20',
        'stream.consume_ok' => '80,21',
        'stream.cancel' => '80,30',
        'stream.cancel_ok' => '80,31',
        'stream.publish' => '80,40',
        'stream.return' => '80,50',
        'stream.deliver' => '80,60',
        'tx.select' => '90,10',
        'tx.select_ok' => '90,11',
        'tx.commit' => '90,20',
        'tx.commit_ok' => '90,21',
        'tx.rollback' => '90,30',
        'tx.rollback_ok' => '90,31',
        'dtx.select' => '100,10',
        'dtx.select_ok' => '100,11',
        'dtx.start' => '100,20',
        'dtx.start_ok' => '100,21',
        'tunnel.request' => '110,10',
        'test.integer' => '120,10',
        'test.integer_ok' => '120,11',
        'test.string' => '120,20',
        'test.string_ok' => '120,21',
        'test.table' => '120,30',
        'test.table_ok' => '120,31',
        'test.content' => '120,40',
        'test.content_ok' => '120,41',
    );

    /**
     * @var string $method
     * @return string
     */
    public function get_wait($method)
    {
        return $this->wait[$method];
    }
}
<?php
namespace PhpAmqpLib\Helper;

class DebugHelper
{
    /**
     * @var bool
     */
    protected $debug;
    
    /**
     * @var resource
     */
    protected $debug_output;
    
    /**
     * @var string
     */
    protected $PROTOCOL_CONSTANTS_CLASS;

    /**
     * @param string $PROTOCOL_CONSTANTS_CLASS
     */
    public function __construct($PROTOCOL_CONSTANTS_CLASS) {
        if(!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'w'));

        $this->debug = defined('AMQP_DEBUG') ? AMQP_DEBUG : false;
        $this->debug_output = defined('AMQP_DEBUG_OUTPUT') ? AMQP_DEBUG_OUTPUT : STDOUT;
        $this->PROTOCOL_CONSTANTS_CLASS = $PROTOCOL_CONSTANTS_CLASS;
    }

    /**
     * @param string $msg
     */
    public function debug_msg($msg) {
        if ($this->debug) {
            $this->print_msg($msg);
        }
    }

    /**
     * @param array $allowed_methods
     */
    public function debug_allowed_methods($allowed_methods) {
        if ($allowed_methods) {
            $msg = 'waiting for ' . implode(', ', $allowed_methods);
        } else {
            $msg = 'waiting for any method';
        }
        $this->debug_msg($msg);
    }

    /**
     * @param string $method_sig
     */
    public function debug_method_signature1($method_sig) {
        $this->debug_method_signature('< %s:', $method_sig);
    }

    /**
     * @param string $msg
     * @param string $method_sig
     */
    public function debug_method_signature($msg, $method_sig) {
        if ($this->debug) {
            $protocolClass = $this->PROTOCOL_CONSTANTS_CLASS;
            $this->debug_msg(sprintf(
                $msg . ': %s',
                MiscHelper::methodSig($method_sig),
                $protocolClass::$GLOBAL_METHOD_NAMES[MiscHelper::methodSig($method_sig)]
            ));
        }
    }

    /**
     * @param string $data
     */
    public function debug_hexdump($data) {
        if ($this->debug) {
            $this->debug_msg(sprintf(
                '< [hex]: %s%s',
                PHP_EOL,
                MiscHelper::hexdump($data, $htmloutput = false, $uppercase = true, $return = true)
            ));
        }
    }

    /**
     * @param int $version_major
     * @param int $version_minor
     * @param array $server_properties
     * @param array $mechanisms
     * @param array $locales
     */
    public function debug_connection_start($version_major, $version_minor, $server_properties, $mechanisms, $locales) {
        if ($this->debug) {
            $this->debug_msg(sprintf(
                'Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s',
                $version_major,
                $version_minor,
                MiscHelper::dump_table($server_properties),
                implode(', ', $mechanisms),
                implode(', ', $locales)
            ));
        }
    }

    /**
     * @param string $s
     */
    protected function print_msg($s) {
        fwrite($this->debug_output, $s . PHP_EOL);
    }
}
<?php
namespace PhpAmqpLib\Exception;

interface AMQPExceptionInterface
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPBasicCancelException extends \Exception implements AMQPExceptionInterface
{
    /** @var string */
    public $consumerTag;

    /**
     * @param string $consumerTag
     */
    public function __construct($consumerTag)
    {
        $this->consumerTag = $consumerTag;
    }
}
<?php
namespace PhpAmqpLib\Exception;

//TODO refactor usage of static methods
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Helper\MiscHelper;

/**
 * @deprecated use AMQPProtocolException instead
 */
class AMQPException extends \Exception
{
    /** @var string */
    public $amqp_reply_code;

    /** @var int */
    public $amqp_reply_text;

    /** @var \Exception */
    public $amqp_method_sig;

    /** @var array */
    public $args;

    /**
     * @param string $reply_code
     * @param int $reply_text
     * @param array $method_sig
     */
    public function __construct($reply_code, $reply_text, $method_sig)
    {
        parent::__construct($reply_text, $reply_code);

        $this->amqp_reply_code = $reply_code; // redundant, but kept for BC
        $this->amqp_reply_text = $reply_text; // redundant, but kept for BC
        $this->amqp_method_sig = $method_sig;

        $ms = MiscHelper::methodSig($method_sig);
        $protocolClass = AbstractChannel::$PROTOCOL_CONSTANTS_CLASS;
        $mn = isset($protocolClass::$GLOBAL_METHOD_NAMES[$ms])
            ? $protocolClass::$GLOBAL_METHOD_NAMES[$ms]
            : $mn = '';

        $this->args = array($reply_code, $reply_text, $method_sig, $mn);
    }
}
<?php
namespace PhpAmqpLib\Exception;

//TODO refactor usage of static methods
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Helper\MiscHelper;

class AMQPProtocolException extends \Exception implements AMQPExceptionInterface
{
    /** @var string */
    public $amqp_reply_code;

    /** @var int */
    public $amqp_reply_text;

    /** @var \Exception */
    public $amqp_method_sig;

    /** @var array */
    public $args;

    /**
     * @param string $reply_code
     * @param int $reply_text
     * @param array $method_sig
     */
    public function __construct($reply_code, $reply_text, $method_sig)
    {
        parent::__construct($reply_text, $reply_code);

        $this->amqp_reply_code = $reply_code; // redundant, but kept for BC
        $this->amqp_reply_text = $reply_text; // redundant, but kept for BC
        $this->amqp_method_sig = $method_sig;

        $ms = MiscHelper::methodSig($method_sig);

        $protocolClass = AbstractChannel::$PROTOCOL_CONSTANTS_CLASS;
        $mn = isset($protocolClass::$GLOBAL_METHOD_NAMES[$ms])
            ? $protocolClass::$GLOBAL_METHOD_NAMES[$ms]
            : $mn = '';

        $this->args = array($reply_code, $reply_text, $method_sig, $mn);
    }
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPLogicException extends \LogicException implements AMQPExceptionInterface
{

}
<?php
namespace PhpAmqpLib\Exception;

class AMQPTimeoutException extends \RuntimeException implements AMQPExceptionInterface
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPProtocolChannelException extends AMQPProtocolException
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPProtocolConnectionException extends AMQPProtocolException
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPInvalidArgumentException extends \RuntimeException implements AMQPExceptionInterface
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPIOWaitException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPRuntimeException extends \RuntimeException implements AMQPExceptionInterface
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPOutOfBoundsException extends \OutOfBoundsException implements AMQPExceptionInterface
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPOutOfRangeException extends \OutOfRangeException implements AMQPExceptionInterface
{

}
<?php
namespace PhpAmqpLib\Exception;

/**
 * @deprecated use AMQPProtocolChannelException instead
 */
class AMQPChannelException extends AMQPException
{
}
<?php
namespace PhpAmqpLib\Exception;

/**
 * @deprecated use AMQPProtocolConnectionException instead
 */
class AMQPConnectionException extends AMQPException
{
}
<?php
namespace PhpAmqpLib\Exception;

class AMQPIOException extends \Exception implements AMQPExceptionInterface
{
}
{
    "name": "php-amqplib/php-amqplib",
    "replace": {
        "videlalvaro/php-amqplib": "self.version"
    },
    "type": "library",
    "description": "Formerly videlalvaro/php-amqplib.  This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
    "keywords": ["rabbitmq", "message", "queue"],
    "homepage": "https://github.com/php-amqplib/php-amqplib/",
    "authors": [
        {
            "name": "Alvaro Videla",
            "role": "Original Maintainer"
        },
        {
            "name": "John Kelly",
            "email": "johnmkelly86@gmail.com",
            "role": "Maintainer"
        },
        {
            "name": "RaÃºl Araya",
            "email": "nubeiro@gmail.com",
            "role": "Maintainer"
        }
    ],
    "require": {
        "php": ">=5.3.0",
        "ext-bcmath": "*",
        "ext-mbstring": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.8",
        "scrutinizer/ocular": "^1.1",
        "squizlabs/php_codesniffer": "^2.5",
        "phpdocumentor/phpdocumentor": "^2.9"
    },
    "suggest": {
        "ext-sockets": "Use AMQPSocketConnection"
    },
    "autoload": {
        "psr-4": {
            "PhpAmqpLib\\": "PhpAmqpLib/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "PhpAmqpLib\\Tests\\Functional\\": "tests/Functional",
            "PhpAmqpLib\\Tests\\Unit\\": "tests/Unit"
        }
    },
    "license": "LGPL-2.1-or-later",
    "extra": {
        "branch-alias": {
            "dev-master": "2.7-dev"
        }
    }
}
<?php

// autoload_namespaces.php @generated by Composer

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

return array(
);
<?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

// autoload_psr4.php @generated by Composer

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

return array(
    'voku\\helper\\' => array($vendorDir . '/voku/php-hooks/src/voku/helper'),
    'test\\' => array($vendorDir . '/linways/nucleus-core/test'),
    'linways\\cli\\' => array($baseDir . '/src'),
    'com\\linways\\base\\' => array($vendorDir . '/linways/linways-base/src/com/linways/base'),
    'com\\linways\\' => array($vendorDir . '/linways/nucleus-core/src/com/linways'),
    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
    'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
    'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
    'Symfony\\Component\\Dotenv\\' => array($vendorDir . '/symfony/dotenv'),
    'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
    'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
    'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'),
    'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
    'PhpAmqpLib\\' => array($vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib'),
    'Phinx\\' => array($vendorDir . '/robmorgan/phinx/src/Phinx'),
    'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
    'Cron\\' => array($vendorDir . '/dragonmantank/cron-expression/src/Cron'),
);
<?php

// autoload_classmap.php @generated by Composer

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

return array(
    'Cron\\AbstractField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/AbstractField.php',
    'Cron\\CronExpression' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/CronExpression.php',
    'Cron\\DayOfMonthField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php',
    'Cron\\DayOfWeekField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php',
    'Cron\\FieldFactory' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/FieldFactory.php',
    'Cron\\FieldInterface' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/FieldInterface.php',
    'Cron\\HoursField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/HoursField.php',
    'Cron\\MinutesField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/MinutesField.php',
    'Cron\\MonthField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/MonthField.php',
    'Cron\\YearField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/YearField.php',
    'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php',
    'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php',
    'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php',
    'Firebase\\JWT\\SignatureInvalidException' => $vendorDir . '/firebase/php-jwt/src/SignatureInvalidException.php',
    'Phinx\\Config\\Config' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/Config.php',
    'Phinx\\Config\\ConfigInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php',
    'Phinx\\Config\\NamespaceAwareInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareInterface.php',
    'Phinx\\Config\\NamespaceAwareTrait' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareTrait.php',
    'Phinx\\Console\\Command\\AbstractCommand' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php',
    'Phinx\\Console\\Command\\Breakpoint' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php',
    'Phinx\\Console\\Command\\Create' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Create.php',
    'Phinx\\Console\\Command\\Init' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Init.php',
    'Phinx\\Console\\Command\\Migrate' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php',
    'Phinx\\Console\\Command\\Rollback' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php',
    'Phinx\\Console\\Command\\SeedCreate' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php',
    'Phinx\\Console\\Command\\SeedRun' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php',
    'Phinx\\Console\\Command\\Status' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Status.php',
    'Phinx\\Console\\Command\\Test' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Test.php',
    'Phinx\\Console\\PhinxApplication' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php',
    'Phinx\\Db\\Adapter\\AbstractAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php',
    'Phinx\\Db\\Adapter\\AdapterFactory' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php',
    'Phinx\\Db\\Adapter\\AdapterInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php',
    'Phinx\\Db\\Adapter\\AdapterWrapper' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php',
    'Phinx\\Db\\Adapter\\MysqlAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php',
    'Phinx\\Db\\Adapter\\PdoAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php',
    'Phinx\\Db\\Adapter\\PostgresAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php',
    'Phinx\\Db\\Adapter\\ProxyAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php',
    'Phinx\\Db\\Adapter\\SQLiteAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php',
    'Phinx\\Db\\Adapter\\SqlServerAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php',
    'Phinx\\Db\\Adapter\\TablePrefixAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php',
    'Phinx\\Db\\Adapter\\TimedOutputAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php',
    'Phinx\\Db\\Adapter\\WrapperInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php',
    'Phinx\\Db\\Table' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table.php',
    'Phinx\\Db\\Table\\Column' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table/Column.php',
    'Phinx\\Db\\Table\\ForeignKey' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php',
    'Phinx\\Db\\Table\\Index' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table/Index.php',
    'Phinx\\Migration\\AbstractMigration' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php',
    'Phinx\\Migration\\AbstractTemplateCreation' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php',
    'Phinx\\Migration\\CreationInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php',
    'Phinx\\Migration\\IrreversibleMigrationException' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php',
    'Phinx\\Migration\\Manager' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/Manager.php',
    'Phinx\\Migration\\Manager\\Environment' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php',
    'Phinx\\Migration\\MigrationInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/MigrationInterface.php',
    'Phinx\\Seed\\AbstractSeed' => $vendorDir . '/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php',
    'Phinx\\Seed\\SeedInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Seed/SeedInterface.php',
    'Phinx\\Util\\Util' => $vendorDir . '/robmorgan/phinx/src/Phinx/Util/Util.php',
    'Phinx\\Wrapper\\TextWrapper' => $vendorDir . '/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php',
    'PhpAmqpLib\\Channel\\AMQPChannel' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.php',
    'PhpAmqpLib\\Channel\\AbstractChannel' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php',
    'PhpAmqpLib\\Connection\\AMQPConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnection.php',
    'PhpAmqpLib\\Connection\\AMQPLazyConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.php',
    'PhpAmqpLib\\Connection\\AMQPLazySocketConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySocketConnection.php',
    'PhpAmqpLib\\Connection\\AMQPSSLConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSSLConnection.php',
    'PhpAmqpLib\\Connection\\AMQPSocketConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSocketConnection.php',
    'PhpAmqpLib\\Connection\\AMQPStreamConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPStreamConnection.php',
    'PhpAmqpLib\\Connection\\AbstractConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AbstractConnection.php',
    'PhpAmqpLib\\Exception\\AMQPBasicCancelException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.php',
    'PhpAmqpLib\\Exception\\AMQPChannelException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelException.php',
    'PhpAmqpLib\\Exception\\AMQPConnectionException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionException.php',
    'PhpAmqpLib\\Exception\\AMQPException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPException.php',
    'PhpAmqpLib\\Exception\\AMQPExceptionInterface' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPExceptionInterface.php',
    'PhpAmqpLib\\Exception\\AMQPIOException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOException.php',
    'PhpAmqpLib\\Exception\\AMQPIOWaitException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOWaitException.php',
    'PhpAmqpLib\\Exception\\AMQPInvalidArgumentException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidArgumentException.php',
    'PhpAmqpLib\\Exception\\AMQPLogicException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.php',
    'PhpAmqpLib\\Exception\\AMQPOutOfBoundsException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfBoundsException.php',
    'PhpAmqpLib\\Exception\\AMQPOutOfRangeException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfRangeException.php',
    'PhpAmqpLib\\Exception\\AMQPProtocolChannelException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolChannelException.php',
    'PhpAmqpLib\\Exception\\AMQPProtocolConnectionException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolConnectionException.php',
    'PhpAmqpLib\\Exception\\AMQPProtocolException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolException.php',
    'PhpAmqpLib\\Exception\\AMQPRuntimeException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPRuntimeException.php',
    'PhpAmqpLib\\Exception\\AMQPTimeoutException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.php',
    'PhpAmqpLib\\Helper\\DebugHelper' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/DebugHelper.php',
    'PhpAmqpLib\\Helper\\MiscHelper' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/MiscHelper.php',
    'PhpAmqpLib\\Helper\\Protocol\\MethodMap080' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap080.php',
    'PhpAmqpLib\\Helper\\Protocol\\MethodMap091' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap091.php',
    'PhpAmqpLib\\Helper\\Protocol\\Protocol080' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol080.php',
    'PhpAmqpLib\\Helper\\Protocol\\Protocol091' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol091.php',
    'PhpAmqpLib\\Helper\\Protocol\\Wait080' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait080.php',
    'PhpAmqpLib\\Helper\\Protocol\\Wait091' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait091.php',
    'PhpAmqpLib\\Message\\AMQPMessage' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.php',
    'PhpAmqpLib\\Wire\\AMQPAbstractCollection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPAbstractCollection.php',
    'PhpAmqpLib\\Wire\\AMQPArray' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPArray.php',
    'PhpAmqpLib\\Wire\\AMQPDecimal' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.php',
    'PhpAmqpLib\\Wire\\AMQPReader' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPReader.php',
    'PhpAmqpLib\\Wire\\AMQPTable' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPTable.php',
    'PhpAmqpLib\\Wire\\AMQPWriter' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPWriter.php',
    'PhpAmqpLib\\Wire\\AbstractClient' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AbstractClient.php',
    'PhpAmqpLib\\Wire\\Constants080' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants080.php',
    'PhpAmqpLib\\Wire\\Constants091' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants091.php',
    'PhpAmqpLib\\Wire\\GenericContent' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/GenericContent.php',
    'PhpAmqpLib\\Wire\\IO\\AbstractIO' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/AbstractIO.php',
    'PhpAmqpLib\\Wire\\IO\\SocketIO' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/SocketIO.php',
    'PhpAmqpLib\\Wire\\IO\\StreamIO' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/StreamIO.php',
    'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php',
    'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php',
    'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php',
    'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php',
    'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php',
    'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
    'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
    'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
    'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
    'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
    'Symfony\\Component\\Config\\ConfigCache' => $vendorDir . '/symfony/config/ConfigCache.php',
    'Symfony\\Component\\Config\\ConfigCacheFactory' => $vendorDir . '/symfony/config/ConfigCacheFactory.php',
    'Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => $vendorDir . '/symfony/config/ConfigCacheFactoryInterface.php',
    'Symfony\\Component\\Config\\ConfigCacheInterface' => $vendorDir . '/symfony/config/ConfigCacheInterface.php',
    'Symfony\\Component\\Config\\Definition\\ArrayNode' => $vendorDir . '/symfony/config/Definition/ArrayNode.php',
    'Symfony\\Component\\Config\\Definition\\BaseNode' => $vendorDir . '/symfony/config/Definition/BaseNode.php',
    'Symfony\\Component\\Config\\Definition\\BooleanNode' => $vendorDir . '/symfony/config/Definition/BooleanNode.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/EnumNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ExprBuilder.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/FloatNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\IntegerNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/IntegerNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\MergeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/MergeBuilder.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\NodeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/NodeBuilder.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/NodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface' => $vendorDir . '/symfony/config/Definition/Builder/NodeParentInterface.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\NormalizationBuilder' => $vendorDir . '/symfony/config/Definition/Builder/NormalizationBuilder.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\NumericNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/NumericNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\ParentNodeDefinitionInterface' => $vendorDir . '/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\ScalarNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ScalarNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/TreeBuilder.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\ValidationBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ValidationBuilder.php',
    'Symfony\\Component\\Config\\Definition\\Builder\\VariableNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/VariableNodeDefinition.php',
    'Symfony\\Component\\Config\\Definition\\ConfigurationInterface' => $vendorDir . '/symfony/config/Definition/ConfigurationInterface.php',
    'Symfony\\Component\\Config\\Definition\\Dumper\\XmlReferenceDumper' => $vendorDir . '/symfony/config/Definition/Dumper/XmlReferenceDumper.php',
    'Symfony\\Component\\Config\\Definition\\Dumper\\YamlReferenceDumper' => $vendorDir . '/symfony/config/Definition/Dumper/YamlReferenceDumper.php',
    'Symfony\\Component\\Config\\Definition\\EnumNode' => $vendorDir . '/symfony/config/Definition/EnumNode.php',
    'Symfony\\Component\\Config\\Definition\\Exception\\DuplicateKeyException' => $vendorDir . '/symfony/config/Definition/Exception/DuplicateKeyException.php',
    'Symfony\\Component\\Config\\Definition\\Exception\\Exception' => $vendorDir . '/symfony/config/Definition/Exception/Exception.php',
    'Symfony\\Component\\Config\\Definition\\Exception\\ForbiddenOverwriteException' => $vendorDir . '/symfony/config/Definition/Exception/ForbiddenOverwriteException.php',
    'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidConfigurationException.php',
    'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidDefinitionException.php',
    'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidTypeException.php',
    'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => $vendorDir . '/symfony/config/Definition/Exception/UnsetKeyException.php',
    'Symfony\\Component\\Config\\Definition\\FloatNode' => $vendorDir . '/symfony/config/Definition/FloatNode.php',
    'Symfony\\Component\\Config\\Definition\\IntegerNode' => $vendorDir . '/symfony/config/Definition/IntegerNode.php',
    'Symfony\\Component\\Config\\Definition\\NodeInterface' => $vendorDir . '/symfony/config/Definition/NodeInterface.php',
    'Symfony\\Component\\Config\\Definition\\NumericNode' => $vendorDir . '/symfony/config/Definition/NumericNode.php',
    'Symfony\\Component\\Config\\Definition\\Processor' => $vendorDir . '/symfony/config/Definition/Processor.php',
    'Symfony\\Component\\Config\\Definition\\PrototypeNodeInterface' => $vendorDir . '/symfony/config/Definition/PrototypeNodeInterface.php',
    'Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => $vendorDir . '/symfony/config/Definition/PrototypedArrayNode.php',
    'Symfony\\Component\\Config\\Definition\\ScalarNode' => $vendorDir . '/symfony/config/Definition/ScalarNode.php',
    'Symfony\\Component\\Config\\Definition\\VariableNode' => $vendorDir . '/symfony/config/Definition/VariableNode.php',
    'Symfony\\Component\\Config\\DependencyInjection\\ConfigCachePass' => $vendorDir . '/symfony/config/DependencyInjection/ConfigCachePass.php',
    'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => $vendorDir . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php',
    'Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => $vendorDir . '/symfony/config/Exception/FileLoaderLoadException.php',
    'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => $vendorDir . '/symfony/config/Exception/FileLocatorFileNotFoundException.php',
    'Symfony\\Component\\Config\\FileLocator' => $vendorDir . '/symfony/config/FileLocator.php',
    'Symfony\\Component\\Config\\FileLocatorInterface' => $vendorDir . '/symfony/config/FileLocatorInterface.php',
    'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => $vendorDir . '/symfony/config/Loader/DelegatingLoader.php',
    'Symfony\\Component\\Config\\Loader\\FileLoader' => $vendorDir . '/symfony/config/Loader/FileLoader.php',
    'Symfony\\Component\\Config\\Loader\\GlobFileLoader' => $vendorDir . '/symfony/config/Loader/GlobFileLoader.php',
    'Symfony\\Component\\Config\\Loader\\Loader' => $vendorDir . '/symfony/config/Loader/Loader.php',
    'Symfony\\Component\\Config\\Loader\\LoaderInterface' => $vendorDir . '/symfony/config/Loader/LoaderInterface.php',
    'Symfony\\Component\\Config\\Loader\\LoaderResolver' => $vendorDir . '/symfony/config/Loader/LoaderResolver.php',
    'Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => $vendorDir . '/symfony/config/Loader/LoaderResolverInterface.php',
    'Symfony\\Component\\Config\\ResourceCheckerConfigCache' => $vendorDir . '/symfony/config/ResourceCheckerConfigCache.php',
    'Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => $vendorDir . '/symfony/config/ResourceCheckerConfigCacheFactory.php',
    'Symfony\\Component\\Config\\ResourceCheckerInterface' => $vendorDir . '/symfony/config/ResourceCheckerInterface.php',
    'Symfony\\Component\\Config\\Resource\\ClassExistenceResource' => $vendorDir . '/symfony/config/Resource/ClassExistenceResource.php',
    'Symfony\\Component\\Config\\Resource\\ComposerResource' => $vendorDir . '/symfony/config/Resource/ComposerResource.php',
    'Symfony\\Component\\Config\\Resource\\DirectoryResource' => $vendorDir . '/symfony/config/Resource/DirectoryResource.php',
    'Symfony\\Component\\Config\\Resource\\FileExistenceResource' => $vendorDir . '/symfony/config/Resource/FileExistenceResource.php',
    'Symfony\\Component\\Config\\Resource\\FileResource' => $vendorDir . '/symfony/config/Resource/FileResource.php',
    'Symfony\\Component\\Config\\Resource\\GlobResource' => $vendorDir . '/symfony/config/Resource/GlobResource.php',
    'Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php',
    'Symfony\\Component\\Config\\Resource\\ReflectionMethodHhvmWrapper' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php',
    'Symfony\\Component\\Config\\Resource\\ReflectionParameterHhvmWrapper' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php',
    'Symfony\\Component\\Config\\Resource\\ResourceInterface' => $vendorDir . '/symfony/config/Resource/ResourceInterface.php',
    'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceChecker.php',
    'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceInterface.php',
    'Symfony\\Component\\Config\\Util\\Exception\\InvalidXmlException' => $vendorDir . '/symfony/config/Util/Exception/InvalidXmlException.php',
    'Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => $vendorDir . '/symfony/config/Util/Exception/XmlParsingException.php',
    'Symfony\\Component\\Config\\Util\\XmlUtils' => $vendorDir . '/symfony/config/Util/XmlUtils.php',
    'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Application.php',
    'Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface' => $vendorDir . '/symfony/console/CommandLoader/CommandLoaderInterface.php',
    'Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader' => $vendorDir . '/symfony/console/CommandLoader/ContainerCommandLoader.php',
    'Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader' => $vendorDir . '/symfony/console/CommandLoader/FactoryCommandLoader.php',
    'Symfony\\Component\\Console\\Command\\Command' => $vendorDir . '/symfony/console/Command/Command.php',
    'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Command/HelpCommand.php',
    'Symfony\\Component\\Console\\Command\\ListCommand' => $vendorDir . '/symfony/console/Command/ListCommand.php',
    'Symfony\\Component\\Console\\Command\\LockableTrait' => $vendorDir . '/symfony/console/Command/LockableTrait.php',
    'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/ConsoleEvents.php',
    'Symfony\\Component\\Console\\DependencyInjection\\AddConsoleCommandPass' => $vendorDir . '/symfony/console/DependencyInjection/AddConsoleCommandPass.php',
    'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => $vendorDir . '/symfony/console/Descriptor/ApplicationDescription.php',
    'Symfony\\Component\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/console/Descriptor/Descriptor.php',
    'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => $vendorDir . '/symfony/console/Descriptor/DescriptorInterface.php',
    'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/console/Descriptor/JsonDescriptor.php',
    'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/console/Descriptor/MarkdownDescriptor.php',
    'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/console/Descriptor/TextDescriptor.php',
    'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/console/Descriptor/XmlDescriptor.php',
    'Symfony\\Component\\Console\\EventListener\\ErrorListener' => $vendorDir . '/symfony/console/EventListener/ErrorListener.php',
    'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => $vendorDir . '/symfony/console/Event/ConsoleCommandEvent.php',
    'Symfony\\Component\\Console\\Event\\ConsoleErrorEvent' => $vendorDir . '/symfony/console/Event/ConsoleErrorEvent.php',
    'Symfony\\Component\\Console\\Event\\ConsoleEvent' => $vendorDir . '/symfony/console/Event/ConsoleEvent.php',
    'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => $vendorDir . '/symfony/console/Event/ConsoleExceptionEvent.php',
    'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => $vendorDir . '/symfony/console/Event/ConsoleTerminateEvent.php',
    'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => $vendorDir . '/symfony/console/Exception/CommandNotFoundException.php',
    'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/console/Exception/ExceptionInterface.php',
    'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/console/Exception/InvalidArgumentException.php',
    'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => $vendorDir . '/symfony/console/Exception/InvalidOptionException.php',
    'Symfony\\Component\\Console\\Exception\\LogicException' => $vendorDir . '/symfony/console/Exception/LogicException.php',
    'Symfony\\Component\\Console\\Exception\\RuntimeException' => $vendorDir . '/symfony/console/Exception/RuntimeException.php',
    'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => $vendorDir . '/symfony/console/Formatter/OutputFormatter.php',
    'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterInterface.php',
    'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyle.php',
    'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleInterface.php',
    'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleStack.php',
    'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => $vendorDir . '/symfony/console/Helper/DebugFormatterHelper.php',
    'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/console/Helper/DescriptorHelper.php',
    'Symfony\\Component\\Console\\Helper\\FormatterHelper' => $vendorDir . '/symfony/console/Helper/FormatterHelper.php',
    'Symfony\\Component\\Console\\Helper\\Helper' => $vendorDir . '/symfony/console/Helper/Helper.php',
    'Symfony\\Component\\Console\\Helper\\HelperInterface' => $vendorDir . '/symfony/console/Helper/HelperInterface.php',
    'Symfony\\Component\\Console\\Helper\\HelperSet' => $vendorDir . '/symfony/console/Helper/HelperSet.php',
    'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => $vendorDir . '/symfony/console/Helper/InputAwareHelper.php',
    'Symfony\\Component\\Console\\Helper\\ProcessHelper' => $vendorDir . '/symfony/console/Helper/ProcessHelper.php',
    'Symfony\\Component\\Console\\Helper\\ProgressBar' => $vendorDir . '/symfony/console/Helper/ProgressBar.php',
    'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => $vendorDir . '/symfony/console/Helper/ProgressIndicator.php',
    'Symfony\\Component\\Console\\Helper\\QuestionHelper' => $vendorDir . '/symfony/console/Helper/QuestionHelper.php',
    'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => $vendorDir . '/symfony/console/Helper/SymfonyQuestionHelper.php',
    'Symfony\\Component\\Console\\Helper\\Table' => $vendorDir . '/symfony/console/Helper/Table.php',
    'Symfony\\Component\\Console\\Helper\\TableCell' => $vendorDir . '/symfony/console/Helper/TableCell.php',
    'Symfony\\Component\\Console\\Helper\\TableSeparator' => $vendorDir . '/symfony/console/Helper/TableSeparator.php',
    'Symfony\\Component\\Console\\Helper\\TableStyle' => $vendorDir . '/symfony/console/Helper/TableStyle.php',
    'Symfony\\Component\\Console\\Input\\ArgvInput' => $vendorDir . '/symfony/console/Input/ArgvInput.php',
    'Symfony\\Component\\Console\\Input\\ArrayInput' => $vendorDir . '/symfony/console/Input/ArrayInput.php',
    'Symfony\\Component\\Console\\Input\\Input' => $vendorDir . '/symfony/console/Input/Input.php',
    'Symfony\\Component\\Console\\Input\\InputArgument' => $vendorDir . '/symfony/console/Input/InputArgument.php',
    'Symfony\\Component\\Console\\Input\\InputAwareInterface' => $vendorDir . '/symfony/console/Input/InputAwareInterface.php',
    'Symfony\\Component\\Console\\Input\\InputDefinition' => $vendorDir . '/symfony/console/Input/InputDefinition.php',
    'Symfony\\Component\\Console\\Input\\InputInterface' => $vendorDir . '/symfony/console/Input/InputInterface.php',
    'Symfony\\Component\\Console\\Input\\InputOption' => $vendorDir . '/symfony/console/Input/InputOption.php',
    'Symfony\\Component\\Console\\Input\\StreamableInputInterface' => $vendorDir . '/symfony/console/Input/StreamableInputInterface.php',
    'Symfony\\Component\\Console\\Input\\StringInput' => $vendorDir . '/symfony/console/Input/StringInput.php',
    'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => $vendorDir . '/symfony/console/Logger/ConsoleLogger.php',
    'Symfony\\Component\\Console\\Output\\BufferedOutput' => $vendorDir . '/symfony/console/Output/BufferedOutput.php',
    'Symfony\\Component\\Console\\Output\\ConsoleOutput' => $vendorDir . '/symfony/console/Output/ConsoleOutput.php',
    'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => $vendorDir . '/symfony/console/Output/ConsoleOutputInterface.php',
    'Symfony\\Component\\Console\\Output\\NullOutput' => $vendorDir . '/symfony/console/Output/NullOutput.php',
    'Symfony\\Component\\Console\\Output\\Output' => $vendorDir . '/symfony/console/Output/Output.php',
    'Symfony\\Component\\Console\\Output\\OutputInterface' => $vendorDir . '/symfony/console/Output/OutputInterface.php',
    'Symfony\\Component\\Console\\Output\\StreamOutput' => $vendorDir . '/symfony/console/Output/StreamOutput.php',
    'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => $vendorDir . '/symfony/console/Question/ChoiceQuestion.php',
    'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => $vendorDir . '/symfony/console/Question/ConfirmationQuestion.php',
    'Symfony\\Component\\Console\\Question\\Question' => $vendorDir . '/symfony/console/Question/Question.php',
    'Symfony\\Component\\Console\\Style\\OutputStyle' => $vendorDir . '/symfony/console/Style/OutputStyle.php',
    'Symfony\\Component\\Console\\Style\\StyleInterface' => $vendorDir . '/symfony/console/Style/StyleInterface.php',
    'Symfony\\Component\\Console\\Style\\SymfonyStyle' => $vendorDir . '/symfony/console/Style/SymfonyStyle.php',
    'Symfony\\Component\\Console\\Terminal' => $vendorDir . '/symfony/console/Terminal.php',
    'Symfony\\Component\\Console\\Tester\\ApplicationTester' => $vendorDir . '/symfony/console/Tester/ApplicationTester.php',
    'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Tester/CommandTester.php',
    'Symfony\\Component\\Debug\\BufferingLogger' => $vendorDir . '/symfony/debug/BufferingLogger.php',
    'Symfony\\Component\\Debug\\Debug' => $vendorDir . '/symfony/debug/Debug.php',
    'Symfony\\Component\\Debug\\DebugClassLoader' => $vendorDir . '/symfony/debug/DebugClassLoader.php',
    'Symfony\\Component\\Debug\\ErrorHandler' => $vendorDir . '/symfony/debug/ErrorHandler.php',
    'Symfony\\Component\\Debug\\ExceptionHandler' => $vendorDir . '/symfony/debug/ExceptionHandler.php',
    'Symfony\\Component\\Debug\\Exception\\ClassNotFoundException' => $vendorDir . '/symfony/debug/Exception/ClassNotFoundException.php',
    'Symfony\\Component\\Debug\\Exception\\ContextErrorException' => $vendorDir . '/symfony/debug/Exception/ContextErrorException.php',
    'Symfony\\Component\\Debug\\Exception\\FatalErrorException' => $vendorDir . '/symfony/debug/Exception/FatalErrorException.php',
    'Symfony\\Component\\Debug\\Exception\\FatalThrowableError' => $vendorDir . '/symfony/debug/Exception/FatalThrowableError.php',
    'Symfony\\Component\\Debug\\Exception\\FlattenException' => $vendorDir . '/symfony/debug/Exception/FlattenException.php',
    'Symfony\\Component\\Debug\\Exception\\OutOfMemoryException' => $vendorDir . '/symfony/debug/Exception/OutOfMemoryException.php',
    'Symfony\\Component\\Debug\\Exception\\SilencedErrorContext' => $vendorDir . '/symfony/debug/Exception/SilencedErrorContext.php',
    'Symfony\\Component\\Debug\\Exception\\UndefinedFunctionException' => $vendorDir . '/symfony/debug/Exception/UndefinedFunctionException.php',
    'Symfony\\Component\\Debug\\Exception\\UndefinedMethodException' => $vendorDir . '/symfony/debug/Exception/UndefinedMethodException.php',
    'Symfony\\Component\\Debug\\FatalErrorHandler\\ClassNotFoundFatalErrorHandler' => $vendorDir . '/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php',
    'Symfony\\Component\\Debug\\FatalErrorHandler\\FatalErrorHandlerInterface' => $vendorDir . '/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php',
    'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedFunctionFatalErrorHandler' => $vendorDir . '/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php',
    'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedMethodFatalErrorHandler' => $vendorDir . '/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php',
    'Symfony\\Component\\Dotenv\\Dotenv' => $vendorDir . '/symfony/dotenv/Dotenv.php',
    'Symfony\\Component\\Dotenv\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/dotenv/Exception/ExceptionInterface.php',
    'Symfony\\Component\\Dotenv\\Exception\\FormatException' => $vendorDir . '/symfony/dotenv/Exception/FormatException.php',
    'Symfony\\Component\\Dotenv\\Exception\\FormatExceptionContext' => $vendorDir . '/symfony/dotenv/Exception/FormatExceptionContext.php',
    'Symfony\\Component\\Dotenv\\Exception\\PathException' => $vendorDir . '/symfony/dotenv/Exception/PathException.php',
    'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/ExceptionInterface.php',
    'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/filesystem/Exception/FileNotFoundException.php',
    'Symfony\\Component\\Filesystem\\Exception\\IOException' => $vendorDir . '/symfony/filesystem/Exception/IOException.php',
    'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/IOExceptionInterface.php',
    'Symfony\\Component\\Filesystem\\Filesystem' => $vendorDir . '/symfony/filesystem/Filesystem.php',
    'Symfony\\Component\\Filesystem\\LockHandler' => $vendorDir . '/symfony/filesystem/LockHandler.php',
    'Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php',
    'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php',
    'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Escaper.php',
    'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Exception/DumpException.php',
    'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/yaml/Exception/ExceptionInterface.php',
    'Symfony\\Component\\Yaml\\Exception\\ParseException' => $vendorDir . '/symfony/yaml/Exception/ParseException.php',
    'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => $vendorDir . '/symfony/yaml/Exception/RuntimeException.php',
    'Symfony\\Component\\Yaml\\Inline' => $vendorDir . '/symfony/yaml/Inline.php',
    'Symfony\\Component\\Yaml\\Parser' => $vendorDir . '/symfony/yaml/Parser.php',
    'Symfony\\Component\\Yaml\\Tag\\TaggedValue' => $vendorDir . '/symfony/yaml/Tag/TaggedValue.php',
    'Symfony\\Component\\Yaml\\Unescaper' => $vendorDir . '/symfony/yaml/Unescaper.php',
    'Symfony\\Component\\Yaml\\Yaml' => $vendorDir . '/symfony/yaml/Yaml.php',
    'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php',
    'com\\linways\\base\\cache\\LinCache' => $vendorDir . '/linways/linways-base/src/com/linways/base/cache/LinCache.php',
    'com\\linways\\base\\connection\\IMySqlConnector' => $vendorDir . '/linways/linways-base/src/com/linways/base/connection/IMySqlConnector.php',
    'com\\linways\\base\\connection\\MySqlConnector' => $vendorDir . '/linways/linways-base/src/com/linways/base/connection/MySqlConnector.php',
    'com\\linways\\base\\connection\\MySqlQuery' => $vendorDir . '/linways/linways-base/src/com/linways/base/connection/MySqlQuery.php',
    'com\\linways\\base\\dto\\AjaxPaginator' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/AjaxPaginator.php',
    'com\\linways\\base\\dto\\BaseDTO' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/BaseDTO.php',
    'com\\linways\\base\\dto\\Email' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/Email.php',
    'com\\linways\\base\\dto\\EmailCC' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/EmailTo.php',
    'com\\linways\\base\\dto\\EmailFrom' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/EmailCC.php',
    'com\\linways\\base\\dto\\EmailTo' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/EmailFrom.php',
    'com\\linways\\base\\dto\\MySqlResult' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/MySqlResult.php',
    'com\\linways\\base\\dto\\PageSupport' => $vendorDir . '/linways/linways-base/src/com/linways/base/dto/PageSupport.php',
    'com\\linways\\base\\email\\PHPMailer' => $vendorDir . '/linways/linways-base/src/com/linways/base/email/PHPMailer.php',
    'com\\linways\\base\\email\\SMTP' => $vendorDir . '/linways/linways-base/src/com/linways/base/email/SMTP.php',
    'com\\linways\\base\\exception\\CoreException' => $vendorDir . '/linways/linways-base/src/com/linways/base/exception/CoreException.php',
    'com\\linways\\base\\exception\\EmailException' => $vendorDir . '/linways/linways-base/src/com/linways/base/exception/EmailException.php',
    'com\\linways\\base\\exception\\MapperException' => $vendorDir . '/linways/linways-base/src/com/linways/base/exception/MapperException.php',
    'com\\linways\\base\\exception\\SQLException' => $vendorDir . '/linways/linways-base/src/com/linways/base/exception/SQLException.php',
    'com\\linways\\base\\exception\\TaskQueueException' => $vendorDir . '/linways/linways-base/src/com/linways/base/exception/TaskQueueException.php',
    'com\\linways\\base\\helper\\ResultHandler' => $vendorDir . '/linways/linways-base/src/com/linways/base/helper/ResultHandler.php',
    'com\\linways\\base\\hooks\\LinwaysHooks' => $vendorDir . '/linways/linways-base/src/com/linways/base/hooks/LinwaysHooks.php',
    'com\\linways\\base\\mapper\\IMapper' => $vendorDir . '/linways/linways-base/src/com/linways/base/mapper/IMapper.php',
    'com\\linways\\base\\mapper\\Result' => $vendorDir . '/linways/linways-base/src/com/linways/base/mapper/Result.php',
    'com\\linways\\base\\mapper\\ResultMap' => $vendorDir . '/linways/linways-base/src/com/linways/base/mapper/ResultMap.php',
    'com\\linways\\base\\mcrypt\\CryptAES' => $vendorDir . '/linways/linways-base/src/com/linways/base/mcrypt/CryptAES.php',
    'com\\linways\\base\\messages\\CommonMessages' => $vendorDir . '/linways/linways-base/src/com/linways/base/messages/CommonMessages.php',
    'com\\linways\\base\\queue\\TaskQueue' => $vendorDir . '/linways/linways-base/src/com/linways/base/queue/TaskQueue.php',
    'com\\linways\\base\\request\\BaseRequest' => $vendorDir . '/linways/linways-base/src/com/linways/base/request/BaseRequest.php',
    'com\\linways\\base\\response\\BaseResponse' => $vendorDir . '/linways/linways-base/src/com/linways/base/response/BaseResponse.php',
    'com\\linways\\base\\service\\EmailService' => $vendorDir . '/linways/linways-base/src/com/linways/base/service/EmailService.php',
    'com\\linways\\base\\soap\\XMLSchema' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/XMLSchema.php',
    'com\\linways\\base\\soap\\nusoap_base' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/nusoap_base.php',
    'com\\linways\\base\\soap\\nusoap_client' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/nusoap_client.php',
    'com\\linways\\base\\soap\\nusoap_fault' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/nusoap_fault.php',
    'com\\linways\\base\\soap\\nusoap_parser' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/nusoap_parser.php',
    'com\\linways\\base\\soap\\nusoap_server' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/nusoap_server.php',
    'com\\linways\\base\\soap\\nusoap_xmlschema' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/nusoap_xmlschema.php',
    'com\\linways\\base\\soap\\soap_fault' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/soap_fault.php',
    'com\\linways\\base\\soap\\soap_parser' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/soap_parser.php',
    'com\\linways\\base\\soap\\soap_server' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/soap_server.php',
    'com\\linways\\base\\soap\\soap_transport_http' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/soap_transport_http.php',
    'com\\linways\\base\\soap\\soapclient' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/soapclient.php',
    'com\\linways\\base\\soap\\soapval' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/soapval.php',
    'com\\linways\\base\\soap\\wsdl' => $vendorDir . '/linways/linways-base/src/com/linways/base/soap/wsdl.php',
    'com\\linways\\base\\test\\BaseTestCase' => $vendorDir . '/linways/linways-base/src/com/linways/base/testcase/BaseTestCase.php',
    'com\\linways\\base\\util\\CalendarUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/CalendarUtil.php',
    'com\\linways\\base\\util\\CommonUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/CommonUtil.php',
    'com\\linways\\base\\util\\MapperUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/MapperUtil.php',
    'com\\linways\\base\\util\\MySqlUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/MySqlUtil.php',
    'com\\linways\\base\\util\\ObjectUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/ObjectUtil.php',
    'com\\linways\\base\\util\\PhoneUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/PhoneUtil.php',
    'com\\linways\\base\\util\\RequestUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/RequestUtil.php',
    'com\\linways\\base\\util\\StringUtil' => $vendorDir . '/linways/linways-base/src/com/linways/base/util/StringUtil.php',
    'com\\linways\\nucleus\\core\\ams_task_queue\\AMSTaskQueue' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/ams_task_queue/AMSTaskQueue.php',
    'com\\linways\\nucleus\\core\\constants\\Scope' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/constants/Scope.php',
    'com\\linways\\nucleus\\core\\constants\\SingleSignonConstant' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/constants/SingleSignonConstant.php',
    'com\\linways\\nucleus\\core\\dto\\Addon' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/Addon.php',
    'com\\linways\\nucleus\\core\\dto\\AddonAttribute' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonAttribute.php',
    'com\\linways\\nucleus\\core\\dto\\AddonAttributeValue' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonAttributeValue.php',
    'com\\linways\\nucleus\\core\\dto\\AddonDataStore' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonDataStore.php',
    'com\\linways\\nucleus\\core\\dto\\AddonEnabled' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonEnabled.php',
    'com\\linways\\nucleus\\core\\dto\\ApiOrganizationDetails' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/ApiOrganizationDetails.php',
    'com\\linways\\nucleus\\core\\dto\\Client' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/Client.php',
    'com\\linways\\nucleus\\core\\dto\\College' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/College.php',
    'com\\linways\\nucleus\\core\\dto\\Expense' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/Expense.php',
    'com\\linways\\nucleus\\core\\dto\\ExpenseCategory' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/ExpenseCategory.php',
    'com\\linways\\nucleus\\core\\dto\\ScheduledTask' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/ScheduledTask.php',
    'com\\linways\\nucleus\\core\\dto\\SingleSignonLog' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/SingleSignonLog.php',
    'com\\linways\\nucleus\\core\\dto\\user\\User' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/user/User.php',
    'com\\linways\\nucleus\\core\\exception\\NucleusException' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/exception/NucleusException.php',
    'com\\linways\\nucleus\\core\\exception\\SchedulerException' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/exception/SchedulerException.php',
    'com\\linways\\nucleus\\core\\exception\\TaskQueueWorkerException' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/exception/TaskQueueWorkerException.php',
    'com\\linways\\nucleus\\core\\mapper\\AddonDataStoreMapper' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/AddonDataStoreMapper.php',
    'com\\linways\\nucleus\\core\\mapper\\AddonServiceMapper' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/AddonServiceMapper.php',
    'com\\linways\\nucleus\\core\\mapper\\ClientServiceMapper' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ClientServiceMapper.php',
    'com\\linways\\nucleus\\core\\mapper\\CollegeServiceMapper' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/CollegeServiceMapper.php',
    'com\\linways\\nucleus\\core\\mapper\\ExpenseServiceMapper' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ExpenseServiceMapper.php',
    'com\\linways\\nucleus\\core\\mapper\\SchedulerServiceMapper' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ScheduleServiceMapper.php',
    'com\\linways\\nucleus\\core\\service\\APIAccessTokenService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/APIAccessTokenService.php',
    'com\\linways\\nucleus\\core\\service\\AddonAttributeService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonAttributeService.php',
    'com\\linways\\nucleus\\core\\service\\AddonDataStoreSevice' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonDataStoreSevice.php',
    'com\\linways\\nucleus\\core\\service\\AddonService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonService.php',
    'com\\linways\\nucleus\\core\\service\\BaseService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/BaseService.php',
    'com\\linways\\nucleus\\core\\service\\ClientService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ClientService.php',
    'com\\linways\\nucleus\\core\\service\\CollegeService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/CollegeService.php',
    'com\\linways\\nucleus\\core\\service\\ConfigurationService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ConfigurationService.php',
    'com\\linways\\nucleus\\core\\service\\ExpenseCategoryService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ExpenseCategoryService.php',
    'com\\linways\\nucleus\\core\\service\\ExpenseService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ExpenseService.php',
    'com\\linways\\nucleus\\core\\service\\PermissionService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/PermissionService.php',
    'com\\linways\\nucleus\\core\\service\\SchedulerService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/SchedulerService.php',
    'com\\linways\\nucleus\\core\\service\\SingleSignonService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/SingleSignonService.php',
    'com\\linways\\nucleus\\core\\service\\user\\UserService' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/service/user/UserService.php',
    'com\\linways\\nucleus\\core\\utils\\JWTUtils' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/utils/JWTUtils.php',
    'com\\linways\\nucleus\\core\\utils\\StringUtils' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/utils/StringUtils.php',
    'com\\linways\\nucleus\\core\\utils\\user\\UserServiceUtils' => $vendorDir . '/linways/nucleus-core/src/com/linways/nucleus/core/utils/user/UserServiceUtils.php',
    'linways\\cli\\command\\db\\CreateMigrationCommand' => $baseDir . '/src/commands/db/CreateMigrationCommand.php',
    'linways\\cli\\command\\db\\MigrateCommand' => $baseDir . '/src/commands/db/MigrateCommand.php',
    'linways\\cli\\command\\db\\MigrateFakeCommand' => $baseDir . '/src/commands/db/MigrateFakeCommand.php',
    'linways\\cli\\command\\git\\UpdateStyleGuideCommand' => $baseDir . '/src/commands/git/UpdateStyleGuideCommand.php',
    'linways\\cli\\service\\db\\MigrateFakeService' => $baseDir . '/src/service/db/MigrateFakeService.php',
    'linways\\cli\\service\\db\\MigrateService' => $baseDir . '/src/service/db/MigrateService.php',
    'linways\\cli\\utls\\MigrationUtils' => $baseDir . '/src/utils/MigrationUtils.php',
    'test\\scripts\\testServiceClass' => $vendorDir . '/linways/nucleus-core/test/scripts/testServiceClass.php',
    'test\\service\\APIAccessTokenServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/APIAccessTokenServiceTest.php',
    'test\\service\\AddonDataStoreServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/AddonDataStoreServiceTest.php',
    'test\\service\\AddonServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/AddonServiceTest.php',
    'test\\service\\AddonTestHelpers' => $vendorDir . '/linways/nucleus-core/test/service/AddonTestHelpers.php',
    'test\\service\\ClientServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/ClientServiceTest.php',
    'test\\service\\CollegeServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/CollegeServiceTest.php',
    'test\\service\\ExpenseCategoryServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/ExpenseCategoryServiceTest.php',
    'test\\service\\PermissionServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/PermissionServiceTest.php',
    'test\\service\\SchedulerServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/SchedulerServiceTest.php',
    'test\\service\\user\\UserServiceTest' => $vendorDir . '/linways/nucleus-core/test/service/user/UserServiceTest.php',
    'test\\utils\\UserServiceUtilsTest' => $vendorDir . '/linways/nucleus-core/test/utils/UserServiceUtilsTest.php',
    'voku\\helper\\Hooks' => $vendorDir . '/voku/php-hooks/src/voku/helper/Hooks.php',
);
<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInit555f246cb7db6869b6fb9f48d5e5f3d2
{
    public static $files = array (
        '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
    );

    public static $prefixLengthsPsr4 = array (
        'v' => 
        array (
            'voku\\helper\\' => 12,
        ),
        't' => 
        array (
            'test\\' => 5,
        ),
        'l' => 
        array (
            'linways\\cli\\' => 12,
        ),
        'c' => 
        array (
            'com\\linways\\base\\' => 17,
            'com\\linways\\' => 12,
        ),
        'S' => 
        array (
            'Symfony\\Polyfill\\Mbstring\\' => 26,
            'Symfony\\Component\\Yaml\\' => 23,
            'Symfony\\Component\\Filesystem\\' => 29,
            'Symfony\\Component\\Dotenv\\' => 25,
            'Symfony\\Component\\Debug\\' => 24,
            'Symfony\\Component\\Console\\' => 26,
            'Symfony\\Component\\Config\\' => 25,
        ),
        'P' => 
        array (
            'Psr\\Log\\' => 8,
            'PhpAmqpLib\\' => 11,
            'Phinx\\' => 6,
        ),
        'F' => 
        array (
            'Firebase\\JWT\\' => 13,
        ),
        'C' => 
        array (
            'Cron\\' => 5,
        ),
    );

    public static $prefixDirsPsr4 = array (
        'voku\\helper\\' => 
        array (
            0 => __DIR__ . '/..' . '/voku/php-hooks/src/voku/helper',
        ),
        'test\\' => 
        array (
            0 => __DIR__ . '/..' . '/linways/nucleus-core/test',
        ),
        'linways\\cli\\' => 
        array (
            0 => __DIR__ . '/../..' . '/src',
        ),
        'com\\linways\\base\\' => 
        array (
            0 => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base',
        ),
        'com\\linways\\' => 
        array (
            0 => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways',
        ),
        'Symfony\\Polyfill\\Mbstring\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
        ),
        'Symfony\\Component\\Yaml\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/yaml',
        ),
        'Symfony\\Component\\Filesystem\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/filesystem',
        ),
        'Symfony\\Component\\Dotenv\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/dotenv',
        ),
        'Symfony\\Component\\Debug\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/debug',
        ),
        'Symfony\\Component\\Console\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/console',
        ),
        'Symfony\\Component\\Config\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/config',
        ),
        'Psr\\Log\\' => 
        array (
            0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
        ),
        'PhpAmqpLib\\' => 
        array (
            0 => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib',
        ),
        'Phinx\\' => 
        array (
            0 => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx',
        ),
        'Firebase\\JWT\\' => 
        array (
            0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
        ),
        'Cron\\' => 
        array (
            0 => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron',
        ),
    );

    public static $classMap = array (
        'Cron\\AbstractField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/AbstractField.php',
        'Cron\\CronExpression' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/CronExpression.php',
        'Cron\\DayOfMonthField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php',
        'Cron\\DayOfWeekField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php',
        'Cron\\FieldFactory' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/FieldFactory.php',
        'Cron\\FieldInterface' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/FieldInterface.php',
        'Cron\\HoursField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/HoursField.php',
        'Cron\\MinutesField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/MinutesField.php',
        'Cron\\MonthField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/MonthField.php',
        'Cron\\YearField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/YearField.php',
        'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php',
        'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php',
        'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php',
        'Firebase\\JWT\\SignatureInvalidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/SignatureInvalidException.php',
        'Phinx\\Config\\Config' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/Config.php',
        'Phinx\\Config\\ConfigInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php',
        'Phinx\\Config\\NamespaceAwareInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareInterface.php',
        'Phinx\\Config\\NamespaceAwareTrait' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareTrait.php',
        'Phinx\\Console\\Command\\AbstractCommand' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php',
        'Phinx\\Console\\Command\\Breakpoint' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php',
        'Phinx\\Console\\Command\\Create' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Create.php',
        'Phinx\\Console\\Command\\Init' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Init.php',
        'Phinx\\Console\\Command\\Migrate' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php',
        'Phinx\\Console\\Command\\Rollback' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php',
        'Phinx\\Console\\Command\\SeedCreate' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php',
        'Phinx\\Console\\Command\\SeedRun' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php',
        'Phinx\\Console\\Command\\Status' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Status.php',
        'Phinx\\Console\\Command\\Test' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Test.php',
        'Phinx\\Console\\PhinxApplication' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php',
        'Phinx\\Db\\Adapter\\AbstractAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php',
        'Phinx\\Db\\Adapter\\AdapterFactory' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php',
        'Phinx\\Db\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php',
        'Phinx\\Db\\Adapter\\AdapterWrapper' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php',
        'Phinx\\Db\\Adapter\\MysqlAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php',
        'Phinx\\Db\\Adapter\\PdoAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php',
        'Phinx\\Db\\Adapter\\PostgresAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php',
        'Phinx\\Db\\Adapter\\ProxyAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php',
        'Phinx\\Db\\Adapter\\SQLiteAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php',
        'Phinx\\Db\\Adapter\\SqlServerAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php',
        'Phinx\\Db\\Adapter\\TablePrefixAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php',
        'Phinx\\Db\\Adapter\\TimedOutputAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php',
        'Phinx\\Db\\Adapter\\WrapperInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php',
        'Phinx\\Db\\Table' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table.php',
        'Phinx\\Db\\Table\\Column' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table/Column.php',
        'Phinx\\Db\\Table\\ForeignKey' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php',
        'Phinx\\Db\\Table\\Index' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table/Index.php',
        'Phinx\\Migration\\AbstractMigration' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php',
        'Phinx\\Migration\\AbstractTemplateCreation' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php',
        'Phinx\\Migration\\CreationInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php',
        'Phinx\\Migration\\IrreversibleMigrationException' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php',
        'Phinx\\Migration\\Manager' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/Manager.php',
        'Phinx\\Migration\\Manager\\Environment' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php',
        'Phinx\\Migration\\MigrationInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/MigrationInterface.php',
        'Phinx\\Seed\\AbstractSeed' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php',
        'Phinx\\Seed\\SeedInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Seed/SeedInterface.php',
        'Phinx\\Util\\Util' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Util/Util.php',
        'Phinx\\Wrapper\\TextWrapper' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php',
        'PhpAmqpLib\\Channel\\AMQPChannel' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.php',
        'PhpAmqpLib\\Channel\\AbstractChannel' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php',
        'PhpAmqpLib\\Connection\\AMQPConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnection.php',
        'PhpAmqpLib\\Connection\\AMQPLazyConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.php',
        'PhpAmqpLib\\Connection\\AMQPLazySocketConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySocketConnection.php',
        'PhpAmqpLib\\Connection\\AMQPSSLConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSSLConnection.php',
        'PhpAmqpLib\\Connection\\AMQPSocketConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSocketConnection.php',
        'PhpAmqpLib\\Connection\\AMQPStreamConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPStreamConnection.php',
        'PhpAmqpLib\\Connection\\AbstractConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AbstractConnection.php',
        'PhpAmqpLib\\Exception\\AMQPBasicCancelException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.php',
        'PhpAmqpLib\\Exception\\AMQPChannelException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelException.php',
        'PhpAmqpLib\\Exception\\AMQPConnectionException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionException.php',
        'PhpAmqpLib\\Exception\\AMQPException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPException.php',
        'PhpAmqpLib\\Exception\\AMQPExceptionInterface' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPExceptionInterface.php',
        'PhpAmqpLib\\Exception\\AMQPIOException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOException.php',
        'PhpAmqpLib\\Exception\\AMQPIOWaitException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOWaitException.php',
        'PhpAmqpLib\\Exception\\AMQPInvalidArgumentException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidArgumentException.php',
        'PhpAmqpLib\\Exception\\AMQPLogicException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.php',
        'PhpAmqpLib\\Exception\\AMQPOutOfBoundsException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfBoundsException.php',
        'PhpAmqpLib\\Exception\\AMQPOutOfRangeException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfRangeException.php',
        'PhpAmqpLib\\Exception\\AMQPProtocolChannelException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolChannelException.php',
        'PhpAmqpLib\\Exception\\AMQPProtocolConnectionException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolConnectionException.php',
        'PhpAmqpLib\\Exception\\AMQPProtocolException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolException.php',
        'PhpAmqpLib\\Exception\\AMQPRuntimeException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPRuntimeException.php',
        'PhpAmqpLib\\Exception\\AMQPTimeoutException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.php',
        'PhpAmqpLib\\Helper\\DebugHelper' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/DebugHelper.php',
        'PhpAmqpLib\\Helper\\MiscHelper' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/MiscHelper.php',
        'PhpAmqpLib\\Helper\\Protocol\\MethodMap080' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap080.php',
        'PhpAmqpLib\\Helper\\Protocol\\MethodMap091' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap091.php',
        'PhpAmqpLib\\Helper\\Protocol\\Protocol080' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol080.php',
        'PhpAmqpLib\\Helper\\Protocol\\Protocol091' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol091.php',
        'PhpAmqpLib\\Helper\\Protocol\\Wait080' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait080.php',
        'PhpAmqpLib\\Helper\\Protocol\\Wait091' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait091.php',
        'PhpAmqpLib\\Message\\AMQPMessage' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.php',
        'PhpAmqpLib\\Wire\\AMQPAbstractCollection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPAbstractCollection.php',
        'PhpAmqpLib\\Wire\\AMQPArray' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPArray.php',
        'PhpAmqpLib\\Wire\\AMQPDecimal' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.php',
        'PhpAmqpLib\\Wire\\AMQPReader' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPReader.php',
        'PhpAmqpLib\\Wire\\AMQPTable' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPTable.php',
        'PhpAmqpLib\\Wire\\AMQPWriter' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPWriter.php',
        'PhpAmqpLib\\Wire\\AbstractClient' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AbstractClient.php',
        'PhpAmqpLib\\Wire\\Constants080' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants080.php',
        'PhpAmqpLib\\Wire\\Constants091' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants091.php',
        'PhpAmqpLib\\Wire\\GenericContent' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/GenericContent.php',
        'PhpAmqpLib\\Wire\\IO\\AbstractIO' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/AbstractIO.php',
        'PhpAmqpLib\\Wire\\IO\\SocketIO' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/SocketIO.php',
        'PhpAmqpLib\\Wire\\IO\\StreamIO' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/StreamIO.php',
        'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php',
        'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php',
        'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php',
        'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php',
        'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php',
        'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
        'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
        'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
        'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
        'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
        'Symfony\\Component\\Config\\ConfigCache' => __DIR__ . '/..' . '/symfony/config/ConfigCache.php',
        'Symfony\\Component\\Config\\ConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactory.php',
        'Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactoryInterface.php',
        'Symfony\\Component\\Config\\ConfigCacheInterface' => __DIR__ . '/..' . '/symfony/config/ConfigCacheInterface.php',
        'Symfony\\Component\\Config\\Definition\\ArrayNode' => __DIR__ . '/..' . '/symfony/config/Definition/ArrayNode.php',
        'Symfony\\Component\\Config\\Definition\\BaseNode' => __DIR__ . '/..' . '/symfony/config/Definition/BaseNode.php',
        'Symfony\\Component\\Config\\Definition\\BooleanNode' => __DIR__ . '/..' . '/symfony/config/Definition/BooleanNode.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/EnumNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ExprBuilder.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/FloatNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\IntegerNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/IntegerNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\MergeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/MergeBuilder.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\NodeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeBuilder.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeParentInterface.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\NormalizationBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NormalizationBuilder.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\NumericNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NumericNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\ParentNodeDefinitionInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\ScalarNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ScalarNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/TreeBuilder.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\ValidationBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ValidationBuilder.php',
        'Symfony\\Component\\Config\\Definition\\Builder\\VariableNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/VariableNodeDefinition.php',
        'Symfony\\Component\\Config\\Definition\\ConfigurationInterface' => __DIR__ . '/..' . '/symfony/config/Definition/ConfigurationInterface.php',
        'Symfony\\Component\\Config\\Definition\\Dumper\\XmlReferenceDumper' => __DIR__ . '/..' . '/symfony/config/Definition/Dumper/XmlReferenceDumper.php',
        'Symfony\\Component\\Config\\Definition\\Dumper\\YamlReferenceDumper' => __DIR__ . '/..' . '/symfony/config/Definition/Dumper/YamlReferenceDumper.php',
        'Symfony\\Component\\Config\\Definition\\EnumNode' => __DIR__ . '/..' . '/symfony/config/Definition/EnumNode.php',
        'Symfony\\Component\\Config\\Definition\\Exception\\DuplicateKeyException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/DuplicateKeyException.php',
        'Symfony\\Component\\Config\\Definition\\Exception\\Exception' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/Exception.php',
        'Symfony\\Component\\Config\\Definition\\Exception\\ForbiddenOverwriteException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/ForbiddenOverwriteException.php',
        'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidConfigurationException.php',
        'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidDefinitionException.php',
        'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidTypeException.php',
        'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/UnsetKeyException.php',
        'Symfony\\Component\\Config\\Definition\\FloatNode' => __DIR__ . '/..' . '/symfony/config/Definition/FloatNode.php',
        'Symfony\\Component\\Config\\Definition\\IntegerNode' => __DIR__ . '/..' . '/symfony/config/Definition/IntegerNode.php',
        'Symfony\\Component\\Config\\Definition\\NodeInterface' => __DIR__ . '/..' . '/symfony/config/Definition/NodeInterface.php',
        'Symfony\\Component\\Config\\Definition\\NumericNode' => __DIR__ . '/..' . '/symfony/config/Definition/NumericNode.php',
        'Symfony\\Component\\Config\\Definition\\Processor' => __DIR__ . '/..' . '/symfony/config/Definition/Processor.php',
        'Symfony\\Component\\Config\\Definition\\PrototypeNodeInterface' => __DIR__ . '/..' . '/symfony/config/Definition/PrototypeNodeInterface.php',
        'Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => __DIR__ . '/..' . '/symfony/config/Definition/PrototypedArrayNode.php',
        'Symfony\\Component\\Config\\Definition\\ScalarNode' => __DIR__ . '/..' . '/symfony/config/Definition/ScalarNode.php',
        'Symfony\\Component\\Config\\Definition\\VariableNode' => __DIR__ . '/..' . '/symfony/config/Definition/VariableNode.php',
        'Symfony\\Component\\Config\\DependencyInjection\\ConfigCachePass' => __DIR__ . '/..' . '/symfony/config/DependencyInjection/ConfigCachePass.php',
        'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php',
        'Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderLoadException.php',
        'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLocatorFileNotFoundException.php',
        'Symfony\\Component\\Config\\FileLocator' => __DIR__ . '/..' . '/symfony/config/FileLocator.php',
        'Symfony\\Component\\Config\\FileLocatorInterface' => __DIR__ . '/..' . '/symfony/config/FileLocatorInterface.php',
        'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => __DIR__ . '/..' . '/symfony/config/Loader/DelegatingLoader.php',
        'Symfony\\Component\\Config\\Loader\\FileLoader' => __DIR__ . '/..' . '/symfony/config/Loader/FileLoader.php',
        'Symfony\\Component\\Config\\Loader\\GlobFileLoader' => __DIR__ . '/..' . '/symfony/config/Loader/GlobFileLoader.php',
        'Symfony\\Component\\Config\\Loader\\Loader' => __DIR__ . '/..' . '/symfony/config/Loader/Loader.php',
        'Symfony\\Component\\Config\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderInterface.php',
        'Symfony\\Component\\Config\\Loader\\LoaderResolver' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolver.php',
        'Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolverInterface.php',
        'Symfony\\Component\\Config\\ResourceCheckerConfigCache' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCache.php',
        'Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCacheFactory.php',
        'Symfony\\Component\\Config\\ResourceCheckerInterface' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerInterface.php',
        'Symfony\\Component\\Config\\Resource\\ClassExistenceResource' => __DIR__ . '/..' . '/symfony/config/Resource/ClassExistenceResource.php',
        'Symfony\\Component\\Config\\Resource\\ComposerResource' => __DIR__ . '/..' . '/symfony/config/Resource/ComposerResource.php',
        'Symfony\\Component\\Config\\Resource\\DirectoryResource' => __DIR__ . '/..' . '/symfony/config/Resource/DirectoryResource.php',
        'Symfony\\Component\\Config\\Resource\\FileExistenceResource' => __DIR__ . '/..' . '/symfony/config/Resource/FileExistenceResource.php',
        'Symfony\\Component\\Config\\Resource\\FileResource' => __DIR__ . '/..' . '/symfony/config/Resource/FileResource.php',
        'Symfony\\Component\\Config\\Resource\\GlobResource' => __DIR__ . '/..' . '/symfony/config/Resource/GlobResource.php',
        'Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php',
        'Symfony\\Component\\Config\\Resource\\ReflectionMethodHhvmWrapper' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php',
        'Symfony\\Component\\Config\\Resource\\ReflectionParameterHhvmWrapper' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php',
        'Symfony\\Component\\Config\\Resource\\ResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/ResourceInterface.php',
        'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceChecker.php',
        'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceInterface.php',
        'Symfony\\Component\\Config\\Util\\Exception\\InvalidXmlException' => __DIR__ . '/..' . '/symfony/config/Util/Exception/InvalidXmlException.php',
        'Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => __DIR__ . '/..' . '/symfony/config/Util/Exception/XmlParsingException.php',
        'Symfony\\Component\\Config\\Util\\XmlUtils' => __DIR__ . '/..' . '/symfony/config/Util/XmlUtils.php',
        'Symfony\\Component\\Console\\Application' => __DIR__ . '/..' . '/symfony/console/Application.php',
        'Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface' => __DIR__ . '/..' . '/symfony/console/CommandLoader/CommandLoaderInterface.php',
        'Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader' => __DIR__ . '/..' . '/symfony/console/CommandLoader/ContainerCommandLoader.php',
        'Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader' => __DIR__ . '/..' . '/symfony/console/CommandLoader/FactoryCommandLoader.php',
        'Symfony\\Component\\Console\\Command\\Command' => __DIR__ . '/..' . '/symfony/console/Command/Command.php',
        'Symfony\\Component\\Console\\Command\\HelpCommand' => __DIR__ . '/..' . '/symfony/console/Command/HelpCommand.php',
        'Symfony\\Component\\Console\\Command\\ListCommand' => __DIR__ . '/..' . '/symfony/console/Command/ListCommand.php',
        'Symfony\\Component\\Console\\Command\\LockableTrait' => __DIR__ . '/..' . '/symfony/console/Command/LockableTrait.php',
        'Symfony\\Component\\Console\\ConsoleEvents' => __DIR__ . '/..' . '/symfony/console/ConsoleEvents.php',
        'Symfony\\Component\\Console\\DependencyInjection\\AddConsoleCommandPass' => __DIR__ . '/..' . '/symfony/console/DependencyInjection/AddConsoleCommandPass.php',
        'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => __DIR__ . '/..' . '/symfony/console/Descriptor/ApplicationDescription.php',
        'Symfony\\Component\\Console\\Descriptor\\Descriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/Descriptor.php',
        'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => __DIR__ . '/..' . '/symfony/console/Descriptor/DescriptorInterface.php',
        'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/JsonDescriptor.php',
        'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/MarkdownDescriptor.php',
        'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/TextDescriptor.php',
        'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/XmlDescriptor.php',
        'Symfony\\Component\\Console\\EventListener\\ErrorListener' => __DIR__ . '/..' . '/symfony/console/EventListener/ErrorListener.php',
        'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleCommandEvent.php',
        'Symfony\\Component\\Console\\Event\\ConsoleErrorEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleErrorEvent.php',
        'Symfony\\Component\\Console\\Event\\ConsoleEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleEvent.php',
        'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleExceptionEvent.php',
        'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleTerminateEvent.php',
        'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => __DIR__ . '/..' . '/symfony/console/Exception/CommandNotFoundException.php',
        'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/console/Exception/ExceptionInterface.php',
        'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidArgumentException.php',
        'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidOptionException.php',
        'Symfony\\Component\\Console\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/console/Exception/LogicException.php',
        'Symfony\\Component\\Console\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/console/Exception/RuntimeException.php',
        'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatter.php',
        'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterInterface.php',
        'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyle.php',
        'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleInterface.php',
        'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleStack.php',
        'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DebugFormatterHelper.php',
        'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DescriptorHelper.php',
        'Symfony\\Component\\Console\\Helper\\FormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/FormatterHelper.php',
        'Symfony\\Component\\Console\\Helper\\Helper' => __DIR__ . '/..' . '/symfony/console/Helper/Helper.php',
        'Symfony\\Component\\Console\\Helper\\HelperInterface' => __DIR__ . '/..' . '/symfony/console/Helper/HelperInterface.php',
        'Symfony\\Component\\Console\\Helper\\HelperSet' => __DIR__ . '/..' . '/symfony/console/Helper/HelperSet.php',
        'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => __DIR__ . '/..' . '/symfony/console/Helper/InputAwareHelper.php',
        'Symfony\\Component\\Console\\Helper\\ProcessHelper' => __DIR__ . '/..' . '/symfony/console/Helper/ProcessHelper.php',
        'Symfony\\Component\\Console\\Helper\\ProgressBar' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressBar.php',
        'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressIndicator.php',
        'Symfony\\Component\\Console\\Helper\\QuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/QuestionHelper.php',
        'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/SymfonyQuestionHelper.php',
        'Symfony\\Component\\Console\\Helper\\Table' => __DIR__ . '/..' . '/symfony/console/Helper/Table.php',
        'Symfony\\Component\\Console\\Helper\\TableCell' => __DIR__ . '/..' . '/symfony/console/Helper/TableCell.php',
        'Symfony\\Component\\Console\\Helper\\TableSeparator' => __DIR__ . '/..' . '/symfony/console/Helper/TableSeparator.php',
        'Symfony\\Component\\Console\\Helper\\TableStyle' => __DIR__ . '/..' . '/symfony/console/Helper/TableStyle.php',
        'Symfony\\Component\\Console\\Input\\ArgvInput' => __DIR__ . '/..' . '/symfony/console/Input/ArgvInput.php',
        'Symfony\\Component\\Console\\Input\\ArrayInput' => __DIR__ . '/..' . '/symfony/console/Input/ArrayInput.php',
        'Symfony\\Component\\Console\\Input\\Input' => __DIR__ . '/..' . '/symfony/console/Input/Input.php',
        'Symfony\\Component\\Console\\Input\\InputArgument' => __DIR__ . '/..' . '/symfony/console/Input/InputArgument.php',
        'Symfony\\Component\\Console\\Input\\InputAwareInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputAwareInterface.php',
        'Symfony\\Component\\Console\\Input\\InputDefinition' => __DIR__ . '/..' . '/symfony/console/Input/InputDefinition.php',
        'Symfony\\Component\\Console\\Input\\InputInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputInterface.php',
        'Symfony\\Component\\Console\\Input\\InputOption' => __DIR__ . '/..' . '/symfony/console/Input/InputOption.php',
        'Symfony\\Component\\Console\\Input\\StreamableInputInterface' => __DIR__ . '/..' . '/symfony/console/Input/StreamableInputInterface.php',
        'Symfony\\Component\\Console\\Input\\StringInput' => __DIR__ . '/..' . '/symfony/console/Input/StringInput.php',
        'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => __DIR__ . '/..' . '/symfony/console/Logger/ConsoleLogger.php',
        'Symfony\\Component\\Console\\Output\\BufferedOutput' => __DIR__ . '/..' . '/symfony/console/Output/BufferedOutput.php',
        'Symfony\\Component\\Console\\Output\\ConsoleOutput' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutput.php',
        'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutputInterface.php',
        'Symfony\\Component\\Console\\Output\\NullOutput' => __DIR__ . '/..' . '/symfony/console/Output/NullOutput.php',
        'Symfony\\Component\\Console\\Output\\Output' => __DIR__ . '/..' . '/symfony/console/Output/Output.php',
        'Symfony\\Component\\Console\\Output\\OutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/OutputInterface.php',
        'Symfony\\Component\\Console\\Output\\StreamOutput' => __DIR__ . '/..' . '/symfony/console/Output/StreamOutput.php',
        'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ChoiceQuestion.php',
        'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ConfirmationQuestion.php',
        'Symfony\\Component\\Console\\Question\\Question' => __DIR__ . '/..' . '/symfony/console/Question/Question.php',
        'Symfony\\Component\\Console\\Style\\OutputStyle' => __DIR__ . '/..' . '/symfony/console/Style/OutputStyle.php',
        'Symfony\\Component\\Console\\Style\\StyleInterface' => __DIR__ . '/..' . '/symfony/console/Style/StyleInterface.php',
        'Symfony\\Component\\Console\\Style\\SymfonyStyle' => __DIR__ . '/..' . '/symfony/console/Style/SymfonyStyle.php',
        'Symfony\\Component\\Console\\Terminal' => __DIR__ . '/..' . '/symfony/console/Terminal.php',
        'Symfony\\Component\\Console\\Tester\\ApplicationTester' => __DIR__ . '/..' . '/symfony/console/Tester/ApplicationTester.php',
        'Symfony\\Component\\Console\\Tester\\CommandTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandTester.php',
        'Symfony\\Component\\Debug\\BufferingLogger' => __DIR__ . '/..' . '/symfony/debug/BufferingLogger.php',
        'Symfony\\Component\\Debug\\Debug' => __DIR__ . '/..' . '/symfony/debug/Debug.php',
        'Symfony\\Component\\Debug\\DebugClassLoader' => __DIR__ . '/..' . '/symfony/debug/DebugClassLoader.php',
        'Symfony\\Component\\Debug\\ErrorHandler' => __DIR__ . '/..' . '/symfony/debug/ErrorHandler.php',
        'Symfony\\Component\\Debug\\ExceptionHandler' => __DIR__ . '/..' . '/symfony/debug/ExceptionHandler.php',
        'Symfony\\Component\\Debug\\Exception\\ClassNotFoundException' => __DIR__ . '/..' . '/symfony/debug/Exception/ClassNotFoundException.php',
        'Symfony\\Component\\Debug\\Exception\\ContextErrorException' => __DIR__ . '/..' . '/symfony/debug/Exception/ContextErrorException.php',
        'Symfony\\Component\\Debug\\Exception\\FatalErrorException' => __DIR__ . '/..' . '/symfony/debug/Exception/FatalErrorException.php',
        'Symfony\\Component\\Debug\\Exception\\FatalThrowableError' => __DIR__ . '/..' . '/symfony/debug/Exception/FatalThrowableError.php',
        'Symfony\\Component\\Debug\\Exception\\FlattenException' => __DIR__ . '/..' . '/symfony/debug/Exception/FlattenException.php',
        'Symfony\\Component\\Debug\\Exception\\OutOfMemoryException' => __DIR__ . '/..' . '/symfony/debug/Exception/OutOfMemoryException.php',
        'Symfony\\Component\\Debug\\Exception\\SilencedErrorContext' => __DIR__ . '/..' . '/symfony/debug/Exception/SilencedErrorContext.php',
        'Symfony\\Component\\Debug\\Exception\\UndefinedFunctionException' => __DIR__ . '/..' . '/symfony/debug/Exception/UndefinedFunctionException.php',
        'Symfony\\Component\\Debug\\Exception\\UndefinedMethodException' => __DIR__ . '/..' . '/symfony/debug/Exception/UndefinedMethodException.php',
        'Symfony\\Component\\Debug\\FatalErrorHandler\\ClassNotFoundFatalErrorHandler' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php',
        'Symfony\\Component\\Debug\\FatalErrorHandler\\FatalErrorHandlerInterface' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php',
        'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedFunctionFatalErrorHandler' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php',
        'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedMethodFatalErrorHandler' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php',
        'Symfony\\Component\\Dotenv\\Dotenv' => __DIR__ . '/..' . '/symfony/dotenv/Dotenv.php',
        'Symfony\\Component\\Dotenv\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/dotenv/Exception/ExceptionInterface.php',
        'Symfony\\Component\\Dotenv\\Exception\\FormatException' => __DIR__ . '/..' . '/symfony/dotenv/Exception/FormatException.php',
        'Symfony\\Component\\Dotenv\\Exception\\FormatExceptionContext' => __DIR__ . '/..' . '/symfony/dotenv/Exception/FormatExceptionContext.php',
        'Symfony\\Component\\Dotenv\\Exception\\PathException' => __DIR__ . '/..' . '/symfony/dotenv/Exception/PathException.php',
        'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/ExceptionInterface.php',
        'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/FileNotFoundException.php',
        'Symfony\\Component\\Filesystem\\Exception\\IOException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOException.php',
        'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOExceptionInterface.php',
        'Symfony\\Component\\Filesystem\\Filesystem' => __DIR__ . '/..' . '/symfony/filesystem/Filesystem.php',
        'Symfony\\Component\\Filesystem\\LockHandler' => __DIR__ . '/..' . '/symfony/filesystem/LockHandler.php',
        'Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php',
        'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php',
        'Symfony\\Component\\Yaml\\Escaper' => __DIR__ . '/..' . '/symfony/yaml/Escaper.php',
        'Symfony\\Component\\Yaml\\Exception\\DumpException' => __DIR__ . '/..' . '/symfony/yaml/Exception/DumpException.php',
        'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/yaml/Exception/ExceptionInterface.php',
        'Symfony\\Component\\Yaml\\Exception\\ParseException' => __DIR__ . '/..' . '/symfony/yaml/Exception/ParseException.php',
        'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/yaml/Exception/RuntimeException.php',
        'Symfony\\Component\\Yaml\\Inline' => __DIR__ . '/..' . '/symfony/yaml/Inline.php',
        'Symfony\\Component\\Yaml\\Parser' => __DIR__ . '/..' . '/symfony/yaml/Parser.php',
        'Symfony\\Component\\Yaml\\Tag\\TaggedValue' => __DIR__ . '/..' . '/symfony/yaml/Tag/TaggedValue.php',
        'Symfony\\Component\\Yaml\\Unescaper' => __DIR__ . '/..' . '/symfony/yaml/Unescaper.php',
        'Symfony\\Component\\Yaml\\Yaml' => __DIR__ . '/..' . '/symfony/yaml/Yaml.php',
        'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php',
        'com\\linways\\base\\cache\\LinCache' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/cache/LinCache.php',
        'com\\linways\\base\\connection\\IMySqlConnector' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/connection/IMySqlConnector.php',
        'com\\linways\\base\\connection\\MySqlConnector' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/connection/MySqlConnector.php',
        'com\\linways\\base\\connection\\MySqlQuery' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/connection/MySqlQuery.php',
        'com\\linways\\base\\dto\\AjaxPaginator' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/AjaxPaginator.php',
        'com\\linways\\base\\dto\\BaseDTO' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/BaseDTO.php',
        'com\\linways\\base\\dto\\Email' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/Email.php',
        'com\\linways\\base\\dto\\EmailCC' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/EmailTo.php',
        'com\\linways\\base\\dto\\EmailFrom' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/EmailCC.php',
        'com\\linways\\base\\dto\\EmailTo' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/EmailFrom.php',
        'com\\linways\\base\\dto\\MySqlResult' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/MySqlResult.php',
        'com\\linways\\base\\dto\\PageSupport' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/dto/PageSupport.php',
        'com\\linways\\base\\email\\PHPMailer' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/email/PHPMailer.php',
        'com\\linways\\base\\email\\SMTP' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/email/SMTP.php',
        'com\\linways\\base\\exception\\CoreException' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/exception/CoreException.php',
        'com\\linways\\base\\exception\\EmailException' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/exception/EmailException.php',
        'com\\linways\\base\\exception\\MapperException' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/exception/MapperException.php',
        'com\\linways\\base\\exception\\SQLException' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/exception/SQLException.php',
        'com\\linways\\base\\exception\\TaskQueueException' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/exception/TaskQueueException.php',
        'com\\linways\\base\\helper\\ResultHandler' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/helper/ResultHandler.php',
        'com\\linways\\base\\hooks\\LinwaysHooks' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/hooks/LinwaysHooks.php',
        'com\\linways\\base\\mapper\\IMapper' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/mapper/IMapper.php',
        'com\\linways\\base\\mapper\\Result' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/mapper/Result.php',
        'com\\linways\\base\\mapper\\ResultMap' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/mapper/ResultMap.php',
        'com\\linways\\base\\mcrypt\\CryptAES' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/mcrypt/CryptAES.php',
        'com\\linways\\base\\messages\\CommonMessages' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/messages/CommonMessages.php',
        'com\\linways\\base\\queue\\TaskQueue' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/queue/TaskQueue.php',
        'com\\linways\\base\\request\\BaseRequest' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/request/BaseRequest.php',
        'com\\linways\\base\\response\\BaseResponse' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/response/BaseResponse.php',
        'com\\linways\\base\\service\\EmailService' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/service/EmailService.php',
        'com\\linways\\base\\soap\\XMLSchema' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/XMLSchema.php',
        'com\\linways\\base\\soap\\nusoap_base' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/nusoap_base.php',
        'com\\linways\\base\\soap\\nusoap_client' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/nusoap_client.php',
        'com\\linways\\base\\soap\\nusoap_fault' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/nusoap_fault.php',
        'com\\linways\\base\\soap\\nusoap_parser' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/nusoap_parser.php',
        'com\\linways\\base\\soap\\nusoap_server' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/nusoap_server.php',
        'com\\linways\\base\\soap\\nusoap_xmlschema' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/nusoap_xmlschema.php',
        'com\\linways\\base\\soap\\soap_fault' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/soap_fault.php',
        'com\\linways\\base\\soap\\soap_parser' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/soap_parser.php',
        'com\\linways\\base\\soap\\soap_server' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/soap_server.php',
        'com\\linways\\base\\soap\\soap_transport_http' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/soap_transport_http.php',
        'com\\linways\\base\\soap\\soapclient' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/soapclient.php',
        'com\\linways\\base\\soap\\soapval' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/soapval.php',
        'com\\linways\\base\\soap\\wsdl' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/soap/wsdl.php',
        'com\\linways\\base\\test\\BaseTestCase' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/testcase/BaseTestCase.php',
        'com\\linways\\base\\util\\CalendarUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/CalendarUtil.php',
        'com\\linways\\base\\util\\CommonUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/CommonUtil.php',
        'com\\linways\\base\\util\\MapperUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/MapperUtil.php',
        'com\\linways\\base\\util\\MySqlUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/MySqlUtil.php',
        'com\\linways\\base\\util\\ObjectUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/ObjectUtil.php',
        'com\\linways\\base\\util\\PhoneUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/PhoneUtil.php',
        'com\\linways\\base\\util\\RequestUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/RequestUtil.php',
        'com\\linways\\base\\util\\StringUtil' => __DIR__ . '/..' . '/linways/linways-base/src/com/linways/base/util/StringUtil.php',
        'com\\linways\\nucleus\\core\\ams_task_queue\\AMSTaskQueue' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/ams_task_queue/AMSTaskQueue.php',
        'com\\linways\\nucleus\\core\\constants\\Scope' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/constants/Scope.php',
        'com\\linways\\nucleus\\core\\constants\\SingleSignonConstant' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/constants/SingleSignonConstant.php',
        'com\\linways\\nucleus\\core\\dto\\Addon' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/Addon.php',
        'com\\linways\\nucleus\\core\\dto\\AddonAttribute' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonAttribute.php',
        'com\\linways\\nucleus\\core\\dto\\AddonAttributeValue' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonAttributeValue.php',
        'com\\linways\\nucleus\\core\\dto\\AddonDataStore' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonDataStore.php',
        'com\\linways\\nucleus\\core\\dto\\AddonEnabled' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/AddonEnabled.php',
        'com\\linways\\nucleus\\core\\dto\\ApiOrganizationDetails' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/ApiOrganizationDetails.php',
        'com\\linways\\nucleus\\core\\dto\\Client' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/Client.php',
        'com\\linways\\nucleus\\core\\dto\\College' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/College.php',
        'com\\linways\\nucleus\\core\\dto\\Expense' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/Expense.php',
        'com\\linways\\nucleus\\core\\dto\\ExpenseCategory' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/ExpenseCategory.php',
        'com\\linways\\nucleus\\core\\dto\\ScheduledTask' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/ScheduledTask.php',
        'com\\linways\\nucleus\\core\\dto\\SingleSignonLog' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/SingleSignonLog.php',
        'com\\linways\\nucleus\\core\\dto\\user\\User' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/dto/user/User.php',
        'com\\linways\\nucleus\\core\\exception\\NucleusException' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/exception/NucleusException.php',
        'com\\linways\\nucleus\\core\\exception\\SchedulerException' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/exception/SchedulerException.php',
        'com\\linways\\nucleus\\core\\exception\\TaskQueueWorkerException' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/exception/TaskQueueWorkerException.php',
        'com\\linways\\nucleus\\core\\mapper\\AddonDataStoreMapper' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/AddonDataStoreMapper.php',
        'com\\linways\\nucleus\\core\\mapper\\AddonServiceMapper' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/AddonServiceMapper.php',
        'com\\linways\\nucleus\\core\\mapper\\ClientServiceMapper' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ClientServiceMapper.php',
        'com\\linways\\nucleus\\core\\mapper\\CollegeServiceMapper' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/CollegeServiceMapper.php',
        'com\\linways\\nucleus\\core\\mapper\\ExpenseServiceMapper' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ExpenseServiceMapper.php',
        'com\\linways\\nucleus\\core\\mapper\\SchedulerServiceMapper' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/mapper/ScheduleServiceMapper.php',
        'com\\linways\\nucleus\\core\\service\\APIAccessTokenService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/APIAccessTokenService.php',
        'com\\linways\\nucleus\\core\\service\\AddonAttributeService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonAttributeService.php',
        'com\\linways\\nucleus\\core\\service\\AddonDataStoreSevice' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonDataStoreSevice.php',
        'com\\linways\\nucleus\\core\\service\\AddonService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/AddonService.php',
        'com\\linways\\nucleus\\core\\service\\BaseService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/BaseService.php',
        'com\\linways\\nucleus\\core\\service\\ClientService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ClientService.php',
        'com\\linways\\nucleus\\core\\service\\CollegeService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/CollegeService.php',
        'com\\linways\\nucleus\\core\\service\\ConfigurationService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ConfigurationService.php',
        'com\\linways\\nucleus\\core\\service\\ExpenseCategoryService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ExpenseCategoryService.php',
        'com\\linways\\nucleus\\core\\service\\ExpenseService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/ExpenseService.php',
        'com\\linways\\nucleus\\core\\service\\PermissionService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/PermissionService.php',
        'com\\linways\\nucleus\\core\\service\\SchedulerService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/SchedulerService.php',
        'com\\linways\\nucleus\\core\\service\\SingleSignonService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/SingleSignonService.php',
        'com\\linways\\nucleus\\core\\service\\user\\UserService' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/service/user/UserService.php',
        'com\\linways\\nucleus\\core\\utils\\JWTUtils' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/utils/JWTUtils.php',
        'com\\linways\\nucleus\\core\\utils\\StringUtils' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/utils/StringUtils.php',
        'com\\linways\\nucleus\\core\\utils\\user\\UserServiceUtils' => __DIR__ . '/..' . '/linways/nucleus-core/src/com/linways/nucleus/core/utils/user/UserServiceUtils.php',
        'linways\\cli\\command\\db\\CreateMigrationCommand' => __DIR__ . '/../..' . '/src/commands/db/CreateMigrationCommand.php',
        'linways\\cli\\command\\db\\MigrateCommand' => __DIR__ . '/../..' . '/src/commands/db/MigrateCommand.php',
        'linways\\cli\\command\\db\\MigrateFakeCommand' => __DIR__ . '/../..' . '/src/commands/db/MigrateFakeCommand.php',
        'linways\\cli\\command\\git\\UpdateStyleGuideCommand' => __DIR__ . '/../..' . '/src/commands/git/UpdateStyleGuideCommand.php',
        'linways\\cli\\service\\db\\MigrateFakeService' => __DIR__ . '/../..' . '/src/service/db/MigrateFakeService.php',
        'linways\\cli\\service\\db\\MigrateService' => __DIR__ . '/../..' . '/src/service/db/MigrateService.php',
        'linways\\cli\\utls\\MigrationUtils' => __DIR__ . '/../..' . '/src/utils/MigrationUtils.php',
        'test\\scripts\\testServiceClass' => __DIR__ . '/..' . '/linways/nucleus-core/test/scripts/testServiceClass.php',
        'test\\service\\APIAccessTokenServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/APIAccessTokenServiceTest.php',
        'test\\service\\AddonDataStoreServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/AddonDataStoreServiceTest.php',
        'test\\service\\AddonServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/AddonServiceTest.php',
        'test\\service\\AddonTestHelpers' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/AddonTestHelpers.php',
        'test\\service\\ClientServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/ClientServiceTest.php',
        'test\\service\\CollegeServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/CollegeServiceTest.php',
        'test\\service\\ExpenseCategoryServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/ExpenseCategoryServiceTest.php',
        'test\\service\\PermissionServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/PermissionServiceTest.php',
        'test\\service\\SchedulerServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/SchedulerServiceTest.php',
        'test\\service\\user\\UserServiceTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/service/user/UserServiceTest.php',
        'test\\utils\\UserServiceUtilsTest' => __DIR__ . '/..' . '/linways/nucleus-core/test/utils/UserServiceUtilsTest.php',
        'voku\\helper\\Hooks' => __DIR__ . '/..' . '/voku/php-hooks/src/voku/helper/Hooks.php',
    );

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

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

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit555f246cb7db6869b6fb9f48d5e5f3d2
{
    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('ComposerAutoloaderInit555f246cb7db6869b6fb9f48d5e5f3d2', 'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInit555f246cb7db6869b6fb9f48d5e5f3d2', 'loadClassLoader'));

        $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\ComposerStaticInit555f246cb7db6869b6fb9f48d5e5f3d2::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);

        if ($useStaticLoader) {
            $includeFiles = Composer\Autoload\ComposerStaticInit555f246cb7db6869b6fb9f48d5e5f3d2::$files;
        } else {
            $includeFiles = require __DIR__ . '/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire555f246cb7db6869b6fb9f48d5e5f3d2($fileIdentifier, $file);
        }

        return $loader;
    }
}

function composerRequire555f246cb7db6869b6fb9f48d5e5f3d2($fileIdentifier, $file)
{
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        require $file;

        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    }
}
[
    {
        "name": "psr/log",
        "version": "1.0.2",
        "version_normalized": "1.0.2.0",
        "source": {
            "type": "git",
            "url": "https://github.com/php-fig/log.git",
            "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
            "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
            "shasum": ""
        },
        "require": {
            "php": ">=5.3.0"
        },
        "time": "2016-10-10T12:19:37+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.0.x-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Psr\\Log\\": "Psr/Log/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "PHP-FIG",
                "homepage": "http://www.php-fig.org/"
            }
        ],
        "description": "Common interface for logging libraries",
        "homepage": "https://github.com/php-fig/log",
        "keywords": [
            "log",
            "psr",
            "psr-3"
        ]
    },
    {
        "name": "symfony/debug",
        "version": "v3.4.4",
        "version_normalized": "3.4.4.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/debug.git",
            "reference": "53f6af2805daf52a43b393b93d2f24925d35c937"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/debug/zipball/53f6af2805daf52a43b393b93d2f24925d35c937",
            "reference": "53f6af2805daf52a43b393b93d2f24925d35c937",
            "shasum": ""
        },
        "require": {
            "php": "^5.5.9|>=7.0.8",
            "psr/log": "~1.0"
        },
        "conflict": {
            "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
        },
        "require-dev": {
            "symfony/http-kernel": "~2.8|~3.0|~4.0"
        },
        "time": "2018-01-18T22:16:57+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "3.4-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Component\\Debug\\": ""
            },
            "exclude-from-classmap": [
                "/Tests/"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Fabien Potencier",
                "email": "fabien@symfony.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Symfony Debug Component",
        "homepage": "https://symfony.com"
    },
    {
        "name": "symfony/polyfill-mbstring",
        "version": "v1.7.0",
        "version_normalized": "1.7.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/polyfill-mbstring.git",
            "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b",
            "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b",
            "shasum": ""
        },
        "require": {
            "php": ">=5.3.3"
        },
        "suggest": {
            "ext-mbstring": "For best performance"
        },
        "time": "2018-01-30T19:27:44+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "1.7-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Polyfill\\Mbstring\\": ""
            },
            "files": [
                "bootstrap.php"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Nicolas Grekas",
                "email": "p@tchwork.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Symfony polyfill for the Mbstring extension",
        "homepage": "https://symfony.com",
        "keywords": [
            "compatibility",
            "mbstring",
            "polyfill",
            "portable",
            "shim"
        ]
    },
    {
        "name": "symfony/console",
        "version": "v3.4.4",
        "version_normalized": "3.4.4.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/console.git",
            "reference": "26b6f419edda16c19775211987651cb27baea7f1"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/console/zipball/26b6f419edda16c19775211987651cb27baea7f1",
            "reference": "26b6f419edda16c19775211987651cb27baea7f1",
            "shasum": ""
        },
        "require": {
            "php": "^5.5.9|>=7.0.8",
            "symfony/debug": "~2.8|~3.0|~4.0",
            "symfony/polyfill-mbstring": "~1.0"
        },
        "conflict": {
            "symfony/dependency-injection": "<3.4",
            "symfony/process": "<3.3"
        },
        "require-dev": {
            "psr/log": "~1.0",
            "symfony/config": "~3.3|~4.0",
            "symfony/dependency-injection": "~3.4|~4.0",
            "symfony/event-dispatcher": "~2.8|~3.0|~4.0",
            "symfony/lock": "~3.4|~4.0",
            "symfony/process": "~3.3|~4.0"
        },
        "suggest": {
            "psr/log": "For using the console logger",
            "symfony/event-dispatcher": "",
            "symfony/lock": "",
            "symfony/process": ""
        },
        "time": "2018-01-29T09:03:43+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "3.4-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Component\\Console\\": ""
            },
            "exclude-from-classmap": [
                "/Tests/"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Fabien Potencier",
                "email": "fabien@symfony.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Symfony Console Component",
        "homepage": "https://symfony.com"
    },
    {
        "name": "dragonmantank/cron-expression",
        "version": "v1.2.1",
        "version_normalized": "1.2.1.0",
        "source": {
            "type": "git",
            "url": "https://github.com/dragonmantank/cron-expression.git",
            "reference": "9504fa9ea681b586028adaaa0877db4aecf32bad"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/9504fa9ea681b586028adaaa0877db4aecf32bad",
            "reference": "9504fa9ea681b586028adaaa0877db4aecf32bad",
            "shasum": ""
        },
        "require": {
            "php": ">=5.3.2"
        },
        "require-dev": {
            "phpunit/phpunit": "~4.0|~5.0"
        },
        "time": "2017-01-23T04:29:33+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Cron\\": "src/Cron/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Michael Dowling",
                "email": "mtdowling@gmail.com",
                "homepage": "https://github.com/mtdowling"
            }
        ],
        "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
        "keywords": [
            "cron",
            "schedule"
        ]
    },
    {
        "name": "firebase/php-jwt",
        "version": "v5.0.0",
        "version_normalized": "5.0.0.0",
        "source": {
            "type": "git",
            "url": "https://github.com/firebase/php-jwt.git",
            "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
            "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e",
            "shasum": ""
        },
        "require": {
            "php": ">=5.3.0"
        },
        "require-dev": {
            "phpunit/phpunit": " 4.8.35"
        },
        "time": "2017-06-27T22:17:23+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Firebase\\JWT\\": "src"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "BSD-3-Clause"
        ],
        "authors": [
            {
                "name": "Neuman Vong",
                "email": "neuman+pear@twilio.com",
                "role": "Developer"
            },
            {
                "name": "Anant Narayanan",
                "email": "anant@php.net",
                "role": "Developer"
            }
        ],
        "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
        "homepage": "https://github.com/firebase/php-jwt"
    },
    {
        "name": "symfony/yaml",
        "version": "v3.4.4",
        "version_normalized": "3.4.4.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/yaml.git",
            "reference": "eab73b6c21d27ae4cd037c417618dfd4befb0bfe"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/yaml/zipball/eab73b6c21d27ae4cd037c417618dfd4befb0bfe",
            "reference": "eab73b6c21d27ae4cd037c417618dfd4befb0bfe",
            "shasum": ""
        },
        "require": {
            "php": "^5.5.9|>=7.0.8"
        },
        "conflict": {
            "symfony/console": "<3.4"
        },
        "require-dev": {
            "symfony/console": "~3.4|~4.0"
        },
        "suggest": {
            "symfony/console": "For validating YAML files using the lint command"
        },
        "time": "2018-01-21T19:05:02+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "3.4-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Component\\Yaml\\": ""
            },
            "exclude-from-classmap": [
                "/Tests/"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Fabien Potencier",
                "email": "fabien@symfony.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Symfony Yaml Component",
        "homepage": "https://symfony.com"
    },
    {
        "name": "symfony/filesystem",
        "version": "v3.4.4",
        "version_normalized": "3.4.4.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/filesystem.git",
            "reference": "e078773ad6354af38169faf31c21df0f18ace03d"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/filesystem/zipball/e078773ad6354af38169faf31c21df0f18ace03d",
            "reference": "e078773ad6354af38169faf31c21df0f18ace03d",
            "shasum": ""
        },
        "require": {
            "php": "^5.5.9|>=7.0.8"
        },
        "time": "2018-01-03T07:37:34+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "3.4-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Component\\Filesystem\\": ""
            },
            "exclude-from-classmap": [
                "/Tests/"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Fabien Potencier",
                "email": "fabien@symfony.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Symfony Filesystem Component",
        "homepage": "https://symfony.com"
    },
    {
        "name": "symfony/config",
        "version": "v3.4.4",
        "version_normalized": "3.4.4.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/config.git",
            "reference": "72689b934d6c6ecf73eca874e98933bf055313c9"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/config/zipball/72689b934d6c6ecf73eca874e98933bf055313c9",
            "reference": "72689b934d6c6ecf73eca874e98933bf055313c9",
            "shasum": ""
        },
        "require": {
            "php": "^5.5.9|>=7.0.8",
            "symfony/filesystem": "~2.8|~3.0|~4.0"
        },
        "conflict": {
            "symfony/dependency-injection": "<3.3",
            "symfony/finder": "<3.3"
        },
        "require-dev": {
            "symfony/dependency-injection": "~3.3|~4.0",
            "symfony/finder": "~3.3|~4.0",
            "symfony/yaml": "~3.0|~4.0"
        },
        "suggest": {
            "symfony/yaml": "To use the yaml reference dumper"
        },
        "time": "2018-01-21T19:05:02+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "3.4-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Component\\Config\\": ""
            },
            "exclude-from-classmap": [
                "/Tests/"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Fabien Potencier",
                "email": "fabien@symfony.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Symfony Config Component",
        "homepage": "https://symfony.com"
    },
    {
        "name": "robmorgan/phinx",
        "version": "0.9.2",
        "version_normalized": "0.9.2.0",
        "source": {
            "type": "git",
            "url": "https://github.com/cakephp/phinx.git",
            "reference": "e1698319ad55157c233b658c08f7a10617e797ca"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/cakephp/phinx/zipball/e1698319ad55157c233b658c08f7a10617e797ca",
            "reference": "e1698319ad55157c233b658c08f7a10617e797ca",
            "shasum": ""
        },
        "require": {
            "php": ">=5.4",
            "symfony/config": "^2.8|^3.0|^4.0",
            "symfony/console": "^2.8|^3.0|^4.0",
            "symfony/yaml": "^2.8|^3.0|^4.0"
        },
        "require-dev": {
            "cakephp/cakephp-codesniffer": "^3.0",
            "phpunit/phpunit": "^4.8.35|^5.7|^6.5"
        },
        "time": "2017-12-23T06:48:51+00:00",
        "bin": [
            "bin/phinx"
        ],
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Phinx\\": "src/Phinx"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Woody Gilk",
                "email": "woody.gilk@gmail.com",
                "homepage": "http://shadowhand.me",
                "role": "Developer"
            },
            {
                "name": "Rob Morgan",
                "email": "robbym@gmail.com",
                "homepage": "https://robmorgan.id.au",
                "role": "Lead Developer"
            },
            {
                "name": "Richard Quadling",
                "email": "rquadling@gmail.com",
                "role": "Developer"
            },
            {
                "name": "CakePHP Community",
                "homepage": "https://github.com/cakephp/phinx/graphs/contributors"
            }
        ],
        "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
        "homepage": "https://phinx.org",
        "keywords": [
            "database",
            "database migrations",
            "db",
            "migrations",
            "phinx"
        ]
    },
    {
        "name": "php-amqplib/php-amqplib",
        "version": "v2.7.2",
        "version_normalized": "2.7.2.0",
        "source": {
            "type": "git",
            "url": "https://github.com/php-amqplib/php-amqplib.git",
            "reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/dfd3694a86f1a7394d3693485259d4074a6ec79b",
            "reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b",
            "shasum": ""
        },
        "require": {
            "ext-bcmath": "*",
            "ext-mbstring": "*",
            "php": ">=5.3.0"
        },
        "replace": {
            "videlalvaro/php-amqplib": "self.version"
        },
        "require-dev": {
            "phpdocumentor/phpdocumentor": "^2.9",
            "phpunit/phpunit": "^4.8",
            "scrutinizer/ocular": "^1.1",
            "squizlabs/php_codesniffer": "^2.5"
        },
        "suggest": {
            "ext-sockets": "Use AMQPSocketConnection"
        },
        "time": "2018-02-11T19:28:00+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "2.7-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "PhpAmqpLib\\": "PhpAmqpLib/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "LGPL-2.1-or-later"
        ],
        "authors": [
            {
                "name": "Alvaro Videla",
                "role": "Original Maintainer"
            },
            {
                "name": "John Kelly",
                "email": "johnmkelly86@gmail.com",
                "role": "Maintainer"
            },
            {
                "name": "RaÃºl Araya",
                "email": "nubeiro@gmail.com",
                "role": "Maintainer"
            }
        ],
        "description": "Formerly videlalvaro/php-amqplib.  This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
        "homepage": "https://github.com/php-amqplib/php-amqplib/",
        "keywords": [
            "message",
            "queue",
            "rabbitmq"
        ]
    },
    {
        "name": "voku/php-hooks",
        "version": "0.2.11",
        "version_normalized": "0.2.11.0",
        "source": {
            "type": "git",
            "url": "https://github.com/voku/php-hooks.git",
            "reference": "ace0dd3ff6f1a98f8ba9b3e940265d541d78eb8f"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/voku/php-hooks/zipball/ace0dd3ff6f1a98f8ba9b3e940265d541d78eb8f",
            "reference": "ace0dd3ff6f1a98f8ba9b3e940265d541d78eb8f",
            "shasum": ""
        },
        "require": {
            "php": ">=5.3.0"
        },
        "require-dev": {
            "phpunit/phpunit": "4.*"
        },
        "time": "2017-05-11T12:59:53+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "voku\\helper\\": "src/voku/helper/"
            }
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "GPL-3.0"
        ],
        "authors": [
            {
                "name": "Lars Moelleken",
                "homepage": "http://www.moelleken.org/",
                "role": "Developer"
            },
            {
                "name": "Ohad Raz",
                "homepage": "https://github.com/bainternet",
                "role": "Developer"
            }
        ],
        "description": "A fork of the WordPress filters hook system rolled in to a class to be ported into any PHP-based system",
        "homepage": "http://github.com/voku/php-hooks/",
        "keywords": [
            "actions",
            "filters",
            "filters-system",
            "hook-system",
            "hooks"
        ]
    },
    {
        "name": "linways/linways-base",
        "version": "1.0.14.9",
        "version_normalized": "1.0.14.9",
        "source": {
            "type": "git",
            "url": "git@gitlab.com:linways/linways-base.git",
            "reference": "db04c808a950931f7c0322698c9a8e7864527955"
        },
        "dist": {
            "type": "zip",
            "url": "https://gitlab.com/api/v4/projects/linways%2Flinways-base/repository/archive.zip?sha=db04c808a950931f7c0322698c9a8e7864527955",
            "reference": "db04c808a950931f7c0322698c9a8e7864527955",
            "shasum": ""
        },
        "require": {
            "php": ">=5.4.0",
            "php-amqplib/php-amqplib": "^2.7",
            "voku/php-hooks": "^0.2.11"
        },
        "require-dev": {
            "phpunit/phpunit": "^5.7"
        },
        "time": "2018-02-19T05:02:17+00:00",
        "type": "library",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "com\\linways\\base\\": "src/com/linways/base"
            }
        },
        "description": "Linways base"
    },
    {
        "name": "symfony/dotenv",
        "version": "v3.4.4",
        "version_normalized": "3.4.4.0",
        "source": {
            "type": "git",
            "url": "https://github.com/symfony/dotenv.git",
            "reference": "66265f80c0f585cd6aec3fbdfc4ffdf7a0d75992"
        },
        "dist": {
            "type": "zip",
            "url": "https://api.github.com/repos/symfony/dotenv/zipball/66265f80c0f585cd6aec3fbdfc4ffdf7a0d75992",
            "reference": "66265f80c0f585cd6aec3fbdfc4ffdf7a0d75992",
            "shasum": ""
        },
        "require": {
            "php": "^5.5.9|>=7.0.8"
        },
        "require-dev": {
            "symfony/process": "~3.2|~4.0"
        },
        "time": "2018-01-03T17:14:19+00:00",
        "type": "library",
        "extra": {
            "branch-alias": {
                "dev-master": "3.4-dev"
            }
        },
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "Symfony\\Component\\Dotenv\\": ""
            },
            "exclude-from-classmap": [
                "/Tests/"
            ]
        },
        "notification-url": "https://packagist.org/downloads/",
        "license": [
            "MIT"
        ],
        "authors": [
            {
                "name": "Fabien Potencier",
                "email": "fabien@symfony.com"
            },
            {
                "name": "Symfony Community",
                "homepage": "https://symfony.com/contributors"
            }
        ],
        "description": "Registers environment variables from a .env file",
        "homepage": "https://symfony.com",
        "keywords": [
            "dotenv",
            "env",
            "environment"
        ]
    },
    {
        "name": "linways/nucleus-core",
        "version": "v1.0.8",
        "version_normalized": "1.0.8.0",
        "source": {
            "type": "git",
            "url": "git@gitlab.com:linways/nucleus-core.git",
            "reference": "95f5944348f50007cee83af32dad8a835112d6fe"
        },
        "dist": {
            "type": "zip",
            "url": "https://gitlab.com/api/v4/projects/linways%2Fnucleus-core/repository/archive.zip?sha=95f5944348f50007cee83af32dad8a835112d6fe",
            "reference": "95f5944348f50007cee83af32dad8a835112d6fe",
            "shasum": ""
        },
        "require": {
            "dragonmantank/cron-expression": "^1.2",
            "firebase/php-jwt": "^5.0",
            "linways/linways-base": "^1.0.0",
            "robmorgan/phinx": "^0.9.1"
        },
        "require-dev": {
            "phpunit/phpunit": "^5.7"
        },
        "time": "2018-02-25T17:00:13+00:00",
        "type": "project",
        "installation-source": "dist",
        "autoload": {
            "psr-4": {
                "com\\linways\\": "src/com/linways",
                "test\\": "test/"
            }
        },
        "description": "Project for tenant and api access management for professional"
    }
]
<?php

// autoload_files.php @generated by Composer

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

return array(
    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
);
{
    "name": "firebase/php-jwt",
    "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
    "homepage": "https://github.com/firebase/php-jwt",
    "authors": [
        {
            "name": "Neuman Vong",
            "email": "neuman+pear@twilio.com",
            "role": "Developer"
        },
        {
            "name": "Anant Narayanan",
            "email": "anant@php.net",
            "role": "Developer"
        }
    ],
    "license": "BSD-3-Clause",
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "Firebase\\JWT\\": "src"
        }
    },
    "require-dev": {
        "phpunit/phpunit": " 4.8.35"
    }
}
<?php
namespace Firebase\JWT;

class SignatureInvalidException extends \UnexpectedValueException
{

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

$autoloader = require __DIR__ . '/../src/composer_autoloader.php';

if (!$autoloader()) {
    die(
        'You need to set up the project dependencies using the following commands:' . PHP_EOL .
        'curl -sS https://getcomposer.org/installer | php' . PHP_EOL .
        'php composer.phar install' . PHP_EOL
    );
}

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

// This script can be run as a router with the built in PHP web server:
//
//   php -S localhost:8000 app/web.php
//
// Or can be run from any other web server with:
//
//   require 'phinx/app/web.php';
//
// This script uses the following query string arguments:
//
// - (string) "e" environment name
// - (string) "t" target version
// - (boolean) "debug" enable debugging?

// Get the phinx console application and inject it into TextWrapper.
$app = require __DIR__ . '/phinx.php';
$wrap = new Phinx\Wrapper\TextWrapper($app);

// Mapping of route names to commands.
$routes = [
    'status' => 'getStatus',
    'migrate' => 'getMigrate',
    'rollback' => 'getRollback',
];

// Extract the requested command from the URL, default to "status".
$command = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
if (!$command) {
    $command = 'status';
}

// Verify that the command exists, or list available commands.
if (!isset($routes[$command])) {
    $commands = implode(', ', array_keys($routes));
    header('Content-Type: text/plain', true, 404);
    die("Command not found! Valid commands are: {$commands}.");
}

// Get the environment and target version parameters.
$env = isset($_GET['e']) ? $_GET['e'] : null;
$target = isset($_GET['t']) ? $_GET['t'] : null;

// Check if debugging is enabled.
$debug = !empty($_GET['debug']) && filter_var($_GET['debug'], FILTER_VALIDATE_BOOLEAN);

// Execute the command and determine if it was successful.
$output = call_user_func([$wrap, $routes[$command]], $env, $target);
$error = $wrap->getExitCode() > 0;

// Finally, display the output of the command.
header('Content-Type: text/plain', true, $error ? 500 : 200);
if ($debug) {
    // Show what command was executed based on request parameters.
    $args = implode(', ', [var_export($env, true), var_export($target, true)]);
    echo "DEBUG: $command($args)" . PHP_EOL . PHP_EOL;
}
echo $output;
{
    "name": "robmorgan/phinx",
    "type": "library",
    "description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
    "keywords": ["phinx", "migrations", "database", "db", "database migrations"],
    "homepage": "https://phinx.org",
    "license": "MIT",
    "version": "0.9.2",
    "authors": [{
        "name": "Rob Morgan",
        "email": "robbym@gmail.com",
        "homepage": "https://robmorgan.id.au",
        "role": "Lead Developer"
    }, {
        "name": "Woody Gilk",
        "email": "woody.gilk@gmail.com",
        "homepage": "http://shadowhand.me",
        "role": "Developer"
    }, {
        "name": "Richard Quadling",
        "email": "rquadling@gmail.com",
        "role": "Developer"
    }, {
        "name": "CakePHP Community",
        "homepage": "https://github.com/cakephp/phinx/graphs/contributors"
    }],
    "require": {
        "php": ">=5.4",
        "symfony/console": "^2.8|^3.0|^4.0",
        "symfony/config": "^2.8|^3.0|^4.0",
        "symfony/yaml": "^2.8|^3.0|^4.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.8.35|^5.7|^6.5",
        "cakephp/cakephp-codesniffer": "^3.0"
    },
    "autoload": {
        "psr-4": {
            "Phinx\\": "src/Phinx"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Test\\Phinx\\": "tests/Phinx"
        }
    },
    "scripts": {
        "check": [
            "@test",
            "@cs-check"
        ],
        "cs-check": "phpcs --colors -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./app",
        "cs-fix": "phpcbf --colors --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./app",
        "test": "phpunit --colors=always"
    },
    "bin": ["bin/phinx"]
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Seed
 */
namespace Phinx\Seed;

use Phinx\Db\Adapter\AdapterInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Seed interface
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
interface SeedInterface
{
    /**
     * @var string
     */
    const RUN = 'run';

    /**
     * Run the seeder.
     *
     * @return void
     */
    public function run();

    /**
     * Sets the database adapter.
     *
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
     * @return \Phinx\Seed\SeedInterface
     */
    public function setAdapter(AdapterInterface $adapter);

    /**
     * Gets the database adapter.
     *
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getAdapter();

    /**
     * Sets the input object to be used in migration object
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @return \Phinx\Seed\SeedInterface
     */
    public function setInput(InputInterface $input);

    /**
     * Gets the input object to be used in migration object
     *
     * @return \Symfony\Component\Console\Input\InputInterface
     */
    public function getInput();

    /**
     * Sets the output object to be used in migration object
     *
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return \Phinx\Seed\SeedInterface
     */
    public function setOutput(OutputInterface $output);

    /**
     * Gets the output object to be used in migration object
     *
     * @return \Symfony\Component\Console\Output\OutputInterface
     */
    public function getOutput();

    /**
     * Gets the name.
     *
     * @return string
     */
    public function getName();

    /**
     * Executes a SQL statement and returns the number of affected rows.
     *
     * @param string $sql SQL
     * @return int
     */
    public function execute($sql);

    /**
     * Executes a SQL statement and returns the result as an array.
     *
     * @param string $sql SQL
     * @return array
     */
    public function query($sql);

    /**
     * Executes a query and returns only one row as an array.
     *
     * @param string $sql SQL
     * @return array
     */
    public function fetchRow($sql);

    /**
     * Executes a query and returns an array of rows.
     *
     * @param string $sql SQL
     * @return array
     */
    public function fetchAll($sql);

    /**
     * Insert data into a table.
     *
     * @param string $tableName
     * @param array $data
     * @return void
     */
    public function insert($tableName, $data);

    /**
     * Checks to see if a table exists.
     *
     * @param string $tableName Table Name
     * @return bool
     */
    public function hasTable($tableName);

    /**
     * Returns an instance of the <code>\Table</code> class.
     *
     * You can use this class to create and manipulate tables.
     *
     * @param string $tableName Table Name
     * @param array $options Options
     * @return \Phinx\Db\Table
     */
    public function table($tableName, $options);
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Seed
 */
namespace Phinx\Seed;

use Phinx\Db\Adapter\AdapterInterface;
use Phinx\Db\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Abstract Seed Class.
 *
 * It is expected that the seeds you write extend from this class.
 *
 * This abstract class proxies the various database methods to your specified
 * adapter.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
abstract class AbstractSeed implements SeedInterface
{
    /**
     * @var \Phinx\Db\Adapter\AdapterInterface
     */
    protected $adapter;

    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    protected $input;

    /**
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    protected $output;

    /**
     * Class Constructor.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     */
    final public function __construct(InputInterface $input = null, OutputInterface $output = null)
    {
        if (!is_null($input)) {
            $this->setInput($input);
        }
        if (!is_null($output)) {
            $this->setOutput($output);
        }

        $this->init();
    }

    /**
     * Initialize method.
     *
     * @return void
     */
    protected function init()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function run()
    {
    }

    /**
     * Return seeds dependencies.
     *
     * @return array
     */
    public function getDependencies()
    {
        return [];
    }

    /**
     * {@inheritdoc}
     */
    public function setAdapter(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getAdapter()
    {
        return $this->adapter;
    }

    /**
     * {@inheritdoc}
     */
    public function setInput(InputInterface $input)
    {
        $this->input = $input;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * {@inheritdoc}
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return get_class($this);
    }

    /**
     * {@inheritdoc}
     */
    public function execute($sql)
    {
        return $this->getAdapter()->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function query($sql)
    {
        return $this->getAdapter()->query($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function fetchRow($sql)
    {
        return $this->getAdapter()->fetchRow($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function fetchAll($sql)
    {
        return $this->getAdapter()->fetchAll($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function insert($table, $data)
    {
        // convert to table object
        if (is_string($table)) {
            $table = new Table($table, [], $this->getAdapter());
        }
        $table->insert($data)->save();
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        return $this->getAdapter()->hasTable($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function table($tableName, $options = [])
    {
        return new Table($tableName, $options, $this->getAdapter());
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Util
 */
namespace Phinx\Util;

class Util
{
    /**
     * @var string
     */
    const DATE_FORMAT = 'YmdHis';

    /**
     * @var string
     */
    const MIGRATION_FILE_NAME_PATTERN = '/^\d+_([\w_]+).php$/i';

    /**
     * @var string
     */
    const SEED_FILE_NAME_PATTERN = '/^([A-Z][a-z0-9]+).php$/i';

    /**
     * Gets the current timestamp string, in UTC.
     *
     * @return string
     */
    public static function getCurrentTimestamp()
    {
        $dt = new \DateTime('now', new \DateTimeZone('UTC'));

        return $dt->format(static::DATE_FORMAT);
    }

    /**
     * Gets an array of all the existing migration class names.
     *
     * @return string[]
     */
    public static function getExistingMigrationClassNames($path)
    {
        $classNames = [];

        if (!is_dir($path)) {
            return $classNames;
        }

        // filter the files to only get the ones that match our naming scheme
        $phpFiles = glob($path . DIRECTORY_SEPARATOR . '*.php');

        foreach ($phpFiles as $filePath) {
            if (preg_match('/([0-9]+)_([_a-z0-9]*).php/', basename($filePath))) {
                $classNames[] = static::mapFileNameToClassName(basename($filePath));
            }
        }

        return $classNames;
    }

    /**
     * Get the version from the beginning of a file name.
     *
     * @param string $fileName File Name
     * @return string
     */
    public static function getVersionFromFileName($fileName)
    {
        $matches = [];
        preg_match('/^[0-9]+/', basename($fileName), $matches);

        return $matches[0];
    }

    /**
     * Turn migration names like 'CreateUserTable' into file names like
     * '12345678901234_create_user_table.php' or 'LimitResourceNamesTo30Chars' into
     * '12345678901234_limit_resource_names_to_30_chars.php'.
     *
     * @param string $className Class Name
     * @return string
     */
    public static function mapClassNameToFileName($className)
    {
        $arr = preg_split('/(?=[A-Z])/', $className);
        unset($arr[0]); // remove the first element ('')
        $fileName = static::getCurrentTimestamp() . '_' . strtolower(implode($arr, '_')) . '.php';

        return $fileName;
    }

    /**
     * Turn file names like '12345678901234_create_user_table.php' into class
     * names like 'CreateUserTable'.
     *
     * @param string $fileName File Name
     * @return string
     */
    public static function mapFileNameToClassName($fileName)
    {
        $matches = [];
        if (preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName, $matches)) {
            $fileName = $matches[1];
        }

        return str_replace(' ', '', ucwords(str_replace('_', ' ', $fileName)));
    }

    /**
     * Check if a migration class name is unique regardless of the
     * timestamp.
     *
     * This method takes a class name and a path to a migrations directory.
     *
     * Migration class names must be in CamelCase format.
     * e.g: CreateUserTable or AddIndexToPostsTable.
     *
     * Single words are not allowed on their own.
     *
     * @param string $className Class Name
     * @param string $path Path
     * @return bool
     */
    public static function isUniqueMigrationClassName($className, $path)
    {
        $existingClassNames = static::getExistingMigrationClassNames($path);

        return !(in_array($className, $existingClassNames));
    }

    /**
     * Check if a migration/seed class name is valid.
     *
     * Migration & Seed class names must be in CamelCase format.
     * e.g: CreateUserTable, AddIndexToPostsTable or UserSeeder.
     *
     * Single words are not allowed on their own.
     *
     * @param string $className Class Name
     * @return bool
     */
    public static function isValidPhinxClassName($className)
    {
        return (bool)preg_match('/^([A-Z][a-z0-9]+)+$/', $className);
    }

    /**
     * Check if a migration file name is valid.
     *
     * @param string $fileName File Name
     * @return bool
     */
    public static function isValidMigrationFileName($fileName)
    {
        $matches = [];

        return preg_match(static::MIGRATION_FILE_NAME_PATTERN, $fileName, $matches);
    }

    /**
     * Check if a seed file name is valid.
     *
     * @param string $fileName File Name
     * @return bool
     */
    public static function isValidSeedFileName($fileName)
    {
        $matches = [];

        return preg_match(static::SEED_FILE_NAME_PATTERN, $fileName, $matches);
    }

    /**
     * Expands a set of paths with curly braces (if supported by the OS).
     *
     * @param array $paths
     * @return array
     */
    public static function globAll(array $paths)
    {
        $result = [];

        foreach ($paths as $path) {
            $result = array_merge($result, static::glob($path));
        }

        return $result;
    }

    /**
     * Expands a path with curly braces (if supported by the OS).
     *
     * @param string $path
     * @return array
     */
    public static function glob($path)
    {
        return glob($path, defined('GLOB_BRACE') ? GLOB_BRACE : 0);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 * Copyright (c) 2015 Woody Gilk
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

namespace Phinx\Wrapper;

use Phinx\Console\PhinxApplication;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;

/**
 * Phinx text wrapper: a way to run `status`, `migrate`, and `rollback` commands
 * and get the output of the command back as plain text.
 *
 * @author Woody Gilk <woody.gilk@gmail.com>
 */
class TextWrapper
{
    /**
     * @var \Phinx\Console\PhinxApplication
     */
    private $app;

    /**
     * @var array
     */
    private $options = [];

    /**
     * @var integer
     */
    private $exit_code;

    /**
     * @param \Phinx\Console\PhinxApplication $app
     * @param array $options
     */
    public function __construct(PhinxApplication $app, array $options = [])
    {
        $this->app = $app;
        $this->options = $options;
    }

    /**
     * Get the application instance.
     *
     * @return \Phinx\Console\PhinxApplication
     */
    public function getApp()
    {
        return $this->app;
    }

    /**
     * Returns the exit code from the last run command.
     * @return int
     */
    public function getExitCode()
    {
        return $this->exit_code;
    }

    /**
     * Returns the output from running the "status" command.
     * @param  string $env environment name (optional)
     * @return string
     */
    public function getStatus($env = null)
    {
        $command = ['status'];
        if ($env ?: $this->hasOption('environment')) {
            $command += ['-e' => $env ?: $this->getOption('environment')];
        }
        if ($this->hasOption('configuration')) {
            $command += ['-c' => $this->getOption('configuration')];
        }
        if ($this->hasOption('parser')) {
            $command += ['-p' => $this->getOption('parser')];
        }

        return $this->executeRun($command);
    }

    /**
     * Returns the output from running the "migrate" command.
     * @param  string $env environment name (optional)
     * @param  string $target target version (optional)
     * @return string
     */
    public function getMigrate($env = null, $target = null)
    {
        $command = ['migrate'];
        if ($env ?: $this->hasOption('environment')) {
            $command += ['-e' => $env ?: $this->getOption('environment')];
        }
        if ($this->hasOption('configuration')) {
            $command += ['-c' => $this->getOption('configuration')];
        }
        if ($this->hasOption('parser')) {
            $command += ['-p' => $this->getOption('parser')];
        }
        if ($target) {
            $command += ['-t' => $target];
        }

        return $this->executeRun($command);
    }

    /**
     * Returns the output from running the "seed:run" command.
     * @param  string|null       $env environment name
     * @param  string|null       $target target version
     * @param  array|string|null $seed array of seed names or seed name
     * @return string
     */
    public function getSeed($env = null, $target = null, $seed = null)
    {
        $command = ['seed:run'];
        if ($env ?: $this->hasOption('environment')) {
            $command += ['-e' => $env ?: $this->getOption('environment')];
        }
        if ($this->hasOption('configuration')) {
            $command += ['-c' => $this->getOption('configuration')];
        }
        if ($this->hasOption('parser')) {
            $command += ['-p' => $this->getOption('parser')];
        }
        if ($target) {
            $command += ['-t' => $target];
        }
        if ($seed) {
            $seed = (array)$seed;
            $command += ['-s' => $seed];
        }

        return $this->executeRun($command);
    }

    /**
     * Returns the output from running the "rollback" command.
     * @param  string $env environment name (optional)
     * @param  mixed $target target version, or 0 (zero) fully revert (optional)
     * @return string
     */
    public function getRollback($env = null, $target = null)
    {
        $command = ['rollback'];
        if ($env ?: $this->hasOption('environment')) {
            $command += ['-e' => $env ?: $this->getOption('environment')];
        }
        if ($this->hasOption('configuration')) {
            $command += ['-c' => $this->getOption('configuration')];
        }
        if ($this->hasOption('parser')) {
            $command += ['-p' => $this->getOption('parser')];
        }
        if (isset($target)) {
            // Need to use isset() with rollback, because -t0 is a valid option!
            // See http://docs.phinx.org/en/latest/commands.html#the-rollback-command
            $command += ['-t' => $target];
        }

        return $this->executeRun($command);
    }

    /**
     * Check option from options array
     *
     * @param  string $key
     * @return bool
     */
    protected function hasOption($key)
    {
        return isset($this->options[$key]);
    }

    /**
     * Get option from options array
     *
     * @param  string $key
     * @return string|null
     */
    protected function getOption($key)
    {
        if (!isset($this->options[$key])) {
            return null;
        }

        return $this->options[$key];
    }

    /**
     * Set option in options array
     *
     * @param  string $key
     * @param  string $value
     * @return object
     */
    public function setOption($key, $value)
    {
        $this->options[$key] = $value;

        return $this;
    }

    /**
     * Execute a command, capturing output and storing the exit code.
     *
     * @param  array $command
     * @return string
     */
    protected function executeRun(array $command)
    {
        // Output will be written to a temporary stream, so that it can be
        // collected after running the command.
        $stream = fopen('php://temp', 'w+');

        // Execute the command, capturing the output in the temporary stream
        // and storing the exit code for debugging purposes.
        $this->exit_code = $this->app->doRun(new ArrayInput($command), new StreamOutput($stream));

        // Get the output of the command and close the stream, which will
        // destroy the temporary file.
        $result = stream_get_contents($stream, -1, 0);
        fclose($stream);

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

namespace Phinx\Config;

/**
 * Config aware getNamespaceByPath method.
 * @package Phinx\Config
 * @author Andrey N. Mokhov
 */
interface NamespaceAwareInterface
{
    /**
     * Get Migration Namespace associated with path.
     *
     * @param string $path
     * @return string|null
     */
    public function getMigrationNamespaceByPath($path);

    /**
     * Get Seed Namespace associated with path.
     *
     * @param string $path
     * @return string|null
     */
    public function getSeedNamespaceByPath($path);
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Config
 */
namespace Phinx\Config;

/**
 * Phinx configuration interface.
 *
 * @package Phinx
 * @author  Woody Gilk
 */
interface ConfigInterface extends \ArrayAccess
{
    /**
     * Returns the configuration for each environment.
     *
     * This method returns <code>null</code> if no environments exist.
     *
     * @return array|null
     */
    public function getEnvironments();

    /**
     * Returns the configuration for a given environment.
     *
     * This method returns <code>null</code> if the specified environment
     * doesn't exist.
     *
     * @param string $name
     * @return array|null
     */
    public function getEnvironment($name);

    /**
     * Does the specified environment exist in the configuration file?
     *
     * @param string $name Environment Name
     * @return bool
     */
    public function hasEnvironment($name);

    /**
     * Gets the default environment name.
     *
     * @throws \RuntimeException
     * @return string
     */
    public function getDefaultEnvironment();

    /**
     * Get the aliased value from a supplied alias.
     *
     * @param string $alias
     *
     * @return string|null
     */
    public function getAlias($alias);

    /**
     * Gets the config file path.
     *
     * @return string
     */
    public function getConfigFilePath();

    /**
     * Gets the paths to search for migration files.
     *
     * @return string[]
     */
    public function getMigrationPaths();

    /**
     * Gets the paths to search for seed files.
     *
     * @return string[]
     */
    public function getSeedPaths();

    /**
     * Get the template file name.
     *
     * @return string|false
     */
    public function getTemplateFile();

    /**
     * Get the template class name.
     *
     * @return string|false
     */
    public function getTemplateClass();

    /**
     * Get the version order.
     *
     * @return string
     */
    public function getVersionOrder();

    /**
     * Is version order creation time?
     *
     * @return bool
     */
    public function isVersionOrderCreationTime();

    /**
     * Gets the base class name for migrations.
     *
     * @param bool $dropNamespace Return the base migration class name without the namespace.
     * @return string
     */
    public function getMigrationBaseClassName($dropNamespace = true);
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2017 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Config
 */

namespace Phinx\Config;

/**
 * Trait implemented NamespaceAwareInterface.
 * @package Phinx\Config
 * @author Andrey N. Mokhov
 */
trait NamespaceAwareTrait
{
    /**
     * Gets the paths to search for migration files.
     *
     * @return string[]
     */
    abstract public function getMigrationPaths();

    /**
     * Gets the paths to search for seed files.
     *
     * @return string[]
     */
    abstract public function getSeedPaths();

    /**
     * Search $needle in $haystack and return key associate with him.
     *
     * @param string $needle
     * @param array  $haystack
     * @return null|string
     */
    protected function searchNamespace($needle, $haystack)
    {
        $needle = realpath($needle);
        $haystack = array_map('realpath', $haystack);

        $key = array_search($needle, $haystack);

        return is_string($key) ? trim($key, '\\') : null;
    }

    /**
     * Get Migration Namespace associated with path.
     *
     * @param string $path
     * @return string|null
     */
    public function getMigrationNamespaceByPath($path)
    {
        $paths = $this->getMigrationPaths();

        return $this->searchNamespace($path, $paths);
    }

    /**
     * Get Seed Namespace associated with path.
     *
     * @param string $path
     * @return string|null
     */
    public function getSeedNamespaceByPath($path)
    {
        $paths = $this->getSeedPaths();

        return $this->searchNamespace($path, $paths);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Config
 */
namespace Phinx\Config;

use Symfony\Component\Yaml\Yaml;

/**
 * Phinx configuration class.
 *
 * @package Phinx
 * @author Rob Morgan
 */
class Config implements ConfigInterface, NamespaceAwareInterface
{
    use NamespaceAwareTrait;

    /**
     * The value that identifies a version order by creation time.
     */
    const VERSION_ORDER_CREATION_TIME = 'creation';

    /**
     * The value that identifies a version order by execution time.
     */
    const VERSION_ORDER_EXECUTION_TIME = 'execution';

    /**
     * @var array
     */
    private $values = [];

    /**
     * @var string
     */
    protected $configFilePath;

    /**
     * {@inheritdoc}
     */
    public function __construct(array $configArray, $configFilePath = null)
    {
        $this->configFilePath = $configFilePath;
        $this->values = $this->replaceTokens($configArray);
    }

    /**
     * Create a new instance of the config class using a Yaml file path.
     *
     * @param  string $configFilePath Path to the Yaml File
     * @throws \RuntimeException
     * @return \Phinx\Config\Config
     */
    public static function fromYaml($configFilePath)
    {
        $configFile = file_get_contents($configFilePath);
        $configArray = Yaml::parse($configFile);

        if (!is_array($configArray)) {
            throw new \RuntimeException(sprintf(
                'File \'%s\' must be valid YAML',
                $configFilePath
            ));
        }

        return new static($configArray, $configFilePath);
    }

    /**
     * Create a new instance of the config class using a JSON file path.
     *
     * @param  string $configFilePath Path to the JSON File
     * @throws \RuntimeException
     * @return \Phinx\Config\Config
     */
    public static function fromJson($configFilePath)
    {
        $configArray = json_decode(file_get_contents($configFilePath), true);
        if (!is_array($configArray)) {
            throw new \RuntimeException(sprintf(
                'File \'%s\' must be valid JSON',
                $configFilePath
            ));
        }

        return new static($configArray, $configFilePath);
    }

    /**
     * Create a new instance of the config class using a PHP file path.
     *
     * @param  string $configFilePath Path to the PHP File
     * @throws \RuntimeException
     * @return \Phinx\Config\Config
     */
    public static function fromPhp($configFilePath)
    {
        ob_start();
        /** @noinspection PhpIncludeInspection */
        $configArray = include($configFilePath);

        // Hide console output
        ob_end_clean();

        if (!is_array($configArray)) {
            throw new \RuntimeException(sprintf(
                'PHP file \'%s\' must return an array',
                $configFilePath
            ));
        }

        return new static($configArray, $configFilePath);
    }

    /**
     * {@inheritdoc}
     */
    public function getEnvironments()
    {
        if (isset($this->values) && isset($this->values['environments'])) {
            $environments = [];
            foreach ($this->values['environments'] as $key => $value) {
                if (is_array($value)) {
                    $environments[$key] = $value;
                }
            }

            return $environments;
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function getEnvironment($name)
    {
        $environments = $this->getEnvironments();

        if (isset($environments[$name])) {
            if (isset($this->values['environments']['default_migration_table'])) {
                $environments[$name]['default_migration_table'] =
                    $this->values['environments']['default_migration_table'];
            }

            return $environments[$name];
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function hasEnvironment($name)
    {
        return ($this->getEnvironment($name) !== null);
    }

    /**
     * {@inheritdoc}
     */
    public function getDefaultEnvironment()
    {
        // The $PHINX_ENVIRONMENT variable overrides all other default settings
        $env = getenv('PHINX_ENVIRONMENT');
        if (!empty($env)) {
            if ($this->hasEnvironment($env)) {
                return $env;
            }

            throw new \RuntimeException(sprintf(
                'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing',
                $env
            ));
        }

        // if the user has configured a default database then use it,
        // providing it actually exists!
        if (isset($this->values['environments']['default_database'])) {
            if ($this->getEnvironment($this->values['environments']['default_database'])) {
                return $this->values['environments']['default_database'];
            }

            throw new \RuntimeException(sprintf(
                'The environment configuration for \'%s\' is missing',
                $this->values['environments']['default_database']
            ));
        }

        // else default to the first available one
        if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) {
            $names = array_keys($this->getEnvironments());

            return $names[0];
        }

        throw new \RuntimeException('Could not find a default environment');
    }

    /**
     * {@inheritdoc}
     */
    public function getAlias($alias)
    {
        return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null;
    }

    /**
     * {@inheritdoc}
     */
    public function getConfigFilePath()
    {
        return $this->configFilePath;
    }

    /**
     * {@inheritdoc}
     */
    public function getMigrationPaths()
    {
        if (!isset($this->values['paths']['migrations'])) {
            throw new \UnexpectedValueException('Migrations path missing from config file');
        }

        if (is_string($this->values['paths']['migrations'])) {
            $this->values['paths']['migrations'] = [$this->values['paths']['migrations']];
        }

        return $this->values['paths']['migrations'];
    }

    /**
     * Gets the base class name for migrations.
     *
     * @param bool $dropNamespace Return the base migration class name without the namespace.
     * @return string
     */
    public function getMigrationBaseClassName($dropNamespace = true)
    {
        $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class'];

        return $dropNamespace ? substr(strrchr($className, '\\'), 1) ?: $className : $className;
    }

    /**
     * {@inheritdoc}
     */
    public function getSeedPaths()
    {
        if (!isset($this->values['paths']['seeds'])) {
            throw new \UnexpectedValueException('Seeds path missing from config file');
        }

        if (is_string($this->values['paths']['seeds'])) {
            $this->values['paths']['seeds'] = [$this->values['paths']['seeds']];
        }

        return $this->values['paths']['seeds'];
    }

    /**
     * Get the template file name.
     *
     * @return string|false
     */
    public function getTemplateFile()
    {
        if (!isset($this->values['templates']['file'])) {
            return false;
        }

        return $this->values['templates']['file'];
    }

    /**
     * Get the template class name.
     *
     * @return string|false
     */
    public function getTemplateClass()
    {
        if (!isset($this->values['templates']['class'])) {
            return false;
        }

        return $this->values['templates']['class'];
    }

    /**
     * Get the version order.
     *
     * @return string
     */
    public function getVersionOrder()
    {
        if (!isset($this->values['version_order'])) {
            return self::VERSION_ORDER_CREATION_TIME;
        }

        return $this->values['version_order'];
    }

    /**
     * Is version order creation time?
     *
     * @return bool
     */
    public function isVersionOrderCreationTime()
    {
        $versionOrder = $this->getVersionOrder();

        return $versionOrder == self::VERSION_ORDER_CREATION_TIME;
    }

    /**
     * Replace tokens in the specified array.
     *
     * @param array $arr Array to replace
     * @return array
     */
    protected function replaceTokens(array $arr)
    {
        // Get environment variables
        // $_ENV is empty because variables_order does not include it normally
        $tokens = [];
        foreach ($_SERVER as $varname => $varvalue) {
            if (0 === strpos($varname, 'PHINX_')) {
                $tokens['%%' . $varname . '%%'] = $varvalue;
            }
        }

        // Phinx defined tokens (override env tokens)
        $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath();
        $tokens['%%PHINX_CONFIG_DIR%%'] = dirname($this->getConfigFilePath());

        // Recurse the array and replace tokens
        return $this->recurseArrayForTokens($arr, $tokens);
    }

    /**
     * Recurse an array for the specified tokens and replace them.
     *
     * @param array $arr Array to recurse
     * @param array $tokens Array of tokens to search for
     * @return array
     */
    protected function recurseArrayForTokens($arr, $tokens)
    {
        $out = [];
        foreach ($arr as $name => $value) {
            if (is_array($value)) {
                $out[$name] = $this->recurseArrayForTokens($value, $tokens);
                continue;
            }
            if (is_string($value)) {
                foreach ($tokens as $token => $tval) {
                    $value = str_replace($token, $tval, $value);
                }
                $out[$name] = $value;
                continue;
            }
            $out[$name] = $value;
        }

        return $out;
    }

    /**
     * {@inheritdoc}
     */
    public function offsetSet($id, $value)
    {
        $this->values[$id] = $value;
    }

    /**
     * {@inheritdoc}
     */
    public function offsetGet($id)
    {
        if (!array_key_exists($id, $this->values)) {
            throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
        }

        return $this->values[$id] instanceof \Closure ? $this->values[$id]($this) : $this->values[$id];
    }

    /**
     * {@inheritdoc}
     */
    public function offsetExists($id)
    {
        return isset($this->values[$id]);
    }

    /**
     * {@inheritdoc}
     */
    public function offsetUnset($id)
    {
        unset($this->values[$id]);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;
use Phinx\Migration\IrreversibleMigrationException;

/**
 * Phinx Proxy Adapter.
 *
 * Used for recording migration commands to automatically reverse them.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
class ProxyAdapter extends AdapterWrapper
{
    /**
     * @var array
     */
    protected $commands;

    /**
     * {@inheritdoc}
     */
    public function getAdapterType()
    {
        return 'ProxyAdapter';
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        $this->recordCommand('createTable', [$table->getName()]);
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $this->recordCommand('renameTable', [$tableName, $newTableName]);
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $this->recordCommand('dropTable', [$tableName]);
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $this->recordCommand('truncateTable', [$tableName]);
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $this->recordCommand('addColumn', [$table, $column]);
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        $this->recordCommand('renameColumn', [$tableName, $columnName, $newColumnName]);
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        $this->recordCommand('changeColumn', [$tableName, $columnName, $newColumn]);
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        $this->recordCommand('dropColumn', [$tableName, $columnName]);
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $this->recordCommand('addIndex', [$table, $index]);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns, $options = [])
    {
        $this->recordCommand('dropIndex', [$tableName, $columns, $options]);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $this->recordCommand('dropIndexByName', [$tableName, $indexName]);
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        $this->recordCommand('addForeignKey', [$table, $foreignKey]);
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        $this->recordCommand('dropForeignKey', [$columns, $constraint]);
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options = [])
    {
        $this->recordCommand('createDatabase', [$name, $options]);
    }

    /**
     * Record a command for execution later.
     *
     * @param string $name Command Name
     * @param array $arguments Command Arguments
     * @return void
     */
    public function recordCommand($name, $arguments)
    {
        $this->commands[] = [
            'name' => $name,
            'arguments' => $arguments
        ];
    }

    /**
     * Sets an array of recorded commands.
     *
     * @param array $commands Commands
     * @return \Phinx\Db\Adapter\ProxyAdapter
     */
    public function setCommands($commands)
    {
        $this->commands = $commands;

        return $this;
    }

    /**
     * Gets an array of the recorded commands.
     *
     * @return array
     */
    public function getCommands()
    {
        return $this->commands;
    }

    /**
     * Gets an array of the recorded commands in reverse.
     *
     * @throws \Phinx\Migration\IrreversibleMigrationException if a command cannot be reversed.
     * @return array
     */
    public function getInvertedCommands()
    {
        if ($this->getCommands() === null) {
            return [];
        }

        $invCommands = [];
        $supportedCommands = [
            'createTable', 'renameTable', 'addColumn',
            'renameColumn', 'addIndex', 'addForeignKey'
        ];
        foreach (array_reverse($this->getCommands()) as $command) {
            if (!in_array($command['name'], $supportedCommands)) {
                throw new IrreversibleMigrationException(sprintf(
                    'Cannot reverse a "%s" command',
                    $command['name']
                ));
            }
            $invertMethod = 'invert' . ucfirst($command['name']);
            $invertedCommand = $this->$invertMethod($command['arguments']);
            $invCommands[] = [
                'name' => $invertedCommand['name'],
                'arguments' => $invertedCommand['arguments']
            ];
        }

        return $invCommands;
    }

    /**
     * Execute the recorded commands.
     *
     * @return void
     */
    public function executeCommands()
    {
        $commands = $this->getCommands();
        foreach ($commands as $command) {
            call_user_func_array([$this->getAdapter(), $command['name']], $command['arguments']);
        }
    }

    /**
     * Execute the recorded commands in reverse.
     *
     * @return void
     */
    public function executeInvertedCommands()
    {
        $commands = $this->getInvertedCommands();
        foreach ($commands as $command) {
            call_user_func_array([$this->getAdapter(), $command['name']], $command['arguments']);
        }
    }

    /**
     * Returns the reverse of a createTable command.
     *
     * @param array $args Method Arguments
     * @return array
     */
    public function invertCreateTable($args)
    {
        return ['name' => 'dropTable', 'arguments' => [$args[0]]];
    }

    /**
     * Returns the reverse of a renameTable command.
     *
     * @param array $args Method Arguments
     * @return array
     */
    public function invertRenameTable($args)
    {
        return ['name' => 'renameTable', 'arguments' => [$args[1], $args[0]]];
    }

    /**
     * Returns the reverse of a addColumn command.
     *
     * @param array $args Method Arguments
     * @return array
     */
    public function invertAddColumn($args)
    {
        return ['name' => 'dropColumn', 'arguments' => [$args[0]->getName(), $args[1]->getName()]];
    }

    /**
     * Returns the reverse of a renameColumn command.
     *
     * @param array $args Method Arguments
     * @return array
     */
    public function invertRenameColumn($args)
    {
        return ['name' => 'renameColumn', 'arguments' => [$args[0], $args[2], $args[1]]];
    }

    /**
     * Returns the reverse of a addIndex command.
     *
     * @param array $args Method Arguments
     * @return array
     */
    public function invertAddIndex($args)
    {
        return ['name' => 'dropIndex', 'arguments' => [$args[0]->getName(), $args[1]->getColumns()]];
    }

    /**
     * Returns the reverse of a addForeignKey command.
     *
     * @param array $args Method Arguments
     * @return array
     */
    public function invertAddForeignKey($args)
    {
        return ['name' => 'dropForeignKey', 'arguments' => [$args[0]->getName(), $args[1]->getColumns()]];
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use BadMethodCallException;
use Phinx\Db\Table;
use Phinx\Migration\MigrationInterface;

/**
 * Phinx PDO Adapter.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
abstract class PdoAdapter extends AbstractAdapter
{
    /**
     * @var \PDO|null
     */
    protected $connection;

    /**
     * {@inheritdoc}
     */
    public function setOptions(array $options)
    {
        parent::setOptions($options);

        if (isset($options['connection'])) {
            $this->setConnection($options['connection']);
        }

        return $this;
    }

    /**
     * Sets the database connection.
     *
     * @param \PDO $connection Connection
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function setConnection(\PDO $connection)
    {
        $this->connection = $connection;

        // Create the schema table if it doesn't already exist
        if (!$this->hasSchemaTable()) {
            $this->createSchemaTable();
        } else {
            $table = new Table($this->getSchemaTableName(), [], $this);
            if (!$table->hasColumn('migration_name')) {
                $table
                    ->addColumn(
                        'migration_name',
                        'string',
                        ['limit' => 100, 'after' => 'version', 'default' => null, 'null' => true]
                    )
                    ->save();
            }
            if (!$table->hasColumn('breakpoint')) {
                $table
                    ->addColumn('breakpoint', 'boolean', ['default' => false])
                    ->save();
            }
        }

        return $this;
    }

    /**
     * Gets the database connection
     *
     * @return \PDO
     */
    public function getConnection()
    {
        if ($this->connection === null) {
            $this->connect();
        }

        return $this->connection;
    }

    /**
     * {@inheritdoc}
     */
    public function connect()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function disconnect()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function execute($sql)
    {
        if ($this->isDryRunEnabled()) {
            $this->getOutput()->writeln($sql);

            return 0;
        }

        return $this->getConnection()->exec($sql);
    }

    /**
     * Executes a query and returns PDOStatement.
     *
     * @param string $sql SQL
     * @return \PDOStatement
     */
    public function query($sql)
    {
        return $this->getConnection()->query($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function fetchRow($sql)
    {
        $result = $this->query($sql);

        return $result->fetch();
    }

    /**
     * {@inheritdoc}
     */
    public function fetchAll($sql)
    {
        $rows = [];
        $result = $this->query($sql);
        while ($row = $result->fetch()) {
            $rows[] = $row;
        }

        return $rows;
    }

    /**
     * {@inheritdoc}
     */
    public function insert(Table $table, $row)
    {
        $sql = sprintf(
            "INSERT INTO %s ",
            $this->quoteTableName($table->getName())
        );

        $columns = array_keys($row);
        $sql .= "(" . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ")";
        $sql .= " VALUES (" . implode(', ', array_fill(0, count($columns), '?')) . ")";

        $stmt = $this->getConnection()->prepare($sql);
        $stmt->execute(array_values($row));
    }

    /**
     * {@inheritdoc}
     */
    public function bulkinsert(Table $table, $rows)
    {
        $sql = sprintf(
            "INSERT INTO %s ",
            $this->quoteTableName($table->getName())
        );

        $current = current($rows);
        $keys = array_keys($current);
        $sql .= "(" . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ") VALUES";

        $vals = [];
        foreach ($rows as $row) {
            foreach ($row as $v) {
                $vals[] = $v;
            }
        }

        $count_keys = count($keys);
        $query = "(" . implode(', ', array_fill(0, $count_keys, '?')) . ")";

        $count_vars = count($rows);
        $queries = array_fill(0, $count_vars, $query);
        $sql .= implode(',', $queries);

        $stmt = $this->getConnection()->prepare($sql);
        $stmt->execute($vals);
    }

    /**
     * {@inheritdoc}
     */
    public function getVersions()
    {
        $rows = $this->getVersionLog();

        return array_keys($rows);
    }

    /**
     * {@inheritdoc}
     */
    public function getVersionLog()
    {
        $result = [];

        switch ($this->options['version_order']) {
            case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME:
                $orderBy = 'version ASC';
                break;
            case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME:
                $orderBy = 'start_time ASC, version ASC';
                break;
            default:
                throw new \RuntimeException('Invalid version_order configuration option');
        }

        $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->getSchemaTableName(), $orderBy));
        foreach ($rows as $version) {
            $result[$version['version']] = $version;
        }

        return $result;
    }

    /**
     * {@inheritdoc}
     */
    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
    {
        if (strcasecmp($direction, MigrationInterface::UP) === 0) {
            // up
            $sql = sprintf(
                "INSERT INTO %s (%s, %s, %s, %s, %s) VALUES ('%s', '%s', '%s', '%s', %s);",
                $this->getSchemaTableName(),
                $this->quoteColumnName('version'),
                $this->quoteColumnName('migration_name'),
                $this->quoteColumnName('start_time'),
                $this->quoteColumnName('end_time'),
                $this->quoteColumnName('breakpoint'),
                $migration->getVersion(),
                substr($migration->getName(), 0, 100),
                $startTime,
                $endTime,
                $this->castToBool(false)
            );

            $this->execute($sql);
        } else {
            // down
            $sql = sprintf(
                "DELETE FROM %s WHERE %s = '%s'",
                $this->getSchemaTableName(),
                $this->quoteColumnName('version'),
                $migration->getVersion()
            );

            $this->execute($sql);
        }

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function toggleBreakpoint(MigrationInterface $migration)
    {
        $this->query(
            sprintf(
                'UPDATE %1$s SET %2$s = CASE %2$s WHEN %3$s THEN %4$s ELSE %3$s END, %7$s = %7$s WHERE %5$s = \'%6$s\';',
                $this->getSchemaTableName(),
                $this->quoteColumnName('breakpoint'),
                $this->castToBool(true),
                $this->castToBool(false),
                $this->quoteColumnName('version'),
                $migration->getVersion(),
                $this->quoteColumnName('start_time')
            )
        );

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function resetAllBreakpoints()
    {
        return $this->execute(
            sprintf(
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %2$s <> %3$s;',
                $this->getSchemaTableName(),
                $this->quoteColumnName('breakpoint'),
                $this->castToBool(false),
                $this->quoteColumnName('start_time')
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function createSchema($schemaName = 'public')
    {
        throw new BadMethodCallException('Creating a schema is not supported');
    }

    /**
     * {@inheritdoc}
     */
    public function dropSchema($name)
    {
        throw new BadMethodCallException('Dropping a schema is not supported');
    }

    /**
     * {@inheritdoc}
     */
    public function getColumnTypes()
    {
        return [
            'string',
            'char',
            'text',
            'integer',
            'biginteger',
            'float',
            'decimal',
            'datetime',
            'timestamp',
            'time',
            'date',
            'blob',
            'binary',
            'varbinary',
            'boolean',
            'uuid',
            // Geospatial data types
            'geometry',
            'point',
            'linestring',
            'polygon',
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function castToBool($value)
    {
        return (bool)$value ? 1 : 0;
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2017 Cake Software Foundation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Wraps any adpter to record the time spend executing its commands
 */
class TimedOutputAdapter extends AdapterWrapper
{

    /**
     * {@inheritdoc}
     */
    public function getAdapterType()
    {
        return $this->getAdapter()->getAdapterType();
    }

    /**
     * Start timing a command.
     *
     * @return callable A function that is to be called when the command finishes
     */
    public function startCommandTimer()
    {
        $started = microtime(true);

        return function () use ($started) {
            $end = microtime(true);
            if (OutputInterface::VERBOSITY_VERBOSE <= $this->getOutput()->getVerbosity()) {
                $this->getOutput()->writeln('    -> ' . sprintf('%.4fs', $end - $started));
            }
        };
    }

    /**
     * Write a Phinx command to the output.
     *
     * @param string $command Command Name
     * @param array  $args    Command Args
     * @return void
     */
    public function writeCommand($command, $args = [])
    {
        if (OutputInterface::VERBOSITY_VERBOSE > $this->getOutput()->getVerbosity()) {
            return;
        }

        if (count($args)) {
            $outArr = [];
            foreach ($args as $arg) {
                if (is_array($arg)) {
                    $arg = array_map(
                        function ($value) {
                            return '\'' . $value . '\'';
                        },
                        $arg
                    );
                    $outArr[] = '[' . implode(', ', $arg) . ']';
                    continue;
                }

                $outArr[] = '\'' . $arg . '\'';
            }
            $this->getOutput()->writeln(' -- ' . $command . '(' . implode(', ', $outArr) . ')');

            return;
        }

        $this->getOutput()->writeln(' -- ' . $command);
    }

    /**
     * {@inheritdoc}
     *
     * @return void
     */
    public function insert(Table $table, $row)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('insert', [$table->getName()]);
        parent::insert($table, $row);
        $end();
    }

    /**
     * {@inheritdoc}
     *
     * @return void
     */
    public function bulkinsert(Table $table, $rows)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('bulkinsert', [$table->getName()]);
        parent::bulkinsert($table, $rows);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('createTable', [$table->getName()]);
        parent::createTable($table);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('renameTable', [$tableName, $newTableName]);
        parent::renameTable($tableName, $newTableName);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('dropTable', [$tableName]);
        parent::dropTable($tableName);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('truncateTable', [$tableName]);
        parent::truncateTable($tableName);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand(
            'addColumn',
            [
                $table->getName(),
                $column->getName(),
                $column->getType()
            ]
        );
        parent::addColumn($table, $column);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('renameColumn', [$tableName, $columnName, $newColumnName]);
        parent::renameColumn($tableName, $columnName, $newColumnName);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('changeColumn', [$tableName, $columnName, $newColumn->getType()]);
        parent::changeColumn($tableName, $columnName, $newColumn);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('dropColumn', [$tableName, $columnName]);
        parent::dropColumn($tableName, $columnName);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('addIndex', [$table->getName(), $index->getColumns()]);
        parent::addIndex($table, $index);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('dropIndex', [$tableName, $columns]);
        parent::dropIndex($tableName, $columns);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('dropIndexByName', [$tableName, $indexName]);
        parent::dropIndexByName($tableName, $indexName);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('addForeignKey', [$table->getName(), $foreignKey->getColumns()]);
        parent::addForeignKey($table, $foreignKey);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('dropForeignKey', [$tableName, $columns]);
        parent::dropForeignKey($tableName, $columns, $constraint);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options = [])
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('createDatabase', [$name]);
        parent::createDatabase($name, $options);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function dropDatabase($name)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('dropDatabase', [$name]);
        parent::dropDatabase($name);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function createSchema($name = 'public')
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('createSchema', [$name]);
        parent::createSchema($name);
        $end();
    }

    /**
     * {@inheritdoc}
     */
    public function dropSchema($name)
    {
        $end = $this->startCommandTimer();
        $this->writeCommand('dropSchema', [$name]);
        parent::dropSchema($name);
        $end();
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;

/**
 * Phinx MySQL Adapter.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
class MysqlAdapter extends PdoAdapter implements AdapterInterface
{

    protected $signedColumnTypes = ['integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true, 'boolean' => true];

    const TEXT_TINY = 255;
    const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */
    const TEXT_REGULAR = 65535;
    const TEXT_MEDIUM = 16777215;
    const TEXT_LONG = 4294967295;

    // According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT
    const BLOB_TINY = 255;
    const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */
    const BLOB_REGULAR = 65535;
    const BLOB_MEDIUM = 16777215;
    const BLOB_LONG = 4294967295;

    const INT_TINY = 255;
    const INT_SMALL = 65535;
    const INT_MEDIUM = 16777215;
    const INT_REGULAR = 4294967295;
    const INT_BIG = 18446744073709551615;

    const TYPE_YEAR = 'year';

    /**
     * {@inheritdoc}
     */
    public function connect()
    {
        if ($this->connection === null) {
            if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) {
                // @codeCoverageIgnoreStart
                throw new \RuntimeException('You need to enable the PDO_Mysql extension for Phinx to run properly.');
                // @codeCoverageIgnoreEnd
            }

            $db = null;
            $options = $this->getOptions();

            $dsn = 'mysql:';

            if (!empty($options['unix_socket'])) {
                // use socket connection
                $dsn .= 'unix_socket=' . $options['unix_socket'];
            } else {
                // use network connection
                $dsn .= 'host=' . $options['host'];
                if (!empty($options['port'])) {
                    $dsn .= ';port=' . $options['port'];
                }
            }

            $dsn .= ';dbname=' . $options['name'];

            // charset support
            if (!empty($options['charset'])) {
                $dsn .= ';charset=' . $options['charset'];
            }

            $driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION];

            // support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO
            // http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants
            foreach ($options as $key => $option) {
                if (strpos($key, 'mysql_attr_') === 0) {
                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
                }
            }

            try {
                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
            } catch (\PDOException $exception) {
                throw new \InvalidArgumentException(sprintf(
                    'There was a problem connecting to the database: %s',
                    $exception->getMessage()
                ));
            }

            $this->setConnection($db);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function disconnect()
    {
        $this->connection = null;
    }

    /**
     * {@inheritdoc}
     */
    public function hasTransactions()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function beginTransaction()
    {
        $this->execute('START TRANSACTION');
    }

    /**
     * {@inheritdoc}
     */
    public function commitTransaction()
    {
        $this->execute('COMMIT');
    }

    /**
     * {@inheritdoc}
     */
    public function rollbackTransaction()
    {
        $this->execute('ROLLBACK');
    }

    /**
     * {@inheritdoc}
     */
    public function quoteTableName($tableName)
    {
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
    }

    /**
     * {@inheritdoc}
     */
    public function quoteColumnName($columnName)
    {
        return '`' . str_replace('`', '``', $columnName) . '`';
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        $options = $this->getOptions();

        $exists = $this->fetchRow(sprintf(
            "SELECT TABLE_NAME
            FROM INFORMATION_SCHEMA.TABLES
            WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'",
            $options['name'],
            $tableName
        ));

        return !empty($exists);
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        // This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html
        $defaultOptions = [
            'engine' => 'InnoDB',
            'collation' => 'utf8_general_ci'
        ];

        $options = array_merge(
            $defaultOptions,
            array_intersect_key($this->getOptions(), $defaultOptions),
            $table->getOptions()
        );

        // Add the default primary key
        $columns = $table->getPendingColumns();
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
            $column = new Column();
            $column->setName('id')
                   ->setType('integer')
                   ->setSigned(isset($options['signed']) ? $options['signed'] : true)
                   ->setIdentity(true);

            array_unshift($columns, $column);
            $options['primary_key'] = 'id';
        } elseif (isset($options['id']) && is_string($options['id'])) {
            // Handle id => "field_name" to support AUTO_INCREMENT
            $column = new Column();
            $column->setName($options['id'])
                   ->setType('integer')
                   ->setIdentity(true);

            array_unshift($columns, $column);
            $options['primary_key'] = $options['id'];
        }

        // TODO - process table options like collation etc

        // process table engine (default to InnoDB)
        $optionsStr = 'ENGINE = InnoDB';
        if (isset($options['engine'])) {
            $optionsStr = sprintf('ENGINE = %s', $options['engine']);
        }

        // process table collation
        if (isset($options['collation'])) {
            $charset = explode('_', $options['collation']);
            $optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]);
            $optionsStr .= sprintf(' COLLATE %s', $options['collation']);
        }

        // set the table comment
        if (isset($options['comment'])) {
            $optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment']));
        }

        $sql = 'CREATE TABLE ';
        $sql .= $this->quoteTableName($table->getName()) . ' (';
        foreach ($columns as $column) {
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
        }

        // set the primary key(s)
        if (isset($options['primary_key'])) {
            $sql = rtrim($sql);
            $sql .= ' PRIMARY KEY (';
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
                $sql .= $this->quoteColumnName($options['primary_key']);
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
            }
            $sql .= ')';
        } else {
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
        }

        // set the indexes
        $indexes = $table->getIndexes();
        foreach ($indexes as $index) {
            $sql .= ', ' . $this->getIndexSqlDefinition($index);
        }

        // set the foreign keys
        $foreignKeys = $table->getForeignKeys();
        foreach ($foreignKeys as $foreignKey) {
            $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey);
        }

        $sql .= ') ' . $optionsStr;
        $sql = rtrim($sql) . ';';

        // execute the sql
        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $this->execute(sprintf('RENAME TABLE %s TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $sql = sprintf(
            'TRUNCATE TABLE %s',
            $this->quoteTableName($tableName)
        );

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function getColumns($tableName)
    {
        $columns = [];
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
        foreach ($rows as $columnInfo) {
            $phinxType = $this->getPhinxType($columnInfo['Type']);

            $column = new Column();
            $column->setName($columnInfo['Field'])
                   ->setNull($columnInfo['Null'] !== 'NO')
                   ->setDefault($columnInfo['Default'])
                   ->setType($phinxType['name'])
                   ->setLimit($phinxType['limit']);

            if ($columnInfo['Extra'] === 'auto_increment') {
                $column->setIdentity(true);
            }

            if (isset($phinxType['values'])) {
                $column->setValues($phinxType['values']);
            }

            $columns[] = $column;
        }

        return $columns;
    }

    /**
     * {@inheritdoc}
     */
    public function hasColumn($tableName, $columnName)
    {
        $rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName)));
        foreach ($rows as $column) {
            if (strcasecmp($column['Field'], $columnName) === 0) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get the defintion for a `DEFAULT` statement.
     *
     * @param  mixed $default
     * @return string
     */
    protected function getDefaultValueDefinition($default)
    {
        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
            $default = $this->getConnection()->quote($default);
        } elseif (is_bool($default)) {
            $default = $this->castToBool($default);
        }

        return isset($default) ? ' DEFAULT ' . $default : '';
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $sql = sprintf(
            'ALTER TABLE %s ADD %s %s',
            $this->quoteTableName($table->getName()),
            $this->quoteColumnName($column->getName()),
            $this->getColumnSqlDefinition($column)
        );

        if ($column->getAfter()) {
            $sql .= ' AFTER ' . $this->quoteColumnName($column->getAfter());
        }

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        $rows = $this->fetchAll(sprintf('DESCRIBE %s', $this->quoteTableName($tableName)));
        foreach ($rows as $row) {
            if (strcasecmp($row['Field'], $columnName) === 0) {
                $null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL';
                $extra = ' ' . strtoupper($row['Extra']);
                if (!is_null($row['Default'])) {
                    $extra .= $this->getDefaultValueDefinition($row['Default']);
                }
                $definition = $row['Type'] . ' ' . $null . $extra;

                $this->execute(
                    sprintf(
                        'ALTER TABLE %s CHANGE COLUMN %s %s %s',
                        $this->quoteTableName($tableName),
                        $this->quoteColumnName($columnName),
                        $this->quoteColumnName($newColumnName),
                        $definition
                    )
                );

                return;
            }
        }

        throw new \InvalidArgumentException(sprintf(
            'The specified column doesn\'t exist: ' .
            $columnName
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : '';
        $this->execute(
            sprintf(
                'ALTER TABLE %s CHANGE %s %s %s%s',
                $this->quoteTableName($tableName),
                $this->quoteColumnName($columnName),
                $this->quoteColumnName($newColumn->getName()),
                $this->getColumnSqlDefinition($newColumn),
                $after
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        $this->execute(
            sprintf(
                'ALTER TABLE %s DROP COLUMN %s',
                $this->quoteTableName($tableName),
                $this->quoteColumnName($columnName)
            )
        );
    }

    /**
     * Get an array of indexes from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    protected function getIndexes($tableName)
    {
        $indexes = [];
        $rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName)));
        foreach ($rows as $row) {
            if (!isset($indexes[$row['Key_name']])) {
                $indexes[$row['Key_name']] = ['columns' => []];
            }
            $indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']);
        }

        return $indexes;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $columns = array_map('strtolower', $columns);
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $index) {
            if ($columns == $index['columns']) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndexByName($tableName, $indexName)
    {
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $name => $index) {
            if ($name === $indexName) {
                 return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $this->execute(
            sprintf(
                'ALTER TABLE %s ADD %s',
                $this->quoteTableName($table->getName()),
                $this->getIndexSqlDefinition($index)
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $indexes = $this->getIndexes($tableName);
        $columns = array_map('strtolower', $columns);

        foreach ($indexes as $indexName => $index) {
            if ($columns == $index['columns']) {
                $this->execute(
                    sprintf(
                        'ALTER TABLE %s DROP INDEX %s',
                        $this->quoteTableName($tableName),
                        $this->quoteColumnName($indexName)
                    )
                );

                return;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $name => $index) {
            //$a = array_diff($columns, $index['columns']);
            if ($name === $indexName) {
                $this->execute(
                    sprintf(
                        'ALTER TABLE %s DROP INDEX %s',
                        $this->quoteTableName($tableName),
                        $this->quoteColumnName($indexName)
                    )
                );

                return;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasForeignKey($tableName, $columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }
        $foreignKeys = $this->getForeignKeys($tableName);
        if ($constraint) {
            if (isset($foreignKeys[$constraint])) {
                return !empty($foreignKeys[$constraint]);
            }

            return false;
        } else {
            foreach ($foreignKeys as $key) {
                if ($columns == $key['columns']) {
                    return true;
                }
            }

            return false;
        }
    }

    /**
     * Get an array of foreign keys from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    protected function getForeignKeys($tableName)
    {
        $foreignKeys = [];
        $rows = $this->fetchAll(sprintf(
            "SELECT
              CONSTRAINT_NAME,
              TABLE_NAME,
              COLUMN_NAME,
              REFERENCED_TABLE_NAME,
              REFERENCED_COLUMN_NAME
            FROM information_schema.KEY_COLUMN_USAGE
            WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
              AND REFERENCED_TABLE_NAME IS NOT NULL
              AND TABLE_NAME = '%s'
            ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
            $tableName
        ));
        foreach ($rows as $row) {
            $foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME'];
            $foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME'];
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME'];
            $foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
        }

        return $foreignKeys;
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        $this->execute(
            sprintf(
                'ALTER TABLE %s ADD %s',
                $this->quoteTableName($table->getName()),
                $this->getForeignKeySqlDefinition($foreignKey)
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        if ($constraint) {
            $this->execute(
                sprintf(
                    'ALTER TABLE %s DROP FOREIGN KEY %s',
                    $this->quoteTableName($tableName),
                    $constraint
                )
            );

            return;
        } else {
            foreach ($columns as $column) {
                $rows = $this->fetchAll(sprintf(
                    "SELECT
                        CONSTRAINT_NAME
                      FROM information_schema.KEY_COLUMN_USAGE
                      WHERE REFERENCED_TABLE_SCHEMA = DATABASE()
                        AND REFERENCED_TABLE_NAME IS NOT NULL
                        AND TABLE_NAME = '%s'
                        AND COLUMN_NAME = '%s'
                      ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
                    $tableName,
                    $column
                ));
                foreach ($rows as $row) {
                    $this->dropForeignKey($tableName, $columns, $row['CONSTRAINT_NAME']);
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getSqlType($type, $limit = null)
    {
        switch ($type) {
            case static::PHINX_TYPE_STRING:
                return ['name' => 'varchar', 'limit' => $limit ?: 255];
            case static::PHINX_TYPE_CHAR:
                return ['name' => 'char', 'limit' => $limit ?: 255];
            case static::PHINX_TYPE_TEXT:
                if ($limit) {
                    $sizes = [
                        // Order matters! Size must always be tested from longest to shortest!
                        'longtext' => static::TEXT_LONG,
                        'mediumtext' => static::TEXT_MEDIUM,
                        'text' => static::TEXT_REGULAR,
                        'tinytext' => static::TEXT_SMALL,
                    ];
                    foreach ($sizes as $name => $length) {
                        if ($limit >= $length) {
                            return ['name' => $name];
                        }
                    }
                }

                return ['name' => 'text'];
            case static::PHINX_TYPE_BINARY:
                return ['name' => 'binary', 'limit' => $limit ?: 255];
            case static::PHINX_TYPE_VARBINARY:
                return ['name' => 'varbinary', 'limit' => $limit ?: 255];
            case static::PHINX_TYPE_BLOB:
                if ($limit) {
                    $sizes = [
                        // Order matters! Size must always be tested from longest to shortest!
                        'longblob' => static::BLOB_LONG,
                        'mediumblob' => static::BLOB_MEDIUM,
                        'blob' => static::BLOB_REGULAR,
                        'tinyblob' => static::BLOB_SMALL,
                    ];
                    foreach ($sizes as $name => $length) {
                        if ($limit >= $length) {
                            return ['name' => $name];
                        }
                    }
                }

                return ['name' => 'blob'];
            case static::PHINX_TYPE_INTEGER:
                if ($limit && $limit >= static::INT_TINY) {
                    $sizes = [
                        // Order matters! Size must always be tested from longest to shortest!
                        'bigint' => static::INT_BIG,
                        'int' => static::INT_REGULAR,
                        'mediumint' => static::INT_MEDIUM,
                        'smallint' => static::INT_SMALL,
                        'tinyint' => static::INT_TINY,
                    ];
                    $limits = [
                        'int' => 11,
                        'bigint' => 20,
                    ];
                    foreach ($sizes as $name => $length) {
                        if ($limit >= $length) {
                            $def = ['name' => $name];
                            if (isset($limits[$name])) {
                                $def['limit'] = $limits[$name];
                            }

                            return $def;
                        }
                    }
                } elseif (!$limit) {
                    $limit = 11;
                }

                return ['name' => 'int', 'limit' => $limit];
            case static::PHINX_TYPE_BIG_INTEGER:
                return ['name' => 'bigint', 'limit' => 20];
            case static::PHINX_TYPE_FLOAT:
                return ['name' => 'float'];
            case static::PHINX_TYPE_DECIMAL:
                return ['name' => 'decimal'];
            case static::PHINX_TYPE_DATETIME:
                return ['name' => 'datetime'];
            case static::PHINX_TYPE_TIMESTAMP:
                return ['name' => 'timestamp'];
            case static::PHINX_TYPE_TIME:
                return ['name' => 'time'];
            case static::PHINX_TYPE_DATE:
                return ['name' => 'date'];
            case static::PHINX_TYPE_BOOLEAN:
                return ['name' => 'tinyint', 'limit' => 1];
            case static::PHINX_TYPE_UUID:
                return ['name' => 'char', 'limit' => 36];
            // Geospatial database types
            case static::PHINX_TYPE_GEOMETRY:
            case static::PHINX_TYPE_POINT:
            case static::PHINX_TYPE_LINESTRING:
            case static::PHINX_TYPE_POLYGON:
                return ['name' => $type];
            case static::PHINX_TYPE_ENUM:
                return ['name' => 'enum'];
            case static::PHINX_TYPE_SET:
                return ['name' => 'set'];
            case static::TYPE_YEAR:
                if (!$limit || in_array($limit, [2, 4])) {
                    $limit = 4;
                }

                return ['name' => 'year', 'limit' => $limit];
            case static::PHINX_TYPE_JSON:
                return ['name' => 'json'];
            default:
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
        }
    }

    /**
     * Returns Phinx type by SQL type
     *
     * @param string $sqlTypeDef
     * @throws \RuntimeException
     * @internal param string $sqlType SQL type
     * @returns string Phinx type
     */
    public function getPhinxType($sqlTypeDef)
    {
        $matches = [];
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) {
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
        } else {
            $limit = null;
            $precision = null;
            $type = $matches[1];
            if (count($matches) > 2) {
                $limit = $matches[3] ? (int)$matches[3] : null;
            }
            if (count($matches) > 4) {
                $precision = (int)$matches[5];
            }
            if ($type === 'tinyint' && $limit === 1) {
                $type = static::PHINX_TYPE_BOOLEAN;
                $limit = null;
            }
            switch ($type) {
                case 'varchar':
                    $type = static::PHINX_TYPE_STRING;
                    if ($limit === 255) {
                        $limit = null;
                    }
                    break;
                case 'char':
                    $type = static::PHINX_TYPE_CHAR;
                    if ($limit === 255) {
                        $limit = null;
                    }
                    if ($limit === 36) {
                        $type = static::PHINX_TYPE_UUID;
                    }
                    break;
                case 'tinyint':
                    $type = static::PHINX_TYPE_INTEGER;
                    $limit = static::INT_TINY;
                    break;
                case 'smallint':
                    $type = static::PHINX_TYPE_INTEGER;
                    $limit = static::INT_SMALL;
                    break;
                case 'mediumint':
                    $type = static::PHINX_TYPE_INTEGER;
                    $limit = static::INT_MEDIUM;
                    break;
                case 'int':
                    $type = static::PHINX_TYPE_INTEGER;
                    if ($limit === 11) {
                        $limit = null;
                    }
                    break;
                case 'bigint':
                    if ($limit === 20) {
                        $limit = null;
                    }
                    $type = static::PHINX_TYPE_BIG_INTEGER;
                    break;
                case 'blob':
                    $type = static::PHINX_TYPE_BINARY;
                    break;
                case 'tinyblob':
                    $type = static::PHINX_TYPE_BINARY;
                    $limit = static::BLOB_TINY;
                    break;
                case 'mediumblob':
                    $type = static::PHINX_TYPE_BINARY;
                    $limit = static::BLOB_MEDIUM;
                    break;
                case 'longblob':
                    $type = static::PHINX_TYPE_BINARY;
                    $limit = static::BLOB_LONG;
                    break;
                case 'tinytext':
                    $type = static::PHINX_TYPE_TEXT;
                    $limit = static::TEXT_TINY;
                    break;
                case 'mediumtext':
                    $type = static::PHINX_TYPE_TEXT;
                    $limit = static::TEXT_MEDIUM;
                    break;
                case 'longtext':
                    $type = static::PHINX_TYPE_TEXT;
                    $limit = static::TEXT_LONG;
                    break;
            }

            // Call this to check if parsed type is supported.
            $this->getSqlType($type, $limit);

            $phinxType = [
                'name' => $type,
                'limit' => $limit,
                'precision' => $precision
            ];

            if (static::PHINX_TYPE_ENUM == $type) {
                $phinxType['values'] = explode("','", trim($matches[6], "()'"));
            }

            return $phinxType;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options = [])
    {
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';

        if (isset($options['collation'])) {
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation']));
        } else {
            $this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasDatabase($name)
    {
        $rows = $this->fetchAll(
            sprintf(
                'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'',
                $name
            )
        );

        foreach ($rows as $row) {
            if (!empty($row)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function dropDatabase($name)
    {
        $this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name));
    }

    /**
     * Gets the MySQL Column Definition for a Column object.
     *
     * @param \Phinx\Db\Table\Column $column Column
     * @return string
     */
    protected function getColumnSqlDefinition(Column $column)
    {
        $sqlType = $this->getSqlType($column->getType(), $column->getLimit());

        $def = '';
        $def .= strtoupper($sqlType['name']);
        if ($column->getPrecision() && $column->getScale()) {
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
        } elseif (isset($sqlType['limit'])) {
            $def .= '(' . $sqlType['limit'] . ')';
        }
        if (($values = $column->getValues()) && is_array($values)) {
            $def .= "('" . implode("', '", $values) . "')";
        }
        $def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : '';
        $def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : '';
        $def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : '';
        $def .= ($column->isNull() == false) ? ' NOT NULL' : ' NULL';
        $def .= ($column->isIdentity()) ? ' AUTO_INCREMENT' : '';
        $def .= $this->getDefaultValueDefinition($column->getDefault());

        if ($column->getComment()) {
            $def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment());
        }

        if ($column->getUpdate()) {
            $def .= ' ON UPDATE ' . $column->getUpdate();
        }

        return $def;
    }

    /**
     * Gets the MySQL Index Definition for an Index object.
     *
     * @param \Phinx\Db\Table\Index $index Index
     * @return string
     */
    protected function getIndexSqlDefinition(Index $index)
    {
        $def = '';
        $limit = '';
        if ($index->getLimit()) {
            $limit = '(' . $index->getLimit() . ')';
        }

        if ($index->getType() == Index::UNIQUE) {
            $def .= ' UNIQUE';
        }

        if ($index->getType() == Index::FULLTEXT) {
            $def .= ' FULLTEXT';
        }

        $def .= ' KEY';

        if (is_string($index->getName())) {
            $def .= ' `' . $index->getName() . '`';
        }

        $def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')';

        return $def;
    }

    /**
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
     *
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
     * @return string
     */
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey)
    {
        $def = '';
        if ($foreignKey->getConstraint()) {
            $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint());
        }
        $columnNames = [];
        foreach ($foreignKey->getColumns() as $column) {
            $columnNames[] = $this->quoteColumnName($column);
        }
        $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
        $refColumnNames = [];
        foreach ($foreignKey->getReferencedColumns() as $column) {
            $refColumnNames[] = $this->quoteColumnName($column);
        }
        $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
        if ($foreignKey->getOnDelete()) {
            $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
        }
        if ($foreignKey->getOnUpdate()) {
            $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
        }

        return $def;
    }

    /**
     * Describes a database table. This is a MySQL adapter specific method.
     *
     * @param string $tableName Table name
     * @return array
     */
    public function describeTable($tableName)
    {
        $options = $this->getOptions();

        // mysql specific
        $sql = sprintf(
            "SELECT *
             FROM information_schema.tables
             WHERE table_schema = '%s'
             AND table_name = '%s'",
            $options['name'],
            $tableName
        );

        return $this->fetchRow($sql);
    }

    /**
     * Returns MySQL column types (inherited and MySQL specified).
     * @return array
     */
    public function getColumnTypes()
    {
        return array_merge(parent::getColumnTypes(), ['enum', 'set', 'year', 'json']);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Migration
 */
namespace Phinx\Db\Adapter;

/**
 * Adapter factory and registry.
 *
 * Used for registering adapters and creating instances of adapters.
 *
 * @author Woody Gilk <woody.gilk@gmail.com>
 */
class AdapterFactory
{
    /**
     * @var \Phinx\Db\Adapter\AdapterFactory
     */
    protected static $instance;

    /**
     * Get the factory singleton instance.
     *
     * @return \Phinx\Db\Adapter\AdapterFactory
     */
    public static function instance()
    {
        if (!static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    /**
     * Class map of database adapters, indexed by PDO::ATTR_DRIVER_NAME.
     *
     * @var array
     */
    protected $adapters = [
        'mysql' => 'Phinx\Db\Adapter\MysqlAdapter',
        'pgsql' => 'Phinx\Db\Adapter\PostgresAdapter',
        'sqlite' => 'Phinx\Db\Adapter\SQLiteAdapter',
        'sqlsrv' => 'Phinx\Db\Adapter\SqlServerAdapter',
    ];

    /**
     * Class map of adapters wrappers, indexed by name.
     *
     * @var array
     */
    protected $wrappers = [
        'prefix' => 'Phinx\Db\Adapter\TablePrefixAdapter',
        'proxy' => 'Phinx\Db\Adapter\ProxyAdapter',
        'timed' => 'Phinx\Db\Adapter\TimedOutputAdapter',
    ];

    /**
     * Add or replace an adapter with a fully qualified class name.
     *
     * @throws \RuntimeException
     * @param  string $name
     * @param  string $class
     * @return $this
     */
    public function registerAdapter($name, $class)
    {
        if (!is_subclass_of($class, 'Phinx\Db\Adapter\AdapterInterface')) {
            throw new \RuntimeException(sprintf(
                'Adapter class "%s" must implement Phinx\\Db\\Adapter\\AdapterInterface',
                $class
            ));
        }
        $this->adapters[$name] = $class;

        return $this;
    }

    /**
     * Get an adapter class by name.
     *
     * @throws \RuntimeException
     * @param  string $name
     * @return string
     */
    protected function getClass($name)
    {
        if (empty($this->adapters[$name])) {
            throw new \RuntimeException(sprintf(
                'Adapter "%s" has not been registered',
                $name
            ));
        }

        return $this->adapters[$name];
    }

    /**
     * Get an adapter instance by name.
     *
     * @param  string $name
     * @param  array  $options
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getAdapter($name, array $options)
    {
        $class = $this->getClass($name);

        return new $class($options);
    }

    /**
     * Add or replace a wrapper with a fully qualified class name.
     *
     * @throws \RuntimeException
     * @param  string $name
     * @param  string $class
     * @return $this
     */
    public function registerWrapper($name, $class)
    {
        if (!is_subclass_of($class, 'Phinx\Db\Adapter\WrapperInterface')) {
            throw new \RuntimeException(sprintf(
                'Wrapper class "%s" must be implement Phinx\\Db\\Adapter\\WrapperInterface',
                $class
            ));
        }
        $this->wrappers[$name] = $class;

        return $this;
    }

    /**
     * Get a wrapper class by name.
     *
     * @throws \RuntimeException
     * @param  string $name
     * @return string
     */
    protected function getWrapperClass($name)
    {
        if (empty($this->wrappers[$name])) {
            throw new \RuntimeException(sprintf(
                'Wrapper "%s" has not been registered',
                $name
            ));
        }

        return $this->wrappers[$name];
    }

    /**
     * Get a wrapper instance by name.
     *
     * @param  string $name
     * @param  \Phinx\Db\Adapter\AdapterInterface $adapter
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getWrapper($name, AdapterInterface $adapter)
    {
        $class = $this->getWrapperClass($name);

        return new $class($adapter);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;

/**
 * Table prefix/suffix adapter.
 *
 * Used for inserting a prefix or suffix into table names.
 *
 * @author Samuel Fisher <sam@sfisher.co>
 */
class TablePrefixAdapter extends AdapterWrapper
{
    /**
     * {@inheritdoc}
     */
    public function getAdapterType()
    {
        return 'TablePrefixAdapter';
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);

        return parent::hasTable($adapterTableName);
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        $adapterTable = clone $table;
        $adapterTableName = $this->getAdapterTableName($table->getName());
        $adapterTable->setName($adapterTableName);

        foreach ($adapterTable->getForeignKeys() as $fk) {
            $adapterReferenceTable = $fk->getReferencedTable();
            $adapterReferenceTableName = $this->getAdapterTableName($adapterReferenceTable->getName());
            $adapterReferenceTable->setName($adapterReferenceTableName);
        }

        parent::createTable($adapterTable);
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        $adapterNewTableName = $this->getAdapterTableName($newTableName);
        parent::renameTable($adapterTableName, $adapterNewTableName);
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        parent::dropTable($adapterTableName);
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        parent::truncateTable($adapterTableName);
    }

    /**
     * {@inheritdoc}
     */
    public function getColumns($tableName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);

        return parent::getColumns($adapterTableName);
    }

    /**
     * {@inheritdoc}
     */
    public function hasColumn($tableName, $columnName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);

        return parent::hasColumn($adapterTableName, $columnName);
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $adapterTable = clone $table;
        $adapterTableName = $this->getAdapterTableName($table->getName());
        $adapterTable->setName($adapterTableName);
        parent::addColumn($adapterTable, $column);
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        parent::renameColumn($adapterTableName, $columnName, $newColumnName);
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);

        return parent::changeColumn($adapterTableName, $columnName, $newColumn);
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        parent::dropColumn($adapterTableName, $columnName);
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndex($tableName, $columns)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);

        return parent::hasIndex($adapterTableName, $columns);
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndexByName($tableName, $indexName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);

        return parent::hasIndexByName($adapterTableName, $indexName);
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $adapterTable = clone $table;
        $adapterTableName = $this->getAdapterTableName($table->getName());
        $adapterTable->setName($adapterTableName);
        parent::addIndex($adapterTable, $index);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        parent::dropIndex($adapterTableName, $columns);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        parent::dropIndexByName($adapterTableName, $indexName);
    }

    /**
     * {@inheritdoc}
     */
    public function hasForeignKey($tableName, $columns, $constraint = null)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);

        return parent::hasForeignKey($adapterTableName, $columns, $constraint);
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        $adapterTable = clone $table;
        $adapterTableName = $this->getAdapterTableName($table->getName());
        $adapterTable->setName($adapterTableName);
        parent::addForeignKey($adapterTable, $foreignKey);
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        $adapterTableName = $this->getAdapterTableName($tableName);
        parent::dropForeignKey($adapterTableName, $columns, $constraint);
    }

    /**
     * {@inheritdoc}
     */
    public function insert(Table $table, $row)
    {
        $adapterTable = clone $table;
        $adapterTableName = $this->getAdapterTableName($table->getName());
        $adapterTable->setName($adapterTableName);
        parent::insert($adapterTable, $row);
    }

    /**
     * {@inheritdoc}
     */
    public function bulkinsert(Table $table, $rows)
    {
        $adapterTable = clone $table;
        $adapterTableName = $this->getAdapterTableName($table->getName());
        $adapterTable->setName($adapterTableName);
        parent::bulkinsert($adapterTable, $rows);
    }

    /**
     * Gets the table prefix.
     *
     * @return string
     */
    public function getPrefix()
    {
        return (string)$this->getOption('table_prefix');
    }

    /**
     * Gets the table suffix.
     *
     * @return string
     */
    public function getSuffix()
    {
        return (string)$this->getOption('table_suffix');
    }

    /**
     * Applies the prefix and suffix to the table name.
     *
     * @param string $tableName
     * @return string
     */
    public function getAdapterTableName($tableName)
    {
        return $this->getPrefix() . $tableName . $this->getSuffix();
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;
use Phinx\Migration\MigrationInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Adapter Interface.
 *
 * @author Rob Morgan <robbym@gmail.com>
 * @method \PDO getConnection()
 */
interface AdapterInterface
{
    const PHINX_TYPE_STRING = 'string';
    const PHINX_TYPE_CHAR = 'char';
    const PHINX_TYPE_TEXT = 'text';
    const PHINX_TYPE_INTEGER = 'integer';
    const PHINX_TYPE_BIG_INTEGER = 'biginteger';
    const PHINX_TYPE_FLOAT = 'float';
    const PHINX_TYPE_DECIMAL = 'decimal';
    const PHINX_TYPE_DATETIME = 'datetime';
    const PHINX_TYPE_TIMESTAMP = 'timestamp';
    const PHINX_TYPE_TIME = 'time';
    const PHINX_TYPE_DATE = 'date';
    const PHINX_TYPE_BINARY = 'binary';
    const PHINX_TYPE_VARBINARY = 'varbinary';
    const PHINX_TYPE_BLOB = 'blob';
    const PHINX_TYPE_BOOLEAN = 'boolean';
    const PHINX_TYPE_JSON = 'json';
    const PHINX_TYPE_JSONB = 'jsonb';
    const PHINX_TYPE_UUID = 'uuid';
    const PHINX_TYPE_FILESTREAM = 'filestream';

    // Geospatial database types
    const PHINX_TYPE_GEOMETRY = 'geometry';
    const PHINX_TYPE_POINT = 'point';
    const PHINX_TYPE_LINESTRING = 'linestring';
    const PHINX_TYPE_POLYGON = 'polygon';

    // only for mysql so far
    const PHINX_TYPE_ENUM = 'enum';
    const PHINX_TYPE_SET = 'set';

    // only for postgresql so far
    const PHINX_TYPE_CIDR = 'cidr';
    const PHINX_TYPE_INET = 'inet';
    const PHINX_TYPE_MACADDR = 'macaddr';
    const PHINX_TYPE_INTERVAL = 'interval';

    /**
     * Get all migrated version numbers.
     *
     * @return array
     */
    public function getVersions();

    /**
     * Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's
     * version order option
     *
     * @return array
     */
    public function getVersionLog();

    /**
     * Set adapter configuration options.
     *
     * @param  array $options
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function setOptions(array $options);

    /**
     * Get all adapter options.
     *
     * @return array
     */
    public function getOptions();

    /**
     * Check if an option has been set.
     *
     * @param  string $name
     * @return bool
     */
    public function hasOption($name);

    /**
     * Get a single adapter option, or null if the option does not exist.
     *
     * @param  string $name
     * @return mixed
     */
    public function getOption($name);

    /**
     * Sets the console input.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input Input
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function setInput(InputInterface $input);

    /**
     * Gets the console input.
     *
     * @return \Symfony\Component\Console\Input\InputInterface
     */
    public function getInput();

    /**
     * Sets the console output.
     *
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function setOutput(OutputInterface $output);

    /**
     * Gets the console output.
     *
     * @return \Symfony\Component\Console\Output\OutputInterface
     */
    public function getOutput();

    /**
     * Records a migration being run.
     *
     * @param \Phinx\Migration\MigrationInterface $migration Migration
     * @param string $direction Direction
     * @param int $startTime Start Time
     * @param int $endTime End Time
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime);

    /**
     * Toggle a migration breakpoint.
     *
     * @param \Phinx\Migration\MigrationInterface $migration
     *
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function toggleBreakpoint(MigrationInterface $migration);

    /**
     * Reset all migration breakpoints.
     *
     * @return int The number of breakpoints reset
     */
    public function resetAllBreakpoints();

    /**
     * Does the schema table exist?
     *
     * @deprecated use hasTable instead.
     * @return bool
     */
    public function hasSchemaTable();

    /**
     * Creates the schema table.
     *
     * @return void
     */
    public function createSchemaTable();

    /**
     * Returns the adapter type.
     *
     * @return string
     */
    public function getAdapterType();

    /**
     * Initializes the database connection.
     *
     * @throws \RuntimeException When the requested database driver is not installed.
     * @return void
     */
    public function connect();

    /**
     * Closes the database connection.
     *
     * @return void
     */
    public function disconnect();

    /**
     * Does the adapter support transactions?
     *
     * @return bool
     */
    public function hasTransactions();

    /**
     * Begin a transaction.
     *
     * @return void
     */
    public function beginTransaction();

    /**
     * Commit a transaction.
     *
     * @return void
     */
    public function commitTransaction();

    /**
     * Rollback a transaction.
     *
     * @return void
     */
    public function rollbackTransaction();

    /**
     * Executes a SQL statement and returns the number of affected rows.
     *
     * @param string $sql SQL
     * @return int
     */
    public function execute($sql);

    /**
     * Executes a SQL statement and returns the result as an array.
     *
     * @param string $sql SQL
     * @return mixed
     */
    public function query($sql);

    /**
     * Executes a query and returns only one row as an array.
     *
     * @param string $sql SQL
     * @return array
     */
    public function fetchRow($sql);

    /**
     * Executes a query and returns an array of rows.
     *
     * @param string $sql SQL
     * @return array
     */
    public function fetchAll($sql);

    /**
     * Inserts data into a table.
     *
     * @param \Phinx\Db\Table $table where to insert data
     * @param array $row
     * @return void
     */
    public function insert(Table $table, $row);

    /**
     * Inserts data into a table in a bulk.
     *
     * @param \Phinx\Db\Table $table where to insert data
     * @param array $rows
     * @return void
     */
    public function bulkinsert(Table $table, $rows);

    /**
     * Quotes a table name for use in a query.
     *
     * @param string $tableName Table Name
     * @return string
     */
    public function quoteTableName($tableName);

    /**
     * Quotes a column name for use in a query.
     *
     * @param string $columnName Table Name
     * @return string
     */
    public function quoteColumnName($columnName);

    /**
     * Checks to see if a table exists.
     *
     * @param string $tableName Table Name
     * @return bool
     */
    public function hasTable($tableName);

    /**
     * Creates the specified database table.
     *
     * @param \Phinx\Db\Table $table Table
     * @return void
     */
    public function createTable(Table $table);

    /**
     * Renames the specified database table.
     *
     * @param string $tableName Table Name
     * @param string $newName   New Name
     * @return void
     */
    public function renameTable($tableName, $newName);

    /**
     * Drops the specified database table.
     *
     * @param string $tableName Table Name
     * @return void
     */
    public function dropTable($tableName);

    /**
     * Truncates the specified table
     *
     * @param string $tableName
     * @return void
     */
    public function truncateTable($tableName);

    /**
     * Returns table columns
     *
     * @param string $tableName Table Name
     * @return \Phinx\Db\Table\Column[]
     */
    public function getColumns($tableName);

    /**
     * Checks to see if a column exists.
     *
     * @param string $tableName  Table Name
     * @param string $columnName Column Name
     * @return bool
     */
    public function hasColumn($tableName, $columnName);

    /**
     * Adds the specified column to a database table.
     *
     * @param \Phinx\Db\Table  $table  Table
     * @param \Phinx\Db\Table\Column $column Column
     * @return void
     */
    public function addColumn(Table $table, Column $column);

    /**
     * Renames the specified column.
     *
     * @param string $tableName Table Name
     * @param string $columnName Column Name
     * @param string $newColumnName New Column Name
     * @return void
     */
    public function renameColumn($tableName, $columnName, $newColumnName);

    /**
     * Change a table column type.
     *
     * @param string $tableName  Table Name
     * @param string $columnName Column Name
     * @param \Phinx\Db\Table\Column $newColumn  New Column
     * @return \Phinx\Db\Table
     */
    public function changeColumn($tableName, $columnName, Column $newColumn);

    /**
     * Drops the specified column.
     *
     * @param string $tableName Table Name
     * @param string $columnName Column Name
     * @return void
     */
    public function dropColumn($tableName, $columnName);

    /**
     * Checks to see if an index exists.
     *
     * @param string $tableName Table Name
     * @param mixed  $columns   Column(s)
     * @return bool
     */
    public function hasIndex($tableName, $columns);

    /**
     * Checks to see if an index specified by name exists.
     *
     * @param string $tableName Table Name
     * @param string $indexName
     * @return bool
     */
    public function hasIndexByName($tableName, $indexName);

    /**
     * Adds the specified index to a database table.
     *
     * @param \Phinx\Db\Table $table Table
     * @param \Phinx\Db\Table\Index $index Index
     * @return void
     */
    public function addIndex(Table $table, Index $index);

    /**
     * Drops the specified index from a database table.
     *
     * @param string $tableName
     * @param mixed  $columns Column(s)
     * @return void
     */
    public function dropIndex($tableName, $columns);

    /**
     * Drops the index specified by name from a database table.
     *
     * @param string $tableName
     * @param string $indexName
     * @return void
     */
    public function dropIndexByName($tableName, $indexName);

    /**
     * Checks to see if a foreign key exists.
     *
     * @param string   $tableName
     * @param string[] $columns    Column(s)
     * @param string   $constraint Constraint name
     * @return bool
     */
    public function hasForeignKey($tableName, $columns, $constraint = null);

    /**
     * Adds the specified foreign key to a database table.
     *
     * @param \Phinx\Db\Table      $table
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
     * @return void
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey);

    /**
     * Drops the specified foreign key from a database table.
     *
     * @param string   $tableName
     * @param string[] $columns    Column(s)
     * @param string   $constraint Constraint name
     * @return void
     */
    public function dropForeignKey($tableName, $columns, $constraint = null);

    /**
     * Returns an array of the supported Phinx column types.
     *
     * @return array
     */
    public function getColumnTypes();

    /**
     * Checks that the given column is of a supported type.
     *
     * @param  \Phinx\Db\Table\Column $column
     * @return bool
     */
    public function isValidColumnType(Column $column);

    /**
     * Converts the Phinx logical type to the adapter's SQL type.
     *
     * @param string $type
     * @param int $limit
     * @return string[]
     */
    public function getSqlType($type, $limit = null);

    /**
     * Creates a new database.
     *
     * @param string $name Database Name
     * @param array $options Options
     * @return void
     */
    public function createDatabase($name, $options = []);

    /**
     * Checks to see if a database exists.
     *
     * @param string $name Database Name
     * @return bool
     */
    public function hasDatabase($name);

    /**
     * Drops the specified database.
     *
     * @param string $name Database Name
     * @return void
     */
    public function dropDatabase($name);

    /**
     * Creates the specified schema or throws an exception
     * if there is no support for it.
     *
     * @param  string $schemaName Schema Name
     * @return void
     */
    public function createSchema($schemaName = 'public');

    /**
     * Drops the specified schema table  or throws an exception
     * if there is no support for it.
     *
     * @param string $schemaName Schema name
     * @return void
     */
    public function dropSchema($schemaName);

    /**
     * Cast a value to a boolean appropriate for the adapter.
     *
     * @param mixed $value The value to be cast
     *
     * @return mixed
     */
    public function castToBool($value);
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;

class PostgresAdapter extends PdoAdapter implements AdapterInterface
{
    const INT_SMALL = 65535;

    /**
     * Columns with comments
     *
     * @var array
     */
    protected $columnsWithComments = [];

    /**
     * {@inheritdoc}
     */
    public function connect()
    {
        if ($this->connection === null) {
            if (!class_exists('PDO') || !in_array('pgsql', \PDO::getAvailableDrivers(), true)) {
                // @codeCoverageIgnoreStart
                throw new \RuntimeException('You need to enable the PDO_Pgsql extension for Phinx to run properly.');
                // @codeCoverageIgnoreEnd
            }

            $db = null;
            $options = $this->getOptions();

            // if port is specified use it, otherwise use the PostgreSQL default
            if (isset($options['port'])) {
                $dsn = 'pgsql:host=' . $options['host'] . ';port=' . $options['port'] . ';dbname=' . $options['name'];
            } else {
                $dsn = 'pgsql:host=' . $options['host'] . ';dbname=' . $options['name'];
            }

            try {
                $db = new \PDO($dsn, $options['user'], $options['pass'], [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
            } catch (\PDOException $exception) {
                throw new \InvalidArgumentException(sprintf(
                    'There was a problem connecting to the database: %s',
                    $exception->getMessage()
                ));
            }

            $this->setConnection($db);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function disconnect()
    {
        $this->connection = null;
    }

    /**
     * {@inheritdoc}
     */
    public function hasTransactions()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function beginTransaction()
    {
        $this->execute('BEGIN');
    }

    /**
     * {@inheritdoc}
     */
    public function commitTransaction()
    {
        $this->execute('COMMIT');
    }

    /**
     * {@inheritdoc}
     */
    public function rollbackTransaction()
    {
        $this->execute('ROLLBACK');
    }

    /**
     * Quotes a schema name for use in a query.
     *
     * @param string $schemaName Schema Name
     * @return string
     */
    public function quoteSchemaName($schemaName)
    {
        return $this->quoteColumnName($schemaName);
    }

    /**
     * {@inheritdoc}
     */
    public function quoteTableName($tableName)
    {
        return $this->quoteSchemaName($this->getSchemaName()) . '.' . $this->quoteColumnName($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function quoteColumnName($columnName)
    {
        return '"' . $columnName . '"';
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        $result = $this->getConnection()->query(
            sprintf(
                'SELECT *
                FROM information_schema.tables
                WHERE table_schema = %s
                AND lower(table_name) = lower(%s)',
                $this->getConnection()->quote($this->getSchemaName()),
                $this->getConnection()->quote($tableName)
            )
        );

        return $result->rowCount() === 1;
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        $options = $table->getOptions();

         // Add the default primary key
        $columns = $table->getPendingColumns();
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
            $column = new Column();
            $column->setName('id')
                   ->setType('integer')
                   ->setIdentity(true);

            array_unshift($columns, $column);
            $options['primary_key'] = 'id';
        } elseif (isset($options['id']) && is_string($options['id'])) {
            // Handle id => "field_name" to support AUTO_INCREMENT
            $column = new Column();
            $column->setName($options['id'])
                   ->setType('integer')
                   ->setIdentity(true);

            array_unshift($columns, $column);
            $options['primary_key'] = $options['id'];
        }

        // TODO - process table options like collation etc
        $sql = 'CREATE TABLE ';
        $sql .= $this->quoteTableName($table->getName()) . ' (';

        $this->columnsWithComments = [];
        foreach ($columns as $column) {
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';

            // set column comments, if needed
            if ($column->getComment()) {
                $this->columnsWithComments[] = $column;
            }
        }

         // set the primary key(s)
        if (isset($options['primary_key'])) {
            $sql = rtrim($sql);
            $sql .= sprintf(' CONSTRAINT %s_pkey PRIMARY KEY (', $table->getName());
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
                $sql .= $this->quoteColumnName($options['primary_key']);
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
            }
            $sql .= ')';
        } else {
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
        }

        // set the foreign keys
        $foreignKeys = $table->getForeignKeys();
        if (!empty($foreignKeys)) {
            foreach ($foreignKeys as $foreignKey) {
                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey, $table->getName());
            }
        }

        $sql .= ');';

        // process column comments
        if (!empty($this->columnsWithComments)) {
            foreach ($this->columnsWithComments as $column) {
                $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
            }
        }

        // set the indexes
        $indexes = $table->getIndexes();
        if (!empty($indexes)) {
            foreach ($indexes as $index) {
                $sql .= $this->getIndexSqlDefinition($index, $table->getName());
            }
        }

        // execute the sql
        $this->execute($sql);

        // process table comments
        if (isset($options['comment'])) {
            $sql = sprintf(
                'COMMENT ON TABLE %s IS %s',
                $this->quoteTableName($table->getName()),
                $this->getConnection()->quote($options['comment'])
            );
            $this->execute($sql);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $sql = sprintf(
            'ALTER TABLE %s RENAME TO %s',
            $this->quoteTableName($tableName),
            $this->quoteColumnName($newTableName)
        );
        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $sql = sprintf(
            'TRUNCATE TABLE %s',
            $this->quoteTableName($tableName)
        );

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function getColumns($tableName)
    {
        $columns = [];
        $sql = sprintf(
            "SELECT column_name, data_type, is_identity, is_nullable,
             column_default, character_maximum_length, numeric_precision, numeric_scale
             FROM information_schema.columns
             WHERE table_name ='%s'",
            $tableName
        );
        $columnsInfo = $this->fetchAll($sql);

        foreach ($columnsInfo as $columnInfo) {
            $column = new Column();
            $column->setName($columnInfo['column_name'])
                   ->setType($this->getPhinxType($columnInfo['data_type']))
                   ->setNull($columnInfo['is_nullable'] === 'YES')
                   ->setDefault($columnInfo['column_default'])
                   ->setIdentity($columnInfo['is_identity'] === 'YES')
                   ->setPrecision($columnInfo['numeric_precision'])
                   ->setScale($columnInfo['numeric_scale']);

            if (preg_match('/\bwith time zone$/', $columnInfo['data_type'])) {
                $column->setTimezone(true);
            }

            if (isset($columnInfo['character_maximum_length'])) {
                $column->setLimit($columnInfo['character_maximum_length']);
            }
            $columns[] = $column;
        }

        return $columns;
    }

    /**
     * {@inheritdoc}
     */
    public function hasColumn($tableName, $columnName)
    {
        $sql = sprintf(
            "SELECT count(*)
            FROM information_schema.columns
            WHERE table_schema = '%s' AND table_name = '%s' AND column_name = '%s'",
            $this->getSchemaName(),
            $tableName,
            $columnName
        );

        $result = $this->fetchRow($sql);

        return $result['count'] > 0;
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $sql = sprintf(
            'ALTER TABLE %s ADD %s %s;',
            $this->quoteTableName($table->getName()),
            $this->quoteColumnName($column->getName()),
            $this->getColumnSqlDefinition($column)
        );

        if ($column->getComment()) {
            $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
        }

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        $sql = sprintf(
            "SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END AS column_exists
             FROM information_schema.columns
             WHERE table_name ='%s' AND column_name = '%s'",
            $tableName,
            $columnName
        );
        $result = $this->fetchRow($sql);
        if (!(bool)$result['column_exists']) {
            throw new \InvalidArgumentException("The specified column does not exist: $columnName");
        }
        $this->execute(
            sprintf(
                'ALTER TABLE %s RENAME COLUMN %s TO %s',
                $this->quoteTableName($tableName),
                $this->quoteColumnName($columnName),
                $this->quoteColumnName($newColumnName)
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        // TODO - is it possible to merge these 3 queries into less?
        // change data type
        $sql = sprintf(
            'ALTER TABLE %s ALTER COLUMN %s TYPE %s',
            $this->quoteTableName($tableName),
            $this->quoteColumnName($columnName),
            $this->getColumnSqlDefinition($newColumn)
        );
        //NULL and DEFAULT cannot be set while changing column type
        $sql = preg_replace('/ NOT NULL/', '', $sql);
        $sql = preg_replace('/ NULL/', '', $sql);
        //If it is set, DEFAULT is the last definition
        $sql = preg_replace('/DEFAULT .*/', '', $sql);
        $this->execute($sql);
        // process null
        $sql = sprintf(
            'ALTER TABLE %s ALTER COLUMN %s',
            $this->quoteTableName($tableName),
            $this->quoteColumnName($columnName)
        );
        if ($newColumn->isNull()) {
            $sql .= ' DROP NOT NULL';
        } else {
            $sql .= ' SET NOT NULL';
        }
        $this->execute($sql);
        if (!is_null($newColumn->getDefault())) {
            //change default
            $this->execute(
                sprintf(
                    'ALTER TABLE %s ALTER COLUMN %s SET %s',
                    $this->quoteTableName($tableName),
                    $this->quoteColumnName($columnName),
                    $this->getDefaultValueDefinition($newColumn->getDefault())
                )
            );
        } else {
            //drop default
            $this->execute(
                sprintf(
                    'ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT',
                    $this->quoteTableName($tableName),
                    $this->quoteColumnName($columnName)
                )
            );
        }
        // rename column
        if ($columnName !== $newColumn->getName()) {
            $this->execute(
                sprintf(
                    'ALTER TABLE %s RENAME COLUMN %s TO %s',
                    $this->quoteTableName($tableName),
                    $this->quoteColumnName($columnName),
                    $this->quoteColumnName($newColumn->getName())
                )
            );
        }

        // change column comment if needed
        if ($newColumn->getComment()) {
            $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName);
            $this->execute($sql);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        $this->execute(
            sprintf(
                'ALTER TABLE %s DROP COLUMN %s',
                $this->quoteTableName($tableName),
                $this->quoteColumnName($columnName)
            )
        );
    }

    /**
     * Get an array of indexes from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    protected function getIndexes($tableName)
    {
        $indexes = [];
        $sql = "SELECT
            i.relname AS index_name,
            a.attname AS column_name
        FROM
            pg_class t,
            pg_class i,
            pg_index ix,
            pg_attribute a
        WHERE
            t.oid = ix.indrelid
            AND i.oid = ix.indexrelid
            AND a.attrelid = t.oid
            AND a.attnum = ANY(ix.indkey)
            AND t.relkind = 'r'
            AND t.relname = '$tableName'
        ORDER BY
            t.relname,
            i.relname;";
        $rows = $this->fetchAll($sql);
        foreach ($rows as $row) {
            if (!isset($indexes[$row['index_name']])) {
                $indexes[$row['index_name']] = ['columns' => []];
            }
            $indexes[$row['index_name']]['columns'][] = strtolower($row['column_name']);
        }

        return $indexes;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns];
        }
        $columns = array_map('strtolower', $columns);
        $indexes = $this->getIndexes($tableName);
        foreach ($indexes as $index) {
            if (array_diff($index['columns'], $columns) === array_diff($columns, $index['columns'])) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndexByName($tableName, $indexName)
    {
        $indexes = $this->getIndexes($tableName);
        foreach ($indexes as $name => $index) {
            if ($name === $indexName) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $sql = $this->getIndexSqlDefinition($index, $table->getName());
        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $indexes = $this->getIndexes($tableName);
        $columns = array_map('strtolower', $columns);

        foreach ($indexes as $indexName => $index) {
            $a = array_diff($columns, $index['columns']);
            if (empty($a)) {
                $this->execute(
                    sprintf(
                        'DROP INDEX IF EXISTS %s',
                        $this->quoteColumnName($indexName)
                    )
                );

                return;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $sql = sprintf(
            'DROP INDEX IF EXISTS %s',
            $indexName
        );
        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function hasForeignKey($tableName, $columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }
        $foreignKeys = $this->getForeignKeys($tableName);
        if ($constraint) {
            if (isset($foreignKeys[$constraint])) {
                return !empty($foreignKeys[$constraint]);
            }

            return false;
        } else {
            foreach ($foreignKeys as $key) {
                $a = array_diff($columns, $key['columns']);
                if (empty($a)) {
                    return true;
                }
            }

            return false;
        }
    }

    /**
     * Get an array of foreign keys from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    protected function getForeignKeys($tableName)
    {
        $foreignKeys = [];
        $rows = $this->fetchAll(sprintf(
            "SELECT
                    tc.constraint_name,
                    tc.table_name, kcu.column_name,
                    ccu.table_name AS referenced_table_name,
                    ccu.column_name AS referenced_column_name
                FROM
                    information_schema.table_constraints AS tc
                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s'
                ORDER BY kcu.position_in_unique_constraint",
            $tableName
        ));
        foreach ($rows as $row) {
            $foreignKeys[$row['constraint_name']]['table'] = $row['table_name'];
            $foreignKeys[$row['constraint_name']]['columns'][] = $row['column_name'];
            $foreignKeys[$row['constraint_name']]['referenced_table'] = $row['referenced_table_name'];
            $foreignKeys[$row['constraint_name']]['referenced_columns'][] = $row['referenced_column_name'];
        }

        return $foreignKeys;
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        $sql = sprintf(
            'ALTER TABLE %s ADD %s',
            $this->quoteTableName($table->getName()),
            $this->getForeignKeySqlDefinition($foreignKey, $table->getName())
        );
        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        if ($constraint) {
            $this->execute(
                sprintf(
                    'ALTER TABLE %s DROP CONSTRAINT %s',
                    $this->quoteTableName($tableName),
                    $constraint
                )
            );
        } else {
            foreach ($columns as $column) {
                $rows = $this->fetchAll(sprintf(
                    "SELECT CONSTRAINT_NAME
                      FROM information_schema.KEY_COLUMN_USAGE
                      WHERE TABLE_SCHEMA = CURRENT_SCHEMA()
                        AND TABLE_NAME IS NOT NULL
                        AND TABLE_NAME = '%s'
                        AND COLUMN_NAME = '%s'
                      ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
                    $tableName,
                    $column
                ));

                foreach ($rows as $row) {
                    $this->dropForeignKey($tableName, $columns, $row['constraint_name']);
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getSqlType($type, $limit = null)
    {
        switch ($type) {
            case static::PHINX_TYPE_INTEGER:
                if ($limit && $limit == static::INT_SMALL) {
                    return [
                        'name' => 'smallint',
                        'limit' => static::INT_SMALL
                    ];
                }

                return ['name' => $type];
            case static::PHINX_TYPE_TEXT:
            case static::PHINX_TYPE_TIME:
            case static::PHINX_TYPE_DATE:
            case static::PHINX_TYPE_BOOLEAN:
            case static::PHINX_TYPE_JSON:
            case static::PHINX_TYPE_JSONB:
            case static::PHINX_TYPE_UUID:
            case static::PHINX_TYPE_CIDR:
            case static::PHINX_TYPE_INET:
            case static::PHINX_TYPE_MACADDR:
                return ['name' => $type];
            case static::PHINX_TYPE_DECIMAL:
                return ['name' => $type, 'precision' => 18, 'scale' => 0];
            case static::PHINX_TYPE_STRING:
                return ['name' => 'character varying', 'limit' => 255];
            case static::PHINX_TYPE_CHAR:
                return ['name' => 'character', 'limit' => 255];
            case static::PHINX_TYPE_BIG_INTEGER:
                return ['name' => 'bigint'];
            case static::PHINX_TYPE_FLOAT:
                return ['name' => 'real'];
            case static::PHINX_TYPE_DATETIME:
            case static::PHINX_TYPE_TIMESTAMP:
                return ['name' => 'timestamp'];
            case static::PHINX_TYPE_BLOB:
            case static::PHINX_TYPE_BINARY:
                return ['name' => 'bytea'];
            case static::PHINX_TYPE_INTERVAL:
                return ['name' => 'interval'];
            // Geospatial database types
            // Spatial storage in Postgres is done via the PostGIS extension,
            // which enables the use of the "geography" type in combination
            // with SRID 4326.
            case static::PHINX_TYPE_GEOMETRY:
                return ['name' => 'geography', 'type' => 'geometry', 'srid' => 4326];
            case static::PHINX_TYPE_POINT:
                return ['name' => 'geography', 'type' => 'point', 'srid' => 4326];
            case static::PHINX_TYPE_LINESTRING:
                return ['name' => 'geography', 'type' => 'linestring', 'srid' => 4326];
            case static::PHINX_TYPE_POLYGON:
                return ['name' => 'geography', 'type' => 'polygon', 'srid' => 4326];
            default:
                if ($this->isArrayType($type)) {
                    return ['name' => $type];
                }
                // Return array type
                throw new \RuntimeException('The type: "' . $type . '" is not supported');
        }
    }

    /**
     * Returns Phinx type by SQL type
     *
     * @param string $sqlType SQL type
     * @returns string Phinx type
     */
    public function getPhinxType($sqlType)
    {
        switch ($sqlType) {
            case 'character varying':
            case 'varchar':
                return static::PHINX_TYPE_STRING;
            case 'character':
            case 'char':
                return static::PHINX_TYPE_CHAR;
            case 'text':
                return static::PHINX_TYPE_TEXT;
            case 'json':
                return static::PHINX_TYPE_JSON;
            case 'jsonb':
                return static::PHINX_TYPE_JSONB;
            case 'smallint':
                return [
                    'name' => 'smallint',
                    'limit' => static::INT_SMALL
                ];
            case 'int':
            case 'int4':
            case 'integer':
                return static::PHINX_TYPE_INTEGER;
            case 'decimal':
            case 'numeric':
                return static::PHINX_TYPE_DECIMAL;
            case 'bigint':
            case 'int8':
                return static::PHINX_TYPE_BIG_INTEGER;
            case 'real':
            case 'float4':
                return static::PHINX_TYPE_FLOAT;
            case 'bytea':
                return static::PHINX_TYPE_BINARY;
            case 'interval':
                return static::PHINX_TYPE_INTERVAL;
            case 'time':
            case 'timetz':
            case 'time with time zone':
            case 'time without time zone':
                return static::PHINX_TYPE_TIME;
            case 'date':
                return static::PHINX_TYPE_DATE;
            case 'timestamp':
            case 'timestamptz':
            case 'timestamp with time zone':
            case 'timestamp without time zone':
                return static::PHINX_TYPE_DATETIME;
            case 'bool':
            case 'boolean':
                return static::PHINX_TYPE_BOOLEAN;
            case 'uuid':
                return static::PHINX_TYPE_UUID;
            case 'cidr':
                return static::PHINX_TYPE_CIDR;
            case 'inet':
                return static::PHINX_TYPE_INET;
            case 'macaddr':
                return static::PHINX_TYPE_MACADDR;
            default:
                throw new \RuntimeException('The PostgreSQL type: "' . $sqlType . '" is not supported');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options = [])
    {
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
        $this->execute(sprintf("CREATE DATABASE %s WITH ENCODING = '%s'", $name, $charset));
    }

    /**
     * {@inheritdoc}
     */
    public function hasDatabase($databaseName)
    {
        $sql = sprintf("SELECT count(*) FROM pg_database WHERE datname = '%s'", $databaseName);
        $result = $this->fetchRow($sql);

        return $result['count'] > 0;
    }

    /**
     * {@inheritdoc}
     */
    public function dropDatabase($name)
    {
        $this->disconnect();
        $this->execute(sprintf('DROP DATABASE IF EXISTS %s', $name));
        $this->connect();
    }

    /**
     * Get the defintion for a `DEFAULT` statement.
     *
     * @param  mixed $default
     * @return string
     */
    protected function getDefaultValueDefinition($default)
    {
        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
            $default = $this->getConnection()->quote($default);
        } elseif (is_bool($default)) {
            $default = $this->castToBool($default);
        }

        return isset($default) ? 'DEFAULT ' . $default : '';
    }

    /**
     * Gets the PostgreSQL Column Definition for a Column object.
     *
     * @param \Phinx\Db\Table\Column $column Column
     * @return string
     */
    protected function getColumnSqlDefinition(Column $column)
    {
        $buffer = [];
        if ($column->isIdentity()) {
            $buffer[] = $column->getType() == 'biginteger' ? 'BIGSERIAL' : 'SERIAL';
        } else {
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
            $buffer[] = strtoupper($sqlType['name']);
            // integers cant have limits in postgres
            if (static::PHINX_TYPE_DECIMAL === $sqlType['name'] && ($column->getPrecision() || $column->getScale())) {
                $buffer[] = sprintf(
                    '(%s, %s)',
                    $column->getPrecision() ?: $sqlType['precision'],
                    $column->getScale() ?: $sqlType['scale']
                );
            } elseif (in_array($sqlType['name'], ['geography'])) {
                // geography type must be written with geometry type and srid, like this: geography(POLYGON,4326)
                $buffer[] = sprintf(
                    '(%s,%s)',
                    strtoupper($sqlType['type']),
                    $sqlType['srid']
                );
            } elseif (!in_array($sqlType['name'], ['integer', 'smallint', 'bigint'])) {
                if ($column->getLimit() || isset($sqlType['limit'])) {
                    $buffer[] = sprintf('(%s)', $column->getLimit() ?: $sqlType['limit']);
                }
            }

            $timeTypes = [
                'time',
                'timestamp',
            ];
            if (in_array($sqlType['name'], $timeTypes) && $column->isTimezone()) {
                $buffer[] = strtoupper('with time zone');
            }
        }

        $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL';

        if (!is_null($column->getDefault())) {
            $buffer[] = $this->getDefaultValueDefinition($column->getDefault());
        }

        return implode(' ', $buffer);
    }

    /**
     * Gets the PostgreSQL Column Comment Defininition for a column object.
     *
     * @param \Phinx\Db\Table\Column $column Column
     * @param string $tableName Table name
     * @return string
     */
    protected function getColumnCommentSqlDefinition(Column $column, $tableName)
    {
        // passing 'null' is to remove column comment
        $comment = (strcasecmp($column->getComment(), 'NULL') !== 0)
                 ? $this->getConnection()->quote($column->getComment())
                 : 'NULL';

        return sprintf(
            'COMMENT ON COLUMN %s.%s IS %s;',
            $this->quoteSchemaName($tableName),
            $this->quoteColumnName($column->getName()),
            $comment
        );
    }

    /**
     * Gets the PostgreSQL Index Definition for an Index object.
     *
     * @param \Phinx\Db\Table\Index  $index Index
     * @param string $tableName Table name
     * @return string
     */
    protected function getIndexSqlDefinition(Index $index, $tableName)
    {
        if (is_string($index->getName())) {
            $indexName = $index->getName();
        } else {
            $columnNames = $index->getColumns();
            if (is_string($columnNames)) {
                $columnNames = [$columnNames];
            }
            $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames));
        }
        $def = sprintf(
            "CREATE %s INDEX %s ON %s (%s);",
            ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''),
            $indexName,
            $this->quoteTableName($tableName),
            implode(',', array_map([$this, 'quoteColumnName'], $index->getColumns()))
        );

        return $def;
    }

    /**
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
     *
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
     * @param string     $tableName  Table name
     * @return string
     */
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, $tableName)
    {
        $constraintName = $foreignKey->getConstraint() ?: $tableName . '_' . implode('_', $foreignKey->getColumns());

        $def = ' CONSTRAINT "' . $constraintName . '" FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")';
        $def .= " REFERENCES {$this->quoteTableName($foreignKey->getReferencedTable()->getName())} (\"" . implode('", "', $foreignKey->getReferencedColumns()) . '")';
        if ($foreignKey->getOnDelete()) {
            $def .= " ON DELETE {$foreignKey->getOnDelete()}";
        }
        if ($foreignKey->getOnUpdate()) {
            $def .= " ON UPDATE {$foreignKey->getOnUpdate()}";
        }

        return $def;
    }

    /**
     * {@inheritdoc}
     */
    public function createSchemaTable()
    {
        // Create the public/custom schema if it doesn't already exist
        if ($this->hasSchema($this->getSchemaName()) === false) {
            $this->createSchema($this->getSchemaName());
        }

        $this->fetchAll(sprintf('SET search_path TO %s', $this->getSchemaName()));

        parent::createSchemaTable();
    }

    /**
     * Creates the specified schema.
     *
     * @param  string $schemaName Schema Name
     * @return void
     */
    public function createSchema($schemaName = 'public')
    {
        $sql = sprintf('CREATE SCHEMA %s;', $this->quoteSchemaName($schemaName)); // from postgres 9.3 we can use "CREATE SCHEMA IF NOT EXISTS schema_name"
        $this->execute($sql);
    }

    /**
     * Checks to see if a schema exists.
     *
     * @param string $schemaName Schema Name
     * @return bool
     */
    public function hasSchema($schemaName)
    {
        $sql = sprintf(
            "SELECT count(*)
             FROM pg_namespace
             WHERE nspname = '%s'",
            $schemaName
        );
        $result = $this->fetchRow($sql);

        return $result['count'] > 0;
    }

    /**
     * Drops the specified schema table.
     *
     * @param string $schemaName Schema name
     * @return void
     */
    public function dropSchema($schemaName)
    {
        $sql = sprintf("DROP SCHEMA IF EXISTS %s CASCADE;", $this->quoteSchemaName($schemaName));
        $this->execute($sql);
    }

    /**
     * Drops all schemas.
     *
     * @return void
     */
    public function dropAllSchemas()
    {
        foreach ($this->getAllSchemas() as $schema) {
            $this->dropSchema($schema);
        }
    }

    /**
     * Returns schemas.
     *
     * @return array
     */
    public function getAllSchemas()
    {
        $sql = "SELECT schema_name
                FROM information_schema.schemata
                WHERE schema_name <> 'information_schema' AND schema_name !~ '^pg_'";
        $items = $this->fetchAll($sql);
        $schemaNames = [];
        foreach ($items as $item) {
            $schemaNames[] = $item['schema_name'];
        }

        return $schemaNames;
    }

    /**
     * {@inheritdoc}
     */
    public function getColumnTypes()
    {
        return array_merge(parent::getColumnTypes(), ['json', 'jsonb', 'cidr', 'inet', 'macaddr', 'interval']);
    }

    /**
     * {@inheritdoc}
     */
    public function isValidColumnType(Column $column)
    {
        // If not a standard column type, maybe it is array type?
        return (parent::isValidColumnType($column) || $this->isArrayType($column->getType()));
    }

    /**
     * Check if the given column is an array of a valid type.
     *
     * @param  string $columnType
     * @return bool
     */
    protected function isArrayType($columnType)
    {
        if (!preg_match('/^([a-z]+)(?:\[\]){1,}$/', $columnType, $matches)) {
            return false;
        }

        $baseType = $matches[1];

        return in_array($baseType, $this->getColumnTypes());
    }

    /**
     * Gets the schema name.
     *
     * @return string
     */
    private function getSchemaName()
    {
        $options = $this->getOptions();

        return empty($options['schema']) ? 'public' : $options['schema'];
    }

    /**
     * {@inheritdoc}
     */
    public function castToBool($value)
    {
        return (bool)$value ? 'TRUE' : 'FALSE';
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;
use Phinx\Migration\MigrationInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Adapter Wrapper.
 *
 * Proxy commands through to another adapter, allowing modification of
 * parameters during calls.
 *
 * @author Woody Gilk <woody.gilk@gmail.com>
 */
abstract class AdapterWrapper implements AdapterInterface, WrapperInterface
{
    /**
     * @var \Phinx\Db\Adapter\AdapterInterface
     */
    protected $adapter;

    /**
     * {@inheritdoc}
     */
    public function __construct(AdapterInterface $adapter)
    {
        $this->setAdapter($adapter);
    }

    /**
     * {@inheritdoc}
     */
    public function setAdapter(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getAdapter()
    {
        return $this->adapter;
    }

    /**
     * {@inheritdoc}
     */
    public function setOptions(array $options)
    {
        $this->adapter->setOptions($options);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getOptions()
    {
        return $this->adapter->getOptions();
    }

    /**
     * {@inheritdoc}
     */
    public function hasOption($name)
    {
        return $this->adapter->hasOption($name);
    }

    /**
     * {@inheritdoc}
     */
    public function getOption($name)
    {
        return $this->adapter->getOption($name);
    }

    /**
     * {@inheritdoc}
     */
    public function setInput(InputInterface $input)
    {
        $this->adapter->setInput($input);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getInput()
    {
        return $this->adapter->getInput();
    }

    /**
     * {@inheritdoc}
     */
    public function setOutput(OutputInterface $output)
    {
        $this->adapter->setOutput($output);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getOutput()
    {
        return $this->adapter->getOutput();
    }

    /**
     * {@inheritdoc}
     */
    public function connect()
    {
        $this->getAdapter()->connect();
    }

    /**
     * {@inheritdoc}
     */
    public function disconnect()
    {
        $this->getAdapter()->disconnect();
    }

    /**
     * {@inheritdoc}
     */
    public function execute($sql)
    {
        return $this->getAdapter()->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function query($sql)
    {
        return $this->getAdapter()->query($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function insert(Table $table, $row)
    {
        $this->getAdapter()->insert($table, $row);
    }

    /**
     * {@inheritdoc}
     */
    public function bulkinsert(Table $table, $rows)
    {
        $this->getAdapter()->bulkinsert($table, $rows);
    }

    /**
     * {@inheritdoc}
     */
    public function fetchRow($sql)
    {
        return $this->getAdapter()->fetchRow($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function fetchAll($sql)
    {
        return $this->getAdapter()->fetchAll($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function getVersions()
    {
        return $this->getAdapter()->getVersions();
    }

    /**
     * {@inheritdoc}
     */
    public function getVersionLog()
    {
        return $this->getAdapter()->getVersionLog();
    }

    /**
     * {@inheritdoc}
     */
    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
    {
        $this->getAdapter()->migrated($migration, $direction, $startTime, $endTime);

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function toggleBreakpoint(MigrationInterface $migration)
    {
        $this->getAdapter()->toggleBreakpoint($migration);

        return $this;
    }

    /**
     * @inheritDoc
     */
    public function resetAllBreakpoints()
    {
        return $this->getAdapter()->resetAllBreakpoints();
    }

    /**
     * {@inheritdoc}
     */
    public function hasSchemaTable()
    {
        return $this->getAdapter()->hasSchemaTable();
    }

    /**
     * {@inheritdoc}
     */
    public function createSchemaTable()
    {
        $this->getAdapter()->createSchemaTable();
    }

    /**
     * {@inheritdoc}
     */
    public function getColumnTypes()
    {
        return $this->getAdapter()->getColumnTypes();
    }

    /**
     * {@inheritdoc}
     */
    public function isValidColumnType(Column $column)
    {
        return $this->getAdapter()->isValidColumnType($column);
    }

    /**
     * {@inheritdoc}
     */
    public function hasTransactions()
    {
        return $this->getAdapter()->hasTransactions();
    }

    /**
     * {@inheritdoc}
     */
    public function beginTransaction()
    {
        $this->getAdapter()->beginTransaction();
    }

    /**
     * {@inheritdoc}
     */
    public function commitTransaction()
    {
        $this->getAdapter()->commitTransaction();
    }

    /**
     * {@inheritdoc}
     */
    public function rollbackTransaction()
    {
        $this->getAdapter()->rollbackTransaction();
    }

    /**
     * {@inheritdoc}
     */
    public function quoteTableName($tableName)
    {
        return $this->getAdapter()->quoteTableName($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function quoteColumnName($columnName)
    {
        return $this->getAdapter()->quoteColumnName($columnName);
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        return $this->getAdapter()->hasTable($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        $this->getAdapter()->createTable($table);
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $this->getAdapter()->renameTable($tableName, $newTableName);
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $this->getAdapter()->dropTable($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $this->getAdapter()->truncateTable($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function getColumns($tableName)
    {
        return $this->getAdapter()->getColumns($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function hasColumn($tableName, $columnName)
    {
        return $this->getAdapter()->hasColumn($tableName, $columnName);
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $this->getAdapter()->addColumn($table, $column);
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        $this->getAdapter()->renameColumn($tableName, $columnName, $newColumnName);
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        return $this->getAdapter()->changeColumn($tableName, $columnName, $newColumn);
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        $this->getAdapter()->dropColumn($tableName, $columnName);
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndex($tableName, $columns)
    {
        return $this->getAdapter()->hasIndex($tableName, $columns);
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndexByName($tableName, $indexName)
    {
        return $this->getAdapter()->hasIndexByName($tableName, $indexName);
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $this->getAdapter()->addIndex($table, $index);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns)
    {
        $this->getAdapter()->dropIndex($tableName, $columns);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $this->getAdapter()->dropIndexByName($tableName, $indexName);
    }

    /**
     * {@inheritdoc}
     */
    public function hasForeignKey($tableName, $columns, $constraint = null)
    {
        return $this->getAdapter()->hasForeignKey($tableName, $columns, $constraint);
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        $this->getAdapter()->addForeignKey($table, $foreignKey);
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        $this->getAdapter()->dropForeignKey($tableName, $columns, $constraint);
    }

    /**
     * {@inheritdoc}
     */
    public function getSqlType($type, $limit = null)
    {
        return $this->getAdapter()->getSqlType($type, $limit);
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options = [])
    {
        $this->getAdapter()->createDatabase($name, $options);
    }

    /**
     * {@inheritdoc}
     */
    public function hasDatabase($name)
    {
        $this->getAdapter()->hasDatabase($name);
    }

    /**
     * {@inheritdoc}
     */
    public function dropDatabase($name)
    {
        $this->getAdapter()->dropDatabase($name);
    }

    /**
     * {@inheritdoc}
     */
    public function createSchema($schemaName = 'public')
    {
        $this->getAdapter()->createSchema($schemaName);
    }

    /**
     * {@inheritdoc}
     */
    public function dropSchema($schemaName)
    {
        $this->getAdapter()->dropSchema($schemaName);
    }

    /**
     * {@inheritdoc}
     */
    public function castToBool($value)
    {
        return $this->getAdapter()->castToBool($value);
    }

    /**
     * {@inheritdoc}
     */
    public function getConnection()
    {
        return $this->getAdapter()->getConnection();
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;

/**
 * Phinx SqlServer Adapter.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
class SqlServerAdapter extends PdoAdapter implements AdapterInterface
{
    protected $schema = 'dbo';

    protected $signedColumnTypes = ['integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true];

    /**
     * {@inheritdoc}
     */
    public function connect()
    {
        if ($this->connection === null) {
            if (!class_exists('PDO') || !in_array('sqlsrv', \PDO::getAvailableDrivers(), true)) {
                // try our connection via freetds (Mac/Linux)
                $this->connectDblib();

                return;
            }

            $db = null;
            $options = $this->getOptions();

            // if port is specified use it, otherwise use the SqlServer default
            if (empty($options['port'])) {
                $dsn = 'sqlsrv:server=' . $options['host'] . ';database=' . $options['name'];
            } else {
                $dsn = 'sqlsrv:server=' . $options['host'] . ',' . $options['port'] . ';database=' . $options['name'];
            }
            $dsn .= ';MultipleActiveResultSets=false';

            $driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION];

            // charset support
            if (isset($options['charset'])) {
                $driverOptions[\PDO::SQLSRV_ATTR_ENCODING] = $options['charset'];
            }

            // support arbitrary \PDO::SQLSRV_ATTR_* driver options and pass them to PDO
            // http://php.net/manual/en/ref.pdo-sqlsrv.php#pdo-sqlsrv.constants
            foreach ($options as $key => $option) {
                if (strpos($key, 'sqlsrv_attr_') === 0) {
                    $driverOptions[constant('\PDO::' . strtoupper($key))] = $option;
                }
            }

            try {
                $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
            } catch (\PDOException $exception) {
                throw new \InvalidArgumentException(sprintf(
                    'There was a problem connecting to the database: %s',
                    $exception->getMessage()
                ));
            }

            $this->setConnection($db);
        }
    }

    /**
     * Connect to MSSQL using dblib/freetds.
     *
     * The "sqlsrv" driver is not available on Unix machines.
     *
     * @throws \InvalidArgumentException
     */
    protected function connectDblib()
    {
        if (!class_exists('PDO') || !in_array('dblib', \PDO::getAvailableDrivers(), true)) {
            // @codeCoverageIgnoreStart
            throw new \RuntimeException('You need to enable the PDO_Dblib extension for Phinx to run properly.');
            // @codeCoverageIgnoreEnd
        }

        $options = $this->getOptions();

        // if port is specified use it, otherwise use the SqlServer default
        if (empty($options['port'])) {
            $dsn = 'dblib:host=' . $options['host'] . ';dbname=' . $options['name'];
        } else {
            $dsn = 'dblib:host=' . $options['host'] . ':' . $options['port'] . ';dbname=' . $options['name'];
        }

        $driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION];

        try {
            $db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions);
        } catch (\PDOException $exception) {
            throw new \InvalidArgumentException(sprintf(
                'There was a problem connecting to the database: %s',
                $exception->getMessage()
            ));
        }

        $this->setConnection($db);
    }

    /**
     * {@inheritdoc}
     */
    public function disconnect()
    {
        $this->connection = null;
    }

    /**
     * {@inheritdoc}
     */
    public function hasTransactions()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function beginTransaction()
    {
        $this->execute('BEGIN TRANSACTION');
    }

    /**
     * {@inheritdoc}
     */
    public function commitTransaction()
    {
        $this->execute('COMMIT TRANSACTION');
    }

    /**
     * {@inheritdoc}
     */
    public function rollbackTransaction()
    {
        $this->execute('ROLLBACK TRANSACTION');
    }

    /**
     * {@inheritdoc}
     */
    public function quoteTableName($tableName)
    {
        return str_replace('.', '].[', $this->quoteColumnName($tableName));
    }

    /**
     * {@inheritdoc}
     */
    public function quoteColumnName($columnName)
    {
        return '[' . str_replace(']', '\]', $columnName) . ']';
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        $result = $this->fetchRow(sprintf('SELECT count(*) as [count] FROM information_schema.tables WHERE table_name = \'%s\';', $tableName));

        return $result['count'] > 0;
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        $options = $table->getOptions();

        // Add the default primary key
        $columns = $table->getPendingColumns();
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
            $column = new Column();
            $column->setName('id')
                   ->setType('integer')
                   ->setIdentity(true);

            array_unshift($columns, $column);
            $options['primary_key'] = 'id';
        } elseif (isset($options['id']) && is_string($options['id'])) {
            // Handle id => "field_name" to support AUTO_INCREMENT
            $column = new Column();
            $column->setName($options['id'])
                   ->setType('integer')
                   ->setIdentity(true);

            array_unshift($columns, $column);
            $options['primary_key'] = $options['id'];
        }

        $sql = 'CREATE TABLE ';
        $sql .= $this->quoteTableName($table->getName()) . ' (';
        $sqlBuffer = [];
        $columnsWithComments = [];
        foreach ($columns as $column) {
            $sqlBuffer[] = $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column);

            // set column comments, if needed
            if ($column->getComment()) {
                $columnsWithComments[] = $column;
            }
        }

        // set the primary key(s)
        if (isset($options['primary_key'])) {
            $pkSql = sprintf('CONSTRAINT PK_%s PRIMARY KEY (', $table->getName());
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
                $pkSql .= $this->quoteColumnName($options['primary_key']);
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
                $pkSql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
            }
            $pkSql .= ')';
            $sqlBuffer[] = $pkSql;
        }

        // set the foreign keys
        $foreignKeys = $table->getForeignKeys();
        foreach ($foreignKeys as $foreignKey) {
            $sqlBuffer[] = $this->getForeignKeySqlDefinition($foreignKey, $table->getName());
        }

        $sql .= implode(', ', $sqlBuffer);
        $sql .= ');';

        // process column comments
        foreach ($columnsWithComments as $column) {
            $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
        }

        // set the indexes
        $indexes = $table->getIndexes();
        foreach ($indexes as $index) {
            $sql .= $this->getIndexSqlDefinition($index, $table->getName());
        }

        // execute the sql
        $this->execute($sql);
    }

    /**
     * Gets the SqlServer Column Comment Defininition for a column object.
     *
     * @param \Phinx\Db\Table\Column $column    Column
     * @param string $tableName Table name
     *
     * @return string
     */
    protected function getColumnCommentSqlDefinition(Column $column, $tableName)
    {
        // passing 'null' is to remove column comment
        $currentComment = $this->getColumnComment($tableName, $column->getName());

        $comment = (strcasecmp($column->getComment(), 'NULL') !== 0) ? $this->getConnection()->quote($column->getComment()) : '\'\'';
        $command = $currentComment === false ? 'sp_addextendedproperty' : 'sp_updateextendedproperty';

        return sprintf(
            "EXECUTE %s N'MS_Description', N%s, N'SCHEMA', N'%s', N'TABLE', N'%s', N'COLUMN', N'%s';",
            $command,
            $comment,
            $this->schema,
            $tableName,
            $column->getName()
        );
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $this->execute(sprintf('EXEC sp_rename \'%s\', \'%s\'', $tableName, $newTableName));
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $sql = sprintf(
            'TRUNCATE TABLE %s',
            $this->quoteTableName($tableName)
        );

        $this->execute($sql);
    }

    public function getColumnComment($tableName, $columnName)
    {
        $sql = sprintf("SELECT cast(extended_properties.[value] as nvarchar(4000)) comment
  FROM sys.schemas
 INNER JOIN sys.tables
    ON schemas.schema_id = tables.schema_id
 INNER JOIN sys.columns
    ON tables.object_id = columns.object_id
 INNER JOIN sys.extended_properties
    ON tables.object_id = extended_properties.major_id
   AND columns.column_id = extended_properties.minor_id
   AND extended_properties.name = 'MS_Description'
   WHERE schemas.[name] = '%s' AND tables.[name] = '%s' AND columns.[name] = '%s'", $this->schema, $tableName, $columnName);
        $row = $this->fetchRow($sql);

        if ($row) {
            return $row['comment'];
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getColumns($tableName)
    {
        $columns = [];
        $sql = sprintf(
            "SELECT DISTINCT TABLE_SCHEMA AS [schema], TABLE_NAME as [table_name], COLUMN_NAME AS [name], DATA_TYPE AS [type],
            IS_NULLABLE AS [null], COLUMN_DEFAULT AS [default],
            CHARACTER_MAXIMUM_LENGTH AS [char_length],
            NUMERIC_PRECISION AS [precision],
            NUMERIC_SCALE AS [scale], ORDINAL_POSITION AS [ordinal_position],
            COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') as [identity]
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = '%s'
        ORDER BY ordinal_position",
            $tableName
        );
        $rows = $this->fetchAll($sql);
        foreach ($rows as $columnInfo) {
            $column = new Column();
            $column->setName($columnInfo['name'])
                   ->setType($this->getPhinxType($columnInfo['type']))
                   ->setNull($columnInfo['null'] !== 'NO')
                   ->setDefault($this->parseDefault($columnInfo['default']))
                   ->setIdentity($columnInfo['identity'] === '1')
                   ->setComment($this->getColumnComment($columnInfo['table_name'], $columnInfo['name']));

            if (!empty($columnInfo['char_length'])) {
                $column->setLimit($columnInfo['char_length']);
            }

            $columns[$columnInfo['name']] = $column;
        }

        return $columns;
    }

    protected function parseDefault($default)
    {
        $default = preg_replace(["/\('(.*)'\)/", "/\(\((.*)\)\)/", "/\((.*)\)/"], '$1', $default);

        if (strtoupper($default) === 'NULL') {
            $default = null;
        } elseif (is_numeric($default)) {
            $default = (int)$default;
        }

        return $default;
    }

    /**
     * {@inheritdoc}
     */
    public function hasColumn($tableName, $columnName)
    {
        $sql = sprintf(
            "SELECT count(*) as [count]
             FROM information_schema.columns
             WHERE table_name = '%s' AND column_name = '%s'",
            $tableName,
            $columnName
        );
        $result = $this->fetchRow($sql);

        return $result['count'] > 0;
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $sql = sprintf(
            'ALTER TABLE %s ADD %s %s',
            $this->quoteTableName($table->getName()),
            $this->quoteColumnName($column->getName()),
            $this->getColumnSqlDefinition($column)
        );

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        if (!$this->hasColumn($tableName, $columnName)) {
            throw new \InvalidArgumentException("The specified column does not exist: $columnName");
        }
        $this->renameDefault($tableName, $columnName, $newColumnName);
        $this->execute(
            sprintf(
                "EXECUTE sp_rename N'%s.%s', N'%s', 'COLUMN' ",
                $tableName,
                $columnName,
                $newColumnName
            )
        );
    }

    protected function renameDefault($tableName, $columnName, $newColumnName)
    {
        $oldConstraintName = "DF_{$tableName}_{$columnName}";
        $newConstraintName = "DF_{$tableName}_{$newColumnName}";
        $sql = <<<SQL
IF (OBJECT_ID('$oldConstraintName', 'D') IS NOT NULL)
BEGIN
     EXECUTE sp_rename N'%s', N'%s', N'OBJECT'
END
SQL;
        $this->execute(sprintf(
            $sql,
            $oldConstraintName,
            $newConstraintName
        ));
    }

    public function changeDefault($tableName, Column $newColumn)
    {
        $constraintName = "DF_{$tableName}_{$newColumn->getName()}";
        $default = $newColumn->getDefault();

        if ($default === null) {
            $default = 'DEFAULT NULL';
        } else {
            $default = $this->getDefaultValueDefinition($default);
        }

        if (empty($default)) {
            return;
        }

        $this->execute(sprintf(
            'ALTER TABLE %s ADD CONSTRAINT %s %s FOR %s',
            $this->quoteTableName($tableName),
            $constraintName,
            $default,
            $this->quoteColumnName($newColumn->getName())
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        $columns = $this->getColumns($tableName);
        $changeDefault = $newColumn->getDefault() !== $columns[$columnName]->getDefault() || $newColumn->getType() !== $columns[$columnName]->getType();
        if ($columnName !== $newColumn->getName()) {
            $this->renameColumn($tableName, $columnName, $newColumn->getName());
        }

        if ($changeDefault) {
            $this->dropDefaultConstraint($tableName, $newColumn->getName());
        }

        $this->execute(
            sprintf(
                'ALTER TABLE %s ALTER COLUMN %s %s',
                $this->quoteTableName($tableName),
                $this->quoteColumnName($newColumn->getName()),
                $this->getColumnSqlDefinition($newColumn, false)
            )
        );
        // change column comment if needed
        if ($newColumn->getComment()) {
            $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName);
            $this->execute($sql);
        }

        if ($changeDefault) {
            $this->changeDefault($tableName, $newColumn);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        $this->dropDefaultConstraint($tableName, $columnName);

        $this->execute(
            sprintf(
                'ALTER TABLE %s DROP COLUMN %s',
                $this->quoteTableName($tableName),
                $this->quoteColumnName($columnName)
            )
        );
    }

    protected function dropDefaultConstraint($tableName, $columnName)
    {
        $defaultConstraint = $this->getDefaultConstraint($tableName, $columnName);

        if (!$defaultConstraint) {
            return;
        }

        $this->dropForeignKey($tableName, $columnName, $defaultConstraint);
    }

    protected function getDefaultConstraint($tableName, $columnName)
    {
        $sql = "SELECT
    default_constraints.name
FROM
    sys.all_columns

        INNER JOIN
    sys.tables
        ON all_columns.object_id = tables.object_id

        INNER JOIN
    sys.schemas
        ON tables.schema_id = schemas.schema_id

        INNER JOIN
    sys.default_constraints
        ON all_columns.default_object_id = default_constraints.object_id

WHERE
        schemas.name = 'dbo'
    AND tables.name = '{$tableName}'
    AND all_columns.name = '{$columnName}'";

        $rows = $this->fetchAll($sql);

        return empty($rows) ? false : $rows[0]['name'];
    }

    protected function getIndexColums($tableId, $indexId)
    {
        $sql = "SELECT AC.[name] AS [column_name]
FROM sys.[index_columns] IC
  INNER JOIN sys.[all_columns] AC ON IC.[column_id] = AC.[column_id]
WHERE AC.[object_id] = {$tableId} AND IC.[index_id] = {$indexId}  AND IC.[object_id] = {$tableId}
ORDER BY IC.[key_ordinal];";

        $rows = $this->fetchAll($sql);
        $columns = [];
        foreach ($rows as $row) {
            $columns[] = strtolower($row['column_name']);
        }

        return $columns;
    }

    /**
     * Get an array of indexes from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    public function getIndexes($tableName)
    {
        $indexes = [];
        $sql = "SELECT I.[name] AS [index_name], I.[index_id] as [index_id], T.[object_id] as [table_id]
FROM sys.[tables] AS T
  INNER JOIN sys.[indexes] I ON T.[object_id] = I.[object_id]
WHERE T.[is_ms_shipped] = 0 AND I.[type_desc] <> 'HEAP'  AND T.[name] = '{$tableName}'
ORDER BY T.[name], I.[index_id];";

        $rows = $this->fetchAll($sql);
        foreach ($rows as $row) {
            $columns = $this->getIndexColums($row['table_id'], $row['index_id']);
            $indexes[$row['index_name']] = ['columns' => $columns];
        }

        return $indexes;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $columns = array_map('strtolower', $columns);
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $index) {
            $a = array_diff($columns, $index['columns']);

            if (empty($a)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndexByName($tableName, $indexName)
    {
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $name => $index) {
            if ($name === $indexName) {
                 return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $sql = $this->getIndexSqlDefinition($index, $table->getName());
        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $indexes = $this->getIndexes($tableName);
        $columns = array_map('strtolower', $columns);

        foreach ($indexes as $indexName => $index) {
            $a = array_diff($columns, $index['columns']);
            if (empty($a)) {
                $this->execute(
                    sprintf(
                        'DROP INDEX %s ON %s',
                        $this->quoteColumnName($indexName),
                        $this->quoteTableName($tableName)
                    )
                );

                return;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $name => $index) {
            if ($name === $indexName) {
                $this->execute(
                    sprintf(
                        'DROP INDEX %s ON %s',
                        $this->quoteColumnName($indexName),
                        $this->quoteTableName($tableName)
                    )
                );

                return;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasForeignKey($tableName, $columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }
        $foreignKeys = $this->getForeignKeys($tableName);
        if ($constraint) {
            if (isset($foreignKeys[$constraint])) {
                return !empty($foreignKeys[$constraint]);
            }

            return false;
        } else {
            foreach ($foreignKeys as $key) {
                $a = array_diff($columns, $key['columns']);
                if (empty($a)) {
                    return true;
                }
            }

            return false;
        }
    }

    /**
     * Get an array of foreign keys from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    protected function getForeignKeys($tableName)
    {
        $foreignKeys = [];
        $rows = $this->fetchAll(sprintf(
            "SELECT
                    tc.constraint_name,
                    tc.table_name, kcu.column_name,
                    ccu.table_name AS referenced_table_name,
                    ccu.column_name AS referenced_column_name
                FROM
                    information_schema.table_constraints AS tc
                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s'
                ORDER BY kcu.ordinal_position",
            $tableName
        ));
        foreach ($rows as $row) {
            $foreignKeys[$row['constraint_name']]['table'] = $row['table_name'];
            $foreignKeys[$row['constraint_name']]['columns'][] = $row['column_name'];
            $foreignKeys[$row['constraint_name']]['referenced_table'] = $row['referenced_table_name'];
            $foreignKeys[$row['constraint_name']]['referenced_columns'][] = $row['referenced_column_name'];
        }

        return $foreignKeys;
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        $this->execute(
            sprintf(
                'ALTER TABLE %s ADD %s',
                $this->quoteTableName($table->getName()),
                $this->getForeignKeySqlDefinition($foreignKey, $table->getName())
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        if ($constraint) {
            $this->execute(
                sprintf(
                    'ALTER TABLE %s DROP CONSTRAINT %s',
                    $this->quoteTableName($tableName),
                    $constraint
                )
            );

            return;
        } else {
            foreach ($columns as $column) {
                $rows = $this->fetchAll(sprintf(
                    "SELECT
                    tc.constraint_name,
                    tc.table_name, kcu.column_name,
                    ccu.table_name AS referenced_table_name,
                    ccu.column_name AS referenced_column_name
                FROM
                    information_schema.table_constraints AS tc
                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s' and ccu.column_name='%s'
                ORDER BY kcu.ordinal_position",
                    $tableName,
                    $column
                ));
                foreach ($rows as $row) {
                    $this->dropForeignKey($tableName, $columns, $row['constraint_name']);
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getSqlType($type, $limit = null)
    {
        switch ($type) {
            case static::PHINX_TYPE_STRING:
                return ['name' => 'nvarchar', 'limit' => 255];
            case static::PHINX_TYPE_CHAR:
                return ['name' => 'nchar', 'limit' => 255];
            case static::PHINX_TYPE_TEXT:
                return ['name' => 'ntext'];
            case static::PHINX_TYPE_INTEGER:
                return ['name' => 'int'];
            case static::PHINX_TYPE_BIG_INTEGER:
                return ['name' => 'bigint'];
            case static::PHINX_TYPE_FLOAT:
                return ['name' => 'float'];
            case static::PHINX_TYPE_DECIMAL:
                return ['name' => 'decimal'];
            case static::PHINX_TYPE_DATETIME:
            case static::PHINX_TYPE_TIMESTAMP:
                return ['name' => 'datetime'];
            case static::PHINX_TYPE_TIME:
                return ['name' => 'time'];
            case static::PHINX_TYPE_DATE:
                return ['name' => 'date'];
            case static::PHINX_TYPE_BLOB:
            case static::PHINX_TYPE_BINARY:
                return ['name' => 'varbinary'];
            case static::PHINX_TYPE_BOOLEAN:
                return ['name' => 'bit'];
            case static::PHINX_TYPE_UUID:
                return ['name' => 'uniqueidentifier'];
            case static::PHINX_TYPE_FILESTREAM:
                return ['name' => 'varbinary', 'limit' => 'max'];
            // Geospatial database types
            case static::PHINX_TYPE_GEOMETRY:
            case static::PHINX_TYPE_POINT:
            case static::PHINX_TYPE_LINESTRING:
            case static::PHINX_TYPE_POLYGON:
                // SQL Server stores all spatial data using a single data type.
                // Specific types (point, polygon, etc) are set at insert time.
                return ['name' => 'geography'];
            default:
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
        }
    }

    /**
     * Returns Phinx type by SQL type
     *
     * @param string $sqlType SQL Type definition
     * @throws \RuntimeException
     * @internal param string $sqlType SQL type
     * @returns string Phinx type
     */
    public function getPhinxType($sqlType)
    {
        switch ($sqlType) {
            case 'nvarchar':
            case 'varchar':
                return static::PHINX_TYPE_STRING;
            case 'char':
            case 'nchar':
                return static::PHINX_TYPE_CHAR;
            case 'text':
            case 'ntext':
                return static::PHINX_TYPE_TEXT;
            case 'int':
            case 'integer':
                return static::PHINX_TYPE_INTEGER;
            case 'decimal':
            case 'numeric':
            case 'money':
                return static::PHINX_TYPE_DECIMAL;
            case 'bigint':
                return static::PHINX_TYPE_BIG_INTEGER;
            case 'real':
            case 'float':
                return static::PHINX_TYPE_FLOAT;
            case 'binary':
            case 'image':
            case 'varbinary':
                return static::PHINX_TYPE_BINARY;
            case 'time':
                return static::PHINX_TYPE_TIME;
            case 'date':
                return static::PHINX_TYPE_DATE;
            case 'datetime':
            case 'timestamp':
                return static::PHINX_TYPE_DATETIME;
            case 'bit':
                return static::PHINX_TYPE_BOOLEAN;
            case 'uniqueidentifier':
                return static::PHINX_TYPE_UUID;
            case 'filestream':
                return static::PHINX_TYPE_FILESTREAM;
            default:
                throw new \RuntimeException('The SqlServer type: "' . $sqlType . '" is not supported');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options = [])
    {
        if (isset($options['collation'])) {
            $this->execute(sprintf('CREATE DATABASE [%s] COLLATE [%s]', $name, $options['collation']));
        } else {
            $this->execute(sprintf('CREATE DATABASE [%s]', $name));
        }
        $this->execute(sprintf('USE [%s]', $name));
    }

    /**
     * {@inheritdoc}
     */
    public function hasDatabase($name)
    {
        $result = $this->fetchRow(
            sprintf(
                'SELECT count(*) as [count] FROM master.dbo.sysdatabases WHERE [name] = \'%s\'',
                $name
            )
        );

        return $result['count'] > 0;
    }

    /**
     * {@inheritdoc}
     */
    public function dropDatabase($name)
    {
        $sql = <<<SQL
USE master;
IF EXISTS(select * from sys.databases where name=N'$name')
ALTER DATABASE [$name] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [$name];
SQL;
        $this->execute($sql);
    }

    /**
     * Get the defintion for a `DEFAULT` statement.
     *
     * @param  mixed $default
     * @return string
     */
    protected function getDefaultValueDefinition($default)
    {
        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
            $default = $this->getConnection()->quote($default);
        } elseif (is_bool($default)) {
            $default = $this->castToBool($default);
        }

        return isset($default) ? ' DEFAULT ' . $default : '';
    }

    /**
     * Gets the SqlServer Column Definition for a Column object.
     *
     * @param \Phinx\Db\Table\Column $column Column
     * @return string
     */
    protected function getColumnSqlDefinition(Column $column, $create = true)
    {
        $buffer = [];

        $sqlType = $this->getSqlType($column->getType());
        $buffer[] = strtoupper($sqlType['name']);
        // integers cant have limits in SQlServer
        $noLimits = [
            'bigint',
            'int',
            'tinyint'
        ];
        if (!in_array($sqlType['name'], $noLimits) && ($column->getLimit() || isset($sqlType['limit']))) {
            $buffer[] = sprintf('(%s)', $column->getLimit() ?: $sqlType['limit']);
        }
        if ($column->getPrecision() && $column->getScale()) {
            $buffer[] = '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
        }

        $properties = $column->getProperties();
        $buffer[] = $column->getType() === 'filestream' ? 'FILESTREAM' : '';
        $buffer[] = isset($properties['rowguidcol']) ? 'ROWGUIDCOL' : '';

        $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL';

        if ($create === true) {
            if ($column->getDefault() === null && $column->isNull()) {
                $buffer[] = ' DEFAULT NULL';
            } else {
                $buffer[] = $this->getDefaultValueDefinition($column->getDefault());
            }
        }

        if ($column->isIdentity()) {
            $buffer[] = 'IDENTITY(1, 1)';
        }

        return implode(' ', $buffer);
    }

    /**
     * Gets the SqlServer Index Definition for an Index object.
     *
     * @param \Phinx\Db\Table\Index $index Index
     * @return string
     */
    protected function getIndexSqlDefinition(Index $index, $tableName)
    {
        if (is_string($index->getName())) {
            $indexName = $index->getName();
        } else {
            $columnNames = $index->getColumns();
            if (is_string($columnNames)) {
                $columnNames = [$columnNames];
            }
            $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames));
        }
        $def = sprintf(
            "CREATE %s INDEX %s ON %s (%s);",
            ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''),
            $indexName,
            $this->quoteTableName($tableName),
            '[' . implode('],[', $index->getColumns()) . ']'
        );

        return $def;
    }

    /**
     * Gets the SqlServer Foreign Key Definition for an ForeignKey object.
     *
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
     * @return string
     */
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, $tableName)
    {
        $constraintName = $foreignKey->getConstraint() ?: $tableName . '_' . implode('_', $foreignKey->getColumns());
        $def = ' CONSTRAINT ' . $this->quoteColumnName($constraintName);
        $def .= ' FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")';
        $def .= " REFERENCES {$this->quoteTableName($foreignKey->getReferencedTable()->getName())} (\"" . implode('", "', $foreignKey->getReferencedColumns()) . '")';
        if ($foreignKey->getOnDelete()) {
            $def .= " ON DELETE {$foreignKey->getOnDelete()}";
        }
        if ($foreignKey->getOnUpdate()) {
            $def .= " ON UPDATE {$foreignKey->getOnUpdate()}";
        }

        return $def;
    }

    /**
     * {@inheritdoc}
     */
    public function getColumnTypes()
    {
        return array_merge(parent::getColumnTypes(), ['filestream']);
    }

    /**
     * Records a migration being run.
     *
     * @param \Phinx\Migration\MigrationInterface $migration Migration
     * @param string $direction Direction
     * @param int $startTime Start Time
     * @param int $endTime End Time
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function migrated(\Phinx\Migration\MigrationInterface $migration, $direction, $startTime, $endTime)
    {
        $startTime = str_replace(' ', 'T', $startTime);
        $endTime = str_replace(' ', 'T', $endTime);

        return parent::migrated($migration, $direction, $startTime, $endTime);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;

/**
 * Phinx SQLite Adapter.
 *
 * @author Rob Morgan <robbym@gmail.com>
 * @author Richard McIntyre <richard.mackstars@gmail.com>
 */
class SQLiteAdapter extends PdoAdapter implements AdapterInterface
{
    protected $definitionsWithLimits = [
        'CHARACTER',
        'VARCHAR',
        'VARYING CHARACTER',
        'NCHAR',
        'NATIVE CHARACTER',
        'NVARCHAR'
    ];

    /**
     * {@inheritdoc}
     */
    public function connect()
    {
        if ($this->connection === null) {
            if (!class_exists('PDO') || !in_array('sqlite', \PDO::getAvailableDrivers(), true)) {
                // @codeCoverageIgnoreStart
                throw new \RuntimeException('You need to enable the PDO_SQLITE extension for Phinx to run properly.');
                // @codeCoverageIgnoreEnd
            }

            $db = null;
            $options = $this->getOptions();

            // if port is specified use it, otherwise use the MySQL default
            if (isset($options['memory'])) {
                $dsn = 'sqlite::memory:';
            } else {
                $dsn = 'sqlite:' . $options['name'];
                if (file_exists($options['name'] . '.sqlite3')) {
                    $dsn = 'sqlite:' . $options['name'] . '.sqlite3';
                }
            }

            try {
                $db = new \PDO($dsn);
            } catch (\PDOException $exception) {
                throw new \InvalidArgumentException(sprintf(
                    'There was a problem connecting to the database: %s',
                    $exception->getMessage()
                ));
            }

            $this->setConnection($db);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function disconnect()
    {
        $this->connection = null;
    }

    /**
     * {@inheritdoc}
     */
    public function hasTransactions()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function beginTransaction()
    {
        $this->execute('BEGIN TRANSACTION');
    }

    /**
     * {@inheritdoc}
     */
    public function commitTransaction()
    {
        $this->execute('COMMIT');
    }

    /**
     * {@inheritdoc}
     */
    public function rollbackTransaction()
    {
        $this->execute('ROLLBACK');
    }

    /**
     * {@inheritdoc}
     */
    public function quoteTableName($tableName)
    {
        return str_replace('.', '`.`', $this->quoteColumnName($tableName));
    }

    /**
     * {@inheritdoc}
     */
    public function quoteColumnName($columnName)
    {
        return '`' . str_replace('`', '``', $columnName) . '`';
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        $tables = [];
        $rows = $this->fetchAll(sprintf('SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'%s\'', $tableName));
        foreach ($rows as $row) {
            $tables[] = strtolower($row[0]);
        }

        return in_array(strtolower($tableName), $tables);
    }

    /**
     * {@inheritdoc}
     */
    public function createTable(Table $table)
    {
        // Add the default primary key
        $columns = $table->getPendingColumns();
        $options = $table->getOptions();
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
            $column = new Column();
            $column->setName('id')
                   ->setType('integer')
                   ->setIdentity(true);

            array_unshift($columns, $column);
        } elseif (isset($options['id']) && is_string($options['id'])) {
            // Handle id => "field_name" to support AUTO_INCREMENT
            $column = new Column();
            $column->setName($options['id'])
                   ->setType('integer')
                   ->setIdentity(true);

            array_unshift($columns, $column);
        }

        $sql = 'CREATE TABLE ';
        $sql .= $this->quoteTableName($table->getName()) . ' (';
        foreach ($columns as $column) {
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
        }

        // set the primary key(s)
        if (isset($options['primary_key'])) {
            $sql = rtrim($sql);
            $sql .= ' PRIMARY KEY (';
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
                $sql .= $this->quoteColumnName($options['primary_key']);
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
            }
            $sql .= ')';
        } else {
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
        }

        // set the foreign keys
        $foreignKeys = $table->getForeignKeys();
        if (!empty($foreignKeys)) {
            foreach ($foreignKeys as $foreignKey) {
                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey);
            }
        }

        $sql = rtrim($sql) . ');';
        // execute the sql
        $this->execute($sql);

        foreach ($table->getIndexes() as $index) {
            $this->addIndex($table, $index);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function renameTable($tableName, $newTableName)
    {
        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function dropTable($tableName)
    {
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function truncateTable($tableName)
    {
        $sql = sprintf(
            'DELETE FROM %s',
            $this->quoteTableName($tableName)
        );

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function getColumns($tableName)
    {
        $columns = [];
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));

        foreach ($rows as $columnInfo) {
            $column = new Column();
            $type = strtolower($columnInfo['type']);
            $column->setName($columnInfo['name'])
                   ->setNull($columnInfo['notnull'] !== '1')
                   ->setDefault($columnInfo['dflt_value']);

            $phinxType = $this->getPhinxType($type);
            $column->setType($phinxType['name'])
                   ->setLimit($phinxType['limit']);

            if ($columnInfo['pk'] == 1) {
                $column->setIdentity(true);
            }

            $columns[] = $column;
        }

        return $columns;
    }

    /**
     * {@inheritdoc}
     */
    public function hasColumn($tableName, $columnName)
    {
        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
        foreach ($rows as $column) {
            if (strcasecmp($column['name'], $columnName) === 0) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function addColumn(Table $table, Column $column)
    {
        $sql = sprintf(
            'ALTER TABLE %s ADD COLUMN %s %s',
            $this->quoteTableName($table->getName()),
            $this->quoteColumnName($column->getName()),
            $this->getColumnSqlDefinition($column)
        );

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function renameColumn($tableName, $columnName, $newColumnName)
    {
        $tmpTableName = 'tmp_' . $tableName;

        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');

        $sql = '';
        foreach ($rows as $table) {
            if ($table['tbl_name'] === $tableName) {
                $sql = $table['sql'];
            }
        }

        $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
        $selectColumns = [];
        $writeColumns = [];
        foreach ($columns as $column) {
            $selectName = $column['name'];
            $writeName = ($selectName == $columnName)? $newColumnName : $selectName;
            $selectColumns[] = $this->quoteColumnName($selectName);
            $writeColumns[] = $this->quoteColumnName($writeName);
        }

        if (!in_array($this->quoteColumnName($columnName), $selectColumns)) {
            throw new \InvalidArgumentException(sprintf(
                'The specified column doesn\'t exist: ' . $columnName
            ));
        }

        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));

        $sql = str_replace(
            $this->quoteColumnName($columnName),
            $this->quoteColumnName($newColumnName),
            $sql
        );
        $this->execute($sql);

        $sql = sprintf(
            'INSERT INTO %s(%s) SELECT %s FROM %s',
            $tableName,
            implode(', ', $writeColumns),
            implode(', ', $selectColumns),
            $tmpTableName
        );

        $this->execute($sql);

        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function changeColumn($tableName, $columnName, Column $newColumn)
    {
        // TODO: DRY this up....
        $tmpTableName = 'tmp_' . $tableName;

        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');

        $sql = '';
        foreach ($rows as $table) {
            if ($table['tbl_name'] === $tableName) {
                $sql = $table['sql'];
            }
        }

        $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
        $selectColumns = [];
        $writeColumns = [];
        foreach ($columns as $column) {
            $selectName = $column['name'];
            $writeName = ($selectName === $columnName)? $newColumn->getName() : $selectName;
            $selectColumns[] = $this->quoteColumnName($selectName);
            $writeColumns[] = $this->quoteColumnName($writeName);
        }

        if (!in_array($this->quoteColumnName($columnName), $selectColumns)) {
            throw new \InvalidArgumentException(sprintf(
                'The specified column doesn\'t exist: ' . $columnName
            ));
        }

        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));

        $sql = preg_replace(
            sprintf("/%s(?:\/\*.*?\*\/|\([^)]+\)|'[^']*?'|[^,])+([,)])/", $this->quoteColumnName($columnName)),
            sprintf('%s %s$1', $this->quoteColumnName($newColumn->getName()), $this->getColumnSqlDefinition($newColumn)),
            $sql,
            1
        );

        $this->execute($sql);

        $sql = sprintf(
            'INSERT INTO %s(%s) SELECT %s FROM %s',
            $tableName,
            implode(', ', $writeColumns),
            implode(', ', $selectColumns),
            $tmpTableName
        );

        $this->execute($sql);
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function dropColumn($tableName, $columnName)
    {
        // TODO: DRY this up....
        $tmpTableName = 'tmp_' . $tableName;

        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');

        $sql = '';
        foreach ($rows as $table) {
            if ($table['tbl_name'] === $tableName) {
                $sql = $table['sql'];
            }
        }

        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
        $columns = [];
        $columnType = null;
        foreach ($rows as $row) {
            if ($row['name'] !== $columnName) {
                $columns[] = $row['name'];
            } else {
                $found = true;
                $columnType = $row['type'];
            }
        }

        if (!isset($found)) {
            throw new \InvalidArgumentException(sprintf(
                'The specified column doesn\'t exist: ' . $columnName
            ));
        }

        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName));

        $sql = preg_replace(
            sprintf("/%s\s%s.*(,\s(?!')|\)$)/U", preg_quote($this->quoteColumnName($columnName)), preg_quote($columnType)),
            "",
            $sql
        );

        if (substr($sql, -2) === ', ') {
            $sql = substr($sql, 0, -2) . ')';
        }

        $this->execute($sql);

        $sql = sprintf(
            'INSERT INTO %s(%s) SELECT %s FROM %s',
            $tableName,
            implode(', ', $columns),
            implode(', ', $columns),
            $tmpTableName
        );

        $this->execute($sql);
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
    }

    /**
     * Get an array of indexes from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    protected function getIndexes($tableName)
    {
        $indexes = [];
        $rows = $this->fetchAll(sprintf('pragma index_list(%s)', $tableName));

        foreach ($rows as $row) {
            $indexData = $this->fetchAll(sprintf('pragma index_info(%s)', $row['name']));
            if (!isset($indexes[$tableName])) {
                $indexes[$tableName] = ['index' => $row['name'], 'columns' => []];
            }
            foreach ($indexData as $indexItem) {
                $indexes[$tableName]['columns'][] = strtolower($indexItem['name']);
            }
        }

        return $indexes;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $columns = array_map('strtolower', $columns);
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $index) {
            $a = array_diff($columns, $index['columns']);
            if (empty($a)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function hasIndexByName($tableName, $indexName)
    {
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $index) {
            if ($indexName === $index['index']) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function addIndex(Table $table, Index $index)
    {
        $indexColumnArray = [];
        foreach ($index->getColumns() as $column) {
            $indexColumnArray[] = sprintf('`%s` ASC', $column);
        }
        $indexColumns = implode(',', $indexColumnArray);
        $this->execute(
            sprintf(
                'CREATE %s ON %s (%s)',
                $this->getIndexSqlDefinition($table, $index),
                $this->quoteTableName($table->getName()),
                $indexColumns
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndex($tableName, $columns)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $indexes = $this->getIndexes($tableName);
        $columns = array_map('strtolower', $columns);

        foreach ($indexes as $index) {
            $a = array_diff($columns, $index['columns']);
            if (empty($a)) {
                $this->execute(
                    sprintf(
                        'DROP INDEX %s',
                        $this->quoteColumnName($index['index'])
                    )
                );

                return;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function dropIndexByName($tableName, $indexName)
    {
        $indexes = $this->getIndexes($tableName);

        foreach ($indexes as $index) {
            if ($indexName === $index['index']) {
                $this->execute(
                    sprintf(
                        'DROP INDEX %s',
                        $this->quoteColumnName($indexName)
                    )
                );

                return;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasForeignKey($tableName, $columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }
        $foreignKeys = $this->getForeignKeys($tableName);

        $a = array_diff($columns, $foreignKeys);
        if (empty($a)) {
            return true;
        }

        return false;
    }

    /**
     * Get an array of foreign keys from a particular table.
     *
     * @param string $tableName Table Name
     * @return array
     */
    protected function getForeignKeys($tableName)
    {
        $foreignKeys = [];
        $rows = $this->fetchAll(
            "SELECT sql, tbl_name
              FROM (
                    SELECT sql sql, type type, tbl_name tbl_name, name name
                      FROM sqlite_master
                     UNION ALL
                    SELECT sql, type, tbl_name, name
                      FROM sqlite_temp_master
                   )
             WHERE type != 'meta'
               AND sql NOTNULL
               AND name NOT LIKE 'sqlite_%'
             ORDER BY substr(type, 2, 1), name"
        );

        foreach ($rows as $row) {
            if ($row['tbl_name'] === $tableName) {
                if (strpos($row['sql'], 'REFERENCES') !== false) {
                    preg_match_all("/\(`([^`]*)`\) REFERENCES/", $row['sql'], $matches);
                    foreach ($matches[1] as $match) {
                        $foreignKeys[] = $match;
                    }
                }
            }
        }

        return $foreignKeys;
    }

    /**
     * {@inheritdoc}
     */
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
    {
        // TODO: DRY this up....
        $this->execute('pragma foreign_keys = ON');

        $tmpTableName = 'tmp_' . $table->getName();
        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');

        $sql = '';
        foreach ($rows as $row) {
            if ($row['tbl_name'] === $table->getName()) {
                $sql = $row['sql'];
            }
        }

        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($table->getName())));
        $columns = [];
        foreach ($rows as $column) {
            $columns[] = $this->quoteColumnName($column['name']);
        }

        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($table->getName()), $tmpTableName));

        $sql = substr($sql, 0, -1) . ',' . $this->getForeignKeySqlDefinition($foreignKey) . ')';
        $this->execute($sql);

        $sql = sprintf(
            'INSERT INTO %s(%s) SELECT %s FROM %s',
            $this->quoteTableName($table->getName()),
            implode(', ', $columns),
            implode(', ', $columns),
            $this->quoteTableName($tmpTableName)
        );

        $this->execute($sql);
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function dropForeignKey($tableName, $columns, $constraint = null)
    {
        // TODO: DRY this up....
        if (is_string($columns)) {
            $columns = [$columns]; // str to array
        }

        $tmpTableName = 'tmp_' . $tableName;

        $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\'');

        $sql = '';
        foreach ($rows as $table) {
            if ($table['tbl_name'] === $tableName) {
                $sql = $table['sql'];
            }
        }

        $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName)));
        $replaceColumns = [];
        foreach ($rows as $row) {
            if (!in_array($row['name'], $columns)) {
                $replaceColumns[] = $row['name'];
            } else {
                $found = true;
            }
        }

        if (!isset($found)) {
            throw new \InvalidArgumentException(sprintf(
                'The specified column doesn\'t exist: '
            ));
        }

        $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $tmpTableName));

        foreach ($columns as $columnName) {
            $search = sprintf(
                "/,[^,]*\(%s(?:,`?(.*)`?)?\) REFERENCES[^,]*\([^\)]*\)[^,)]*/",
                $this->quoteColumnName($columnName)
            );
            $sql = preg_replace($search, '', $sql, 1);
        }

        $this->execute($sql);

        $sql = sprintf(
            'INSERT INTO %s(%s) SELECT %s FROM %s',
            $tableName,
            implode(', ', $columns),
            implode(', ', $columns),
            $tmpTableName
        );

        $this->execute($sql);
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName)));
    }

    /**
     * {@inheritdoc}
     */
    public function insert(Table $table, $row)
    {
        $sql = sprintf(
            "INSERT INTO %s ",
            $this->quoteTableName($table->getName())
        );

        $columns = array_keys($row);
        $sql .= "(" . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ")";
        $sql .= " VALUES ";

        $sql .= "(" . implode(', ', array_map(function ($value) {
            if (is_numeric($value)) {
                return $value;
            }

            if ($value === null) {
                return 'null';
            }

                return $this->getConnection()->quote($value);
        }, $row)) . ")";

        $this->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function getSqlType($type, $limit = null)
    {
        switch ($type) {
            case static::PHINX_TYPE_STRING:
                return ['name' => 'varchar', 'limit' => 255];
            case static::PHINX_TYPE_CHAR:
                return ['name' => 'char', 'limit' => 255];
            case static::PHINX_TYPE_TEXT:
                return ['name' => 'text'];
            case static::PHINX_TYPE_INTEGER:
                return ['name' => 'integer'];
            case static::PHINX_TYPE_BIG_INTEGER:
                return ['name' => 'bigint'];
            case static::PHINX_TYPE_FLOAT:
                return ['name' => 'float'];
            case static::PHINX_TYPE_DECIMAL:
                return ['name' => 'decimal'];
            case static::PHINX_TYPE_DATETIME:
                return ['name' => 'datetime'];
            case static::PHINX_TYPE_TIMESTAMP:
                return ['name' => 'datetime'];
            case static::PHINX_TYPE_TIME:
                return ['name' => 'time'];
            case static::PHINX_TYPE_DATE:
                return ['name' => 'date'];
            case static::PHINX_TYPE_BLOB:
            case static::PHINX_TYPE_BINARY:
                return ['name' => 'blob'];
            case static::PHINX_TYPE_BOOLEAN:
                return ['name' => 'boolean'];
            case static::PHINX_TYPE_UUID:
                return ['name' => 'char', 'limit' => 36];
            case static::PHINX_TYPE_ENUM:
                return ['name' => 'enum'];
            // Geospatial database types
            // No specific data types exist in SQLite, instead all geospatial
            // functionality is handled in the client. See also: SpatiaLite.
            case static::PHINX_TYPE_GEOMETRY:
            case static::PHINX_TYPE_POLYGON:
                return ['name' => 'text'];
            case static::PHINX_TYPE_LINESTRING:
                return ['name' => 'varchar', 'limit' => 255];
            case static::PHINX_TYPE_POINT:
                return ['name' => 'float'];
            default:
                throw new \RuntimeException('The type: "' . $type . '" is not supported.');
        }
    }

    /**
     * Returns Phinx type by SQL type
     *
     * @param string $sqlTypeDef SQL type
     * @returns string Phinx type
     */
    public function getPhinxType($sqlTypeDef)
    {
        if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*$/', $sqlTypeDef, $matches)) {
            throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported');
        } else {
            $limit = null;
            $precision = null;
            $type = $matches[1];
            if (count($matches) > 2) {
                $limit = $matches[3] ?: null;
            }
            if (count($matches) > 4) {
                $precision = $matches[5];
            }
            switch ($matches[1]) {
                case 'varchar':
                    $type = static::PHINX_TYPE_STRING;
                    if ($limit === 255) {
                        $limit = null;
                    }
                    break;
                case 'char':
                    $type = static::PHINX_TYPE_CHAR;
                    if ($limit === 255) {
                        $limit = null;
                    }
                    if ($limit === 36) {
                        $type = static::PHINX_TYPE_UUID;
                    }
                    break;
                case 'int':
                    $type = static::PHINX_TYPE_INTEGER;
                    if ($limit === 11) {
                        $limit = null;
                    }
                    break;
                case 'bigint':
                    if ($limit === 11) {
                        $limit = null;
                    }
                    $type = static::PHINX_TYPE_BIG_INTEGER;
                    break;
                case 'blob':
                    $type = static::PHINX_TYPE_BINARY;
                    break;
            }
            if ($type === 'tinyint') {
                if ($matches[3] === 1) {
                    $type = static::PHINX_TYPE_BOOLEAN;
                    $limit = null;
                }
            }

            $this->getSqlType($type);

            return [
                'name' => $type,
                'limit' => $limit,
                'precision' => $precision
            ];
        }
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options = [])
    {
        touch($name . '.sqlite3');
    }

    /**
     * {@inheritdoc}
     */
    public function hasDatabase($name)
    {
        return is_file($name . '.sqlite3');
    }

    /**
     * {@inheritdoc}
     */
    public function dropDatabase($name)
    {
        if (file_exists($name . '.sqlite3')) {
            unlink($name . '.sqlite3');
        }
    }

    /**
     * Get the definition for a `DEFAULT` statement.
     *
     * @param  mixed $default
     * @return string
     */
    protected function getDefaultValueDefinition($default)
    {
        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
            $default = $this->getConnection()->quote($default);
        } elseif (is_bool($default)) {
            $default = $this->castToBool($default);
        }

        return isset($default) ? ' DEFAULT ' . $default : '';
    }

    /**
     * Gets the SQLite Column Definition for a Column object.
     *
     * @param \Phinx\Db\Table\Column $column Column
     * @return string
     */
    protected function getColumnSqlDefinition(Column $column)
    {
        $sqlType = $this->getSqlType($column->getType());
        $def = '';
        $def .= strtoupper($sqlType['name']);
        if ($column->getPrecision() && $column->getScale()) {
            $def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')';
        }
        $limitable = in_array(strtoupper($sqlType['name']), $this->definitionsWithLimits);
        if (($column->getLimit() || isset($sqlType['limit'])) && $limitable) {
            $def .= '(' . ($column->getLimit() ?: $sqlType['limit']) . ')';
        }
        if (($values = $column->getValues()) && is_array($values)) {
            $def .= " CHECK({$column->getName()} IN ('" . implode("', '", $values) . "'))";
        }

        $default = $column->getDefault();

        $def .= ($column->isNull() || is_null($default)) ? ' NULL' : ' NOT NULL';
        $def .= $this->getDefaultValueDefinition($default);
        $def .= ($column->isIdentity()) ? ' PRIMARY KEY AUTOINCREMENT' : '';

        if ($column->getUpdate()) {
            $def .= ' ON UPDATE ' . $column->getUpdate();
        }

        $def .= $this->getCommentDefinition($column);

        return $def;
    }

    /**
     * Gets the comment Definition for a Column object.
     *
     * @param \Phinx\Db\Table\Column $column Column
     * @return string
     */
    protected function getCommentDefinition(Column $column)
    {
        if ($column->getComment()) {
            return ' /* ' . $column->getComment() . ' */ ';
        }

        return '';
    }

    /**
     * Gets the SQLite Index Definition for an Index object.
     *
     * @param \Phinx\Db\Table $table Table
     * @param \Phinx\Db\Table\Index $index Index
     * @return string
     */
    protected function getIndexSqlDefinition(Table $table, Index $index)
    {
        if ($index->getType() === Index::UNIQUE) {
            $def = 'UNIQUE INDEX';
        } else {
            $def = 'INDEX';
        }
        if (is_string($index->getName())) {
            $indexName = $index->getName();
        } else {
            $indexName = $table->getName() . '_';
            foreach ($index->getColumns() as $column) {
                $indexName .= $column . '_';
            }
            $indexName .= 'index';
        }
        $def .= ' `' . $indexName . '`';

        return $def;
    }

    /**
     * {@inheritdoc}
     */
    public function getColumnTypes()
    {
        return array_merge(parent::getColumnTypes(), ['enum']);
    }

    /**
     * Gets the SQLite Foreign Key Definition for an ForeignKey object.
     *
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
     * @return string
     */
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey)
    {
        $def = '';
        if ($foreignKey->getConstraint()) {
            $def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint());
        } else {
            $columnNames = [];
            foreach ($foreignKey->getColumns() as $column) {
                $columnNames[] = $this->quoteColumnName($column);
            }
            $def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')';
            $refColumnNames = [];
            foreach ($foreignKey->getReferencedColumns() as $column) {
                $refColumnNames[] = $this->quoteColumnName($column);
            }
            $def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')';
            if ($foreignKey->getOnDelete()) {
                $def .= ' ON DELETE ' . $foreignKey->getOnDelete();
            }
            if ($foreignKey->getOnUpdate()) {
                $def .= ' ON UPDATE ' . $foreignKey->getOnUpdate();
            }
        }

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

use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Base Abstract Database Adapter.
 */
abstract class AbstractAdapter implements AdapterInterface
{
    /**
     * @var array
     */
    protected $options = [];

    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    protected $input;

    /**
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    protected $output;

    /**
     * @var string
     */
    protected $schemaTableName = 'phinxlog';

    /**
     * Class Constructor.
     *
     * @param array $options Options
     * @param \Symfony\Component\Console\Input\InputInterface $input Input Interface
     * @param \Symfony\Component\Console\Output\OutputInterface  $output Output Interface
     */
    public function __construct(array $options, InputInterface $input = null, OutputInterface $output = null)
    {
        $this->setOptions($options);
        if ($input !== null) {
            $this->setInput($input);
        }
        if ($output !== null) {
            $this->setOutput($output);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function setOptions(array $options)
    {
        $this->options = $options;

        if (isset($options['default_migration_table'])) {
            $this->setSchemaTableName($options['default_migration_table']);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * {@inheritdoc}
     */
    public function hasOption($name)
    {
        return isset($this->options[$name]);
    }

    /**
     * {@inheritdoc}
     */
    public function getOption($name)
    {
        if (!$this->hasOption($name)) {
            return null;
        }

        return $this->options[$name];
    }

    /**
     * {@inheritdoc}
     */
    public function setInput(InputInterface $input)
    {
        $this->input = $input;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * {@inheritdoc}
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getOutput()
    {
        if ($this->output === null) {
            $output = new NullOutput();
            $this->setOutput($output);
        }

        return $this->output;
    }

    /**
     * {@inheritdoc}
     *
     * @return array
     */
    public function getVersions()
    {
        $rows = $this->getVersionLog();

        return array_keys($rows);
    }

    /**
     * Gets the schema table name.
     *
     * @return string
     */
    public function getSchemaTableName()
    {
        return $this->schemaTableName;
    }

    /**
     * Sets the schema table name.
     *
     * @param string $schemaTableName Schema Table Name
     * @return $this
     */
    public function setSchemaTableName($schemaTableName)
    {
        $this->schemaTableName = $schemaTableName;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function hasSchemaTable()
    {
        return $this->hasTable($this->getSchemaTableName());
    }

    /**
     * {@inheritdoc}
     */
    public function createSchemaTable()
    {
        try {
            $options = [
                'id' => false,
                'primary_key' => 'version'
            ];

            $table = new Table($this->getSchemaTableName(), $options, $this);
            $table->addColumn('version', 'biginteger')
                ->addColumn('migration_name', 'string', ['limit' => 100, 'default' => null, 'null' => true])
                ->addColumn('start_time', 'timestamp', ['default' => null, 'null' => true])
                ->addColumn('end_time', 'timestamp', ['default' => null, 'null' => true])
                ->addColumn('breakpoint', 'boolean', ['default' => false])
                ->save();
        } catch (\Exception $exception) {
            throw new \InvalidArgumentException('There was a problem creating the schema table: ' . $exception->getMessage());
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getAdapterType()
    {
        return $this->getOption('adapter');
    }

    /**
     * {@inheritdoc}
     */
    public function isValidColumnType(Column $column)
    {
        return in_array($column->getType(), $this->getColumnTypes());
    }

    /**
     * Determines if instead of executing queries a dump to standard output is needed
     *
     * @return bool
     */
    public function isDryRunEnabled()
    {
        $input = $this->getInput();

        return ($input && $input->hasOption('dry-run')) ? $input->getOption('dry-run') : false;
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db\Adapter
 */
namespace Phinx\Db\Adapter;

/**
 * Wrapper Interface.
 *
 * @author Woody Gilk <woody.gilk@gmail.com>
 */
interface WrapperInterface
{
    /**
     * Class constructor, must always wrap another adapter.
     *
     * @param  \Phinx\Db\Adapter\AdapterInterface $adapter
     */
    public function __construct(AdapterInterface $adapter);

    /**
     * Sets the database adapter to proxy commands to.
     *
     * @param  \Phinx\Db\Adapter\AdapterInterface $adapter
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function setAdapter(AdapterInterface $adapter);

    /**
     * Gets the database adapter.
     *
     * @throws \RuntimeException if the adapter has not been set
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getAdapter();
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db
 */
namespace Phinx\Db\Table;

class Index
{
    /**
     * @var string
     */
    const UNIQUE = 'unique';

    /**
     * @var string
     */
    const INDEX = 'index';

    /**
     * @var string
     */
    const FULLTEXT = 'fulltext';

    /**
     * @var array
     */
    protected $columns;

    /**
     * @var string
     */
    protected $type = self::INDEX;

    /**
     * @var string
     */
    protected $name = null;

    /**
     * @var integer
     */
    protected $limit = null;

    /**
     * Sets the index columns.
     *
     * @param array $columns
     * @return \Phinx\Db\Table\Index
     */
    public function setColumns($columns)
    {
        $this->columns = $columns;

        return $this;
    }

    /**
     * Gets the index columns.
     *
     * @return array
     */
    public function getColumns()
    {
        return $this->columns;
    }

    /**
     * Sets the index type.
     *
     * @param string $type
     * @return \Phinx\Db\Table\Index
     */
    public function setType($type)
    {
        $this->type = $type;

        return $this;
    }

    /**
     * Gets the index type.
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Sets the index name.
     *
     * @param string $name
     * @return \Phinx\Db\Table\Index
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Gets the index name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets the index limit.
     *
     * @param int $limit
     * @return \Phinx\Db\Table\Index
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;

        return $this;
    }

    /**
     * Gets the index limit.
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * Utility method that maps an array of index options to this objects methods.
     *
     * @param array $options Options
     * @throws \RuntimeException
     * @return \Phinx\Db\Table\Index
     */
    public function setOptions($options)
    {
        // Valid Options
        $validOptions = ['type', 'unique', 'name', 'limit'];
        foreach ($options as $option => $value) {
            if (!in_array($option, $validOptions, true)) {
                throw new \RuntimeException(sprintf('"%s" is not a valid index option.', $option));
            }

            // handle $options['unique']
            if (strcasecmp($option, self::UNIQUE) === 0) {
                if ((bool)$value) {
                    $this->setType(self::UNIQUE);
                }
                continue;
            }

            $method = 'set' . ucfirst($option);
            $this->$method($value);
        }

        return $this;
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db
 * @author     Leonid Kuzmin <lndkuzmin@gmail.com>
 */
namespace Phinx\Db\Table;

use Phinx\Db\Table;

class ForeignKey
{
    const CASCADE = 'CASCADE';
    const RESTRICT = 'RESTRICT';
    const SET_NULL = 'SET NULL';
    const NO_ACTION = 'NO ACTION';

    /**
     * @var array
     */
    protected $columns = [];

    /**
     * @var \Phinx\Db\Table
     */
    protected $referencedTable;

    /**
     * @var array
     */
    protected $referencedColumns = [];

    /**
     * @var string
     */
    protected $onDelete;

    /**
     * @var string
     */
    protected $onUpdate;

    /**
     * @var string|boolean
     */
    protected $constraint;

    /**
     * Sets the foreign key columns.
     *
     * @param array|string $columns
     * @return \Phinx\Db\Table\ForeignKey
     */
    public function setColumns($columns)
    {
        $this->columns = is_string($columns) ? [$columns] : $columns;

        return $this;
    }

    /**
     * Gets the foreign key columns.
     *
     * @return array
     */
    public function getColumns()
    {
        return $this->columns;
    }

    /**
     * Sets the foreign key referenced table.
     *
     * @param \Phinx\Db\Table $table
     * @return \Phinx\Db\Table\ForeignKey
     */
    public function setReferencedTable(Table $table)
    {
        $this->referencedTable = $table;

        return $this;
    }

    /**
     * Gets the foreign key referenced table.
     *
     * @return \Phinx\Db\Table
     */
    public function getReferencedTable()
    {
        return $this->referencedTable;
    }

    /**
     * Sets the foreign key referenced columns.
     *
     * @param array $referencedColumns
     * @return \Phinx\Db\Table\ForeignKey
     */
    public function setReferencedColumns(array $referencedColumns)
    {
        $this->referencedColumns = $referencedColumns;

        return $this;
    }

    /**
     * Gets the foreign key referenced columns.
     *
     * @return array
     */
    public function getReferencedColumns()
    {
        return $this->referencedColumns;
    }

    /**
     * Sets ON DELETE action for the foreign key.
     *
     * @param string $onDelete
     * @return \Phinx\Db\Table\ForeignKey
     */
    public function setOnDelete($onDelete)
    {
        $this->onDelete = $this->normalizeAction($onDelete);

        return $this;
    }

    /**
     * Gets ON DELETE action for the foreign key.
     *
     * @return string
     */
    public function getOnDelete()
    {
        return $this->onDelete;
    }

    /**
     * Gets ON UPDATE action for the foreign key.
     *
     * @return string
     */
    public function getOnUpdate()
    {
        return $this->onUpdate;
    }

    /**
     * Sets ON UPDATE action for the foreign key.
     *
     * @param string $onUpdate
     * @return \Phinx\Db\Table\ForeignKey
     */
    public function setOnUpdate($onUpdate)
    {
        $this->onUpdate = $this->normalizeAction($onUpdate);

        return $this;
    }

    /**
     * Sets constraint for the foreign key.
     *
     * @param string $constraint
     * @return \Phinx\Db\Table\ForeignKey
     */
    public function setConstraint($constraint)
    {
        $this->constraint = $constraint;

        return $this;
    }

    /**
     * Gets constraint name for the foreign key.
     *
     * @return string|bool
     */
    public function getConstraint()
    {
        return $this->constraint;
    }

    /**
     * Utility method that maps an array of index options to this objects methods.
     *
     * @param array $options Options
     * @throws \RuntimeException
     * @throws \InvalidArgumentException
     * @return \Phinx\Db\Table\ForeignKey
     */
    public function setOptions($options)
    {
        // Valid Options
        $validOptions = ['delete', 'update', 'constraint'];
        foreach ($options as $option => $value) {
            if (!in_array($option, $validOptions, true)) {
                throw new \RuntimeException(sprintf('"%s" is not a valid foreign key option.', $option));
            }

            // handle $options['delete'] as $options['update']
            if ('delete' === $option) {
                $this->setOnDelete($value);
            } elseif ('update' === $option) {
                $this->setOnUpdate($value);
            } else {
                $method = 'set' . ucfirst($option);
                $this->$method($value);
            }
        }

        return $this;
    }

    /**
     * From passed value checks if it's correct and fixes if needed
     *
     * @param string $action
     * @throws \InvalidArgumentException
     * @return string
     */
    protected function normalizeAction($action)
    {
        $constantName = 'static::' . str_replace(' ', '_', strtoupper(trim($action)));
        if (!defined($constantName)) {
            throw new \InvalidArgumentException('Unknown action passed: ' . $action);
        }

        return constant($constantName);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Db
 */
namespace Phinx\Db\Table;

use Phinx\Db\Adapter\AdapterInterface;

/**
 *
 * This object is based loosely on: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
 */
class Column
{
    /**
     * @var string
     */
    protected $name;

    /**
     * @var string
     */
    protected $type;

    /**
     * @var integer
     */
    protected $limit = null;

    /**
     * @var boolean
     */
    protected $null = false;

    /**
     * @var mixed
     */
    protected $default = null;

    /**
     * @var boolean
     */
    protected $identity = false;

    /**
     * @var integer
     */
    protected $precision;

    /**
     * @var integer
     */
    protected $scale;

    /**
     * @var string
     */
    protected $after;

    /**
     * @var string
     */
    protected $update;

    /**
     * @var string
     */
    protected $comment;

    /**
     * @var boolean
     */
    protected $signed = true;

    /**
     * @var boolean
     */
    protected $timezone = false;

    /**
     * @var array
     */
    protected $properties = [];

    /**
     * @var string
     */
    protected $collation;

    /**
     * @var string
     */
    protected $encoding;

    /**
     * @var array
     */
    protected $values;

    /**
     * Sets the column name.
     *
     * @param string $name
     * @return \Phinx\Db\Table\Column
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Gets the column name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets the column type.
     *
     * @param string $type
     * @return \Phinx\Db\Table\Column
     */
    public function setType($type)
    {
        $this->type = $type;

        return $this;
    }

    /**
     * Gets the column type.
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Sets the column limit.
     *
     * @param int $limit
     * @return \Phinx\Db\Table\Column
     */
    public function setLimit($limit)
    {
        $this->limit = $limit;

        return $this;
    }

    /**
     * Gets the column limit.
     *
     * @return int
     */
    public function getLimit()
    {
        return $this->limit;
    }

    /**
     * Sets whether the column allows nulls.
     *
     * @param bool $null
     * @return \Phinx\Db\Table\Column
     */
    public function setNull($null)
    {
        $this->null = (bool)$null;

        return $this;
    }

    /**
     * Gets whether the column allows nulls.
     *
     * @return bool
     */
    public function getNull()
    {
        return $this->null;
    }

    /**
     * Does the column allow nulls?
     *
     * @return bool
     */
    public function isNull()
    {
        return $this->getNull();
    }

    /**
     * Sets the default column value.
     *
     * @param mixed $default
     * @return \Phinx\Db\Table\Column
     */
    public function setDefault($default)
    {
        $this->default = $default;

        return $this;
    }

    /**
     * Gets the default column value.
     *
     * @return mixed
     */
    public function getDefault()
    {
        return $this->default;
    }

    /**
     * Sets whether or not the column is an identity column.
     *
     * @param bool $identity
     * @return \Phinx\Db\Table\Column
     */
    public function setIdentity($identity)
    {
        $this->identity = $identity;

        return $this;
    }

    /**
     * Gets whether or not the column is an identity column.
     *
     * @return bool
     */
    public function getIdentity()
    {
        return $this->identity;
    }

    /**
     * Is the column an identity column?
     *
     * @return bool
     */
    public function isIdentity()
    {
        return $this->getIdentity();
    }

    /**
     * Sets the name of the column to add this column after.
     *
     * @param string $after After
     * @return \Phinx\Db\Table\Column
     */
    public function setAfter($after)
    {
        $this->after = $after;

        return $this;
    }

    /**
     * Returns the name of the column to add this column after.
     *
     * @return string
     */
    public function getAfter()
    {
        return $this->after;
    }

    /**
     * Sets the 'ON UPDATE' mysql column function.
     *
     * @param  string $update On Update function
     * @return \Phinx\Db\Table\Column
     */
    public function setUpdate($update)
    {
        $this->update = $update;

        return $this;
    }

    /**
     * Returns the value of the ON UPDATE column function.
     *
     * @return string
     */
    public function getUpdate()
    {
        return $this->update;
    }

    /**
     * Sets the column precision for decimal.
     *
     * @param int $precision
     * @return \Phinx\Db\Table\Column
     */
    public function setPrecision($precision)
    {
        $this->precision = $precision;

        return $this;
    }

    /**
     * Gets the column precision for decimal.
     *
     * @return int
     */
    public function getPrecision()
    {
        return $this->precision;
    }

    /**
     * Sets the column scale for decimal.
     *
     * @param int $scale
     * @return \Phinx\Db\Table\Column
     */
    public function setScale($scale)
    {
        $this->scale = $scale;

        return $this;
    }

    /**
     * Gets the column scale for decimal.
     *
     * @return int
     */
    public function getScale()
    {
        return $this->scale;
    }

    /**
     * Sets the column comment.
     *
     * @param string $comment
     * @return \Phinx\Db\Table\Column
     */
    public function setComment($comment)
    {
        $this->comment = $comment;

        return $this;
    }

    /**
     * Gets the column comment.
     *
     * @return string
     */
    public function getComment()
    {
        return $this->comment;
    }

    /**
     * Sets whether field should be signed.
     *
     * @param bool $signed
     * @return \Phinx\Db\Table\Column
     */
    public function setSigned($signed)
    {
        $this->signed = (bool)$signed;

        return $this;
    }

    /**
     * Gets whether field should be signed.
     *
     * @return bool
     */
    public function getSigned()
    {
        return $this->signed;
    }

    /**
     * Should the column be signed?
     *
     * @return bool
     */
    public function isSigned()
    {
        return $this->getSigned();
    }

    /**
     * Sets whether the field should have a timezone identifier.
     * Used for date/time columns only!
     *
     * @param bool $timezone
     * @return \Phinx\Db\Table\Column
     */
    public function setTimezone($timezone)
    {
        $this->timezone = (bool)$timezone;

        return $this;
    }

    /**
     * Gets whether field has a timezone identifier.
     *
     * @return bool
     */
    public function getTimezone()
    {
        return $this->timezone;
    }

    /**
     * Should the column have a timezone?
     *
     * @return bool
     */
    public function isTimezone()
    {
        return $this->getTimezone();
    }

    /**
     * Sets field properties.
     *
     * @param array $properties
     *
     * @return \Phinx\Db\Table\Column
     */
    public function setProperties($properties)
    {
        $this->properties = $properties;

        return $this;
    }

    /**
     * Gets field properties
     *
     * @return array
     */
    public function getProperties()
    {
        return $this->properties;
    }

    /**
     * Sets field values.
     *
     * @param mixed (array|string) $values
     *
     * @return \Phinx\Db\Table\Column
     */
    public function setValues($values)
    {
        if (!is_array($values)) {
            $values = preg_split('/,\s*/', $values);
        }
        $this->values = $values;

        return $this;
    }

    /**
     * Gets field values
     *
     * @return array
     */
    public function getValues()
    {
        return $this->values;
    }

    /**
     * Sets the column collation.
     *
     * @param string $collation
     *
     * @throws \UnexpectedValueException If collation not allowed for type
     * @return $this
     */
    public function setCollation($collation)
    {
        $allowedTypes = [
            AdapterInterface::PHINX_TYPE_CHAR,
            AdapterInterface::PHINX_TYPE_STRING,
            AdapterInterface::PHINX_TYPE_TEXT,
        ];
        if (!in_array($this->getType(), $allowedTypes)) {
            throw new \UnexpectedValueException('Collation may be set only for types: ' . implode(', ', $allowedTypes));
        }

        $this->collation = $collation;

        return $this;
    }

    /**
     * Gets the column collation.
     *
     * @return string
     */
    public function getCollation()
    {
        return $this->collation;
    }

    /**
     * Sets the column character set.
     *
     * @param string $encoding
     *
     * @throws \UnexpectedValueException If character set not allowed for type
     * @return $this
     */
    public function setEncoding($encoding)
    {
        $allowedTypes = [
            AdapterInterface::PHINX_TYPE_CHAR,
            AdapterInterface::PHINX_TYPE_STRING,
            AdapterInterface::PHINX_TYPE_TEXT,
        ];
        if (!in_array($this->getType(), $allowedTypes)) {
            throw new \UnexpectedValueException('Character set may be set only for types: ' . implode(', ', $allowedTypes));
        }

        $this->encoding = $encoding;

        return $this;
    }

    /**
     * Gets the column character set.
     *
     * @return string
     */
    public function getEncoding()
    {
        return $this->encoding;
    }

    /**
     * Gets all allowed options. Each option must have a corresponding `setFoo` method.
     *
     * @return array
     */
    protected function getValidOptions()
    {
        return [
            'limit',
            'default',
            'null',
            'identity',
            'precision',
            'scale',
            'after',
            'update',
            'comment',
            'signed',
            'timezone',
            'properties',
            'values',
            'collation',
            'encoding',
        ];
    }

    /**
     * Gets all aliased options. Each alias must reference a valid option.
     *
     * @return array
     */
    protected function getAliasedOptions()
    {
        return [
            'length' => 'limit',
        ];
    }

    /**
     * Utility method that maps an array of column options to this objects methods.
     *
     * @param array $options Options
     * @return \Phinx\Db\Table\Column
     */
    public function setOptions($options)
    {
        $validOptions = $this->getValidOptions();
        $aliasOptions = $this->getAliasedOptions();

        foreach ($options as $option => $value) {
            if (isset($aliasOptions[$option])) {
                // proxy alias -> option
                $option = $aliasOptions[$option];
            }

            if (!in_array($option, $validOptions, true)) {
                throw new \RuntimeException(sprintf('"%s" is not a valid column option.', $option));
            }

            $method = 'set' . ucfirst($option);
            $this->$method($value);
        }

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

use Phinx\Db\Adapter\AdapterInterface;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;

/**
 *
 * This object is based loosely on: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
 */
class Table
{
    /**
     * @var string
     */
    protected $name;

    /**
     * @var array
     */
    protected $options = [];

    /**
     * @var \Phinx\Db\Adapter\AdapterInterface
     */
    protected $adapter;

    /**
     * @var array
     */
    protected $columns = [];

    /**
     * @var array
     */
    protected $indexes = [];

    /**
     * @var \Phinx\Db\Table\ForeignKey[]
     */
    protected $foreignKeys = [];

    /**
     * @var array
     */
    protected $data = [];

    /**
     * Class Constuctor.
     *
     * @param string $name Table Name
     * @param array $options Options
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
     */
    public function __construct($name, $options = [], AdapterInterface $adapter = null)
    {
        $this->setName($name);
        $this->setOptions($options);

        if ($adapter !== null) {
            $this->setAdapter($adapter);
        }
    }

    /**
     * Sets the table name.
     *
     * @param string $name Table Name
     * @return \Phinx\Db\Table
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Gets the table name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets the table options.
     *
     * @param array $options
     * @return \Phinx\Db\Table
     */
    public function setOptions($options)
    {
        $this->options = $options;

        return $this;
    }

    /**
     * Gets the table options.
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Sets the database adapter.
     *
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
     * @return \Phinx\Db\Table
     */
    public function setAdapter(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;

        return $this;
    }

    /**
     * Gets the database adapter.
     *
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getAdapter()
    {
        return $this->adapter;
    }

    /**
     * Does the table exist?
     *
     * @return bool
     */
    public function exists()
    {
        return $this->getAdapter()->hasTable($this->getName());
    }

    /**
     * Drops the database table.
     *
     * @return void
     */
    public function drop()
    {
        $this->getAdapter()->dropTable($this->getName());
    }

    /**
     * Renames the database table.
     *
     * @param string $newTableName New Table Name
     * @return \Phinx\Db\Table
     */
    public function rename($newTableName)
    {
        $this->getAdapter()->renameTable($this->getName(), $newTableName);
        $this->setName($newTableName);

        return $this;
    }

    /**
     * Sets an array of columns waiting to be committed.
     * Use setPendingColumns
     *
     * @deprecated
     * @param array $columns Columns
     * @return \Phinx\Db\Table
     */
    public function setColumns($columns)
    {
        $this->setPendingColumns($columns);

        return $this;
    }

    /**
     * Gets an array of the table columns.
     *
     * @return \Phinx\Db\Table\Column[]
     */
    public function getColumns()
    {
        return $this->getAdapter()->getColumns($this->getName());
    }

    /**
     * Sets an array of columns waiting to be committed.
     *
     * @param array $columns Columns
     * @return \Phinx\Db\Table
     */
    public function setPendingColumns($columns)
    {
        $this->columns = $columns;

        return $this;
    }

    /**
     * Gets an array of columns waiting to be committed.
     *
     * @return \Phinx\Db\Table\Column[]
     */
    public function getPendingColumns()
    {
        return $this->columns;
    }

    /**
     * Sets an array of columns waiting to be indexed.
     *
     * @param array $indexes Indexes
     * @return \Phinx\Db\Table
     */
    public function setIndexes($indexes)
    {
        $this->indexes = $indexes;

        return $this;
    }

    /**
     * Gets an array of indexes waiting to be committed.
     *
     * @return array
     */
    public function getIndexes()
    {
        return $this->indexes;
    }

    /**
     * Sets an array of foreign keys waiting to be commited.
     *
     * @param \Phinx\Db\Table\ForeignKey[] $foreignKeys foreign keys
     * @return \Phinx\Db\Table
     */
    public function setForeignKeys($foreignKeys)
    {
        $this->foreignKeys = $foreignKeys;

        return $this;
    }

    /**
     * Gets an array of foreign keys waiting to be commited.
     *
     * @return array|\Phinx\Db\Table\ForeignKey[]
     */
    public function getForeignKeys()
    {
        return $this->foreignKeys;
    }

    /**
     * Sets an array of data to be inserted.
     *
     * @param array $data Data
     * @return \Phinx\Db\Table
     */
    public function setData($data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Gets the data waiting to be inserted.
     *
     * @return array
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Resets all of the pending table changes.
     *
     * @return void
     */
    public function reset()
    {
        $this->setPendingColumns([]);
        $this->setIndexes([]);
        $this->setForeignKeys([]);
        $this->setData([]);
    }

    /**
     * Add a table column.
     *
     * Type can be: string, text, integer, float, decimal, datetime, timestamp,
     * time, date, binary, boolean.
     *
     * Valid options can be: limit, default, null, precision or scale.
     *
     * @param string|\Phinx\Db\Table\Column $columnName Column Name
     * @param string $type Column Type
     * @param array $options Column Options
     * @throws \RuntimeException
     * @throws \InvalidArgumentException
     * @return \Phinx\Db\Table
     */
    public function addColumn($columnName, $type = null, $options = [])
    {
        // we need an adapter set to add a column
        if ($this->getAdapter() === null) {
            throw new \RuntimeException('An adapter must be specified to add a column.');
        }

        // create a new column object if only strings were supplied
        if (!$columnName instanceof Column) {
            $column = new Column();
            $column->setName($columnName);
            $column->setType($type);
            $column->setOptions($options); // map options to column methods
        } else {
            $column = $columnName;
        }

        // Delegate to Adapters to check column type
        if (!$this->getAdapter()->isValidColumnType($column)) {
            throw new \InvalidArgumentException(sprintf(
                'An invalid column type "%s" was specified for column "%s".',
                $column->getType(),
                $column->getName()
            ));
        }

        $this->columns[] = $column;

        return $this;
    }

    /**
     * Remove a table column.
     *
     * @param string $columnName Column Name
     * @return \Phinx\Db\Table
     */
    public function removeColumn($columnName)
    {
        $this->getAdapter()->dropColumn($this->getName(), $columnName);

        return $this;
    }

    /**
     * Rename a table column.
     *
     * @param string $oldName Old Column Name
     * @param string $newName New Column Name
     * @return \Phinx\Db\Table
     */
    public function renameColumn($oldName, $newName)
    {
        $this->getAdapter()->renameColumn($this->getName(), $oldName, $newName);

        return $this;
    }

    /**
     * Change a table column type.
     *
     * @param string        $columnName    Column Name
     * @param string|\Phinx\Db\Table\Column $newColumnType New Column Type
     * @param array         $options       Options
     * @return \Phinx\Db\Table
     */
    public function changeColumn($columnName, $newColumnType, $options = [])
    {
        // create a column object if one wasn't supplied
        if (!$newColumnType instanceof Column) {
            $newColumn = new Column();
            $newColumn->setType($newColumnType);
            $newColumn->setOptions($options);
        } else {
            $newColumn = $newColumnType;
        }

        // if the name was omitted use the existing column name
        if ($newColumn->getName() === null || strlen($newColumn->getName()) === 0) {
            $newColumn->setName($columnName);
        }

        $this->getAdapter()->changeColumn($this->getName(), $columnName, $newColumn);

        return $this;
    }

    /**
     * Checks to see if a column exists.
     *
     * @param string $columnName Column Name
     * @return bool
     */
    public function hasColumn($columnName)
    {
        return $this->getAdapter()->hasColumn($this->getName(), $columnName);
    }

    /**
     * Add an index to a database table.
     *
     * In $options you can specific unique = true/false or name (index name).
     *
     * @param string|array|\Phinx\Db\Table\Index $columns Table Column(s)
     * @param array $options Index Options
     * @return \Phinx\Db\Table
     */
    public function addIndex($columns, $options = [])
    {
        // create a new index object if strings or an array of strings were supplied
        if (!$columns instanceof Index) {
            $index = new Index();
            if (is_string($columns)) {
                $columns = [$columns]; // str to array
            }
            $index->setColumns($columns);
            $index->setOptions($options);
        } else {
            $index = $columns;
        }

        $this->indexes[] = $index;

        return $this;
    }

    /**
     * Removes the given index from a table.
     *
     * @param array $columns Columns
     * @return \Phinx\Db\Table
     */
    public function removeIndex($columns)
    {
        $this->getAdapter()->dropIndex($this->getName(), $columns);

        return $this;
    }

    /**
     * Removes the given index identified by its name from a table.
     *
     * @param string $name Index name
     * @return \Phinx\Db\Table
     */
    public function removeIndexByName($name)
    {
        $this->getAdapter()->dropIndexByName($this->getName(), $name);

        return $this;
    }

    /**
     * Checks to see if an index exists.
     *
     * @param string|array $columns Columns
     * @param array        $options Options
     * @return bool
     */
    public function hasIndex($columns)
    {
        return $this->getAdapter()->hasIndex($this->getName(), $columns);
    }

    /**
     * Add a foreign key to a database table.
     *
     * In $options you can specify on_delete|on_delete = cascade|no_action ..,
     * on_update, constraint = constraint name.
     *
     * @param string|array $columns Columns
     * @param string|\Phinx\Db\Table $referencedTable   Referenced Table
     * @param string|array $referencedColumns Referenced Columns
     * @param array $options Options
     * @return \Phinx\Db\Table
     */
    public function addForeignKey($columns, $referencedTable, $referencedColumns = ['id'], $options = [])
    {
        if (is_string($referencedColumns)) {
            $referencedColumns = [$referencedColumns]; // str to array
        }
        $fk = new ForeignKey();
        if ($referencedTable instanceof Table) {
            $fk->setReferencedTable($referencedTable);
        } else {
            $fk->setReferencedTable(new Table($referencedTable, [], $this->adapter));
        }
        $fk->setColumns($columns)
           ->setReferencedColumns($referencedColumns)
           ->setOptions($options);
        $this->foreignKeys[] = $fk;

        return $this;
    }

    /**
     * Removes the given foreign key from the table.
     *
     * @param string|array $columns    Column(s)
     * @param null|string  $constraint Constraint names
     * @return \Phinx\Db\Table
     */
    public function dropForeignKey($columns, $constraint = null)
    {
        if (is_string($columns)) {
            $columns = [$columns];
        }
        if ($constraint) {
            $this->getAdapter()->dropForeignKey($this->getName(), [], $constraint);
        } else {
            $this->getAdapter()->dropForeignKey($this->getName(), $columns);
        }

        return $this;
    }

    /**
     * Checks to see if a foreign key exists.
     *
     * @param  string|array $columns    Column(s)
     * @param  null|string  $constraint Constraint names
     * @return bool
     */
    public function hasForeignKey($columns, $constraint = null)
    {
        return $this->getAdapter()->hasForeignKey($this->getName(), $columns, $constraint);
    }

    /**
     * Add timestamp columns created_at and updated_at to the table.
     *
     * @param string $createdAtColumnName
     * @param string $updatedAtColumnName
     *
     * @return \Phinx\Db\Table
     */
    public function addTimestamps($createdAtColumnName = 'created_at', $updatedAtColumnName = 'updated_at')
    {
        $createdAtColumnName = is_null($createdAtColumnName) ? 'created_at' : $createdAtColumnName;
        $updatedAtColumnName = is_null($updatedAtColumnName) ? 'updated_at' : $updatedAtColumnName;
        $this->addColumn($createdAtColumnName, 'timestamp', [
                'default' => 'CURRENT_TIMESTAMP',
                'update' => ''
            ])
             ->addColumn($updatedAtColumnName, 'timestamp', [
                 'null' => true,
                 'default' => null
             ]);

        return $this;
    }

    /**
     * Insert data into the table.
     *
     * @param array $data array of data in the form:
     *              array(
     *                  array("col1" => "value1", "col2" => "anotherValue1"),
     *                  array("col2" => "value2", "col2" => "anotherValue2"),
     *              )
     *              or array("col1" => "value1", "col2" => "anotherValue1")
     *
     * @return \Phinx\Db\Table
     */
    public function insert($data)
    {
        // handle array of array situations
        if (isset($data[0]) && is_array($data[0])) {
            foreach ($data as $row) {
                $this->data[] = $row;
            }

            return $this;
        }
        $this->data[] = $data;

        return $this;
    }

    /**
     * Creates a table from the object instance.
     *
     * @return void
     */
    public function create()
    {
        $this->getAdapter()->createTable($this);
        $this->saveData();
        $this->reset(); // reset pending changes
    }

    /**
     * Updates a table from the object instance.
     *
     * @throws \RuntimeException
     * @return void
     */
    public function update()
    {
        if (!$this->exists()) {
            throw new \RuntimeException('Cannot update a table that doesn\'t exist!');
        }

        // update table
        foreach ($this->getPendingColumns() as $column) {
            $this->getAdapter()->addColumn($this, $column);
        }

        foreach ($this->getIndexes() as $index) {
            $this->getAdapter()->addIndex($this, $index);
        }

        foreach ($this->getForeignKeys() as $foreignKey) {
            $this->getAdapter()->addForeignKey($this, $foreignKey);
        }

        $this->saveData();
        $this->reset(); // reset pending changes
    }

    /**
     * Commit the pending data waiting for insertion.
     *
     * @return void
     */
    public function saveData()
    {
        $rows = $this->getData();
        if (empty($rows)) {
            return;
        }

        $bulk = true;
        $row = current($rows);
        $c = array_keys($row);
        foreach ($this->getData() as $row) {
            $k = array_keys($row);
            if ($k != $c) {
                $bulk = false;
                break;
            }
        }

        if ($bulk) {
            $this->getAdapter()->bulkinsert($this, $this->getData());
        } else {
            foreach ($this->getData() as $row) {
                $this->getAdapter()->insert($this, $row);
            }
        }
    }

    /**
     * Truncates the table.
     *
     * @return void
     */
    public function truncate()
    {
        $this->getAdapter()->truncateTable($this->getName());
    }

    /**
     * Commits the table changes.
     *
     * If the table doesn't exist it is created otherwise it is updated.
     *
     * @return void
     */
    public function save()
    {
        if ($this->exists()) {
            $this->update(); // update the table
        } else {
            $this->create(); // create the table
        }

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

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

abstract class AbstractTemplateCreation implements CreationInterface
{
    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    protected $input;

    /**
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    protected $output;

    /**
     * Class Constructor.
     *
     * @param \Symfony\Component\Console\Input\InputInterface|null  $input
     * @param \Symfony\Component\Console\Output\OutputInterface|null $output
     */
    public function __construct(InputInterface $input = null, OutputInterface $output = null)
    {
        if (!is_null($input)) {
            $this->setInput($input);
        }
        if (!is_null($output)) {
            $this->setOutput($output);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * {@inheritdoc}
     */
    public function setInput(InputInterface $input)
    {
        $this->input = $input;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * {@inheritdoc}
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;

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

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Migration interface
 *
 * @author Richard Quadling <RQuadling@GMail.com>
 */
interface CreationInterface
{
    /**
     * CreationInterface constructor.
     *
     * @param \Symfony\Component\Console\Input\InputInterface|null  $input
     * @param \Symfony\Component\Console\Output\OutputInterface|null $output
     */
    public function __construct(InputInterface $input = null, OutputInterface $output = null);

    /**
     * @param \Symfony\Component\Console\Input\InputInterface $input
     *
     * @return \Phinx\Migration\CreationInterface
     */
    public function setInput(InputInterface $input);

    /**
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     *
     * @return \Phinx\Migration\CreationInterface
     */
    public function setOutput(OutputInterface $output);

    /**
     * @return \Symfony\Component\Console\Input\InputInterface
     */
    public function getInput();

    /**
     * @return \Symfony\Component\Console\Output\OutputInterface
     */
    public function getOutput();

    /**
     * Get the migration template.
     *
     * This will be the content that Phinx will amend to generate the migration file.
     *
     * @return string The content of the template for Phinx to amend.
     */
    public function getMigrationTemplate();

    /**
     * Post Migration Creation.
     *
     * Once the migration file has been created, this method will be called, allowing any additional
     * processing, specific to the template to be performed.
     *
     * @param string $migrationFilename The name of the newly created migration.
     * @param string $className         The class name.
     * @param string $baseClassName     The name of the base class.
     * @return void
     */
    public function postMigrationCreation($migrationFilename, $className, $baseClassName);
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Migration
 */
namespace Phinx\Migration;

/**
 * Exception class thrown when migrations cannot be reversed using the 'change'
 * feature.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
class IrreversibleMigrationException extends \Exception
{
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Migration\Manager
 */
namespace Phinx\Migration\Manager;

use Phinx\Db\Adapter\AdapterFactory;
use Phinx\Db\Adapter\AdapterInterface;
use Phinx\Migration\MigrationInterface;
use Phinx\Seed\SeedInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Environment
{
    /**
     * @var string
     */
    protected $name;

    /**
     * @var array
     */
    protected $options;

    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    protected $input;

    /**
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    protected $output;

    /**
     * @var int
     */
    protected $currentVersion;

    /**
     * @var string
     */
    protected $schemaTableName = 'phinxlog';

    /**
     * @var \Phinx\Db\Adapter\AdapterInterface
     */
    protected $adapter;

    /**
     * Class Constructor.
     *
     * @param string $name Environment Name
     * @param array $options Options
     * @return \Phinx\Migration\Manager\Environment
     */
    public function __construct($name, $options)
    {
        $this->name = $name;
        $this->options = $options;
    }

    /**
     * Executes the specified migration on this environment.
     *
     * @param \Phinx\Migration\MigrationInterface $migration Migration
     * @param string $direction Direction
     * @return void
     */
    public function executeMigration(MigrationInterface $migration, $direction = MigrationInterface::UP)
    {
        $direction = ($direction === MigrationInterface::UP) ? MigrationInterface::UP : MigrationInterface::DOWN;
        $migration->setMigratingUp($direction === MigrationInterface::UP);

        $startTime = time();
        $migration->setAdapter($this->getAdapter());

        // begin the transaction if the adapter supports it
        if ($this->getAdapter()->hasTransactions()) {
            $this->getAdapter()->beginTransaction();
        }

        // Run the migration
        if (method_exists($migration, MigrationInterface::CHANGE)) {
            if ($direction === MigrationInterface::DOWN) {
                // Create an instance of the ProxyAdapter so we can record all
                // of the migration commands for reverse playback

                /** @var \Phinx\Db\Adapter\ProxyAdapter $proxyAdapter */
                $proxyAdapter = AdapterFactory::instance()
                    ->getWrapper('proxy', $this->getAdapter());
                $migration->setAdapter($proxyAdapter);
                /** @noinspection PhpUndefinedMethodInspection */
                $migration->change();
                $proxyAdapter->executeInvertedCommands();
                $migration->setAdapter($this->getAdapter());
            } else {
                /** @noinspection PhpUndefinedMethodInspection */
                $migration->change();
            }
        } else {
            $migration->{$direction}();
        }

        // commit the transaction if the adapter supports it
        if ($this->getAdapter()->hasTransactions()) {
            $this->getAdapter()->commitTransaction();
        }

        // Record it in the database
        $this->getAdapter()->migrated($migration, $direction, date('Y-m-d H:i:s', $startTime), date('Y-m-d H:i:s', time()));
    }

    /**
     * Executes the specified seeder on this environment.
     *
     * @param \Phinx\Seed\SeedInterface $seed
     * @return void
     */
    public function executeSeed(SeedInterface $seed)
    {
        $seed->setAdapter($this->getAdapter());

        // begin the transaction if the adapter supports it
        if ($this->getAdapter()->hasTransactions()) {
            $this->getAdapter()->beginTransaction();
        }

        // Run the seeder
        if (method_exists($seed, SeedInterface::RUN)) {
            $seed->run();
        }

        // commit the transaction if the adapter supports it
        if ($this->getAdapter()->hasTransactions()) {
            $this->getAdapter()->commitTransaction();
        }
    }

    /**
     * Sets the environment's name.
     *
     * @param string $name Environment Name
     * @return \Phinx\Migration\Manager\Environment
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Gets the environment name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets the environment's options.
     *
     * @param array $options Environment Options
     * @return \Phinx\Migration\Manager\Environment
     */
    public function setOptions($options)
    {
        $this->options = $options;

        return $this;
    }

    /**
     * Gets the environment's options.
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Sets the console input.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @return \Phinx\Migration\Manager\Environment
     */
    public function setInput(InputInterface $input)
    {
        $this->input = $input;

        return $this;
    }

    /**
     * Gets the console input.
     *
     * @return \Symfony\Component\Console\Input\InputInterface
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * Sets the console output.
     *
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
     * @return \Phinx\Migration\Manager\Environment
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;

        return $this;
    }

    /**
     * Gets the console output.
     *
     * @return \Symfony\Component\Console\Output\OutputInterface
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * Gets all migrated version numbers.
     *
     * @return array
     */
    public function getVersions()
    {
        return $this->getAdapter()->getVersions();
    }

    /**
     * Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's
     * version_order option
     *
     * @return array
     */
    public function getVersionLog()
    {
        return $this->getAdapter()->getVersionLog();
    }

    /**
     * Sets the current version of the environment.
     *
     * @param int $version Environment Version
     * @return \Phinx\Migration\Manager\Environment
     */
    public function setCurrentVersion($version)
    {
        $this->currentVersion = $version;

        return $this;
    }

    /**
     * Gets the current version of the environment.
     *
     * @return int
     */
    public function getCurrentVersion()
    {
        // We don't cache this code as the current version is pretty volatile.
        // TODO - that means they're no point in a setter then?
        // maybe we should cache and call a reset() method everytime a migration is run
        $versions = $this->getVersions();
        $version = 0;

        if (!empty($versions)) {
            $version = end($versions);
        }

        $this->setCurrentVersion($version);

        return $this->currentVersion;
    }

    /**
     * Sets the database adapter.
     *
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
     * @return \Phinx\Migration\Manager\Environment
     */
    public function setAdapter(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;

        return $this;
    }

    /**
     * Gets the database adapter.
     *
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getAdapter()
    {
        if (isset($this->adapter)) {
            return $this->adapter;
        }
        if (isset($this->options['connection'])) {
            if (!($this->options['connection'] instanceof \PDO)) {
                throw new \RuntimeException('The specified connection is not a PDO instance');
            }

            $this->options['connection']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
            $this->options['adapter'] = $this->options['connection']->getAttribute(\PDO::ATTR_DRIVER_NAME);
        }
        if (!isset($this->options['adapter'])) {
            throw new \RuntimeException('No adapter was specified for environment: ' . $this->getName());
        }

        $factory = AdapterFactory::instance();
        $adapter = $factory
            ->getAdapter($this->options['adapter'], $this->options);

        // Automatically time the executed commands
        $adapter = $factory->getWrapper('timed', $adapter);

        if (isset($this->options['wrapper'])) {
            $adapter = $factory
                ->getWrapper($this->options['wrapper'], $adapter);
        }

        if ($this->getInput()) {
            $adapter->setInput($this->getInput());
        }

        if ($this->getOutput()) {
            $adapter->setOutput($this->getOutput());
        }

        // Use the TablePrefixAdapter if table prefix/suffixes are in use
        if ($adapter->hasOption('table_prefix') || $adapter->hasOption('table_suffix')) {
            $adapter = AdapterFactory::instance()
                ->getWrapper('prefix', $adapter);
        }

        $this->setAdapter($adapter);

        return $adapter;
    }

    /**
     * Sets the schema table name.
     *
     * @param string $schemaTableName Schema Table Name
     * @return \Phinx\Migration\Manager\Environment
     */
    public function setSchemaTableName($schemaTableName)
    {
        $this->schemaTableName = $schemaTableName;

        return $this;
    }

    /**
     * Gets the schema table name.
     *
     * @return string
     */
    public function getSchemaTableName()
    {
        return $this->schemaTableName;
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Migration
 */
namespace Phinx\Migration;

use Phinx\Config\ConfigInterface;
use Phinx\Config\NamespaceAwareInterface;
use Phinx\Migration\Manager\Environment;
use Phinx\Seed\AbstractSeed;
use Phinx\Seed\SeedInterface;
use Phinx\Util\Util;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Manager
{
    /**
     * @var \Phinx\Config\ConfigInterface
     */
    protected $config;

    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    protected $input;

    /**
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    protected $output;

    /**
     * @var array
     */
    protected $environments;

    /**
     * @var array
     */
    protected $migrations;

    /**
     * @var array
     */
    protected $seeds;

    /**
     * @var integer
     */
    const EXIT_STATUS_DOWN = 1;

    /**
     * @var integer
     */
    const EXIT_STATUS_MISSING = 2;

    /**
     * Class Constructor.
     *
     * @param \Phinx\Config\ConfigInterface $config Configuration Object
     * @param \Symfony\Component\Console\Input\InputInterface $input Console Input
     * @param \Symfony\Component\Console\Output\OutputInterface $output Console Output
     */
    public function __construct(ConfigInterface $config, InputInterface $input, OutputInterface $output)
    {
        $this->setConfig($config);
        $this->setInput($input);
        $this->setOutput($output);
    }

    /**
     * Prints the specified environment's migration status.
     *
     * @param string $environment
     * @param null $format
     * @return int 0 if all migrations are up, or an error code
     */
    public function printStatus($environment, $format = null)
    {
        $output = $this->getOutput();
        $migrations = [];
        $hasDownMigration = false;
        $hasMissingMigration = false;
        $migrations = $this->getMigrations();
        if (count($migrations)) {
            // TODO - rewrite using Symfony Table Helper as we already have this library
            // included and it will fix formatting issues (e.g drawing the lines)
            $output->writeln('');

            switch ($this->getConfig()->getVersionOrder()) {
                case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME:
                    $migrationIdAndStartedHeader = "<info>[Migration ID]</info>  Started            ";
                    break;
                case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME:
                    $migrationIdAndStartedHeader = "Migration ID    <info>[Started          ]</info>";
                    break;
                default:
                    throw new \RuntimeException('Invalid version_order configuration option');
            }

            $output->writeln(" Status  $migrationIdAndStartedHeader  Finished             Migration Name ");
            $output->writeln('----------------------------------------------------------------------------------');

            $env = $this->getEnvironment($environment);
            $versions = $env->getVersionLog();

            $maxNameLength = $versions ? max(array_map(function ($version) {
                return strlen($version['migration_name']);
            }, $versions)) : 0;

            $missingVersions = array_diff_key($versions, $migrations);

            $hasMissingMigration = !empty($missingVersions);

            // get the migrations sorted in the same way as the versions
            $sortedMigrations = [];

            foreach ($versions as $versionCreationTime => $version) {
                if (isset($migrations[$versionCreationTime])) {
                    array_push($sortedMigrations, $migrations[$versionCreationTime]);
                    unset($migrations[$versionCreationTime]);
                }
            }

            if (empty($sortedMigrations) && !empty($missingVersions)) {
                // this means we have no up migrations, so we write all the missing versions already so they show up
                // before any possible down migration
                foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
                    $this->printMissingVersion($missingVersion, $maxNameLength);

                    unset($missingVersions[$missingVersionCreationTime]);
                }
            }

            // any migration left in the migrations (ie. not unset when sorting the migrations by the version order) is
            // a migration that is down, so we add them to the end of the sorted migrations list
            if (!empty($migrations)) {
                $sortedMigrations = array_merge($sortedMigrations, $migrations);
            }

            foreach ($sortedMigrations as $migration) {
                $version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false;
                if ($version) {
                    // check if there are missing versions before this version
                    foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
                        if ($this->getConfig()->isVersionOrderCreationTime()) {
                            if ($missingVersion['version'] > $version['version']) {
                                break;
                            }
                        } else {
                            if ($missingVersion['start_time'] > $version['start_time']) {
                                break;
                            } elseif ($missingVersion['start_time'] == $version['start_time'] &&
                                $missingVersion['version'] > $version['version']) {
                                break;
                            }
                        }

                        $this->printMissingVersion($missingVersion, $maxNameLength);

                        unset($missingVersions[$missingVersionCreationTime]);
                    }

                    $status = '     <info>up</info> ';
                } else {
                    $hasDownMigration = true;
                    $status = '   <error>down</error> ';
                }
                $maxNameLength = max($maxNameLength, strlen($migration->getName()));

                $output->writeln(sprintf(
                    '%s %14.0f  %19s  %19s  <comment>%s</comment>',
                    $status,
                    $migration->getVersion(),
                    $version['start_time'],
                    $version['end_time'],
                    $migration->getName()
                ));

                if ($version && $version['breakpoint']) {
                    $output->writeln('         <error>BREAKPOINT SET</error>');
                }

                $migrations[] = ['migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName()];
                unset($versions[$migration->getVersion()]);
            }

            // and finally add any possibly-remaining missing migrations
            foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
                $this->printMissingVersion($missingVersion, $maxNameLength);

                unset($missingVersions[$missingVersionCreationTime]);
            }
        } else {
            // there are no migrations
            $output->writeln('');
            $output->writeln('There are no available migrations. Try creating one using the <info>create</info> command.');
        }

        // write an empty line
        $output->writeln('');
        if ($format !== null) {
            switch ($format) {
                case 'json':
                    $output->writeln(json_encode(
                        [
                            'pending_count' => count($this->getMigrations()),
                            'migrations' => $migrations
                        ]
                    ));
                    break;
                default:
                    $output->writeln('<info>Unsupported format: ' . $format . '</info>');
            }
        }

        if ($hasMissingMigration) {
            return self::EXIT_STATUS_MISSING;
        } elseif ($hasDownMigration) {
            return self::EXIT_STATUS_DOWN;
        } else {
            return 0;
        }
    }

    /**
     * Print Missing Version
     *
     * @param array     $version        The missing version to print (in the format returned by Environment.getVersionLog).
     * @param int   $maxNameLength  The maximum migration name length.
     */
    private function printMissingVersion($version, $maxNameLength)
    {
        $this->getOutput()->writeln(sprintf(
            '     <error>up</error>  %14.0f  %19s  %19s  <comment>%s</comment>  <error>** MISSING **</error>',
            $version['version'],
            $version['start_time'],
            $version['end_time'],
            str_pad($version['migration_name'], $maxNameLength, ' ')
        ));

        if ($version && $version['breakpoint']) {
            $this->getOutput()->writeln('         <error>BREAKPOINT SET</error>');
        }
    }

    /**
     * Migrate to the version of the database on a given date.
     *
     * @param string    $environment Environment
     * @param \DateTime $dateTime    Date to migrate to
     *
     * @return void
     */
    public function migrateToDateTime($environment, \DateTime $dateTime)
    {
        $versions = array_keys($this->getMigrations());
        $dateString = $dateTime->format('YmdHis');

        $outstandingMigrations = array_filter($versions, function ($version) use ($dateString) {
            return $version <= $dateString;
        });

        if (count($outstandingMigrations) > 0) {
            $migration = max($outstandingMigrations);
            $this->getOutput()->writeln('Migrating to version ' . $migration);
            $this->migrate($environment, $migration);
        }
    }

    /**
     * Migrate an environment to the specified version.
     *
     * @param string $environment Environment
     * @param int $version
     * @return void
     */
    public function migrate($environment, $version = null)
    {
        $migrations = $this->getMigrations();
        $env = $this->getEnvironment($environment);
        $versions = $env->getVersions();
        $current = $env->getCurrentVersion();

        if (empty($versions) && empty($migrations)) {
            return;
        }

        if ($version === null) {
            $version = max(array_merge($versions, array_keys($migrations)));
        } else {
            if (0 != $version && !isset($migrations[$version])) {
                $this->output->writeln(sprintf(
                    '<comment>warning</comment> %s is not a valid version',
                    $version
                ));

                return;
            }
        }

        // are we migrating up or down?
        $direction = $version > $current ? MigrationInterface::UP : MigrationInterface::DOWN;

        if ($direction === MigrationInterface::DOWN) {
            // run downs first
            krsort($migrations);
            foreach ($migrations as $migration) {
                if ($migration->getVersion() <= $version) {
                    break;
                }

                if (in_array($migration->getVersion(), $versions)) {
                    $this->executeMigration($environment, $migration, MigrationInterface::DOWN);
                }
            }
        }

        ksort($migrations);
        foreach ($migrations as $migration) {
            if ($migration->getVersion() > $version) {
                break;
            }

            if (!in_array($migration->getVersion(), $versions)) {
                $this->executeMigration($environment, $migration, MigrationInterface::UP);
            }
        }
    }

    /**
     * Execute a migration against the specified environment.
     *
     * @param string $name Environment Name
     * @param \Phinx\Migration\MigrationInterface $migration Migration
     * @param string $direction Direction
     * @return void
     */
    public function executeMigration($name, MigrationInterface $migration, $direction = MigrationInterface::UP)
    {
        $this->getOutput()->writeln('');
        $this->getOutput()->writeln(
            ' ==' .
            ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' .
            ' <comment>' . ($direction === MigrationInterface::UP ? 'migrating' : 'reverting') . '</comment>'
        );

        // Execute the migration and log the time elapsed.
        $start = microtime(true);
        $this->getEnvironment($name)->executeMigration($migration, $direction);
        $end = microtime(true);

        $this->getOutput()->writeln(
            ' ==' .
            ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' .
            ' <comment>' . ($direction === MigrationInterface::UP ? 'migrated' : 'reverted') .
            ' ' . sprintf('%.4fs', $end - $start) . '</comment>'
        );
    }

    /**
     * Execute a seeder against the specified environment.
     *
     * @param string $name Environment Name
     * @param \Phinx\Seed\SeedInterface $seed Seed
     * @return void
     */
    public function executeSeed($name, SeedInterface $seed)
    {
        $this->getOutput()->writeln('');
        $this->getOutput()->writeln(
            ' ==' .
            ' <info>' . $seed->getName() . ':</info>' .
            ' <comment>seeding</comment>'
        );

        // Execute the seeder and log the time elapsed.
        $start = microtime(true);
        $this->getEnvironment($name)->executeSeed($seed);
        $end = microtime(true);

        $this->getOutput()->writeln(
            ' ==' .
            ' <info>' . $seed->getName() . ':</info>' .
            ' <comment>seeded' .
            ' ' . sprintf('%.4fs', $end - $start) . '</comment>'
        );
    }

    /**
     * Rollback an environment to the specified version.
     *
     * @param string $environment Environment
     * @param int|string $target
     * @param bool $force
     * @param bool $targetMustMatchVersion
     * @return void
     */
    public function rollback($environment, $target = null, $force = false, $targetMustMatchVersion = true)
    {
        // note that the migrations are indexed by name (aka creation time) in ascending order
        $migrations = $this->getMigrations();

        // note that the version log are also indexed by name with the proper ascending order according to the version order
        $executedVersions = $this->getEnvironment($environment)->getVersionLog();

        // get a list of migrations sorted in the opposite way of the executed versions
        $sortedMigrations = [];

        foreach ($executedVersions as $versionCreationTime => &$executedVersion) {
            // if we have a date (ie. the target must not match a version) and we are sorting by execution time, we
            // convert the version start time so we can compare directly with the target date
            if (!$this->getConfig()->isVersionOrderCreationTime() && !$targetMustMatchVersion) {
                $dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $executedVersion['start_time']);
                $executedVersion['start_time'] = $dateTime->format('YmdHis');
            }

            if (isset($migrations[$versionCreationTime])) {
                array_unshift($sortedMigrations, $migrations[$versionCreationTime]);
            } else {
                // this means the version is missing so we unset it so that we don't consider it when rolling back
                // migrations (or choosing the last up version as target)
                unset($executedVersions[$versionCreationTime]);
            }
        }

        if ($target === 'all' || $target === '0') {
            $target = 0;
        } elseif (!is_numeric($target) && !is_null($target)) { // try to find a target version based on name
            // search through the migrations using the name
            $migrationNames = array_map(function ($item) {
                return $item['migration_name'];
            }, $executedVersions);
            $found = array_search($target, $migrationNames);

            // check on was found
            if ($found !== false) {
                $target = (string)$found;
            } else {
                $this->getOutput()->writeln("<error>No migration found with name ($target)</error>");

                return;
            }
        }

        // Check we have at least 1 migration to revert
        $executedVersionCreationTimes = array_keys($executedVersions);
        if (empty($executedVersionCreationTimes) || $target == end($executedVersionCreationTimes)) {
            $this->getOutput()->writeln('<error>No migrations to rollback</error>');

            return;
        }

        // If no target was supplied, revert the last migration
        if ($target === null) {
            // Get the migration before the last run migration
            $prev = count($executedVersionCreationTimes) - 2;
            $target = $prev >= 0 ? $executedVersionCreationTimes[$prev] : 0;
        }

        // If the target must match a version, check the target version exists
        if ($targetMustMatchVersion && 0 !== $target && !isset($migrations[$target])) {
            $this->getOutput()->writeln("<error>Target version ($target) not found</error>");

            return;
        }

        // Rollback all versions until we find the wanted rollback target
        $rollbacked = false;

        foreach ($sortedMigrations as $migration) {
            if ($targetMustMatchVersion && $migration->getVersion() == $target) {
                break;
            }

            if (in_array($migration->getVersion(), $executedVersionCreationTimes)) {
                $executedVersion = $executedVersions[$migration->getVersion()];

                if (!$targetMustMatchVersion) {
                    if (($this->getConfig()->isVersionOrderCreationTime() && $executedVersion['version'] <= $target) ||
                        (!$this->getConfig()->isVersionOrderCreationTime() && $executedVersion['start_time'] <= $target)) {
                        break;
                    }
                }

                if (0 != $executedVersion['breakpoint'] && !$force) {
                    $this->getOutput()->writeln('<error>Breakpoint reached. Further rollbacks inhibited.</error>');
                    break;
                }
                $this->executeMigration($environment, $migration, MigrationInterface::DOWN);
                $rollbacked = true;
            }
        }

        if (!$rollbacked) {
            $this->getOutput()->writeln('<error>No migrations to rollback</error>');
        }
    }

    /**
     * Run database seeders against an environment.
     *
     * @param string $environment Environment
     * @param string $seed Seeder
     * @return void
     */
    public function seed($environment, $seed = null)
    {
        $seeds = $this->getSeeds();

        if ($seed === null) {
            // run all seeders
            foreach ($seeds as $seeder) {
                if (array_key_exists($seeder->getName(), $seeds)) {
                    $this->executeSeed($environment, $seeder);
                }
            }
        } else {
            // run only one seeder
            if (array_key_exists($seed, $seeds)) {
                $this->executeSeed($environment, $seeds[$seed]);
            } else {
                throw new \InvalidArgumentException(sprintf('The seed class "%s" does not exist', $seed));
            }
        }
    }

    /**
     * Sets the environments.
     *
     * @param array $environments Environments
     * @return \Phinx\Migration\Manager
     */
    public function setEnvironments($environments = [])
    {
        $this->environments = $environments;

        return $this;
    }

    /**
     * Gets the manager class for the given environment.
     *
     * @param string $name Environment Name
     * @throws \InvalidArgumentException
     * @return \Phinx\Migration\Manager\Environment
     */
    public function getEnvironment($name)
    {
        if (isset($this->environments[$name])) {
            return $this->environments[$name];
        }

        // check the environment exists
        if (!$this->getConfig()->hasEnvironment($name)) {
            throw new \InvalidArgumentException(sprintf(
                'The environment "%s" does not exist',
                $name
            ));
        }

        // create an environment instance and cache it
        $envOptions = $this->getConfig()->getEnvironment($name);
        $envOptions['version_order'] = $this->getConfig()->getVersionOrder();

        $environment = new Environment($name, $envOptions);
        $this->environments[$name] = $environment;
        $environment->setInput($this->getInput());
        $environment->setOutput($this->getOutput());

        return $environment;
    }

    /**
     * Sets the console input.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input Input
     * @return \Phinx\Migration\Manager
     */
    public function setInput(InputInterface $input)
    {
        $this->input = $input;

        return $this;
    }

    /**
     * Gets the console input.
     *
     * @return \Symfony\Component\Console\Input\InputInterface
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * Sets the console output.
     *
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
     * @return \Phinx\Migration\Manager
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;

        return $this;
    }

    /**
     * Gets the console output.
     *
     * @return \Symfony\Component\Console\Output\OutputInterface
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * Sets the database migrations.
     *
     * @param array $migrations Migrations
     * @return \Phinx\Migration\Manager
     */
    public function setMigrations(array $migrations)
    {
        $this->migrations = $migrations;

        return $this;
    }

    /**
     * Gets an array of the database migrations, indexed by migration name (aka creation time) and sorted in ascending
     * order
     *
     * @throws \InvalidArgumentException
     * @return \Phinx\Migration\AbstractMigration[]
     */
    public function getMigrations()
    {
        if ($this->migrations === null) {
            $phpFiles = $this->getMigrationFiles();

            // filter the files to only get the ones that match our naming scheme
            $fileNames = [];
            /** @var \Phinx\Migration\AbstractMigration[] $versions */
            $versions = [];

            foreach ($phpFiles as $filePath) {
                if (Util::isValidMigrationFileName(basename($filePath))) {
                    $version = Util::getVersionFromFileName(basename($filePath));

                    if (isset($versions[$version])) {
                        throw new \InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion()));
                    }

                    $config = $this->getConfig();
                    $namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath(dirname($filePath)) : null;

                    // convert the filename to a class name
                    $class = ($namespace === null ? '' : $namespace . '\\') . Util::mapFileNameToClassName(basename($filePath));

                    if (isset($fileNames[$class])) {
                        throw new \InvalidArgumentException(sprintf(
                            'Migration "%s" has the same name as "%s"',
                            basename($filePath),
                            $fileNames[$class]
                        ));
                    }

                    $fileNames[$class] = basename($filePath);

                    // load the migration file
                    /** @noinspection PhpIncludeInspection */
                    require_once $filePath;
                    if (!class_exists($class)) {
                        throw new \InvalidArgumentException(sprintf(
                            'Could not find class "%s" in file "%s"',
                            $class,
                            $filePath
                        ));
                    }

                    // instantiate it
                    $migration = new $class($version, $this->getInput(), $this->getOutput());

                    if (!($migration instanceof AbstractMigration)) {
                        throw new \InvalidArgumentException(sprintf(
                            'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration',
                            $class,
                            $filePath
                        ));
                    }

                    $versions[$version] = $migration;
                }
            }

            ksort($versions);
            $this->setMigrations($versions);
        }

        return $this->migrations;
    }

    /**
     * Returns a list of migration files found in the provided migration paths.
     *
     * @return string[]
     */
    protected function getMigrationFiles()
    {
        $config = $this->getConfig();
        $paths = $config->getMigrationPaths();
        $files = [];

        foreach ($paths as $path) {
            $files = array_merge(
                $files,
                Util::glob($path . DIRECTORY_SEPARATOR . '*.php')
            );
        }
        // glob() can return the same file multiple times
        // This will cause the migration to fail with a
        // false assumption of duplicate migrations
        // http://php.net/manual/en/function.glob.php#110340
        $files = array_unique($files);

        return $files;
    }

    /**
     * Sets the database seeders.
     *
     * @param array $seeds Seeders
     * @return \Phinx\Migration\Manager
     */
    public function setSeeds(array $seeds)
    {
        $this->seeds = $seeds;

        return $this;
    }

    /**
     * Get seed dependencies instances from seed dependency array
     *
     * @param AbstractSeed $seed Seed
     *
     * @return AbstractSeed[]
     */
    private function getSeedDependenciesInstances(AbstractSeed $seed)
    {
        $dependenciesInstances = [];
        $dependencies = $seed->getDependencies();
        if (!empty($dependencies)) {
            foreach ($dependencies as $dependency) {
                foreach ($this->seeds as $seed) {
                    if (get_class($seed) === $dependency) {
                        $dependenciesInstances[get_class($seed)] = $seed;
                    }
                }
            }
        }

        return $dependenciesInstances;
    }

    /**
     * Order seeds by dependencies
     *
     * @param AbstractSeed[] $seeds Seeds
     *
     * @return AbstractSeed[]
     */
    private function orderSeedsByDependencies(array $seeds)
    {
        $orderedSeeds = [];
        foreach ($seeds as $seed) {
            $key = get_class($seed);
            $dependencies = $this->getSeedDependenciesInstances($seed);
            if (!empty($dependencies)) {
                $orderedSeeds[$key] = $seed;
                $orderedSeeds = array_merge($this->orderSeedsByDependencies($dependencies), $orderedSeeds);
            } else {
                $orderedSeeds[$key] = $seed;
            }
        }

        return $orderedSeeds;
    }

    /**
     * Gets an array of database seeders.
     *
     * @throws \InvalidArgumentException
     * @return \Phinx\Seed\AbstractSeed[]
     */
    public function getSeeds()
    {
        if ($this->seeds === null) {
            $phpFiles = $this->getSeedFiles();

            // filter the files to only get the ones that match our naming scheme
            $fileNames = [];
            /** @var \Phinx\Seed\AbstractSeed[] $seeds */
            $seeds = [];

            foreach ($phpFiles as $filePath) {
                if (Util::isValidSeedFileName(basename($filePath))) {
                    $config = $this->getConfig();
                    $namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath(dirname($filePath)) : null;

                    // convert the filename to a class name
                    $class = ($namespace === null ? '' : $namespace . '\\') . pathinfo($filePath, PATHINFO_FILENAME);
                    $fileNames[$class] = basename($filePath);

                    // load the seed file
                    /** @noinspection PhpIncludeInspection */
                    require_once $filePath;
                    if (!class_exists($class)) {
                        throw new \InvalidArgumentException(sprintf(
                            'Could not find class "%s" in file "%s"',
                            $class,
                            $filePath
                        ));
                    }

                    // instantiate it
                    $seed = new $class($this->getInput(), $this->getOutput());

                    if (!($seed instanceof AbstractSeed)) {
                        throw new \InvalidArgumentException(sprintf(
                            'The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed',
                            $class,
                            $filePath
                        ));
                    }

                    $seeds[$class] = $seed;
                }
            }

            ksort($seeds);
            $this->setSeeds($seeds);
        }

        $this->seeds = $this->orderSeedsByDependencies($this->seeds);
        return $this->seeds;
    }

    /**
     * Returns a list of seed files found in the provided seed paths.
     *
     * @return string[]
     */
    protected function getSeedFiles()
    {
        $config = $this->getConfig();
        $paths = $config->getSeedPaths();
        $files = [];

        foreach ($paths as $path) {
            $files = array_merge(
                $files,
                Util::glob($path . DIRECTORY_SEPARATOR . '*.php')
            );
        }
        // glob() can return the same file multiple times
        // This will cause the migration to fail with a
        // false assumption of duplicate migrations
        // http://php.net/manual/en/function.glob.php#110340
        $files = array_unique($files);

        return $files;
    }

    /**
     * Sets the config.
     *
     * @param  \Phinx\Config\ConfigInterface $config Configuration Object
     * @return \Phinx\Migration\Manager
     */
    public function setConfig(ConfigInterface $config)
    {
        $this->config = $config;

        return $this;
    }

    /**
     * Gets the config.
     *
     * @return \Phinx\Config\ConfigInterface
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * Toggles the breakpoint for a specific version.
     *
     * @param string $environment
     * @param int $version
     * @return void
     */
    public function toggleBreakpoint($environment, $version)
    {
        $migrations = $this->getMigrations();
        $this->getMigrations();
        $env = $this->getEnvironment($environment);
        $versions = $env->getVersionLog();

        if (empty($versions) || empty($migrations)) {
            return;
        }

        if ($version === null) {
            $lastVersion = end($versions);
            $version = $lastVersion['version'];
        }

        if (0 != $version && !isset($migrations[$version])) {
            $this->output->writeln(sprintf(
                '<comment>warning</comment> %s is not a valid version',
                $version
            ));

            return;
        }

        $env->getAdapter()->toggleBreakpoint($migrations[$version]);

        $versions = $env->getVersionLog();

        $this->getOutput()->writeln(
            ' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') .
            ' for <info>' . $version . '</info>' .
            ' <comment>' . $migrations[$version]->getName() . '</comment>'
        );
    }

    /**
     * Remove all breakpoints
     *
     * @param string $environment
     * @return void
     */
    public function removeBreakpoints($environment)
    {
        $this->getOutput()->writeln(sprintf(
            ' %d breakpoints cleared.',
            $this->getEnvironment($environment)->getAdapter()->resetAllBreakpoints()
        ));
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Migration
 */
namespace Phinx\Migration;

use Phinx\Db\Adapter\AdapterInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Migration interface
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
interface MigrationInterface
{
    /**
     * @var string
     */
    const CHANGE = 'change';

    /**
     * @var string
     */
    const UP = 'up';

    /**
     * @var string
     */
    const DOWN = 'down';

    /**
     * Migrate Up
     *
     * @return void
     */
    public function up();

    /**
     * Migrate Down
     *
     * @return void
     */
    public function down();

    /**
     * Sets the database adapter.
     *
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
     * @return \Phinx\Migration\MigrationInterface
     */
    public function setAdapter(AdapterInterface $adapter);

    /**
     * Gets the database adapter.
     *
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getAdapter();

    /**
     * Sets the input object to be used in migration object
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @return \Phinx\Migration\MigrationInterface
     */
    public function setInput(InputInterface $input);

    /**
     * Gets the input object to be used in migration object
     *
     * @return \Symfony\Component\Console\Input\InputInterface
     */
    public function getInput();

    /**
     * Sets the output object to be used in migration object
     *
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return \Phinx\Migration\MigrationInterface
     */
    public function setOutput(OutputInterface $output);

    /**
     * Gets the output object to be used in migration object
     *
     * @return \Symfony\Component\Console\Output\OutputInterface
     */
    public function getOutput();

    /**
     * Gets the name.
     *
     * @return string
     */
    public function getName();

    /**
     * Sets the migration version number.
     *
     * @param float $version Version
     * @return \Phinx\Migration\MigrationInterface
     */
    public function setVersion($version);

    /**
     * Gets the migration version number.
     *
     * @return float
     */
    public function getVersion();

    /**
     * Sets whether this migration is being applied or reverted
     *
     * @param bool $isMigratingUp True if the migration is being applied
     * @return \Phinx\Migration\MigrationInterface
     */
    public function setMigratingUp($isMigratingUp);

    /**
     * Gets whether this migration is being applied or reverted.
     * True means that the migration is being applied.
     *
     * @return bool
     */
    public function isMigratingUp();

    /**
     * Executes a SQL statement and returns the number of affected rows.
     *
     * @param string $sql SQL
     * @return int
     */
    public function execute($sql);

    /**
     * Executes a SQL statement and returns the result as an array.
     *
     * @param string $sql SQL
     * @return array
     */
    public function query($sql);

    /**
     * Executes a query and returns only one row as an array.
     *
     * @param string $sql SQL
     * @return array
     */
    public function fetchRow($sql);

    /**
     * Executes a query and returns an array of rows.
     *
     * @param string $sql SQL
     * @return array
     */
    public function fetchAll($sql);

    /**
     * Insert data into a table.
     *
     * @param string $tableName
     * @param array $data
     * @return void
     */
    public function insert($tableName, $data);

    /**
     * Create a new database.
     *
     * @param string $name Database Name
     * @param array $options Options
     * @return void
     */
    public function createDatabase($name, $options);

    /**
     * Drop a database.
     *
     * @param string $name Database Name
     * @return void
     */
    public function dropDatabase($name);

    /**
     * Checks to see if a table exists.
     *
     * @param string $tableName Table Name
     * @return bool
     */
    public function hasTable($tableName);

    /**
     * Returns an instance of the <code>\Table</code> class.
     *
     * You can use this class to create and manipulate tables.
     *
     * @param string $tableName Table Name
     * @param array $options Options
     * @return \Phinx\Db\Table
     */
    public function table($tableName, $options);
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Migration
 */
namespace Phinx\Migration;

use Phinx\Db\Adapter\AdapterInterface;
use Phinx\Db\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Abstract Migration Class.
 *
 * It is expected that the migrations you write extend from this class.
 *
 * This abstract class proxies the various database methods to your specified
 * adapter.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
abstract class AbstractMigration implements MigrationInterface
{
    /**
     * @var float
     */
    protected $version;

    /**
     * @var \Phinx\Db\Adapter\AdapterInterface
     */
    protected $adapter;

    /**
     * @var \Symfony\Component\Console\Output\OutputInterface
     */
    protected $output;

    /**
     * @var \Symfony\Component\Console\Input\InputInterface
     */
    protected $input;

    /**
     * Whether this migration is being applied or reverted
     *
     * @var bool
     */
    protected $isMigratingUp = true;

    /**
     * Class Constructor.
     *
     * @param int $version Migration Version
     * @param \Symfony\Component\Console\Input\InputInterface|null $input
     * @param \Symfony\Component\Console\Output\OutputInterface|null $output
     */
    final public function __construct($version, InputInterface $input = null, OutputInterface $output = null)
    {
        $this->version = $version;
        if (!is_null($input)) {
            $this->setInput($input);
        }
        if (!is_null($output)) {
            $this->setOutput($output);
        }

        $this->init();
    }

    /**
     * Initialize method.
     *
     * @return void
     */
    protected function init()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function up()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function setAdapter(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getAdapter()
    {
        return $this->adapter;
    }

    /**
     * {@inheritdoc}
     */
    public function setInput(InputInterface $input)
    {
        $this->input = $input;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * {@inheritdoc}
     */
    public function setOutput(OutputInterface $output)
    {
        $this->output = $output;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return get_class($this);
    }

    /**
     * {@inheritdoc}
     */
    public function setVersion($version)
    {
        $this->version = $version;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * {@inheritdoc}
     */
    public function setMigratingUp($isMigratingUp)
    {
        $this->isMigratingUp = $isMigratingUp;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function isMigratingUp()
    {
        return $this->isMigratingUp;
    }

    /**
     * {@inheritdoc}
     */
    public function execute($sql)
    {
        return $this->getAdapter()->execute($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function query($sql)
    {
        return $this->getAdapter()->query($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function fetchRow($sql)
    {
        return $this->getAdapter()->fetchRow($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function fetchAll($sql)
    {
        return $this->getAdapter()->fetchAll($sql);
    }

    /**
     * {@inheritdoc}
     */
    public function insert($table, $data)
    {
        // convert to table object
        if (is_string($table)) {
            $table = new Table($table, [], $this->getAdapter());
        }
        $table->insert($data)->save();
    }

    /**
     * {@inheritdoc}
     */
    public function createDatabase($name, $options)
    {
        $this->getAdapter()->createDatabase($name, $options);
    }

    /**
     * {@inheritdoc}
     */
    public function dropDatabase($name)
    {
        $this->getAdapter()->dropDatabase($name);
    }

    /**
     * {@inheritdoc}
     */
    public function hasTable($tableName)
    {
        return $this->getAdapter()->hasTable($tableName);
    }

    /**
     * {@inheritdoc}
     */
    public function table($tableName, $options = [])
    {
        return new Table($tableName, $options, $this->getAdapter());
    }

    /**
     * A short-hand method to drop the given database table.
     *
     * @param string $tableName Table Name
     * @return void
     */
    public function dropTable($tableName)
    {
        $this->table($tableName)->drop();
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console;

use Phinx\Console\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Phinx console application.
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
class PhinxApplication extends Application
{
    /**
     * Class Constructor.
     *
     * Initialize the Phinx console application.
     *
     * @param string $version The Application Version
     */
    public function __construct()
    {
        $composerConfig = json_decode(file_get_contents(__DIR__ . '/../../../composer.json'));
        $version = $composerConfig->version;

        parent::__construct('Phinx by CakePHP - https://phinx.org.', $version);

        $this->addCommands([
            new Command\Init(),
            new Command\Create(),
            new Command\Migrate(),
            new Command\Rollback(),
            new Command\Status(),
            new Command\Breakpoint(),
            new Command\Test(),
            new Command\SeedCreate(),
            new Command\SeedRun(),
        ]);
    }

    /**
     * Runs the current application.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input An Input instance
     * @param \Symfony\Component\Console\Output\OutputInterface $output An Output instance
     * @return int 0 if everything went fine, or an error code
     */
    public function doRun(InputInterface $input, OutputInterface $output)
    {
        // always show the version information except when the user invokes the help
        // command as that already does it
        if ($input->hasParameterOption(['--help', '-h']) === false && $input->getFirstArgument() !== null) {
            $output->writeln($this->getLongVersion());
            $output->writeln('');
        }

        return parent::doRun($input, $output);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Init extends Command
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('init')
            ->setDescription('Initialize the application for Phinx')
            ->addArgument('path', InputArgument::OPTIONAL, 'Which path should we initialize for Phinx?')
            ->setHelp(sprintf(
                '%sInitializes the application for Phinx%s',
                PHP_EOL,
                PHP_EOL
            ));
    }

    /**
     * Initializes the application.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @throws \RuntimeException
     * @throws \InvalidArgumentException
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // get the migration path from the config
        $path = $input->getArgument('path');

        if ($path === null) {
            $path = getcwd();
        }

        $path = realpath($path);

        if (!is_writable($path)) {
            throw new \InvalidArgumentException(sprintf(
                'The directory "%s" is not writable',
                $path
            ));
        }

        // Compute the file path
        $fileName = 'phinx.yml'; // TODO - maybe in the future we allow custom config names.
        $filePath = $path . DIRECTORY_SEPARATOR . $fileName;

        if (file_exists($filePath)) {
            throw new \InvalidArgumentException(sprintf(
                'The file "%s" already exists',
                $filePath
            ));
        }

        // load the config template
        if (is_dir(__DIR__ . '/../../../data/Phinx')) {
            $contents = file_get_contents(__DIR__ . '/../../../data/Phinx/phinx.yml');
        } else {
            $contents = file_get_contents(__DIR__ . '/../../../../phinx.yml');
        }

        if (file_put_contents($filePath, $contents) === false) {
            throw new \RuntimeException(sprintf(
                'The file "%s" could not be written to',
                $path
            ));
        }

        $output->writeln('<info>created</info> .' . str_replace(getcwd(), '', $filePath));
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class SeedRun extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment');

        $this->setName('seed:run')
            ->setDescription('Run database seeders')
            ->addOption('--seed', '-s', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'What is the name of the seeder?')
            ->setHelp(
                <<<EOT
The <info>seed:run</info> command runs all available or individual seeders

<info>phinx seed:run -e development</info>
<info>phinx seed:run -e development -s UserSeeder</info>
<info>phinx seed:run -e development -s UserSeeder -s PermissionSeeder -s LogSeeder</info>
<info>phinx seed:run -e development -v</info>

EOT
            );
    }

    /**
     * Run database seeders.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->bootstrap($input, $output);

        $seedSet = $input->getOption('seed');
        $environment = $input->getOption('environment');

        if ($environment === null) {
            $environment = $this->getConfig()->getDefaultEnvironment();
            $output->writeln('<comment>warning</comment> no environment specified, defaulting to: ' . $environment);
        } else {
            $output->writeln('<info>using environment</info> ' . $environment);
        }

        $envOptions = $this->getConfig()->getEnvironment($environment);
        if (isset($envOptions['adapter'])) {
            $output->writeln('<info>using adapter</info> ' . $envOptions['adapter']);
        }

        if (isset($envOptions['wrapper'])) {
            $output->writeln('<info>using wrapper</info> ' . $envOptions['wrapper']);
        }

        if (isset($envOptions['name'])) {
            $output->writeln('<info>using database</info> ' . $envOptions['name']);
        } else {
            $output->writeln('<error>Could not determine database name! Please specify a database name in your config file.</error>');

            return;
        }

        if (isset($envOptions['table_prefix'])) {
            $output->writeln('<info>using table prefix</info> ' . $envOptions['table_prefix']);
        }
        if (isset($envOptions['table_suffix'])) {
            $output->writeln('<info>using table suffix</info> ' . $envOptions['table_suffix']);
        }

        $start = microtime(true);

        if (empty($seedSet)) {
            // run all the seed(ers)
            $this->getManager()->seed($environment);
        } else {
            // run seed(ers) specified in a comma-separated list of classes
            foreach ($seedSet as $seed) {
                $this->getManager()->seed($environment, trim($seed));
            }
        }

        $end = microtime(true);

        $output->writeln('');
        $output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Migrate extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment');

        $this->setName('migrate')
            ->setDescription('Migrate the database')
            ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to migrate to')
            ->addOption('--date', '-d', InputOption::VALUE_REQUIRED, 'The date to migrate to')
            ->addOption('--dry-run', '-x', InputOption::VALUE_NONE, 'Dump query to standard output instead of executing it')
            ->setHelp(
                <<<EOT
The <info>migrate</info> command runs all available migrations, optionally up to a specific version

<info>phinx migrate -e development</info>
<info>phinx migrate -e development -t 20110103081132</info>
<info>phinx migrate -e development -d 20110103</info>
<info>phinx migrate -e development -v</info>

EOT
            );
    }

    /**
     * Migrate the database.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return int integer 0 on success, or an error code.
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->bootstrap($input, $output);

        $version = $input->getOption('target');
        $environment = $input->getOption('environment');
        $date = $input->getOption('date');

        if ($environment === null) {
            $environment = $this->getConfig()->getDefaultEnvironment();
            $output->writeln('<comment>warning</comment> no environment specified, defaulting to: ' . $environment);
        } else {
            $output->writeln('<info>using environment</info> ' . $environment);
        }

        $envOptions = $this->getConfig()->getEnvironment($environment);
        if (isset($envOptions['adapter'])) {
            $output->writeln('<info>using adapter</info> ' . $envOptions['adapter']);
        }

        if (isset($envOptions['wrapper'])) {
            $output->writeln('<info>using wrapper</info> ' . $envOptions['wrapper']);
        }

        if (isset($envOptions['name'])) {
            $output->writeln('<info>using database</info> ' . $envOptions['name']);
        } else {
            $output->writeln('<error>Could not determine database name! Please specify a database name in your config file.</error>');

            return 1;
        }

        if (isset($envOptions['table_prefix'])) {
            $output->writeln('<info>using table prefix</info> ' . $envOptions['table_prefix']);
        }
        if (isset($envOptions['table_suffix'])) {
            $output->writeln('<info>using table suffix</info> ' . $envOptions['table_suffix']);
        }

        // run the migrations
        $start = microtime(true);
        if ($date !== null) {
            $this->getManager()->migrateToDateTime($environment, new \DateTime($date));
        } else {
            $this->getManager()->migrate($environment, $version);
        }
        $end = microtime(true);

        $output->writeln('');
        $output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');

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

use Phinx\Config\NamespaceAwareInterface;
use Phinx\Util\Util;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;

class Create extends AbstractCommand
{
    /**
     * The name of the interface that any external template creation class is required to implement.
     */
    const CREATION_INTERFACE = 'Phinx\Migration\CreationInterface';

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->setName('create')
            ->setDescription('Create a new migration')
            ->addArgument('name', InputArgument::REQUIRED, 'What is the name of the migration (in CamelCase)?')
            ->setHelp(sprintf(
                '%sCreates a new database migration%s',
                PHP_EOL,
                PHP_EOL
            ));

        // An alternative template.
        $this->addOption('template', 't', InputOption::VALUE_REQUIRED, 'Use an alternative template');

        // A classname to be used to gain access to the template content as well as the ability to
        // have a callback once the migration file has been created.
        $this->addOption('class', 'l', InputOption::VALUE_REQUIRED, 'Use a class implementing "' . self::CREATION_INTERFACE . '" to generate the template');

        // Allow the migration path to be chosen non-interactively.
        $this->addOption('path', null, InputOption::VALUE_REQUIRED, 'Specify the path in which to create this migration');
    }

    /**
     * Get the confirmation question asking if the user wants to create the
     * migrations directory.
     *
     * @return \Symfony\Component\Console\Question\ConfirmationQuestion
     */
    protected function getCreateMigrationDirectoryQuestion()
    {
        return new ConfirmationQuestion('Create migrations directory? [y]/n ', true);
    }

    /**
     * Get the question that allows the user to select which migration path to use.
     *
     * @param string[] $paths
     * @return \Symfony\Component\Console\Question\ChoiceQuestion
     */
    protected function getSelectMigrationPathQuestion(array $paths)
    {
        return new ChoiceQuestion('Which migrations path would you like to use?', $paths, 0);
    }

    /**
     * Returns the migration path to create the migration in.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return mixed
     * @throws \Exception
     */
    protected function getMigrationPath(InputInterface $input, OutputInterface $output)
    {
        // First, try the non-interactive option:
        $path = $input->getOption('path');

        if (!empty($path)) {
            return $path;
        }

        $paths = $this->getConfig()->getMigrationPaths();

        // No paths? That's a problem.
        if (empty($paths)) {
            throw new \Exception('No migration paths set in your Phinx configuration file.');
        }

        $paths = Util::globAll($paths);

        if (empty($paths)) {
            throw new \Exception(
                'You probably used curly braces to define migration path in your Phinx configuration file, ' .
                'but no directories have been matched using this pattern. ' .
                'You need to create a migration directory manually.'
            );
        }

        // Only one path set, so select that:
        if (1 === count($paths)) {
            return array_shift($paths);
        }

        // Ask the user which of their defined paths they'd like to use:
        $helper = $this->getHelper('question');
        $question = $this->getSelectMigrationPathQuestion($paths);

        return $helper->ask($input, $output, $question);
    }

    /**
     * Create the new migration.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @throws \RuntimeException
     * @throws \InvalidArgumentException
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->bootstrap($input, $output);

        // get the migration path from the config
        $path = $this->getMigrationPath($input, $output);

        if (!file_exists($path)) {
            $helper = $this->getHelper('question');
            $question = $this->getCreateMigrationDirectoryQuestion();

            if ($helper->ask($input, $output, $question)) {
                mkdir($path, 0755, true);
            }
        }

        $this->verifyMigrationDirectory($path);

        $config = $this->getConfig();
        $namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath($path) : null;

        $path = realpath($path);
        $className = $input->getArgument('name');

        if (!Util::isValidPhinxClassName($className)) {
            throw new \InvalidArgumentException(sprintf(
                'The migration class name "%s" is invalid. Please use CamelCase format.',
                $className
            ));
        }

        if (!Util::isUniqueMigrationClassName($className, $path)) {
            throw new \InvalidArgumentException(sprintf(
                'The migration class name "%s%s" already exists',
                $namespace ? ($namespace . '\\') : '',
                $className
            ));
        }

        // Compute the file path
        $fileName = Util::mapClassNameToFileName($className);
        $filePath = $path . DIRECTORY_SEPARATOR . $fileName;

        if (is_file($filePath)) {
            throw new \InvalidArgumentException(sprintf(
                'The file "%s" already exists',
                $filePath
            ));
        }

        // Get the alternative template and static class options from the config, but only allow one of them.
        $defaultAltTemplate = $this->getConfig()->getTemplateFile();
        $defaultCreationClassName = $this->getConfig()->getTemplateClass();
        if ($defaultAltTemplate && $defaultCreationClassName) {
            throw new \InvalidArgumentException('Cannot define template:class and template:file at the same time');
        }

        // Get the alternative template and static class options from the command line, but only allow one of them.
        $altTemplate = $input->getOption('template');
        $creationClassName = $input->getOption('class');
        if ($altTemplate && $creationClassName) {
            throw new \InvalidArgumentException('Cannot use --template and --class at the same time');
        }

        // If no commandline options then use the defaults.
        if (!$altTemplate && !$creationClassName) {
            $altTemplate = $defaultAltTemplate;
            $creationClassName = $defaultCreationClassName;
        }

        // Verify the alternative template file's existence.
        if ($altTemplate && !is_file($altTemplate)) {
            throw new \InvalidArgumentException(sprintf(
                'The alternative template file "%s" does not exist',
                $altTemplate
            ));
        }

        // Verify that the template creation class (or the aliased class) exists and that it implements the required interface.
        $aliasedClassName = null;
        if ($creationClassName) {
            // Supplied class does not exist, is it aliased?
            if (!class_exists($creationClassName)) {
                $aliasedClassName = $this->getConfig()->getAlias($creationClassName);
                if ($aliasedClassName && !class_exists($aliasedClassName)) {
                    throw new \InvalidArgumentException(sprintf(
                        'The class "%s" via the alias "%s" does not exist',
                        $aliasedClassName,
                        $creationClassName
                    ));
                } elseif (!$aliasedClassName) {
                    throw new \InvalidArgumentException(sprintf(
                        'The class "%s" does not exist',
                        $creationClassName
                    ));
                }
            }

            // Does the class implement the required interface?
            if (!$aliasedClassName && !is_subclass_of($creationClassName, self::CREATION_INTERFACE)) {
                throw new \InvalidArgumentException(sprintf(
                    'The class "%s" does not implement the required interface "%s"',
                    $creationClassName,
                    self::CREATION_INTERFACE
                ));
            } elseif ($aliasedClassName && !is_subclass_of($aliasedClassName, self::CREATION_INTERFACE)) {
                throw new \InvalidArgumentException(sprintf(
                    'The class "%s" via the alias "%s" does not implement the required interface "%s"',
                    $aliasedClassName,
                    $creationClassName,
                    self::CREATION_INTERFACE
                ));
            }
        }

        // Use the aliased class.
        $creationClassName = $aliasedClassName ?: $creationClassName;

        // Determine the appropriate mechanism to get the template
        if ($creationClassName) {
            // Get the template from the creation class
            $creationClass = new $creationClassName($input, $output);
            $contents = $creationClass->getMigrationTemplate();
        } else {
            // Load the alternative template if it is defined.
            $contents = file_get_contents($altTemplate ?: $this->getMigrationTemplateFilename());
        }

        // inject the class names appropriate to this migration
        $classes = [
            '$namespaceDefinition' => $namespace !== null ? ('namespace ' . $namespace . ';') : '',
            '$namespace' => $namespace,
            '$useClassName' => $this->getConfig()->getMigrationBaseClassName(false),
            '$className' => $className,
            '$version' => Util::getVersionFromFileName($fileName),
            '$baseClassName' => $this->getConfig()->getMigrationBaseClassName(true),
        ];
        $contents = strtr($contents, $classes);

        if (file_put_contents($filePath, $contents) === false) {
            throw new \RuntimeException(sprintf(
                'The file "%s" could not be written to',
                $path
            ));
        }

        // Do we need to do the post creation call to the creation class?
        if (isset($creationClass)) {
            $creationClass->postMigrationCreation($filePath, $className, $this->getConfig()->getMigrationBaseClassName());
        }

        $output->writeln('<info>using migration base class</info> ' . $classes['$useClassName']);

        if (!empty($altTemplate)) {
            $output->writeln('<info>using alternative template</info> ' . $altTemplate);
        } elseif (!empty($creationClassName)) {
            $output->writeln('<info>using template creation class</info> ' . $creationClassName);
        } else {
            $output->writeln('<info>using default template</info>');
        }

        $output->writeln('<info>created</info> ' . str_replace(getcwd() . DIRECTORY_SEPARATOR, '', $filePath));
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Rollback extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment');

        $this->setName('rollback')
            ->setDescription('Rollback the last or to a specific migration')
            ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to rollback to')
            ->addOption('--date', '-d', InputOption::VALUE_REQUIRED, 'The date to rollback to')
            ->addOption('--force', '-f', InputOption::VALUE_NONE, 'Force rollback to ignore breakpoints')
            ->addOption('--dry-run', '-x', InputOption::VALUE_NONE, 'Dump query to standard output instead of executing it')
            ->setHelp(
                <<<EOT
The <info>rollback</info> command reverts the last migration, or optionally up to a specific version

<info>phinx rollback -e development</info>
<info>phinx rollback -e development -t 20111018185412</info>
<info>phinx rollback -e development -d 20111018</info>
<info>phinx rollback -e development -v</info>
<info>phinx rollback -e development -t 20111018185412 -f</info>

If you have a breakpoint set, then you can rollback to target 0 and the rollbacks will stop at the breakpoint.
<info>phinx rollback -e development -t 0 </info>

The <info>version_order</info> configuration option is used to determine the order of the migrations when rolling back.
This can be used to allow the rolling back of the last executed migration instead of the last created one, or combined
with the <info>-d|--date</info> option to rollback to a certain date using the migration start times to order them.

EOT
            );
    }

    /**
     * Rollback the migration.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->bootstrap($input, $output);

        $environment = $input->getOption('environment');
        $version = $input->getOption('target');
        $date = $input->getOption('date');
        $force = (bool)$input->getOption('force');

        $config = $this->getConfig();

        if ($environment === null) {
            $environment = $config->getDefaultEnvironment();
            $output->writeln('<comment>warning</comment> no environment specified, defaulting to: ' . $environment);
        } else {
            $output->writeln('<info>using environment</info> ' . $environment);
        }

        $envOptions = $config->getEnvironment($environment);
        if (isset($envOptions['adapter'])) {
            $output->writeln('<info>using adapter</info> ' . $envOptions['adapter']);
        }

        if (isset($envOptions['wrapper'])) {
            $output->writeln('<info>using wrapper</info> ' . $envOptions['wrapper']);
        }

        if (isset($envOptions['name'])) {
            $output->writeln('<info>using database</info> ' . $envOptions['name']);
        }

        $versionOrder = $this->getConfig()->getVersionOrder();
        $output->writeln('<info>ordering by </info>' . $versionOrder . " time");

        // rollback the specified environment
        if ($date === null) {
            $targetMustMatchVersion = true;
            $target = $version;
        } else {
            $targetMustMatchVersion = false;
            $target = $this->getTargetFromDate($date);
        }

        $start = microtime(true);
        $this->getManager()->rollback($environment, $target, $force, $targetMustMatchVersion);
        $end = microtime(true);

        $output->writeln('');
        $output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');
    }

    /**
     * Get Target from Date
     *
     * @param string $date The date to convert to a target.
     * @return string The target
     */
    public function getTargetFromDate($date)
    {
        if (!preg_match('/^\d{4,14}$/', $date)) {
            throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
        }

        // what we need to append to the date according to the possible date string lengths
        $dateStrlenToAppend = [
            14 => '',
            12 => '00',
            10 => '0000',
            8 => '000000',
            6 => '01000000',
            4 => '0101000000',
        ];

        if (!isset($dateStrlenToAppend[strlen($date)])) {
            throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
        }

        $target = $date . $dateStrlenToAppend[strlen($date)];

        $dateTime = \DateTime::createFromFormat('YmdHis', $target);

        if ($dateTime === false) {
            throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
        }

        return $dateTime->format('YmdHis');
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Phinx\Migration\Manager\Environment;
use Phinx\Util\Util;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * @author Leonid Kuzmin <lndkuzmin@gmail.com>
 */
class Test extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment');

        $this->setName('test')
            ->setDescription('Verify the configuration file')
            ->setHelp(
                <<<EOT
The <info>test</info> command verifies the YAML configuration file and optionally an environment

<info>phinx test</info>
<info>phinx test -e development</info>

EOT
            );
    }

    /**
     * Verify configuration file
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @throws \RuntimeException
     * @throws \InvalidArgumentException
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->loadConfig($input, $output);
        $this->loadManager($input, $output);

        // Verify the migrations path(s)
        array_map(
            [$this, 'verifyMigrationDirectory'],
            Util::globAll($this->getConfig()->getMigrationPaths())
        );

        // Verify the seed path(s)
        array_map(
            [$this, 'verifySeedDirectory'],
            Util::globAll($this->getConfig()->getSeedPaths())
        );

        $envName = $input->getOption('environment');
        if ($envName) {
            if (!$this->getConfig()->hasEnvironment($envName)) {
                throw new \InvalidArgumentException(sprintf(
                    'The environment "%s" does not exist',
                    $envName
                ));
            }

            $output->writeln(sprintf('<info>validating environment</info> %s', $envName));
            $environment = new Environment(
                $envName,
                $this->getConfig()->getEnvironment($envName)
            );
            // validate environment connection
            $environment->getAdapter()->connect();
        }

        $output->writeln('<info>success!</info>');
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Status extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment.');

        $this->setName('status')
            ->setDescription('Show migration status')
            ->addOption('--format', '-f', InputOption::VALUE_REQUIRED, 'The output format: text or json. Defaults to text.')
            ->setHelp(
                <<<EOT
The <info>status</info> command prints a list of all migrations, along with their current status

<info>phinx status -e development</info>
<info>phinx status -e development -f json</info>

The <info>version_order</info> configuration option is used to determine the order of the status migrations.
EOT
            );
    }

    /**
     * Show the migration status.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return int 0 if all migrations are up, or an error code
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->bootstrap($input, $output);

        $environment = $input->getOption('environment');
        $format = $input->getOption('format');

        if ($environment === null) {
            $environment = $this->getConfig()->getDefaultEnvironment();
            $output->writeln('<comment>warning</comment> no environment specified, defaulting to: ' . $environment);
        } else {
            $output->writeln('<info>using environment</info> ' . $environment);
        }
        if ($format !== null) {
            $output->writeln('<info>using format</info> ' . $format);
        }

        $output->writeln('<info>ordering by </info>' . $this->getConfig()->getVersionOrder() . " time");

        // print the status
        return $this->getManager()->printStatus($environment, $format);
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @author     Richard Quadling
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Breakpoint extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment.');

        $this->setName('breakpoint')
            ->setDescription('Manage breakpoints')
            ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to set or clear a breakpoint against')
            ->addOption('--remove-all', '-r', InputOption::VALUE_NONE, 'Remove all breakpoints')
            ->setHelp(
                <<<EOT
The <info>breakpoint</info> command allows you to set or clear a breakpoint against a specific target to inhibit rollbacks beyond a certain target.
If no target is supplied then the most recent migration will be used.
You cannot specify un-migrated targets

<info>phinx breakpoint -e development</info>
<info>phinx breakpoint -e development -t 20110103081132</info>
<info>phinx breakpoint -e development -r</info>
EOT
            );
    }

    /**
     * Toggle the breakpoint.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->bootstrap($input, $output);

        $environment = $input->getOption('environment');
        $version = $input->getOption('target');
        $removeAll = $input->getOption('remove-all');

        if ($environment === null) {
            $environment = $this->getConfig()->getDefaultEnvironment();
            $output->writeln('<comment>warning</comment> no environment specified, defaulting to: ' . $environment);
        } else {
            $output->writeln('<info>using environment</info> ' . $environment);
        }

        if ($version && $removeAll) {
            throw new \InvalidArgumentException('Cannot toggle a breakpoint and remove all breakpoints at the same time.');
        }

        // Remove all breakpoints
        if ($removeAll) {
            $this->getManager()->removeBreakpoints($environment);
        } else {
            // Toggle the breakpoint.
            $this->getManager()->toggleBreakpoint($environment, $version);
        }
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Phinx\Config\Config;
use Phinx\Config\ConfigInterface;
use Phinx\Db\Adapter\AdapterInterface;
use Phinx\Migration\Manager;
use Phinx\Util\Util;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Abstract command, contains bootstrapping info
 *
 * @author Rob Morgan <robbym@gmail.com>
 */
abstract class AbstractCommand extends Command
{
    /**
     * The location of the default migration template.
     */
    const DEFAULT_MIGRATION_TEMPLATE = '/../../Migration/Migration.template.php.dist';

    /**
     * The location of the default seed template.
     */
    const DEFAULT_SEED_TEMPLATE = '/../../Seed/Seed.template.php.dist';

    /**
     * @var \Phinx\Config\ConfigInterface
     */
    protected $config;

    /**
     * @var \Phinx\Db\Adapter\AdapterInterface
     */
    protected $adapter;

    /**
     * @var \Phinx\Migration\Manager
     */
    protected $manager;

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->addOption('--configuration', '-c', InputOption::VALUE_REQUIRED, 'The configuration file to load');
        $this->addOption('--parser', '-p', InputOption::VALUE_REQUIRED, 'Parser used to read the config file. Defaults to YAML');
    }

    /**
     * Bootstrap Phinx.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return void
     */
    public function bootstrap(InputInterface $input, OutputInterface $output)
    {
        if (!$this->getConfig()) {
            $this->loadConfig($input, $output);
        }

        $this->loadManager($input, $output);

        // report the paths
        $paths = $this->getConfig()->getMigrationPaths();

        $output->writeln('<info>using migration paths</info> ');

        foreach (Util::globAll($paths) as $path) {
            $output->writeln('<info> - ' . realpath($path) . '</info>');
        }

        try {
            $paths = $this->getConfig()->getSeedPaths();

            $output->writeln('<info>using seed paths</info> ');

            foreach (Util::globAll($paths) as $path) {
                $output->writeln('<info> - ' . realpath($path) . '</info>');
            }
        } catch (\UnexpectedValueException $e) {
            // do nothing as seeds are optional
        }
    }

    /**
     * Sets the config.
     *
     * @param  \Phinx\Config\ConfigInterface $config
     * @return \Phinx\Console\Command\AbstractCommand
     */
    public function setConfig(ConfigInterface $config)
    {
        $this->config = $config;

        return $this;
    }

    /**
     * Gets the config.
     *
     * @return \Phinx\Config\ConfigInterface
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * Sets the database adapter.
     *
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter
     * @return \Phinx\Console\Command\AbstractCommand
     */
    public function setAdapter(AdapterInterface $adapter)
    {
        $this->adapter = $adapter;

        return $this;
    }

    /**
     * Gets the database adapter.
     *
     * @return \Phinx\Db\Adapter\AdapterInterface
     */
    public function getAdapter()
    {
        return $this->adapter;
    }

    /**
     * Sets the migration manager.
     *
     * @param \Phinx\Migration\Manager $manager
     * @return \Phinx\Console\Command\AbstractCommand
     */
    public function setManager(Manager $manager)
    {
        $this->manager = $manager;

        return $this;
    }

    /**
     * Gets the migration manager.
     *
     * @return \Phinx\Migration\Manager
     */
    public function getManager()
    {
        return $this->manager;
    }

    /**
     * Returns config file path
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @return string
     */
    protected function locateConfigFile(InputInterface $input)
    {
        $configFile = $input->getOption('configuration');

        $useDefault = false;

        if ($configFile === null || $configFile === false) {
            $useDefault = true;
        }

        $cwd = getcwd();

        // locate the phinx config file (default: phinx.yml)
        // TODO - In future walk the tree in reverse (max 10 levels)
        $locator = new FileLocator([
            $cwd . DIRECTORY_SEPARATOR
        ]);

        if (!$useDefault) {
            // Locate() throws an exception if the file does not exist
            return $locator->locate($configFile, $cwd, $first = true);
        }

        $possibleConfigFiles = ['phinx.php', 'phinx.json', 'phinx.yml'];
        foreach ($possibleConfigFiles as $configFile) {
            try {
                return $locator->locate($configFile, $cwd, $first = true);
            } catch (\InvalidArgumentException $exception) {
                $lastException = $exception;
            }
        }
        throw $lastException;
    }

    /**
     * Parse the config file and load it into the config object
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @throws \InvalidArgumentException
     * @return void
     */
    protected function loadConfig(InputInterface $input, OutputInterface $output)
    {
        $configFilePath = $this->locateConfigFile($input);
        $output->writeln('<info>using config file</info> .' . str_replace(getcwd(), '', realpath($configFilePath)));

        $parser = $input->getOption('parser');

        // If no parser is specified try to determine the correct one from the file extension.  Defaults to YAML
        if ($parser === null) {
            $extension = pathinfo($configFilePath, PATHINFO_EXTENSION);

            switch (strtolower($extension)) {
                case 'json':
                    $parser = 'json';
                    break;
                case 'php':
                    $parser = 'php';
                    break;
                case 'yml':
                default:
                    $parser = 'yaml';
            }
        }

        switch (strtolower($parser)) {
            case 'json':
                $config = Config::fromJson($configFilePath);
                break;
            case 'php':
                $config = Config::fromPhp($configFilePath);
                break;
            case 'yaml':
                $config = Config::fromYaml($configFilePath);
                break;
            default:
                throw new \InvalidArgumentException(sprintf('\'%s\' is not a valid parser.', $parser));
        }

        $output->writeln('<info>using config parser</info> ' . $parser);

        $this->setConfig($config);
    }

    /**
     * Load the migrations manager and inject the config
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     */
    protected function loadManager(InputInterface $input, OutputInterface $output)
    {
        if ($this->getManager() === null) {
            $manager = new Manager($this->getConfig(), $input, $output);
            $this->setManager($manager);
        } else {
            $manager = $this->getManager();
            $manager->setInput($input);
            $manager->setOutput($output);
        }
    }

    /**
     * Verify that the migration directory exists and is writable.
     *
     * @param string $path
     * @throws \InvalidArgumentException
     * @return void
     */
    protected function verifyMigrationDirectory($path)
    {
        if (!is_dir($path)) {
            throw new \InvalidArgumentException(sprintf(
                'Migration directory "%s" does not exist',
                $path
            ));
        }

        if (!is_writable($path)) {
            throw new \InvalidArgumentException(sprintf(
                'Migration directory "%s" is not writable',
                $path
            ));
        }
    }

    /**
     * Verify that the seed directory exists and is writable.
     *
     * @param string $path
     * @throws \InvalidArgumentException
     * @return void
     */
    protected function verifySeedDirectory($path)
    {
        if (!is_dir($path)) {
            throw new \InvalidArgumentException(sprintf(
                'Seed directory "%s" does not exist',
                $path
            ));
        }

        if (!is_writable($path)) {
            throw new \InvalidArgumentException(sprintf(
                'Seed directory "%s" is not writable',
                $path
            ));
        }
    }

    /**
     * Returns the migration template filename.
     *
     * @return string
     */
    protected function getMigrationTemplateFilename()
    {
        return __DIR__ . self::DEFAULT_MIGRATION_TEMPLATE;
    }

    /**
     * Returns the seed template filename.
     *
     * @return string
     */
    protected function getSeedTemplateFilename()
    {
        return __DIR__ . self::DEFAULT_SEED_TEMPLATE;
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * @package    Phinx
 * @subpackage Phinx\Console
 */
namespace Phinx\Console\Command;

use Phinx\Config\NamespaceAwareInterface;
use Phinx\Util\Util;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;

class SeedCreate extends AbstractCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();

        $this->setName('seed:create')
            ->setDescription('Create a new database seeder')
            ->addArgument('name', InputArgument::REQUIRED, 'What is the name of the seeder?')
            ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Specify the path in which to create this seeder')
            ->setHelp(sprintf(
                '%sCreates a new database seeder%s',
                PHP_EOL,
                PHP_EOL
            ));
    }

    /**
     * Get the confirmation question asking if the user wants to create the
     * seeds directory.
     *
     * @return \Symfony\Component\Console\Question\ConfirmationQuestion
     */
    protected function getCreateSeedDirectoryQuestion()
    {
        return new ConfirmationQuestion('Create seeds directory? [y]/n ', true);
    }

    /**
     * Get the question that allows the user to select which seed path to use.
     *
     * @param string[] $paths
     * @return \Symfony\Component\Console\Question\ChoiceQuestion
     */
    protected function getSelectSeedPathQuestion(array $paths)
    {
        return new ChoiceQuestion('Which seeds path would you like to use?', $paths, 0);
    }

    /**
     * Returns the seed path to create the seeder in.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return mixed
     * @throws \Exception
     */
    protected function getSeedPath(InputInterface $input, OutputInterface $output)
    {
        // First, try the non-interactive option:
        $path = $input->getOption('path');

        if (!empty($path)) {
            return $path;
        }

        $paths = $this->getConfig()->getSeedPaths();

        // No paths? That's a problem.
        if (empty($paths)) {
            throw new \Exception('No seed paths set in your Phinx configuration file.');
        }

        $paths = Util::globAll($paths);

        if (empty($paths)) {
            throw new \Exception(
                'You probably used curly braces to define seed path in your Phinx configuration file, ' .
                'but no directories have been matched using this pattern. ' .
                'You need to create a seed directory manually.'
            );
        }

        // Only one path set, so select that:
        if (1 === count($paths)) {
            return array_shift($paths);
        }

        // Ask the user which of their defined paths they'd like to use:
        $helper = $this->getHelper('question');
        $question = $this->getSelectSeedPathQuestion($paths);

        return $helper->ask($input, $output, $question);
    }

    /**
     * Create the new seeder.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @throws \RuntimeException
     * @throws \InvalidArgumentException
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->bootstrap($input, $output);

        // get the seed path from the config
        $path = $this->getSeedPath($input, $output);

        if (!file_exists($path)) {
            $helper = $this->getHelper('question');
            $question = $this->getCreateSeedDirectoryQuestion();

            if ($helper->ask($input, $output, $question)) {
                mkdir($path, 0755, true);
            }
        }

        $this->verifySeedDirectory($path);

        $path = realpath($path);
        $className = $input->getArgument('name');

        if (!Util::isValidPhinxClassName($className)) {
            throw new \InvalidArgumentException(sprintf(
                'The seed class name "%s" is invalid. Please use CamelCase format',
                $className
            ));
        }

        // Compute the file path
        $filePath = $path . DIRECTORY_SEPARATOR . $className . '.php';

        if (is_file($filePath)) {
            throw new \InvalidArgumentException(sprintf(
                'The file "%s" already exists',
                basename($filePath)
            ));
        }

        // inject the class names appropriate to this seeder
        $contents = file_get_contents($this->getSeedTemplateFilename());

        $config = $this->getConfig();
        $namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath($path) : null;
        $classes = [
            '$namespaceDefinition' => $namespace !== null ? ('namespace ' . $namespace . ';') : '',
            '$namespace' => $namespace,
            '$useClassName' => 'Phinx\Seed\AbstractSeed',
            '$className' => $className,
            '$baseClassName' => 'AbstractSeed',
        ];
        $contents = strtr($contents, $classes);

        if (file_put_contents($filePath, $contents) === false) {
            throw new \RuntimeException(sprintf(
                'The file "%s" could not be written to',
                $path
            ));
        }

        $output->writeln('<info>using seed base class</info> ' . $classes['$useClassName']);
        $output->writeln('<info>created</info> .' . str_replace(getcwd(), '', $filePath));
    }
}
<?php
/**
 * Phinx
 *
 * (The MIT license)
 * Copyright (c) 2015 Rob Morgan
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated * documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * Attempts to load Composer's autoload.php as either a dependency or a
 * stand-alone package.
 *
 * @return bool
 */
return function () {
    $files = [
      __DIR__ . '/../../../autoload.php', // composer dependency
      __DIR__ . '/../vendor/autoload.php', // stand-alone package
    ];
    foreach ($files as $file) {
        if (is_file($file)) {
            require_once $file;

            return true;
        }
    }

    return false;
};
<?php

namespace Psr\Log;

/**
 * Describes a logger-aware instance.
 */
interface LoggerAwareInterface
{
    /**
     * Sets a logger instance on the object.
     *
     * @param LoggerInterface $logger
     *
     * @return void
     */
    public function setLogger(LoggerInterface $logger);
}
<?php

namespace Psr\Log;

/**
 * Describes log levels.
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}
<?php

namespace Psr\Log\Test;

use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;

/**
 * Provides a base test class for ensuring compliance with the LoggerInterface.
 *
 * Implementors can extend the class and implement abstract methods to run this
 * as part of their test suite.
 */
abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @return LoggerInterface
     */
    abstract public function getLogger();

    /**
     * This must return the log messages in order.
     *
     * The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
     *
     * Example ->error('Foo') would yield "error Foo".
     *
     * @return string[]
     */
    abstract public function getLogs();

    public function testImplements()
    {
        $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
    }

    /**
     * @dataProvider provideLevelsAndMessages
     */
    public function testLogsAtAllLevels($level, $message)
    {
        $logger = $this->getLogger();
        $logger->{$level}($message, array('user' => 'Bob'));
        $logger->log($level, $message, array('user' => 'Bob'));

        $expected = array(
            $level.' message of level '.$level.' with context: Bob',
            $level.' message of level '.$level.' with context: Bob',
        );
        $this->assertEquals($expected, $this->getLogs());
    }

    public function provideLevelsAndMessages()
    {
        return array(
            LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
            LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
            LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
            LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
            LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
            LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
            LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
            LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
        );
    }

    /**
     * @expectedException \Psr\Log\InvalidArgumentException
     */
    public function testThrowsOnInvalidLevel()
    {
        $logger = $this->getLogger();
        $logger->log('invalid level', 'Foo');
    }

    public function testContextReplacement()
    {
        $logger = $this->getLogger();
        $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));

        $expected = array('info {Message {nothing} Bob Bar a}');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testObjectCastToString()
    {
        if (method_exists($this, 'createPartialMock')) {
            $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString'));
        } else {
            $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
        }
        $dummy->expects($this->once())
            ->method('__toString')
            ->will($this->returnValue('DUMMY'));

        $this->getLogger()->warning($dummy);

        $expected = array('warning DUMMY');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testContextCanContainAnything()
    {
        $context = array(
            'bool' => true,
            'null' => null,
            'string' => 'Foo',
            'int' => 0,
            'float' => 0.5,
            'nested' => array('with object' => new DummyTest),
            'object' => new \DateTime,
            'resource' => fopen('php://memory', 'r'),
        );

        $this->getLogger()->warning('Crazy context data', $context);

        $expected = array('warning Crazy context data');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testContextExceptionKeyCanBeExceptionOrOtherValues()
    {
        $logger = $this->getLogger();
        $logger->warning('Random message', array('exception' => 'oops'));
        $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));

        $expected = array(
            'warning Random message',
            'critical Uncaught Exception!'
        );
        $this->assertEquals($expected, $this->getLogs());
    }
}

class DummyTest
{
    public function __toString()
    {
    }
}
<?php

namespace Psr\Log;

/**
 * Basic Implementation of LoggerAwareInterface.
 */
trait LoggerAwareTrait
{
    /**
     * The logger instance.
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Sets a logger.
     *
     * @param LoggerInterface $logger
     */
    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
}
<?php

namespace Psr\Log;

class InvalidArgumentException extends \InvalidArgumentException
{
}
<?php

namespace Psr\Log;

/**
 * This Logger can be used to avoid conditional log calls.
 *
 * Logging should always be optional, and if no logger is provided to your
 * library creating a NullLogger instance to have something to throw logs at
 * is a good way to avoid littering your code with `if ($this->logger) { }`
 * blocks.
 */
class NullLogger extends AbstractLogger
{
    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function log($level, $message, array $context = array())
    {
        // noop
    }
}
<?php

namespace Psr\Log;

/**
 * Describes a logger instance.
 *
 * The message MUST be a string or object implementing __toString().
 *
 * The message MAY contain placeholders in the form: {foo} where foo
 * will be replaced by the context data in key "foo".
 *
 * The context array can contain arbitrary data. The only assumption that
 * can be made by implementors is that if an Exception instance is given
 * to produce a stack trace, it MUST be in a key named "exception".
 *
 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * for the full interface specification.
 */
interface LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array());

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array());

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array());

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array());

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array());

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array());

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array());

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array());

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function log($level, $message, array $context = array());
}
<?php

namespace Psr\Log;

/**
 * This is a simple Logger trait that classes unable to extend AbstractLogger
 * (because they extend another class, etc) can include.
 *
 * It simply delegates all log-level-specific methods to the `log` method to
 * reduce boilerplate code that a simple Logger that does the same thing with
 * messages regardless of the error level has to implement.
 */
trait LoggerTrait
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    abstract public function log($level, $message, array $context = array());
}
<?php

namespace Psr\Log;

/**
 * This is a simple Logger implementation that other Loggers can inherit from.
 *
 * It simply delegates all log-level-specific methods to the `log` method to
 * reduce boilerplate code that a simple Logger that does the same thing with
 * messages regardless of the error level has to implement.
 */
abstract class AbstractLogger implements LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }
}
{
    "name": "psr/log",
    "description": "Common interface for logging libraries",
    "keywords": ["psr", "psr-3", "log"],
    "homepage": "https://github.com/php-fig/log",
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "http://www.php-fig.org/"
        }
    ],
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "Psr\\Log\\": "Psr/Log/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0.x-dev"
        }
    }
}
{
  "name": "voku/php-hooks",
  "description": "A fork of the WordPress filters hook system rolled in to a class to be ported into any PHP-based system",
  "keywords": [
    "hook-system",
    "filters-system",
    "hooks",
    "actions",
    "filters"
  ],
  "type": "library",
  "homepage": "http://github.com/voku/php-hooks/",
  "license": "GPL-3.0",
  "authors": [
    {
      "name": "Ohad Raz",
      "homepage": "https://github.com/bainternet",
      "role": "Developer"
    },
    {
      "name": "Lars Moelleken",
      "homepage": "http://www.moelleken.org/",
      "role": "Developer"
    }
  ],
  "support": {
    "issues": "https://github.com/voku/php-hooks/issues"
  },
  "require": {
    "php": ">=5.3.0"
  },
  "require-dev": {
    "phpunit/phpunit": "4.*"
  },
  "autoload": {
    "psr-4": {
      "voku\\helper\\": "src/voku/helper/"
    }
  }
}
<?php

namespace voku\helper;

/**
 * PHP Hooks Class (Modified)
 *
 * <p>
 * <br />
 * The PHP Hooks Class is a fork of the WordPress filters hook system rolled in
 * to a class to be ported into any php based system
 *
 * <br /><br />
 * This class is heavily based on the WordPress plugin API and most (if not all)
 * of the code comes from there.
 * </p>
 *
 * @copyright   2011 - 2017
 *
 * @author      Ohad Raz <admin@bainternet.info>
 * @link        http://en.bainternet.info
 * @author      David Miles <david@amereservant.com>
 * @link        http://github.com/amereservant/PHP-Hooks
 * @author      Lars Moelleken <lars@moelleken.org>
 * @link        https://github.com/voku/PHP-Hooks/
 * @author      Damien "Mistic" Sorel <contact@git.strangeplanet.fr>
 * @link        http://www.strangeplanet.fr
 *
 * @license     GNU General Public License v3.0 - license.txt
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @package     voku\helper
 */
class Hooks
{
  /**
   * Filters - holds list of hooks
   *
   * @var array
   */
  protected $filters = array();

  /**
   * Merged Filters
   *
   * @var array
   */
  protected $merged_filters = array();

  /**
   * Actions
   *
   * @var array
   */
  protected $actions = array();

  /**
   * Current Filter - holds the name of the current filter
   *
   * @var array
   */
  protected $current_filter = array();

  /**
   * Container for storing shortcode tags and their hook to call for the shortcode
   *
   * @var array
   */
  public static $shortcode_tags = array();

  /**
   * Default priority
   *
   * @const int
   */
  const PRIORITY_NEUTRAL = 50;

  /**
   * This class is not allowed to call from outside: private!
   */
  protected function __construct()
  {
  }

  /**
   * Prevent the object from being cloned.
   */
  protected function __clone()
  {
  }

  /**
   * Avoid serialization.
   */
  public function __wakeup()
  {
  }

  /**
   * Returns a Singleton instance of this class.
   *
   * @return Hooks
   */
  public static function getInstance()
  {
    static $instance;

    if (null === $instance) {
      $instance = new self();
    }

    return $instance;
  }

  /**
   * FILTERS
   */

  /**
   * Adds Hooks to a function or method to a specific filter action.
   *
   * @param    string       $tag             <p>
   *                                         The name of the filter to hook the
   *                                         {@link $function_to_add} to.
   *                                         </p>
   * @param    string|array $function_to_add <p>
   *                                         The name of the function to be called
   *                                         when the filter is applied.
   *                                         </p>
   * @param    integer      $priority        <p>
   *                                         [optional] Used to specify the order in
   *                                         which the functions associated with a
   *                                         particular action are executed (default: 50).
   *                                         Lower numbers correspond with earlier execution,
   *                                         and functions with the same priority are executed
   *                                         in the order in which they were added to the action.
   *                                         </p>
   * @param string          $include_path    <p>
   *                                         [optional] File to include before executing the callback.
   *                                         </p>
   *
   * @return boolean
   */
  public function add_filter($tag, $function_to_add, $priority = self::PRIORITY_NEUTRAL, $include_path = null)
  {
    $idx = $this->_filter_build_unique_id($function_to_add);

    $this->filters[$tag][$priority][$idx] = array(
        'function'     => $function_to_add,
        'include_path' => is_string($include_path) ? $include_path : null,
    );

    unset($this->merged_filters[$tag]);

    return true;
  }

  /**
   * Removes a function from a specified filter hook.
   *
   * @param string $tag                <p>The filter hook to which the function to be removed is hooked.</p>
   * @param mixed  $function_to_remove <p>The name of the function which should be removed.</p>
   * @param int    $priority           <p>[optional] The priority of the function (default: 50).</p>
   *
   * @return bool
   */
  public function remove_filter($tag, $function_to_remove, $priority = self::PRIORITY_NEUTRAL)
  {
    $function_to_remove = $this->_filter_build_unique_id($function_to_remove);

    if (!isset($this->filters[$tag][$priority][$function_to_remove])) {
      return false;
    }

    unset($this->filters[$tag][$priority][$function_to_remove]);
    if (empty($this->filters[$tag][$priority])) {
      unset($this->filters[$tag][$priority]);
    }

    unset($this->merged_filters[$tag]);

    return true;
  }

  /**
   * Remove all of the hooks from a filter.
   *
   * @param string $tag      <p>The filter to remove hooks from.</p>
   * @param bool   $priority <p>The priority number to remove.</p>
   *
   * @return bool
   */
  public function remove_all_filters($tag, $priority = false)
  {
    if (isset($this->merged_filters[$tag])) {
      unset($this->merged_filters[$tag]);
    }

    if (!isset($this->filters[$tag])) {
      return true;
    }

    if (false !== $priority && isset($this->filters[$tag][$priority])) {
      unset($this->filters[$tag][$priority]);
    } else {
      unset($this->filters[$tag]);
    }

    return true;
  }

  /**
   * Check if any filter has been registered for the given hook.
   *
   * <p>
   * <br />
   * <strong>INFO:</strong> Use !== false to check if it's true!
   * </p>
   *
   * @param    string $tag               <p>The name of the filter hook.</p>
   * @param    bool   $function_to_check <p>[optional] Callback function name to check for </p>
   *
   * @return   mixed                       <p>
   *                                       If {@link $function_to_check} is omitted,
   *                                       returns boolean for whether the hook has
   *                                       anything registered.
   *                                       When checking a specific function, the priority
   *                                       of that hook is returned, or false if the
   *                                       function is not attached.
   *                                       When using the {@link $function_to_check} argument,
   *                                       this function may return a non-boolean value that
   *                                       evaluates to false
   *                                       (e.g.) 0, so use the === operator for testing the return value.
   *                                       </p>
   */
  public function has_filter($tag, $function_to_check = false)
  {
    $has = isset($this->filters[$tag]);
    if (false === $function_to_check || !$has) {
      return $has;
    }

    if (!($idx = $this->_filter_build_unique_id($function_to_check))) {
      return false;
    }

    foreach ((array)array_keys($this->filters[$tag]) as $priority) {
      if (isset($this->filters[$tag][$priority][$idx])) {
        return $priority;
      }
    }

    return false;
  }

  /**
   * Call the functions added to a filter hook.
   *
   * <p>
   * <br />
   * <strong>INFO:</strong> Additional variables passed to the functions hooked to <tt>$tag</tt>.
   * </p>
   *
   * @param    string|array $tag   <p>The name of the filter hook.</p>
   * @param    mixed        $value <p>The value on which the filters hooked to <tt>$tag</tt> are applied on.</p>
   *
   * @return   mixed               <p>The filtered value after all hooked functions are applied to it.</p>
   */
  public function apply_filters($tag, $value)
  {
    $args = array();

    // Do 'all' actions first
    if (isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
      $args = func_get_args();
      $this->_call_all_hook($args);
    }

    if (!isset($this->filters[$tag])) {
      if (isset($this->filters['all'])) {
        array_pop($this->current_filter);
      }

      return $value;
    }

    if (!isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
    }

    // Sort
    if (!isset($this->merged_filters[$tag])) {
      ksort($this->filters[$tag]);
      $this->merged_filters[$tag] = true;
    }

    reset($this->filters[$tag]);

    if (empty($args)) {
      $args = func_get_args();
    }

    array_shift($args);

    do {
      foreach ((array)current($this->filters[$tag]) as $the_) {
        if (null !== $the_['function']) {

          if (null !== $the_['include_path']) {
            /** @noinspection PhpIncludeInspection */
            include_once $the_['include_path'];
          }

          $args[0] = $value;
          $value = call_user_func_array($the_['function'], $args);
        }
      }
    } while (next($this->filters[$tag]) !== false);

    array_pop($this->current_filter);

    return $value;
  }

  /**
   * Execute functions hooked on a specific filter hook, specifying arguments in an array.
   *
   * @param    string $tag  <p>The name of the filter hook.</p>
   * @param    array  $args <p>The arguments supplied to the functions hooked to <tt>$tag</tt></p>
   *
   * @return   mixed        <p>The filtered value after all hooked functions are applied to it.</p>
   */
  public function apply_filters_ref_array($tag, $args)
  {
    // Do 'all' actions first
    if (isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
      $all_args = func_get_args();
      $this->_call_all_hook($all_args);
    }

    if (!isset($this->filters[$tag])) {
      if (isset($this->filters['all'])) {
        array_pop($this->current_filter);
      }

      return $args[0];
    }

    if (!isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
    }

    // Sort
    if (!isset($this->merged_filters[$tag])) {
      ksort($this->filters[$tag]);
      $this->merged_filters[$tag] = true;
    }

    reset($this->filters[$tag]);

    do {
      foreach ((array)current($this->filters[$tag]) as $the_) {
        if (null !== $the_['function']) {

          if (null !== $the_['include_path']) {
            /** @noinspection PhpIncludeInspection */
            include_once $the_['include_path'];
          }

          $args[0] = call_user_func_array($the_['function'], $args);
        }
      }
    } while (next($this->filters[$tag]) !== false);

    array_pop($this->current_filter);

    return $args[0];
  }

  /**
   * Hooks a function on to a specific action.
   *
   * @param    string  $tag             <p>
   *                                    The name of the action to which the
   *                                    <tt>$function_to_add</tt> is hooked.
   *                                    </p>
   * @param    string  $function_to_add <p>The name of the function you wish to be called.</p>
   * @param    integer $priority        <p>
   *                                    [optional] Used to specify the order in which
   *                                    the functions associated with a particular
   *                                    action are executed (default: 50).
   *                                    Lower numbers correspond with earlier execution,
   *                                    and functions with the same priority are executed
   *                                    in the order in which they were added to the action.
   *                                    </p>
   * @param     string $include_path    <p>[optional] File to include before executing the callback.</p>
   *
   * @return bool
   */
  public function add_action($tag, $function_to_add, $priority = self::PRIORITY_NEUTRAL, $include_path = null)
  {
    return $this->add_filter($tag, $function_to_add, $priority, $include_path);
  }

  /**
   * Check if any action has been registered for a hook.
   *
   * <p>
   * <br />
   * <strong>INFO:</strong> Use !== false to check if it's true!
   * </p>
   *
   * @param    string   $tag               <p>The name of the action hook.</p>
   * @param bool|string $function_to_check <p>[optional]</p>
   *
   * @return   mixed                       <p>
   *                                       If <tt>$function_to_check</tt> is omitted,
   *                                       returns boolean for whether the hook has
   *                                       anything registered.
   *                                       When checking a specific function,
   *                                       the priority of that hook is returned,
   *                                       or false if the function is not attached.
   *                                       When using the <tt>$function_to_check</tt>
   *                                       argument, this function may return a non-boolean
   *                                       value that evaluates to false (e.g.) 0,
   *                                       so use the === operator for testing the return value.
   *                                       </p>
   */
  public function has_action($tag, $function_to_check = false)
  {
    return $this->has_filter($tag, $function_to_check);
  }

  /**
   * Removes a function from a specified action hook.
   *
   * @param string $tag                <p>The action hook to which the function to be removed is hooked.</p>
   * @param mixed  $function_to_remove <p>The name of the function which should be removed.</p>
   * @param int    $priority           <p>[optional] The priority of the function (default: 50).</p>
   *
   * @return bool <p>Whether the function is removed.</p>
   */
  public function remove_action($tag, $function_to_remove, $priority = self::PRIORITY_NEUTRAL)
  {
    return $this->remove_filter($tag, $function_to_remove, $priority);
  }

  /**
   * Remove all of the hooks from an action.
   *
   * @param string $tag      <p>The action to remove hooks from.</p>
   * @param bool   $priority <p>The priority number to remove them from.</p>
   *
   * @return bool
   */
  public function remove_all_actions($tag, $priority = false)
  {
    return $this->remove_all_filters($tag, $priority);
  }

  /**
   * Execute functions hooked on a specific action hook.
   *
   * @param    string $tag     <p>The name of the action to be executed.</p>
   * @param    mixed  $arg     <p>
   *                           [optional] Additional arguments which are passed on
   *                           to the functions hooked to the action.
   *                           </p>
   *
   * @return   bool            <p>Will return false if $tag does not exist in $filter array.</p>
   */
  public function do_action($tag, $arg = '')
  {
    if (!is_array($this->actions)) {
      $this->actions = array();
    }

    if (!isset($this->actions[$tag])) {
      $this->actions[$tag] = 1;
    } else {
      ++$this->actions[$tag];
    }

    // Do 'all' actions first
    if (isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
      $all_args = func_get_args();
      $this->_call_all_hook($all_args);
    }

    if (!isset($this->filters[$tag])) {
      if (isset($this->filters['all'])) {
        array_pop($this->current_filter);
      }

      return false;
    }

    if (!isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
    }

    $args = array();

    if (
        is_array($arg)
        &&
        isset($arg[0])
        &&
        is_object($arg[0])
        &&
        1 == count($arg)
    ) {
      $args[] =& $arg[0];
    } else {
      $args[] = $arg;
    }

    $numArgs = func_num_args();

    for ($a = 2; $a < $numArgs; $a++) {
      $args[] = func_get_arg($a);
    }

    // Sort
    if (!isset($this->merged_filters[$tag])) {
      ksort($this->filters[$tag]);
      $this->merged_filters[$tag] = true;
    }

    reset($this->filters[$tag]);

    do {
      foreach ((array)current($this->filters[$tag]) as $the_) {
        if (null !== $the_['function']) {

          if (null !== $the_['include_path']) {
            /** @noinspection PhpIncludeInspection */
            include_once $the_['include_path'];
          }

          call_user_func_array($the_['function'], $args);
        }
      }
    } while (next($this->filters[$tag]) !== false);

    array_pop($this->current_filter);

    return true;
  }

  /**
   * Execute functions hooked on a specific action hook, specifying arguments in an array.
   *
   * @param    string $tag  <p>The name of the action to be executed.</p>
   * @param    array  $args <p>The arguments supplied to the functions hooked to <tt>$tag</tt></p>
   *
   * @return   bool         <p>Will return false if $tag does not exist in $filter array.</p>
   */
  public function do_action_ref_array($tag, $args)
  {
    if (!is_array($this->actions)) {
      $this->actions = array();
    }

    if (!isset($this->actions[$tag])) {
      $this->actions[$tag] = 1;
    } else {
      ++$this->actions[$tag];
    }

    // Do 'all' actions first
    if (isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
      $all_args = func_get_args();
      $this->_call_all_hook($all_args);
    }

    if (!isset($this->filters[$tag])) {
      if (isset($this->filters['all'])) {
        array_pop($this->current_filter);
      }

      return false;
    }

    if (!isset($this->filters['all'])) {
      $this->current_filter[] = $tag;
    }

    // Sort
    if (!isset($merged_filters[$tag])) {
      ksort($this->filters[$tag]);
      $merged_filters[$tag] = true;
    }

    reset($this->filters[$tag]);

    do {
      foreach ((array)current($this->filters[$tag]) as $the_) {
        if (null !== $the_['function']) {

          if (null !== $the_['include_path']) {
            /** @noinspection PhpIncludeInspection */
            include_once $the_['include_path'];
          }

          call_user_func_array($the_['function'], $args);
        }
      }
    } while (next($this->filters[$tag]) !== false);

    array_pop($this->current_filter);

    return true;
  }

  /**
   * Retrieve the number of times an action has fired.
   *
   * @param string $tag <p>The name of the action hook.</p>
   *
   * @return integer <p>The number of times action hook <tt>$tag</tt> is fired.</p>
   */
  public function did_action($tag)
  {
    if (!is_array($this->actions) || !isset($this->actions[$tag])) {
      return 0;
    }

    return $this->actions[$tag];
  }

  /**
   * Retrieve the name of the current filter or action.
   *
   * @return string <p>Hook name of the current filter or action.</p>
   */
  public function current_filter()
  {
    return end($this->current_filter);
  }

  /**
   * Build Unique ID for storage and retrieval.
   *
   * @param    string|array $function <p>Used for creating unique id.</p>
   *
   * @return   string|bool             <p>
   *                                   Unique ID for usage as array key or false if
   *                                   $priority === false and $function is an
   *                                   object reference, and it does not already have a unique id.
   *                                   </p>
   */
  private function _filter_build_unique_id($function)
  {
    if (is_string($function)) {
      return $function;
    }

    if (is_object($function)) {
      // Closures are currently implemented as objects
      $function = array(
          $function,
          '',
      );
    } else {
      $function = (array)$function;
    }

    if (is_object($function[0])) {
      // Object Class Calling
      return spl_object_hash($function[0]) . $function[1];
    }

    if (is_string($function[0])) {
      // Static Calling
      return $function[0] . $function[1];
    }

    return false;
  }

  /**
   * Call "All" Hook
   *
   * @param array $args
   */
  public function _call_all_hook($args)
  {
    reset($this->filters['all']);

    do {
      foreach ((array)current($this->filters['all']) as $the_) {
        if (null !== $the_['function']) {

          if (null !== $the_['include_path']) {
            /** @noinspection PhpIncludeInspection */
            include_once $the_['include_path'];
          }

          call_user_func_array($the_['function'], $args);
        }
      }
    } while (next($this->filters['all']) !== false);
  }

  /** @noinspection MagicMethodsValidityInspection */
  /**
   * @param array $args
   *
   * @deprecated use "this->_call_all_hook()"
   */
  public function __call_all_hook($args)
  {
    // <-- refactoring "__call_all_hook()" into "_call_all_hook()" is a breaking change (BC),
    // so we will only deprecate the usage

    $this->_call_all_hook($args);
  }

  /**
   * Add hook for shortcode tag.
   *
   * <p>
   * <br />
   * There can only be one hook for each shortcode. Which means that if another
   * plugin has a similar shortcode, it will override yours or yours will override
   * theirs depending on which order the plugins are included and/or ran.
   * <br />
   * <br />
   * </p>
   *
   * Simplest example of a shortcode tag using the API:
   *
   * <code>
   * // [footag foo="bar"]
   * function footag_func($atts) {
   *  return "foo = {$atts[foo]}";
   * }
   * add_shortcode('footag', 'footag_func');
   * </code>
   *
   * Example with nice attribute defaults:
   *
   * <code>
   * // [bartag foo="bar"]
   * function bartag_func($atts) {
   *  $args = shortcode_atts(array(
   *    'foo' => 'no foo',
   *    'baz' => 'default baz',
   *  ), $atts);
   *
   *  return "foo = {$args['foo']}";
   * }
   * add_shortcode('bartag', 'bartag_func');
   * </code>
   *
   * Example with enclosed content:
   *
   * <code>
   * // [baztag]content[/baztag]
   * function baztag_func($atts, $content='') {
   *  return "content = $content";
   * }
   * add_shortcode('baztag', 'baztag_func');
   * </code>
   *
   * @param string   $tag  <p>Shortcode tag to be searched in post content.</p>
   * @param callable $func <p>Hook to run when shortcode is found.</p>
   *
   * @return bool
   */
  public function add_shortcode($tag, $func)
  {
    if (is_callable($func)) {
      self::$shortcode_tags[$tag] = $func;

      return true;
    }

    return false;
  }

  /**
   * Removes hook for shortcode.
   *
   * @param string $tag <p>shortcode tag to remove hook for.</p>
   *
   * @return bool
   */
  public function remove_shortcode($tag)
  {
    if (isset(self::$shortcode_tags[$tag])) {
      unset(self::$shortcode_tags[$tag]);

      return true;
    }

    return false;
  }

  /**
   * This function is simple, it clears all of the shortcode tags by replacing the
   * shortcodes by a empty array. This is actually a very efficient method
   * for removing all shortcodes.
   *
   * @return bool
   */
  public function remove_all_shortcodes()
  {
    self::$shortcode_tags = array();

    return true;
  }

  /**
   * Whether a registered shortcode exists named $tag
   *
   * @param string $tag
   *
   * @return boolean
   */
  public function shortcode_exists($tag)
  {
    return array_key_exists($tag, self::$shortcode_tags);
  }

  /**
   * Whether the passed content contains the specified shortcode.
   *
   * @param string $content
   * @param string $tag
   *
   * @return bool
   */
  public function has_shortcode($content, $tag)
  {
    if (false === strpos($content, '[')) {
      return false;
    }

    if ($this->shortcode_exists($tag)) {
      preg_match_all('/' . $this->get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER);
      if (empty($matches)) {
        return false;
      }

      foreach ($matches as $shortcode) {
        if ($tag === $shortcode[2]) {
          return true;
        }

        if (!empty($shortcode[5]) && $this->has_shortcode($shortcode[5], $tag)) {
          return true;
        }
      }
    }

    return false;
  }

  /**
   * Search content for shortcodes and filter shortcodes through their hooks.
   *
   * <p>
   * <br />
   * If there are no shortcode tags defined, then the content will be returned
   * without any filtering. This might cause issues when plugins are disabled but
   * the shortcode will still show up in the post or content.
   * </p>
   *
   * @param string $content <p>Content to search for shortcodes.</p>
   *
   * @return string <p>Content with shortcodes filtered out.</p>
   */
  public function do_shortcode($content)
  {
    if (empty(self::$shortcode_tags) || !is_array(self::$shortcode_tags)) {
      return $content;
    }

    $pattern = $this->get_shortcode_regex();

    return preg_replace_callback(
        "/$pattern/s",
        array(
            $this,
            '_do_shortcode_tag',
        ),
        $content
    );
  }

  /**
   * Retrieve the shortcode regular expression for searching.
   *
   * <p>
   * <br />
   * The regular expression combines the shortcode tags in the regular expression
   * in a regex class.
   * <br /><br />
   *
   * The regular expression contains 6 different sub matches to help with parsing.
   * <br /><br />
   *
   * 1 - An extra [ to allow for escaping shortcodes with double [[]]<br />
   * 2 - The shortcode name<br />
   * 3 - The shortcode argument list<br />
   * 4 - The self closing /<br />
   * 5 - The content of a shortcode when it wraps some content.<br />
   * 6 - An extra ] to allow for escaping shortcodes with double [[]]<br />
   * </p>
   *
   * @return string The shortcode search regular expression
   */
  public function get_shortcode_regex()
  {
    $tagnames = array_keys(self::$shortcode_tags);
    $tagregexp = implode('|', array_map('preg_quote', $tagnames));

    // WARNING! Do not change this regex without changing __do_shortcode_tag() and __strip_shortcode_tag()
    // Also, see shortcode_unautop() and shortcode.js.
    return
        '\\[' // Opening bracket
        . '(\\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]]
        . "($tagregexp)" // 2: Shortcode name
        . '(?![\\w-])' // Not followed by word character or hyphen
        . '(' // 3: Unroll the loop: Inside the opening shortcode tag
        . '[^\\]\\/]*' // Not a closing bracket or forward slash
        . '(?:'
        . '\\/(?!\\])' // A forward slash not followed by a closing bracket
        . '[^\\]\\/]*' // Not a closing bracket or forward slash
        . ')*?'
        . ')'
        . '(?:'
        . '(\\/)' // 4: Self closing tag ...
        . '\\]' // ... and closing bracket
        . '|'
        . '\\]' // Closing bracket
        . '(?:'
        . '(' // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags
        . '[^\\[]*+' // Not an opening bracket
        . '(?:'
        . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
        . '[^\\[]*+' // Not an opening bracket
        . ')*+'
        . ')'
        . '\\[\\/\\2\\]' // Closing shortcode tag
        . ')?'
        . ')'
        . '(\\]?)'; // 6: Optional second closing brocket for escaping shortcodes: [[tag]]
  }

  /**
   * Regular Expression callable for do_shortcode() for calling shortcode hook.
   *
   * @see self::get_shortcode_regex for details of the match array contents.
   *
   * @param array $m <p>regular expression match array</p>
   *
   * @return mixed <p><strong>false</strong> on failure</p>
   */
  private function _do_shortcode_tag($m)
  {
    // allow [[foo]] syntax for escaping a tag
    if ($m[1] == '[' && $m[6] == ']') {
      return substr($m[0], 1, -1);
    }

    $tag = $m[2];
    $attr = $this->shortcode_parse_atts($m[3]);

    // enclosing tag - extra parameter
    if (isset($m[5])) {
      return $m[1] . call_user_func(self::$shortcode_tags[$tag], $attr, $m[5], $tag) . $m[6];
    }

    // self-closing tag
    return $m[1] . call_user_func(self::$shortcode_tags[$tag], $attr, null, $tag) . $m[6];
  }

  /**
   * Retrieve all attributes from the shortcodes tag.
   *
   * <p>
   * <br />
   * The attributes list has the attribute name as the key and the value of the
   * attribute as the value in the key/value pair. This allows for easier
   * retrieval of the attributes, since all attributes have to be known.
   * </p>
   *
   * @param string $text
   *
   * @return array <p>List of attributes and their value.</p>
   */
  public function shortcode_parse_atts($text)
  {
    $atts = array();
    $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
    $text = preg_replace("/[\x{00a0}\x{200b}]+/u", ' ', $text);
    if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) {
      foreach ($match as $m) {
        if (!empty($m[1])) {
          $atts[strtolower($m[1])] = stripcslashes($m[2]);
        } elseif (!empty($m[3])) {
          $atts[strtolower($m[3])] = stripcslashes($m[4]);
        } elseif (!empty($m[5])) {
          $atts[strtolower($m[5])] = stripcslashes($m[6]);
        } elseif (isset($m[7]) && $m[7] !== '') {
          $atts[] = stripcslashes($m[7]);
        } elseif (isset($m[8])) {
          $atts[] = stripcslashes($m[8]);
        }
      }
    } else {
      $atts = ltrim($text);
    }

    return $atts;
  }

  /**
   * Combine user attributes with known attributes and fill in defaults when needed.
   *
   * <p>
   * <br />
   * The pairs should be considered to be all of the attributes which are
   * supported by the caller and given as a list. The returned attributes will
   * only contain the attributes in the $pairs list.
   *
   * <br /><br />
   * If the $atts list has unsupported attributes, then they will be ignored and
   * removed from the final returned list.
   * </p>
   *
   * @param array  $pairs     <p>Entire list of supported attributes and their defaults.</p>
   * @param array  $atts      <p>User defined attributes in shortcode tag.</p>
   * @param string $shortcode <p>[optional] The name of the shortcode, provided for context to enable filtering.</p>
   *
   * @return array <p>Combined and filtered attribute list.</p>
   */
  public function shortcode_atts($pairs, $atts, $shortcode = '')
  {
    $atts = (array)$atts;
    $out = array();
    foreach ($pairs as $name => $default) {
      if (array_key_exists($name, $atts)) {
        $out[$name] = $atts[$name];
      } else {
        $out[$name] = $default;
      }
    }
    /**
     * Filter a shortcode's default attributes.
     *
     * <p>
     * <br />
     * If the third parameter of the shortcode_atts() function is present then this filter is available.
     * The third parameter, $shortcode, is the name of the shortcode.
     * </p>
     *
     * @param array $out   <p>The output array of shortcode attributes.</p>
     * @param array $pairs <p>The supported attributes and their defaults.</p>
     * @param array $atts  <p>The user defined shortcode attributes.</p>
     */
    if ($shortcode) {
      $out = $this->apply_filters(
          array(
              $this,
              "shortcode_atts_{$shortcode}",
          ), $out, $pairs, $atts
      );
    }

    return $out;
  }

  /**
   * Remove all shortcode tags from the given content.
   *
   * @param string $content <p>Content to remove shortcode tags.</p>
   *
   * @return string <p>Content without shortcode tags.</p>
   */
  public function strip_shortcodes($content)
  {

    if (empty(self::$shortcode_tags) || !is_array(self::$shortcode_tags)) {
      return $content;
    }

    $pattern = $this->get_shortcode_regex();

    return preg_replace_callback(
        "/$pattern/s",
        array(
            $this,
            '_strip_shortcode_tag',
        ),
        $content
    );
  }

  /**
   * Strip shortcode by tag.
   *
   * @param array $m
   *
   * @return string
   */
  private function _strip_shortcode_tag($m)
  {
    // allow [[foo]] syntax for escaping a tag
    if ($m[1] == '[' && $m[6] == ']') {
      return substr($m[0], 1, -1);
    }

    return $m[1] . $m[6];
  }

}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Dotenv;

use Symfony\Component\Dotenv\Exception\FormatException;
use Symfony\Component\Dotenv\Exception\FormatExceptionContext;
use Symfony\Component\Dotenv\Exception\PathException;
use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException;
use Symfony\Component\Process\Process;

/**
 * Manages .env files.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class Dotenv
{
    const VARNAME_REGEX = '(?i:[A-Z][A-Z0-9_]*+)';
    const STATE_VARNAME = 0;
    const STATE_VALUE = 1;

    private $path;
    private $cursor;
    private $lineno;
    private $data;
    private $end;
    private $state;
    private $values;

    /**
     * Loads one or several .env files.
     *
     * @param string    $path  A file to load
     * @param ...string $paths A list of additional files to load
     *
     * @throws FormatException when a file has a syntax error
     * @throws PathException   when a file does not exist or is not readable
     */
    public function load($path/*, ...$paths*/)
    {
        // func_get_args() to be replaced by a variadic argument for Symfony 4.0
        foreach (func_get_args() as $path) {
            if (!is_readable($path) || is_dir($path)) {
                throw new PathException($path);
            }

            $this->populate($this->parse(file_get_contents($path), $path));
        }
    }

    /**
     * Sets values as environment variables (via putenv, $_ENV, and $_SERVER).
     *
     * Note that existing environment variables are not overridden.
     *
     * @param array $values An array of env variables
     */
    public function populate($values)
    {
        $loadedVars = array_flip(explode(',', getenv('SYMFONY_DOTENV_VARS')));
        unset($loadedVars['']);

        foreach ($values as $name => $value) {
            $notHttpName = 0 !== strpos($name, 'HTTP_');
            // don't check existence with getenv() because of thread safety issues
            if (!isset($loadedVars[$name]) && (isset($_ENV[$name]) || (isset($_SERVER[$name]) && $notHttpName))) {
                continue;
            }

            putenv("$name=$value");
            $_ENV[$name] = $value;
            if ($notHttpName) {
                $_SERVER[$name] = $value;
            }

            $loadedVars[$name] = true;
        }

        if ($loadedVars) {
            $loadedVars = implode(',', array_keys($loadedVars));
            putenv("SYMFONY_DOTENV_VARS=$loadedVars");
            $_ENV['SYMFONY_DOTENV_VARS'] = $loadedVars;
            $_SERVER['SYMFONY_DOTENV_VARS'] = $loadedVars;
        }
    }

    /**
     * Parses the contents of an .env file.
     *
     * @param string $data The data to be parsed
     * @param string $path The original file name where data where stored (used for more meaningful error messages)
     *
     * @return array An array of env variables
     *
     * @throws FormatException when a file has a syntax error
     */
    public function parse($data, $path = '.env')
    {
        $this->path = $path;
        $this->data = str_replace(array("\r\n", "\r"), "\n", $data);
        $this->lineno = 1;
        $this->cursor = 0;
        $this->end = strlen($this->data);
        $this->state = self::STATE_VARNAME;
        $this->values = array();
        $name = '';

        $this->skipEmptyLines();

        while ($this->cursor < $this->end) {
            switch ($this->state) {
                case self::STATE_VARNAME:
                    $name = $this->lexVarname();
                    $this->state = self::STATE_VALUE;
                    break;

                case self::STATE_VALUE:
                    $this->values[$name] = $this->lexValue();
                    $this->state = self::STATE_VARNAME;
                    break;
            }
        }

        if (self::STATE_VALUE === $this->state) {
            $this->values[$name] = '';
        }

        try {
            return $this->values;
        } finally {
            $this->values = array();
            $this->data = null;
            $this->path = null;
        }
    }

    private function lexVarname()
    {
        // var name + optional export
        if (!preg_match('/(export[ \t]++)?('.self::VARNAME_REGEX.')/A', $this->data, $matches, 0, $this->cursor)) {
            throw $this->createFormatException('Invalid character in variable name');
        }
        $this->moveCursor($matches[0]);

        if ($this->cursor === $this->end || "\n" === $this->data[$this->cursor] || '#' === $this->data[$this->cursor]) {
            if ($matches[1]) {
                throw $this->createFormatException('Unable to unset an environment variable');
            }

            throw $this->createFormatException('Missing = in the environment variable declaration');
        }

        if (' ' === $this->data[$this->cursor] || "\t" === $this->data[$this->cursor]) {
            throw $this->createFormatException('Whitespace are not supported after the variable name');
        }

        if ('=' !== $this->data[$this->cursor]) {
            throw $this->createFormatException('Missing = in the environment variable declaration');
        }
        ++$this->cursor;

        return $matches[2];
    }

    private function lexValue()
    {
        if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, 0, $this->cursor)) {
            $this->moveCursor($matches[0]);
            $this->skipEmptyLines();

            return '';
        }

        if (' ' === $this->data[$this->cursor] || "\t" === $this->data[$this->cursor]) {
            throw $this->createFormatException('Whitespace are not supported before the value');
        }

        $v = '';

        do {
            if ("'" === $this->data[$this->cursor]) {
                $value = '';
                ++$this->cursor;

                while ("\n" !== $this->data[$this->cursor]) {
                    if ("'" === $this->data[$this->cursor]) {
                        break;
                    }
                    $value .= $this->data[$this->cursor];
                    ++$this->cursor;

                    if ($this->cursor === $this->end) {
                        throw $this->createFormatException('Missing quote to end the value');
                    }
                }
                if ("\n" === $this->data[$this->cursor]) {
                    throw $this->createFormatException('Missing quote to end the value');
                }
                ++$this->cursor;
                $v .= $value;
            } elseif ('"' === $this->data[$this->cursor]) {
                $value = '';
                ++$this->cursor;

                while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
                    $value .= $this->data[$this->cursor];
                    ++$this->cursor;

                    if ($this->cursor === $this->end) {
                        throw $this->createFormatException('Missing quote to end the value');
                    }
                }
                if ("\n" === $this->data[$this->cursor]) {
                    throw $this->createFormatException('Missing quote to end the value');
                }
                ++$this->cursor;
                $value = str_replace(array('\\\\', '\\"', '\r', '\n'), array('\\', '"', "\r", "\n"), $value);
                $resolvedValue = $value;
                $resolvedValue = $this->resolveVariables($resolvedValue);
                $resolvedValue = $this->resolveCommands($resolvedValue);
                $v .= $resolvedValue;
            } else {
                $value = '';
                $prevChr = $this->data[$this->cursor - 1];
                while ($this->cursor < $this->end && !in_array($this->data[$this->cursor], array("\n", '"', "'"), true) && !((' ' === $prevChr || "\t" === $prevChr) && '#' === $this->data[$this->cursor])) {
                    if ('\\' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && ('"' === $this->data[$this->cursor + 1] || "'" === $this->data[$this->cursor + 1])) {
                        ++$this->cursor;
                    }

                    $value .= $prevChr = $this->data[$this->cursor];

                    if ('$' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && '(' === $this->data[$this->cursor + 1]) {
                        ++$this->cursor;
                        $value .= '('.$this->lexNestedExpression().')';
                    }

                    ++$this->cursor;
                }
                $value = rtrim($value);
                $resolvedValue = $value;
                $resolvedValue = $this->resolveVariables($resolvedValue);
                $resolvedValue = $this->resolveCommands($resolvedValue);

                if ($resolvedValue === $value && preg_match('/\s+/', $value)) {
                    throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
                }

                $v .= $resolvedValue;

                if ($this->cursor < $this->end && '#' === $this->data[$this->cursor]) {
                    break;
                }
            }
        } while ($this->cursor < $this->end && "\n" !== $this->data[$this->cursor]);

        $this->skipEmptyLines();

        return $v;
    }

    private function lexNestedExpression()
    {
        ++$this->cursor;
        $value = '';

        while ("\n" !== $this->data[$this->cursor] && ')' !== $this->data[$this->cursor]) {
            $value .= $this->data[$this->cursor];

            if ('(' === $this->data[$this->cursor]) {
                $value .= $this->lexNestedExpression().')';
            }

            ++$this->cursor;

            if ($this->cursor === $this->end) {
                throw $this->createFormatException('Missing closing parenthesis.');
            }
        }

        if ("\n" === $this->data[$this->cursor]) {
            throw $this->createFormatException('Missing closing parenthesis.');
        }

        return $value;
    }

    private function skipEmptyLines()
    {
        if (preg_match('/(?:\s*+(?:#[^\n]*+)?+)++/A', $this->data, $match, 0, $this->cursor)) {
            $this->moveCursor($match[0]);
        }
    }

    private function resolveCommands($value)
    {
        if (false === strpos($value, '$')) {
            return $value;
        }

        $regex = '/
            (\\\\)?               # escaped with a backslash?
            \$
            (?<cmd>
                \(                # require opening parenthesis
                ([^()]|\g<cmd>)+  # allow any number of non-parens, or balanced parens (by nesting the <cmd> expression recursively)
                \)                # require closing paren
            )
        /x';

        return preg_replace_callback($regex, function ($matches) {
            if ('\\' === $matches[1]) {
                return substr($matches[0], 1);
            }

            if ('\\' === DIRECTORY_SEPARATOR) {
                throw new \LogicException('Resolving commands is not supported on Windows.');
            }

            if (!class_exists(Process::class)) {
                throw new \LogicException('Resolving commands requires the Symfony Process component.');
            }

            $process = new Process('echo '.$matches[0]);
            $process->inheritEnvironmentVariables(true);
            $process->setEnv($this->values);
            try {
                $process->mustRun();
            } catch (ProcessException $e) {
                throw $this->createFormatException(sprintf('Issue expanding a command (%s)', $process->getErrorOutput()));
            }

            return preg_replace('/[\r\n]+$/', '', $process->getOutput());
        }, $value);
    }

    private function resolveVariables($value)
    {
        if (false === strpos($value, '$')) {
            return $value;
        }

        $regex = '/
            (\\\\)?                    # escaped with a backslash?
            \$
            (?!\()                     # no opening parenthesis
            (\{)?                      # optional brace
            ('.self::VARNAME_REGEX.')  # var name
            (\})?                      # optional closing brace
        /x';

        $value = preg_replace_callback($regex, function ($matches) {
            if ('\\' === $matches[1]) {
                return substr($matches[0], 1);
            }

            if ('{' === $matches[2] && !isset($matches[4])) {
                throw $this->createFormatException('Unclosed braces on variable expansion');
            }

            $name = $matches[3];
            if (isset($this->values[$name])) {
                $value = $this->values[$name];
            } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
                $value = $_SERVER[$name];
            } elseif (isset($_ENV[$name])) {
                $value = $_ENV[$name];
            } else {
                $value = (string) getenv($name);
            }

            if (!$matches[2] && isset($matches[4])) {
                $value .= '}';
            }

            return $value;
        }, $value);

        // unescape $
        return str_replace('\\$', '$', $value);
    }

    private function moveCursor($text)
    {
        $this->cursor += strlen($text);
        $this->lineno += substr_count($text, "\n");
    }

    private function createFormatException($message)
    {
        return new FormatException($message, new FormatExceptionContext($this->data, $this->path, $this->lineno, $this->cursor));
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Dotenv\Exception;

/**
 * Thrown when a file has a syntax error.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class FormatException extends \LogicException implements ExceptionInterface
{
    private $context;

    public function __construct($message, FormatExceptionContext $context, $code = 0, \Exception $previous = null)
    {
        $this->context = $context;

        parent::__construct(sprintf("%s in \"%s\" at line %d.\n%s", $message, $context->getPath(), $context->getLineno(), $context->getDetails()), $code, $previous);
    }

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

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Dotenv\Exception;

/**
 * Thrown when a file does not exist or is not readable.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class PathException extends \RuntimeException implements ExceptionInterface
{
    public function __construct($path, $code = 0, \Exception $previous = null)
    {
        parent::__construct(sprintf('Unable to read the "%s" environment file.', $path), $code, $previous);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Dotenv\Exception;

/**
 * Interface for exceptions.
 *
 * @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\Dotenv\Exception;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class FormatExceptionContext
{
    private $data;
    private $path;
    private $lineno;
    private $cursor;

    public function __construct($data, $path, $lineno, $cursor)
    {
        $this->data = $data;
        $this->path = $path;
        $this->lineno = $lineno;
        $this->cursor = $cursor;
    }

    public function getPath()
    {
        return $this->path;
    }

    public function getLineno()
    {
        return $this->lineno;
    }

    public function getDetails()
    {
        $before = str_replace("\n", '\n', substr($this->data, max(0, $this->cursor - 20), min(20, $this->cursor)));
        $after = str_replace("\n", '\n', substr($this->data, $this->cursor, 20));

        return '...'.$before.$after."...\n".str_repeat(' ', strlen($before) + 2).'^ line '.$this->lineno.' offset '.$this->cursor;
    }
}
{
    "name": "symfony/dotenv",
    "type": "library",
    "description": "Registers environment variables from a .env file",
    "keywords": ["environment", "env", "dotenv"],
    "homepage": "https://symfony.com",
    "license" : "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": "^5.5.9|>=7.0.8"
    },
    "require-dev": {
        "symfony/process": "~3.2|~4.0"
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\Dotenv\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "3.4-dev"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config;

use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;

/**
 * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface
 * to check whether cached data is still fresh.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 */
class ResourceCheckerConfigCache implements ConfigCacheInterface
{
    /**
     * @var string
     */
    private $file;

    /**
     * @var iterable|ResourceCheckerInterface[]
     */
    private $resourceCheckers;

    /**
     * @param string                              $file             The absolute cache path
     * @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
     */
    public function __construct($file, $resourceCheckers = array())
    {
        $this->file = $file;
        $this->resourceCheckers = $resourceCheckers;
    }

    /**
     * {@inheritdoc}
     */
    public function getPath()
    {
        return $this->file;
    }

    /**
     * Checks if the cache is still fresh.
     *
     * This implementation will make a decision solely based on the ResourceCheckers
     * passed in the constructor.
     *
     * The first ResourceChecker that supports a given resource is considered authoritative.
     * Resources with no matching ResourceChecker will silently be ignored and considered fresh.
     *
     * @return bool true if the cache is fresh, false otherwise
     */
    public function isFresh()
    {
        if (!is_file($this->file)) {
            return false;
        }

        if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) {
            $this->resourceCheckers = iterator_to_array($this->resourceCheckers);
        }

        if (!count($this->resourceCheckers)) {
            return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all
        }

        $metadata = $this->getMetaFile();

        if (!is_file($metadata)) {
            return false;
        }

        $meta = $this->safelyUnserialize($metadata);

        if (false === $meta) {
            return false;
        }

        $time = filemtime($this->file);

        foreach ($meta as $resource) {
            /* @var ResourceInterface $resource */
            foreach ($this->resourceCheckers as $checker) {
                if (!$checker->supports($resource)) {
                    continue; // next checker
                }
                if ($checker->isFresh($resource, $time)) {
                    break; // no need to further check this resource
                }

                return false; // cache is stale
            }
            // no suitable checker found, ignore this resource
        }

        return true;
    }

    /**
     * Writes cache.
     *
     * @param string              $content  The content to write in the cache
     * @param ResourceInterface[] $metadata An array of metadata
     *
     * @throws \RuntimeException When cache file can't be written
     */
    public function write($content, array $metadata = null)
    {
        $mode = 0666;
        $umask = umask();
        $filesystem = new Filesystem();
        $filesystem->dumpFile($this->file, $content);
        try {
            $filesystem->chmod($this->file, $mode, $umask);
        } catch (IOException $e) {
            // discard chmod failure (some filesystem may not support it)
        }

        if (null !== $metadata) {
            $filesystem->dumpFile($this->getMetaFile(), serialize($metadata));
            try {
                $filesystem->chmod($this->getMetaFile(), $mode, $umask);
            } catch (IOException $e) {
                // discard chmod failure (some filesystem may not support it)
            }
        }

        if (\function_exists('opcache_invalidate') && ini_get('opcache.enable')) {
            @opcache_invalidate($this->file, true);
        }
    }

    /**
     * Gets the meta file path.
     *
     * @return string The meta file path
     */
    private function getMetaFile()
    {
        return $this->file.'.meta';
    }

    private function safelyUnserialize($file)
    {
        $e = null;
        $meta = false;
        $signalingException = new \UnexpectedValueException();
        $prevUnserializeHandler = ini_set('unserialize_callback_func', '');
        $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$prevErrorHandler, $signalingException) {
            if (E_WARNING === $type && 'Class __PHP_Incomplete_Class has no unserializer' === $msg) {
                throw $signalingException;
            }

            return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
        });

        try {
            $meta = unserialize(file_get_contents($file));
        } catch (\Error $e) {
        } catch (\Exception $e) {
        }
        restore_error_handler();
        ini_set('unserialize_callback_func', $prevUnserializeHandler);
        if (null !== $e && $e !== $signalingException) {
            throw $e;
        }

        return $meta;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Util;

use Symfony\Component\Config\Util\Exception\InvalidXmlException;
use Symfony\Component\Config\Util\Exception\XmlParsingException;

/**
 * XMLUtils is a bunch of utility methods to XML operations.
 *
 * This class contains static methods only and is not meant to be instantiated.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Martin HasoÅˆ <martin.hason@gmail.com>
 * @author Ole RÃ¶ÃŸner <ole@roessner.it>
 */
class XmlUtils
{
    /**
     * This class should not be instantiated.
     */
    private function __construct()
    {
    }

    /**
     * Parses an XML string.
     *
     * @param string               $content          An XML string
     * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation
     *
     * @return \DOMDocument
     *
     * @throws XmlParsingException When parsing of XML file returns error
     * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself
     * @throws \RuntimeException   When DOM extension is missing
     */
    public static function parse($content, $schemaOrCallable = null)
    {
        if (!extension_loaded('dom')) {
            throw new \RuntimeException('Extension DOM is required.');
        }

        $internalErrors = libxml_use_internal_errors(true);
        $disableEntities = libxml_disable_entity_loader(true);
        libxml_clear_errors();

        $dom = new \DOMDocument();
        $dom->validateOnParse = true;
        if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
            libxml_disable_entity_loader($disableEntities);

            throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors)));
        }

        $dom->normalizeDocument();

        libxml_use_internal_errors($internalErrors);
        libxml_disable_entity_loader($disableEntities);

        foreach ($dom->childNodes as $child) {
            if (XML_DOCUMENT_TYPE_NODE === $child->nodeType) {
                throw new XmlParsingException('Document types are not allowed.');
            }
        }

        if (null !== $schemaOrCallable) {
            $internalErrors = libxml_use_internal_errors(true);
            libxml_clear_errors();

            $e = null;
            if (is_callable($schemaOrCallable)) {
                try {
                    $valid = call_user_func($schemaOrCallable, $dom, $internalErrors);
                } catch (\Exception $e) {
                    $valid = false;
                }
            } elseif (!is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) {
                $schemaSource = file_get_contents((string) $schemaOrCallable);
                $valid = @$dom->schemaValidateSource($schemaSource);
            } else {
                libxml_use_internal_errors($internalErrors);

                throw new XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
            }

            if (!$valid) {
                $messages = static::getXmlErrors($internalErrors);
                if (empty($messages)) {
                    throw new InvalidXmlException('The XML is not valid.', 0, $e);
                }
                throw new XmlParsingException(implode("\n", $messages), 0, $e);
            }
        }

        libxml_clear_errors();
        libxml_use_internal_errors($internalErrors);

        return $dom;
    }

    /**
     * Loads an XML file.
     *
     * @param string               $file             An XML file path
     * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation
     *
     * @return \DOMDocument
     *
     * @throws \InvalidArgumentException When loading of XML file returns error
     * @throws XmlParsingException       When XML parsing returns any errors
     * @throws \RuntimeException         When DOM extension is missing
     */
    public static function loadFile($file, $schemaOrCallable = null)
    {
        $content = @file_get_contents($file);
        if ('' === trim($content)) {
            throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file));
        }

        try {
            return static::parse($content, $schemaOrCallable);
        } catch (InvalidXmlException $e) {
            throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious());
        }
    }

    /**
     * Converts a \DOMElement object to a PHP array.
     *
     * The following rules applies during the conversion:
     *
     *  * Each tag is converted to a key value or an array
     *    if there is more than one "value"
     *
     *  * The content of a tag is set under a "value" key (<foo>bar</foo>)
     *    if the tag also has some nested tags
     *
     *  * The attributes are converted to keys (<foo foo="bar"/>)
     *
     *  * The nested-tags are converted to keys (<foo><foo>bar</foo></foo>)
     *
     * @param \DOMElement $element     A \DOMElement instance
     * @param bool        $checkPrefix Check prefix in an element or an attribute name
     *
     * @return array A PHP array
     */
    public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true)
    {
        $prefix = (string) $element->prefix;
        $empty = true;
        $config = array();
        foreach ($element->attributes as $name => $node) {
            if ($checkPrefix && !in_array((string) $node->prefix, array('', $prefix), true)) {
                continue;
            }
            $config[$name] = static::phpize($node->value);
            $empty = false;
        }

        $nodeValue = false;
        foreach ($element->childNodes as $node) {
            if ($node instanceof \DOMText) {
                if ('' !== trim($node->nodeValue)) {
                    $nodeValue = trim($node->nodeValue);
                    $empty = false;
                }
            } elseif ($checkPrefix && $prefix != (string) $node->prefix) {
                continue;
            } elseif (!$node instanceof \DOMComment) {
                $value = static::convertDomElementToArray($node, $checkPrefix);

                $key = $node->localName;
                if (isset($config[$key])) {
                    if (!is_array($config[$key]) || !is_int(key($config[$key]))) {
                        $config[$key] = array($config[$key]);
                    }
                    $config[$key][] = $value;
                } else {
                    $config[$key] = $value;
                }

                $empty = false;
            }
        }

        if (false !== $nodeValue) {
            $value = static::phpize($nodeValue);
            if (count($config)) {
                $config['value'] = $value;
            } else {
                $config = $value;
            }
        }

        return !$empty ? $config : null;
    }

    /**
     * Converts an xml value to a PHP type.
     *
     * @param mixed $value
     *
     * @return mixed
     */
    public static function phpize($value)
    {
        $value = (string) $value;
        $lowercaseValue = strtolower($value);

        switch (true) {
            case 'null' === $lowercaseValue:
                return;
            case ctype_digit($value):
                $raw = $value;
                $cast = (int) $value;

                return '0' == $value[0] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw);
            case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)):
                $raw = $value;
                $cast = (int) $value;

                return '0' == $value[1] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw);
            case 'true' === $lowercaseValue:
                return true;
            case 'false' === $lowercaseValue:
                return false;
            case isset($value[1]) && '0b' == $value[0].$value[1]:
                return bindec($value);
            case is_numeric($value):
                return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value;
            case preg_match('/^0x[0-9a-f]++$/i', $value):
                return hexdec($value);
            case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value):
                return (float) $value;
            default:
                return $value;
        }
    }

    protected static function getXmlErrors($internalErrors)
    {
        $errors = array();
        foreach (libxml_get_errors() as $error) {
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
                LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
                $error->code,
                trim($error->message),
                $error->file ?: 'n/a',
                $error->line,
                $error->column
            );
        }

        libxml_clear_errors();
        libxml_use_internal_errors($internalErrors);

        return $errors;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Util\Exception;

/**
 * Exception class for when XML cannot be parsed properly.
 *
 * @author Ole RÃ¶ÃŸner <ole@roessner.it>
 */
class XmlParsingException extends \InvalidArgumentException
{
}
<?php
/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Util\Exception;

/**
 * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated
 * to the actual XML parsing.
 *
 * @author Ole RÃ¶ÃŸner <ole@roessner.it>
 */
class InvalidXmlException extends XmlParsingException
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config;

use Symfony\Component\Config\Resource\ResourceInterface;

/**
 * Interface for ConfigCache.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 */
interface ConfigCacheInterface
{
    /**
     * Gets the cache file path.
     *
     * @return string The cache file path
     */
    public function getPath();

    /**
     * Checks if the cache is still fresh.
     *
     * This check should take the metadata passed to the write() method into consideration.
     *
     * @return bool Whether the cache is still fresh
     */
    public function isFresh();

    /**
     * Writes the given content into the cache file. Metadata will be stored
     * independently and can be used to check cache freshness at a later time.
     *
     * @param string                   $content  The content to write into the cache
     * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances
     *
     * @throws \RuntimeException When the cache file cannot be written
     */
    public function write($content, array $metadata = 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\Config;

use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface FileLocatorInterface
{
    /**
     * Returns a full path for a given file name.
     *
     * @param string      $name        The file name to locate
     * @param string|null $currentPath The current path
     * @param bool        $first       Whether to return the first occurrence or an array of filenames
     *
     * @return string|array The full path to the file or an array of file paths
     *
     * @throws \InvalidArgumentException        If $name is empty
     * @throws FileLocatorFileNotFoundException If a file is not found
     */
    public function locate($name, $currentPath = null, $first = 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\Config;

use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;

/**
 * ConfigCache caches arbitrary content in files on disk.
 *
 * When in debug mode, those metadata resources that implement
 * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will
 * be used to check cache freshness.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Matthias Pigulla <mp@webfactory.de>
 */
class ConfigCache extends ResourceCheckerConfigCache
{
    private $debug;

    /**
     * @param string $file  The absolute cache path
     * @param bool   $debug Whether debugging is enabled or not
     */
    public function __construct($file, $debug)
    {
        $this->debug = (bool) $debug;

        $checkers = array();
        if (true === $this->debug) {
            $checkers = array(new SelfCheckingResourceChecker());
        }

        parent::__construct($file, $checkers);
    }

    /**
     * Checks if the cache is still fresh.
     *
     * This implementation always returns true when debug is off and the
     * cache file exists.
     *
     * @return bool true if the cache is fresh, false otherwise
     */
    public function isFresh()
    {
        if (!$this->debug && is_file($this->getPath())) {
            return true;
        }

        return parent::isFresh();
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config;

/**
 * Interface for a ConfigCache factory. This factory creates
 * an instance of ConfigCacheInterface and initializes the
 * cache if necessary.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 */
interface ConfigCacheFactoryInterface
{
    /**
     * Creates a cache instance and (re-)initializes it if necessary.
     *
     * @param string   $file     The absolute cache file path
     * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback
     *
     * @return ConfigCacheInterface $configCache The cache instance
     */
    public function cache($file, $callable);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config;

use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;

/**
 * FileLocator uses an array of pre-defined paths to find files.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FileLocator implements FileLocatorInterface
{
    protected $paths;

    /**
     * @param string|array $paths A path or an array of paths where to look for resources
     */
    public function __construct($paths = array())
    {
        $this->paths = (array) $paths;
    }

    /**
     * {@inheritdoc}
     */
    public function locate($name, $currentPath = null, $first = true)
    {
        if ('' == $name) {
            throw new \InvalidArgumentException('An empty file name is not valid to be located.');
        }

        if ($this->isAbsolutePath($name)) {
            if (!file_exists($name)) {
                throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, array($name));
            }

            return $name;
        }

        $paths = $this->paths;

        if (null !== $currentPath) {
            array_unshift($paths, $currentPath);
        }

        $paths = array_unique($paths);
        $filepaths = $notfound = array();

        foreach ($paths as $path) {
            if (@file_exists($file = $path.DIRECTORY_SEPARATOR.$name)) {
                if (true === $first) {
                    return $file;
                }
                $filepaths[] = $file;
            } else {
                $notfound[] = $file;
            }
        }

        if (!$filepaths) {
            throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)), 0, null, $notfound);
        }

        return $filepaths;
    }

    /**
     * Returns whether the file path is an absolute path.
     *
     * @param string $file A file path
     *
     * @return bool
     */
    private function isAbsolutePath($file)
    {
        if ('/' === $file[0] || '\\' === $file[0]
            || (strlen($file) > 3 && ctype_alpha($file[0])
                && ':' === $file[1]
                && ('\\' === $file[2] || '/' === $file[2])
            )
            || null !== parse_url($file, PHP_URL_SCHEME)
        ) {
            return true;
        }

        return false;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config;

use Symfony\Component\Config\Resource\ResourceInterface;

/**
 * Interface for ResourceCheckers.
 *
 * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated
 * metadata resources are passed to ResourceCheckers. The ResourceCheckers
 * can then inspect the resources and decide whether the cache can be considered
 * fresh or not.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 * @author Benjamin Klotz <bk@webfactory.de>
 */
interface ResourceCheckerInterface
{
    /**
     * Queries the ResourceChecker whether it can validate a given
     * resource or not.
     *
     * @param ResourceInterface $metadata The resource to be checked for freshness
     *
     * @return bool True if the ResourceChecker can handle this resource type, false if not
     */
    public function supports(ResourceInterface $metadata);

    /**
     * Validates the resource.
     *
     * @param ResourceInterface $resource  The resource to be validated
     * @param int               $timestamp The timestamp at which the cache associated with this resource was created
     *
     * @return bool True if the resource has not changed since the given timestamp, false otherwise
     */
    public function isFresh(ResourceInterface $resource, $timestamp);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Loader;

use Symfony\Component\Config\Exception\FileLoaderLoadException;

/**
 * Loader is the abstract class used by all built-in loaders.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class Loader implements LoaderInterface
{
    protected $resolver;

    /**
     * {@inheritdoc}
     */
    public function getResolver()
    {
        return $this->resolver;
    }

    /**
     * {@inheritdoc}
     */
    public function setResolver(LoaderResolverInterface $resolver)
    {
        $this->resolver = $resolver;
    }

    /**
     * Imports a resource.
     *
     * @param mixed       $resource A resource
     * @param string|null $type     The resource type or null if unknown
     *
     * @return mixed
     */
    public function import($resource, $type = null)
    {
        return $this->resolve($resource, $type)->load($resource, $type);
    }

    /**
     * Finds a loader able to load an imported resource.
     *
     * @param mixed       $resource A resource
     * @param string|null $type     The resource type or null if unknown
     *
     * @return $this|LoaderInterface
     *
     * @throws FileLoaderLoadException If no loader is found
     */
    public function resolve($resource, $type = null)
    {
        if ($this->supports($resource, $type)) {
            return $this;
        }

        $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type);

        if (false === $loader) {
            throw new FileLoaderLoadException($resource, null, null, null, $type);
        }

        return $loader;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Loader;

use Symfony\Component\Config\FileLocatorInterface;
use Symfony\Component\Config\Exception\FileLoaderLoadException;
use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException;
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
use Symfony\Component\Config\Resource\FileExistenceResource;
use Symfony\Component\Config\Resource\GlobResource;

/**
 * FileLoader is the abstract class used by all built-in loaders that are file based.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class FileLoader extends Loader
{
    protected static $loading = array();

    protected $locator;

    private $currentDir;

    public function __construct(FileLocatorInterface $locator)
    {
        $this->locator = $locator;
    }

    /**
     * Sets the current directory.
     *
     * @param string $dir
     */
    public function setCurrentDir($dir)
    {
        $this->currentDir = $dir;
    }

    /**
     * Returns the file locator used by this loader.
     *
     * @return FileLocatorInterface
     */
    public function getLocator()
    {
        return $this->locator;
    }

    /**
     * Imports a resource.
     *
     * @param mixed       $resource       A Resource
     * @param string|null $type           The resource type or null if unknown
     * @param bool        $ignoreErrors   Whether to ignore import errors or not
     * @param string|null $sourceResource The original resource importing the new resource
     *
     * @return mixed
     *
     * @throws FileLoaderLoadException
     * @throws FileLoaderImportCircularReferenceException
     * @throws FileLocatorFileNotFoundException
     */
    public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
    {
        if (is_string($resource) && strlen($resource) !== $i = strcspn($resource, '*?{[')) {
            $ret = array();
            $isSubpath = 0 !== $i && false !== strpos(substr($resource, 0, $i), '/');
            foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath) as $path => $info) {
                if (null !== $res = $this->doImport($path, $type, $ignoreErrors, $sourceResource)) {
                    $ret[] = $res;
                }
                $isSubpath = true;
            }

            if ($isSubpath) {
                return isset($ret[1]) ? $ret : (isset($ret[0]) ? $ret[0] : null);
            }
        }

        return $this->doImport($resource, $type, $ignoreErrors, $sourceResource);
    }

    /**
     * @internal
     */
    protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false)
    {
        if (strlen($pattern) === $i = strcspn($pattern, '*?{[')) {
            $prefix = $pattern;
            $pattern = '';
        } elseif (0 === $i || false === strpos(substr($pattern, 0, $i), '/')) {
            $prefix = '.';
            $pattern = '/'.$pattern;
        } else {
            $prefix = dirname(substr($pattern, 0, 1 + $i));
            $pattern = substr($pattern, strlen($prefix));
        }

        try {
            $prefix = $this->locator->locate($prefix, $this->currentDir, true);
        } catch (FileLocatorFileNotFoundException $e) {
            if (!$ignoreErrors) {
                throw $e;
            }

            $resource = array();
            foreach ($e->getPaths() as $path) {
                $resource[] = new FileExistenceResource($path);
            }

            return;
        }
        $resource = new GlobResource($prefix, $pattern, $recursive);

        foreach ($resource as $path => $info) {
            yield $path => $info;
        }
    }

    private function doImport($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
    {
        try {
            $loader = $this->resolve($resource, $type);

            if ($loader instanceof self && null !== $this->currentDir) {
                $resource = $loader->getLocator()->locate($resource, $this->currentDir, false);
            }

            $resources = is_array($resource) ? $resource : array($resource);
            for ($i = 0; $i < $resourcesCount = count($resources); ++$i) {
                if (isset(self::$loading[$resources[$i]])) {
                    if ($i == $resourcesCount - 1) {
                        throw new FileLoaderImportCircularReferenceException(array_keys(self::$loading));
                    }
                } else {
                    $resource = $resources[$i];
                    break;
                }
            }
            self::$loading[$resource] = true;

            try {
                $ret = $loader->load($resource, $type);
            } finally {
                unset(self::$loading[$resource]);
            }

            return $ret;
        } catch (FileLoaderImportCircularReferenceException $e) {
            throw $e;
        } catch (\Exception $e) {
            if (!$ignoreErrors) {
                // prevent embedded imports from nesting multiple exceptions
                if ($e instanceof FileLoaderLoadException) {
                    throw $e;
                }

                throw new FileLoaderLoadException($resource, $sourceResource, null, $e, $type);
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Loader;

/**
 * LoaderInterface is the interface implemented by all loader classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface LoaderInterface
{
    /**
     * Loads a resource.
     *
     * @param mixed       $resource The resource
     * @param string|null $type     The resource type or null if unknown
     *
     * @throws \Exception If something went wrong
     */
    public function load($resource, $type = null);

    /**
     * Returns whether this class supports the given resource.
     *
     * @param mixed       $resource A resource
     * @param string|null $type     The resource type or null if unknown
     *
     * @return bool True if this class supports the given resource, false otherwise
     */
    public function supports($resource, $type = null);

    /**
     * Gets the loader resolver.
     *
     * @return LoaderResolverInterface A LoaderResolverInterface instance
     */
    public function getResolver();

    /**
     * Sets the loader resolver.
     */
    public function setResolver(LoaderResolverInterface $resolver);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Loader;

/**
 * LoaderResolverInterface selects a loader for a given resource.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface LoaderResolverInterface
{
    /**
     * Returns a loader able to load the resource.
     *
     * @param mixed       $resource A resource
     * @param string|null $type     The resource type or null if unknown
     *
     * @return LoaderInterface|false The loader or false if none is able to load the resource
     */
    public function resolve($resource, $type = 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\Config\Loader;

/**
 * LoaderResolver selects a loader for a given resource.
 *
 * A resource can be anything (e.g. a full path to a config file or a Closure).
 * Each loader determines whether it can load a resource and how.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class LoaderResolver implements LoaderResolverInterface
{
    /**
     * @var LoaderInterface[] An array of LoaderInterface objects
     */
    private $loaders = array();

    /**
     * @param LoaderInterface[] $loaders An array of loaders
     */
    public function __construct(array $loaders = array())
    {
        foreach ($loaders as $loader) {
            $this->addLoader($loader);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function resolve($resource, $type = null)
    {
        foreach ($this->loaders as $loader) {
            if ($loader->supports($resource, $type)) {
                return $loader;
            }
        }

        return false;
    }

    public function addLoader(LoaderInterface $loader)
    {
        $this->loaders[] = $loader;
        $loader->setResolver($this);
    }

    /**
     * Returns the registered loaders.
     *
     * @return LoaderInterface[] An array of LoaderInterface instances
     */
    public function getLoaders()
    {
        return $this->loaders;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Loader;

use Symfony\Component\Config\Exception\FileLoaderLoadException;

/**
 * DelegatingLoader delegates loading to other loaders using a loader resolver.
 *
 * This loader acts as an array of LoaderInterface objects - each having
 * a chance to load a given resource (handled by the resolver)
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DelegatingLoader extends Loader
{
    public function __construct(LoaderResolverInterface $resolver)
    {
        $this->resolver = $resolver;
    }

    /**
     * {@inheritdoc}
     */
    public function load($resource, $type = null)
    {
        if (false === $loader = $this->resolver->resolve($resource, $type)) {
            throw new FileLoaderLoadException($resource, null, null, null, $type);
        }

        return $loader->load($resource, $type);
    }

    /**
     * {@inheritdoc}
     */
    public function supports($resource, $type = null)
    {
        return false !== $this->resolver->resolve($resource, $type);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Loader;

/**
 * GlobFileLoader loads files from a glob pattern.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class GlobFileLoader extends FileLoader
{
    /**
     * {@inheritdoc}
     */
    public function load($resource, $type = null)
    {
        return $this->import($resource);
    }

    /**
     * {@inheritdoc}
     */
    public function supports($resource, $type = null)
    {
        return 'glob' === $type;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

/**
 * This node represents a float value in the config tree.
 *
 * @author Jeanmonod David <david.jeanmonod@gmail.com>
 */
class FloatNode extends NumericNode
{
    /**
     * {@inheritdoc}
     */
    protected function validateType($value)
    {
        // Integers are also accepted, we just cast them
        if (is_int($value)) {
            $value = (float) $value;
        }

        if (!is_float($value)) {
            $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected float, but got %s.', $this->getPath(), gettype($value)));
            if ($hint = $this->getInfo()) {
                $ex->addHint($hint);
            }
            $ex->setPath($this->getPath());

            throw $ex;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

/**
 * The base node class.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
abstract class BaseNode implements NodeInterface
{
    protected $name;
    protected $parent;
    protected $normalizationClosures = array();
    protected $finalValidationClosures = array();
    protected $allowOverwrite = true;
    protected $required = false;
    protected $deprecationMessage = null;
    protected $equivalentValues = array();
    protected $attributes = array();

    /**
     * @param string        $name   The name of the node
     * @param NodeInterface $parent The parent of this node
     *
     * @throws \InvalidArgumentException if the name contains a period
     */
    public function __construct($name, NodeInterface $parent = null)
    {
        if (false !== strpos($name, '.')) {
            throw new \InvalidArgumentException('The name must not contain ".".');
        }

        $this->name = $name;
        $this->parent = $parent;
    }

    public function setAttribute($key, $value)
    {
        $this->attributes[$key] = $value;
    }

    public function getAttribute($key, $default = null)
    {
        return isset($this->attributes[$key]) ? $this->attributes[$key] : $default;
    }

    public function hasAttribute($key)
    {
        return isset($this->attributes[$key]);
    }

    public function getAttributes()
    {
        return $this->attributes;
    }

    public function setAttributes(array $attributes)
    {
        $this->attributes = $attributes;
    }

    public function removeAttribute($key)
    {
        unset($this->attributes[$key]);
    }

    /**
     * Sets an info message.
     *
     * @param string $info
     */
    public function setInfo($info)
    {
        $this->setAttribute('info', $info);
    }

    /**
     * Returns info message.
     *
     * @return string The info text
     */
    public function getInfo()
    {
        return $this->getAttribute('info');
    }

    /**
     * Sets the example configuration for this node.
     *
     * @param string|array $example
     */
    public function setExample($example)
    {
        $this->setAttribute('example', $example);
    }

    /**
     * Retrieves the example configuration for this node.
     *
     * @return string|array The example
     */
    public function getExample()
    {
        return $this->getAttribute('example');
    }

    /**
     * Adds an equivalent value.
     *
     * @param mixed $originalValue
     * @param mixed $equivalentValue
     */
    public function addEquivalentValue($originalValue, $equivalentValue)
    {
        $this->equivalentValues[] = array($originalValue, $equivalentValue);
    }

    /**
     * Set this node as required.
     *
     * @param bool $boolean Required node
     */
    public function setRequired($boolean)
    {
        $this->required = (bool) $boolean;
    }

    /**
     * Sets this node as deprecated.
     *
     * You can use %node% and %path% placeholders in your message to display,
     * respectively, the node name and its complete path.
     *
     * @param string|null $message Deprecated message
     */
    public function setDeprecated($message)
    {
        $this->deprecationMessage = $message;
    }

    /**
     * Sets if this node can be overridden.
     *
     * @param bool $allow
     */
    public function setAllowOverwrite($allow)
    {
        $this->allowOverwrite = (bool) $allow;
    }

    /**
     * Sets the closures used for normalization.
     *
     * @param \Closure[] $closures An array of Closures used for normalization
     */
    public function setNormalizationClosures(array $closures)
    {
        $this->normalizationClosures = $closures;
    }

    /**
     * Sets the closures used for final validation.
     *
     * @param \Closure[] $closures An array of Closures used for final validation
     */
    public function setFinalValidationClosures(array $closures)
    {
        $this->finalValidationClosures = $closures;
    }

    /**
     * Checks if this node is required.
     *
     * @return bool
     */
    public function isRequired()
    {
        return $this->required;
    }

    /**
     * Checks if this node is deprecated.
     *
     * @return bool
     */
    public function isDeprecated()
    {
        return null !== $this->deprecationMessage;
    }

    /**
     * Returns the deprecated message.
     *
     * @param string $node the configuration node name
     * @param string $path the path of the node
     *
     * @return string
     */
    public function getDeprecationMessage($node, $path)
    {
        return strtr($this->deprecationMessage, array('%node%' => $node, '%path%' => $path));
    }

    /**
     * Returns the name of this node.
     *
     * @return string The Node's name
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Retrieves the path of this node.
     *
     * @return string The Node's path
     */
    public function getPath()
    {
        $path = $this->name;

        if (null !== $this->parent) {
            $path = $this->parent->getPath().'.'.$path;
        }

        return $path;
    }

    /**
     * Merges two values together.
     *
     * @param mixed $leftSide
     * @param mixed $rightSide
     *
     * @return mixed The merged value
     *
     * @throws ForbiddenOverwriteException
     */
    final public function merge($leftSide, $rightSide)
    {
        if (!$this->allowOverwrite) {
            throw new ForbiddenOverwriteException(sprintf(
                'Configuration path "%s" cannot be overwritten. You have to '
               .'define all options for this path, and any of its sub-paths in '
               .'one configuration section.',
                $this->getPath()
            ));
        }

        $this->validateType($leftSide);
        $this->validateType($rightSide);

        return $this->mergeValues($leftSide, $rightSide);
    }

    /**
     * Normalizes a value, applying all normalization closures.
     *
     * @param mixed $value Value to normalize
     *
     * @return mixed The normalized value
     */
    final public function normalize($value)
    {
        $value = $this->preNormalize($value);

        // run custom normalization closures
        foreach ($this->normalizationClosures as $closure) {
            $value = $closure($value);
        }

        // replace value with their equivalent
        foreach ($this->equivalentValues as $data) {
            if ($data[0] === $value) {
                $value = $data[1];
            }
        }

        // validate type
        $this->validateType($value);

        // normalize value
        return $this->normalizeValue($value);
    }

    /**
     * Normalizes the value before any other normalization is applied.
     *
     * @param $value
     *
     * @return $value The normalized array value
     */
    protected function preNormalize($value)
    {
        return $value;
    }

    /**
     * Returns parent node for this node.
     *
     * @return NodeInterface|null
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Finalizes a value, applying all finalization closures.
     *
     * @param mixed $value The value to finalize
     *
     * @return mixed The finalized value
     *
     * @throws Exception
     * @throws InvalidConfigurationException
     */
    final public function finalize($value)
    {
        $this->validateType($value);

        $value = $this->finalizeValue($value);

        // Perform validation on the final value if a closure has been set.
        // The closure is also allowed to return another value.
        foreach ($this->finalValidationClosures as $closure) {
            try {
                $value = $closure($value);
            } catch (Exception $e) {
                throw $e;
            } catch (\Exception $e) {
                throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e);
            }
        }

        return $value;
    }

    /**
     * Validates the type of a Node.
     *
     * @param mixed $value The value to validate
     *
     * @throws InvalidTypeException when the value is invalid
     */
    abstract protected function validateType($value);

    /**
     * Normalizes the value.
     *
     * @param mixed $value The value to normalize
     *
     * @return mixed The normalized value
     */
    abstract protected function normalizeValue($value);

    /**
     * Merges two values together.
     *
     * @param mixed $leftSide
     * @param mixed $rightSide
     *
     * @return mixed The merged value
     */
    abstract protected function mergeValues($leftSide, $rightSide);

    /**
     * Finalizes a value.
     *
     * @param mixed $value The value to finalize
     *
     * @return mixed The finalized value
     */
    abstract protected function finalizeValue($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\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

/**
 * This node represents a numeric value in the config tree.
 *
 * @author David Jeanmonod <david.jeanmonod@gmail.com>
 */
class NumericNode extends ScalarNode
{
    protected $min;
    protected $max;

    public function __construct($name, NodeInterface $parent = null, $min = null, $max = null)
    {
        parent::__construct($name, $parent);
        $this->min = $min;
        $this->max = $max;
    }

    /**
     * {@inheritdoc}
     */
    protected function finalizeValue($value)
    {
        $value = parent::finalizeValue($value);

        $errorMsg = null;
        if (isset($this->min) && $value < $this->min) {
            $errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min);
        }
        if (isset($this->max) && $value > $this->max) {
            $errorMsg = sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max);
        }
        if (isset($errorMsg)) {
            $ex = new InvalidConfigurationException($errorMsg);
            $ex->setPath($this->getPath());
            throw $ex;
        }

        return $value;
    }

    /**
     * {@inheritdoc}
     */
    protected function isValueEmpty($value)
    {
        // a numeric value cannot be empty
        return false;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

/**
 * This node represents an integer value in the config tree.
 *
 * @author Jeanmonod David <david.jeanmonod@gmail.com>
 */
class IntegerNode extends NumericNode
{
    /**
     * {@inheritdoc}
     */
    protected function validateType($value)
    {
        if (!is_int($value)) {
            $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected int, but got %s.', $this->getPath(), gettype($value)));
            if ($hint = $this->getInfo()) {
                $ex->addHint($hint);
            }
            $ex->setPath($this->getPath());

            throw $ex;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

/**
 * This interface must be implemented by nodes which can be used as prototypes.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
interface PrototypeNodeInterface extends NodeInterface
{
    /**
     * Sets the name of the node.
     *
     * @param string $name The name of the node
     */
    public function setName($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\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Exception\DuplicateKeyException;
use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
use Symfony\Component\Config\Definition\Exception\Exception;

/**
 * Represents a prototyped Array node in the config tree.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class PrototypedArrayNode extends ArrayNode
{
    protected $prototype;
    protected $keyAttribute;
    protected $removeKeyAttribute = false;
    protected $minNumberOfElements = 0;
    protected $defaultValue = array();
    protected $defaultChildren;
    /**
     * @var NodeInterface[] An array of the prototypes of the simplified value children
     */
    private $valuePrototypes = array();

    /**
     * Sets the minimum number of elements that a prototype based node must
     * contain. By default this is zero, meaning no elements.
     *
     * @param int $number
     */
    public function setMinNumberOfElements($number)
    {
        $this->minNumberOfElements = $number;
    }

    /**
     * Sets the attribute which value is to be used as key.
     *
     * This is useful when you have an indexed array that should be an
     * associative array. You can select an item from within the array
     * to be the key of the particular item. For example, if "id" is the
     * "key", then:
     *
     *     array(
     *         array('id' => 'my_name', 'foo' => 'bar'),
     *     );
     *
     *  becomes
     *
     *      array(
     *          'my_name' => array('foo' => 'bar'),
     *      );
     *
     * If you'd like "'id' => 'my_name'" to still be present in the resulting
     * array, then you can set the second argument of this method to false.
     *
     * @param string $attribute The name of the attribute which value is to be used as a key
     * @param bool   $remove    Whether or not to remove the key
     */
    public function setKeyAttribute($attribute, $remove = true)
    {
        $this->keyAttribute = $attribute;
        $this->removeKeyAttribute = $remove;
    }

    /**
     * Retrieves the name of the attribute which value should be used as key.
     *
     * @return string The name of the attribute
     */
    public function getKeyAttribute()
    {
        return $this->keyAttribute;
    }

    /**
     * Sets the default value of this node.
     *
     * @param string $value
     *
     * @throws \InvalidArgumentException if the default value is not an array
     */
    public function setDefaultValue($value)
    {
        if (!is_array($value)) {
            throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.');
        }

        $this->defaultValue = $value;
    }

    /**
     * Checks if the node has a default value.
     *
     * @return bool
     */
    public function hasDefaultValue()
    {
        return true;
    }

    /**
     * Adds default children when none are set.
     *
     * @param int|string|array|null $children The number of children|The child name|The children names to be added
     */
    public function setAddChildrenIfNoneSet($children = array('defaults'))
    {
        if (null === $children) {
            $this->defaultChildren = array('defaults');
        } else {
            $this->defaultChildren = is_int($children) && $children > 0 ? range(1, $children) : (array) $children;
        }
    }

    /**
     * Retrieves the default value.
     *
     * The default value could be either explicited or derived from the prototype
     * default value.
     *
     * @return array The default value
     */
    public function getDefaultValue()
    {
        if (null !== $this->defaultChildren) {
            $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array();
            $defaults = array();
            foreach (array_values($this->defaultChildren) as $i => $name) {
                $defaults[null === $this->keyAttribute ? $i : $name] = $default;
            }

            return $defaults;
        }

        return $this->defaultValue;
    }

    /**
     * Sets the node prototype.
     */
    public function setPrototype(PrototypeNodeInterface $node)
    {
        $this->prototype = $node;
    }

    /**
     * Retrieves the prototype.
     *
     * @return PrototypeNodeInterface The prototype
     */
    public function getPrototype()
    {
        return $this->prototype;
    }

    /**
     * Disable adding concrete children for prototyped nodes.
     *
     * @throws Exception
     */
    public function addChild(NodeInterface $node)
    {
        throw new Exception('A prototyped array node can not have concrete children.');
    }

    /**
     * Finalizes the value of this node.
     *
     * @param mixed $value
     *
     * @return mixed The finalized value
     *
     * @throws UnsetKeyException
     * @throws InvalidConfigurationException if the node doesn't have enough children
     */
    protected function finalizeValue($value)
    {
        if (false === $value) {
            $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value));
            throw new UnsetKeyException($msg);
        }

        foreach ($value as $k => $v) {
            $prototype = $this->getPrototypeForChild($k);
            try {
                $value[$k] = $prototype->finalize($v);
            } catch (UnsetKeyException $e) {
                unset($value[$k]);
            }
        }

        if (count($value) < $this->minNumberOfElements) {
            $msg = sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements);
            $ex = new InvalidConfigurationException($msg);
            $ex->setPath($this->getPath());

            throw $ex;
        }

        return $value;
    }

    /**
     * Normalizes the value.
     *
     * @param mixed $value The value to normalize
     *
     * @return mixed The normalized value
     *
     * @throws InvalidConfigurationException
     * @throws DuplicateKeyException
     */
    protected function normalizeValue($value)
    {
        if (false === $value) {
            return $value;
        }

        $value = $this->remapXml($value);

        $isAssoc = array_keys($value) !== range(0, count($value) - 1);
        $normalized = array();
        foreach ($value as $k => $v) {
            if (null !== $this->keyAttribute && is_array($v)) {
                if (!isset($v[$this->keyAttribute]) && is_int($k) && !$isAssoc) {
                    $msg = sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath());
                    $ex = new InvalidConfigurationException($msg);
                    $ex->setPath($this->getPath());

                    throw $ex;
                } elseif (isset($v[$this->keyAttribute])) {
                    $k = $v[$this->keyAttribute];

                    // remove the key attribute when required
                    if ($this->removeKeyAttribute) {
                        unset($v[$this->keyAttribute]);
                    }

                    // if only "value" is left
                    if (array_keys($v) === array('value')) {
                        $v = $v['value'];
                        if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && array_key_exists('value', $children)) {
                            $valuePrototype = current($this->valuePrototypes) ?: clone $children['value'];
                            $valuePrototype->parent = $this;
                            $originalClosures = $this->prototype->normalizationClosures;
                            if (is_array($originalClosures)) {
                                $valuePrototypeClosures = $valuePrototype->normalizationClosures;
                                $valuePrototype->normalizationClosures = is_array($valuePrototypeClosures) ? array_merge($originalClosures, $valuePrototypeClosures) : $originalClosures;
                            }
                            $this->valuePrototypes[$k] = $valuePrototype;
                        }
                    }
                }

                if (array_key_exists($k, $normalized)) {
                    $msg = sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath());
                    $ex = new DuplicateKeyException($msg);
                    $ex->setPath($this->getPath());

                    throw $ex;
                }
            }

            $prototype = $this->getPrototypeForChild($k);
            if (null !== $this->keyAttribute || $isAssoc) {
                $normalized[$k] = $prototype->normalize($v);
            } else {
                $normalized[] = $prototype->normalize($v);
            }
        }

        return $normalized;
    }

    /**
     * Merges values together.
     *
     * @param mixed $leftSide  The left side to merge
     * @param mixed $rightSide The right side to merge
     *
     * @return mixed The merged values
     *
     * @throws InvalidConfigurationException
     * @throws \RuntimeException
     */
    protected function mergeValues($leftSide, $rightSide)
    {
        if (false === $rightSide) {
            // if this is still false after the last config has been merged the
            // finalization pass will take care of removing this key entirely
            return false;
        }

        if (false === $leftSide || !$this->performDeepMerging) {
            return $rightSide;
        }

        foreach ($rightSide as $k => $v) {
            // prototype, and key is irrelevant, so simply append the element
            if (null === $this->keyAttribute) {
                $leftSide[] = $v;
                continue;
            }

            // no conflict
            if (!array_key_exists($k, $leftSide)) {
                if (!$this->allowNewKeys) {
                    $ex = new InvalidConfigurationException(sprintf(
                        'You are not allowed to define new elements for path "%s". '.
                        'Please define all elements for this path in one config file.',
                        $this->getPath()
                    ));
                    $ex->setPath($this->getPath());

                    throw $ex;
                }

                $leftSide[$k] = $v;
                continue;
            }

            $prototype = $this->getPrototypeForChild($k);
            $leftSide[$k] = $prototype->merge($leftSide[$k], $v);
        }

        return $leftSide;
    }

    /**
     * Returns a prototype for the child node that is associated to $key in the value array.
     * For general child nodes, this will be $this->prototype.
     * But if $this->removeKeyAttribute is true and there are only two keys in the child node:
     * one is same as this->keyAttribute and the other is 'value', then the prototype will be different.
     *
     * For example, assume $this->keyAttribute is 'name' and the value array is as follows:
     * array(
     *     array(
     *         'name' => 'name001',
     *         'value' => 'value001'
     *     )
     * )
     *
     * Now, the key is 0 and the child node is:
     * array(
     *    'name' => 'name001',
     *    'value' => 'value001'
     * )
     *
     * When normalizing the value array, the 'name' element will removed from the child node
     * and its value becomes the new key of the child node:
     * array(
     *     'name001' => array('value' => 'value001')
     * )
     *
     * Now only 'value' element is left in the child node which can be further simplified into a string:
     * array('name001' => 'value001')
     *
     * Now, the key becomes 'name001' and the child node becomes 'value001' and
     * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance.
     *
     * @param string $key The key of the child node
     *
     * @return mixed The prototype instance
     */
    private function getPrototypeForChild($key)
    {
        $prototype = isset($this->valuePrototypes[$key]) ? $this->valuePrototypes[$key] : $this->prototype;
        $prototype->setName($key);

        return $prototype;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

/**
 * This class is the entry point for config normalization/merging/finalization.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class Processor
{
    /**
     * Processes an array of configurations.
     *
     * @param NodeInterface $configTree The node tree describing the configuration
     * @param array         $configs    An array of configuration items to process
     *
     * @return array The processed configuration
     */
    public function process(NodeInterface $configTree, array $configs)
    {
        $currentConfig = array();
        foreach ($configs as $config) {
            $config = $configTree->normalize($config);
            $currentConfig = $configTree->merge($currentConfig, $config);
        }

        return $configTree->finalize($currentConfig);
    }

    /**
     * Processes an array of configurations.
     *
     * @param ConfigurationInterface $configuration The configuration class
     * @param array                  $configs       An array of configuration items to process
     *
     * @return array The processed configuration
     */
    public function processConfiguration(ConfigurationInterface $configuration, array $configs)
    {
        return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs);
    }

    /**
     * Normalizes a configuration entry.
     *
     * This method returns a normalize configuration array for a given key
     * to remove the differences due to the original format (YAML and XML mainly).
     *
     * Here is an example.
     *
     * The configuration in XML:
     *
     * <twig:extension>twig.extension.foo</twig:extension>
     * <twig:extension>twig.extension.bar</twig:extension>
     *
     * And the same configuration in YAML:
     *
     * extensions: ['twig.extension.foo', 'twig.extension.bar']
     *
     * @param array  $config A config array
     * @param string $key    The key to normalize
     * @param string $plural The plural form of the key if it is irregular
     *
     * @return array
     */
    public static function normalizeConfig($config, $key, $plural = null)
    {
        if (null === $plural) {
            $plural = $key.'s';
        }

        if (isset($config[$plural])) {
            return $config[$plural];
        }

        if (isset($config[$key])) {
            if (is_string($config[$key]) || !is_int(key($config[$key]))) {
                // only one
                return  array($config[$key]);
            }

            return  $config[$key];
        }

        return array();
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

/**
 * Common Interface among all nodes.
 *
 * In most cases, it is better to inherit from BaseNode instead of implementing
 * this interface yourself.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
interface NodeInterface
{
    /**
     * Returns the name of the node.
     *
     * @return string The name of the node
     */
    public function getName();

    /**
     * Returns the path of the node.
     *
     * @return string The node path
     */
    public function getPath();

    /**
     * Returns true when the node is required.
     *
     * @return bool If the node is required
     */
    public function isRequired();

    /**
     * Returns true when the node has a default value.
     *
     * @return bool If the node has a default value
     */
    public function hasDefaultValue();

    /**
     * Returns the default value of the node.
     *
     * @return mixed The default value
     *
     * @throws \RuntimeException if the node has no default value
     */
    public function getDefaultValue();

    /**
     * Normalizes the supplied value.
     *
     * @param mixed $value The value to normalize
     *
     * @return mixed The normalized value
     */
    public function normalize($value);

    /**
     * Merges two values together.
     *
     * @param mixed $leftSide
     * @param mixed $rightSide
     *
     * @return mixed The merged values
     */
    public function merge($leftSide, $rightSide);

    /**
     * Finalizes a value.
     *
     * @param mixed $value The value to finalize
     *
     * @return mixed The finalized value
     */
    public function finalize($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\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

/**
 * Node which only allows a finite set of values.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class EnumNode extends ScalarNode
{
    private $values;

    public function __construct($name, NodeInterface $parent = null, array $values = array())
    {
        $values = array_unique($values);
        if (empty($values)) {
            throw new \InvalidArgumentException('$values must contain at least one element.');
        }

        parent::__construct($name, $parent);
        $this->values = $values;
    }

    public function getValues()
    {
        return $this->values;
    }

    protected function finalizeValue($value)
    {
        $value = parent::finalizeValue($value);

        if (!in_array($value, $this->values, true)) {
            $ex = new InvalidConfigurationException(sprintf(
                'The value %s is not allowed for path "%s". Permissible values: %s',
                json_encode($value),
                $this->getPath(),
                implode(', ', array_map('json_encode', $this->values))));
            $ex->setPath($this->getPath());

            throw $ex;
        }

        return $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\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

/**
 * This node represents a Boolean value in the config tree.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class BooleanNode extends ScalarNode
{
    /**
     * {@inheritdoc}
     */
    protected function validateType($value)
    {
        if (!is_bool($value)) {
            $ex = new InvalidTypeException(sprintf(
                'Invalid type for path "%s". Expected boolean, but got %s.',
                $this->getPath(),
                gettype($value)
            ));
            if ($hint = $this->getInfo()) {
                $ex->addHint($hint);
            }
            $ex->setPath($this->getPath());

            throw $ex;
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function isValueEmpty($value)
    {
        // a boolean value cannot be empty
        return false;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
use Symfony\Component\Config\Definition\Exception\UnsetKeyException;

/**
 * Represents an Array node in the config tree.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class ArrayNode extends BaseNode implements PrototypeNodeInterface
{
    protected $xmlRemappings = array();
    protected $children = array();
    protected $allowFalse = false;
    protected $allowNewKeys = true;
    protected $addIfNotSet = false;
    protected $performDeepMerging = true;
    protected $ignoreExtraKeys = false;
    protected $removeExtraKeys = true;
    protected $normalizeKeys = true;

    public function setNormalizeKeys($normalizeKeys)
    {
        $this->normalizeKeys = (bool) $normalizeKeys;
    }

    /**
     * Normalizes keys between the different configuration formats.
     *
     * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML.
     * After running this method, all keys are normalized to foo_bar.
     *
     * If you have a mixed key like foo-bar_moo, it will not be altered.
     * The key will also not be altered if the target key already exists.
     *
     * @param mixed $value
     *
     * @return array The value with normalized keys
     */
    protected function preNormalize($value)
    {
        if (!$this->normalizeKeys || !is_array($value)) {
            return $value;
        }

        $normalized = array();

        foreach ($value as $k => $v) {
            if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
                $normalized[$normalizedKey] = $v;
            } else {
                $normalized[$k] = $v;
            }
        }

        return $normalized;
    }

    /**
     * Retrieves the children of this node.
     *
     * @return array The children
     */
    public function getChildren()
    {
        return $this->children;
    }

    /**
     * Sets the xml remappings that should be performed.
     *
     * @param array $remappings An array of the form array(array(string, string))
     */
    public function setXmlRemappings(array $remappings)
    {
        $this->xmlRemappings = $remappings;
    }

    /**
     * Gets the xml remappings that should be performed.
     *
     * @return array $remappings an array of the form array(array(string, string))
     */
    public function getXmlRemappings()
    {
        return $this->xmlRemappings;
    }

    /**
     * Sets whether to add default values for this array if it has not been
     * defined in any of the configuration files.
     *
     * @param bool $boolean
     */
    public function setAddIfNotSet($boolean)
    {
        $this->addIfNotSet = (bool) $boolean;
    }

    /**
     * Sets whether false is allowed as value indicating that the array should be unset.
     *
     * @param bool $allow
     */
    public function setAllowFalse($allow)
    {
        $this->allowFalse = (bool) $allow;
    }

    /**
     * Sets whether new keys can be defined in subsequent configurations.
     *
     * @param bool $allow
     */
    public function setAllowNewKeys($allow)
    {
        $this->allowNewKeys = (bool) $allow;
    }

    /**
     * Sets if deep merging should occur.
     *
     * @param bool $boolean
     */
    public function setPerformDeepMerging($boolean)
    {
        $this->performDeepMerging = (bool) $boolean;
    }

    /**
     * Whether extra keys should just be ignore without an exception.
     *
     * @param bool $boolean To allow extra keys
     * @param bool $remove  To remove extra keys
     */
    public function setIgnoreExtraKeys($boolean, $remove = true)
    {
        $this->ignoreExtraKeys = (bool) $boolean;
        $this->removeExtraKeys = $this->ignoreExtraKeys && $remove;
    }

    /**
     * Sets the node Name.
     *
     * @param string $name The node's name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Checks if the node has a default value.
     *
     * @return bool
     */
    public function hasDefaultValue()
    {
        return $this->addIfNotSet;
    }

    /**
     * Retrieves the default value.
     *
     * @return array The default value
     *
     * @throws \RuntimeException if the node has no default value
     */
    public function getDefaultValue()
    {
        if (!$this->hasDefaultValue()) {
            throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath()));
        }

        $defaults = array();
        foreach ($this->children as $name => $child) {
            if ($child->hasDefaultValue()) {
                $defaults[$name] = $child->getDefaultValue();
            }
        }

        return $defaults;
    }

    /**
     * Adds a child node.
     *
     * @throws \InvalidArgumentException when the child node has no name
     * @throws \InvalidArgumentException when the child node's name is not unique
     */
    public function addChild(NodeInterface $node)
    {
        $name = $node->getName();
        if (!strlen($name)) {
            throw new \InvalidArgumentException('Child nodes must be named.');
        }
        if (isset($this->children[$name])) {
            throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name));
        }

        $this->children[$name] = $node;
    }

    /**
     * Finalizes the value of this node.
     *
     * @param mixed $value
     *
     * @return mixed The finalised value
     *
     * @throws UnsetKeyException
     * @throws InvalidConfigurationException if the node doesn't have enough children
     */
    protected function finalizeValue($value)
    {
        if (false === $value) {
            $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value));
            throw new UnsetKeyException($msg);
        }

        foreach ($this->children as $name => $child) {
            if (!array_key_exists($name, $value)) {
                if ($child->isRequired()) {
                    $msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath());
                    $ex = new InvalidConfigurationException($msg);
                    $ex->setPath($this->getPath());

                    throw $ex;
                }

                if ($child->hasDefaultValue()) {
                    $value[$name] = $child->getDefaultValue();
                }

                continue;
            }

            if ($child->isDeprecated()) {
                @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
            }

            try {
                $value[$name] = $child->finalize($value[$name]);
            } catch (UnsetKeyException $e) {
                unset($value[$name]);
            }
        }

        return $value;
    }

    /**
     * Validates the type of the value.
     *
     * @param mixed $value
     *
     * @throws InvalidTypeException
     */
    protected function validateType($value)
    {
        if (!is_array($value) && (!$this->allowFalse || false !== $value)) {
            $ex = new InvalidTypeException(sprintf(
                'Invalid type for path "%s". Expected array, but got %s',
                $this->getPath(),
                gettype($value)
            ));
            if ($hint = $this->getInfo()) {
                $ex->addHint($hint);
            }
            $ex->setPath($this->getPath());

            throw $ex;
        }
    }

    /**
     * Normalizes the value.
     *
     * @param mixed $value The value to normalize
     *
     * @return mixed The normalized value
     *
     * @throws InvalidConfigurationException
     */
    protected function normalizeValue($value)
    {
        if (false === $value) {
            return $value;
        }

        $value = $this->remapXml($value);

        $normalized = array();
        foreach ($value as $name => $val) {
            if (isset($this->children[$name])) {
                $normalized[$name] = $this->children[$name]->normalize($val);
                unset($value[$name]);
            } elseif (!$this->removeExtraKeys) {
                $normalized[$name] = $val;
            }
        }

        // if extra fields are present, throw exception
        if (count($value) && !$this->ignoreExtraKeys) {
            $msg = sprintf('Unrecognized option%s "%s" under "%s"', 1 === count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath());
            $ex = new InvalidConfigurationException($msg);
            $ex->setPath($this->getPath());

            throw $ex;
        }

        return $normalized;
    }

    /**
     * Remaps multiple singular values to a single plural value.
     *
     * @param array $value The source values
     *
     * @return array The remapped values
     */
    protected function remapXml($value)
    {
        foreach ($this->xmlRemappings as list($singular, $plural)) {
            if (!isset($value[$singular])) {
                continue;
            }

            $value[$plural] = Processor::normalizeConfig($value, $singular, $plural);
            unset($value[$singular]);
        }

        return $value;
    }

    /**
     * Merges values together.
     *
     * @param mixed $leftSide  The left side to merge
     * @param mixed $rightSide The right side to merge
     *
     * @return mixed The merged values
     *
     * @throws InvalidConfigurationException
     * @throws \RuntimeException
     */
    protected function mergeValues($leftSide, $rightSide)
    {
        if (false === $rightSide) {
            // if this is still false after the last config has been merged the
            // finalization pass will take care of removing this key entirely
            return false;
        }

        if (false === $leftSide || !$this->performDeepMerging) {
            return $rightSide;
        }

        foreach ($rightSide as $k => $v) {
            // no conflict
            if (!array_key_exists($k, $leftSide)) {
                if (!$this->allowNewKeys) {
                    $ex = new InvalidConfigurationException(sprintf(
                        'You are not allowed to define new elements for path "%s". '
                       .'Please define all elements for this path in one config file. '
                       .'If you are trying to overwrite an element, make sure you redefine it '
                       .'with the same name.',
                        $this->getPath()
                    ));
                    $ex->setPath($this->getPath());

                    throw $ex;
                }

                $leftSide[$k] = $v;
                continue;
            }

            if (!isset($this->children[$k])) {
                throw new \RuntimeException('merge() expects a normalized config array.');
            }

            $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v);
        }

        return $leftSide;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidTypeException;

/**
 * This node represents a scalar value in the config tree.
 *
 * The following values are considered scalars:
 *   * booleans
 *   * strings
 *   * null
 *   * integers
 *   * floats
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class ScalarNode extends VariableNode
{
    /**
     * {@inheritdoc}
     */
    protected function validateType($value)
    {
        if (!is_scalar($value) && null !== $value) {
            $ex = new InvalidTypeException(sprintf(
                'Invalid type for path "%s". Expected scalar, but got %s.',
                $this->getPath(),
                gettype($value)
            ));
            if ($hint = $this->getInfo()) {
                $ex->addHint($hint);
            }
            $ex->setPath($this->getPath());

            throw $ex;
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function isValueEmpty($value)
    {
        return null === $value || '' === $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\Config\Definition;

/**
 * Configuration interface.
 *
 * @author Victor Berchet <victor@suumit.com>
 */
interface ConfigurationInterface
{
    /**
     * Generates the configuration tree builder.
     *
     * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
     */
    public function getConfigTreeBuilder();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\Exception\UnsetKeyException;

/**
 * This class builds an if expression.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 * @author Christophe Coevoet <stof@notk.org>
 */
class ExprBuilder
{
    protected $node;
    public $ifPart;
    public $thenPart;

    public function __construct(NodeDefinition $node)
    {
        $this->node = $node;
    }

    /**
     * Marks the expression as being always used.
     *
     * @return $this
     */
    public function always(\Closure $then = null)
    {
        $this->ifPart = function ($v) { return true; };

        if (null !== $then) {
            $this->thenPart = $then;
        }

        return $this;
    }

    /**
     * Sets a closure to use as tests.
     *
     * The default one tests if the value is true.
     *
     * @return $this
     */
    public function ifTrue(\Closure $closure = null)
    {
        if (null === $closure) {
            $closure = function ($v) { return true === $v; };
        }

        $this->ifPart = $closure;

        return $this;
    }

    /**
     * Tests if the value is a string.
     *
     * @return $this
     */
    public function ifString()
    {
        $this->ifPart = function ($v) { return is_string($v); };

        return $this;
    }

    /**
     * Tests if the value is null.
     *
     * @return $this
     */
    public function ifNull()
    {
        $this->ifPart = function ($v) { return null === $v; };

        return $this;
    }

    /**
     * Tests if the value is empty.
     *
     * @return ExprBuilder
     */
    public function ifEmpty()
    {
        $this->ifPart = function ($v) { return empty($v); };

        return $this;
    }

    /**
     * Tests if the value is an array.
     *
     * @return $this
     */
    public function ifArray()
    {
        $this->ifPart = function ($v) { return is_array($v); };

        return $this;
    }

    /**
     * Tests if the value is in an array.
     *
     * @return $this
     */
    public function ifInArray(array $array)
    {
        $this->ifPart = function ($v) use ($array) { return in_array($v, $array, true); };

        return $this;
    }

    /**
     * Tests if the value is not in an array.
     *
     * @return $this
     */
    public function ifNotInArray(array $array)
    {
        $this->ifPart = function ($v) use ($array) { return !in_array($v, $array, true); };

        return $this;
    }

    /**
     * Transforms variables of any type into an array.
     *
     * @return $this
     */
    public function castToArray()
    {
        $this->ifPart = function ($v) { return !is_array($v); };
        $this->thenPart = function ($v) { return array($v); };

        return $this;
    }

    /**
     * Sets the closure to run if the test pass.
     *
     * @return $this
     */
    public function then(\Closure $closure)
    {
        $this->thenPart = $closure;

        return $this;
    }

    /**
     * Sets a closure returning an empty array.
     *
     * @return $this
     */
    public function thenEmptyArray()
    {
        $this->thenPart = function ($v) { return array(); };

        return $this;
    }

    /**
     * Sets a closure marking the value as invalid at validation time.
     *
     * if you want to add the value of the node in your message just use a %s placeholder.
     *
     * @param string $message
     *
     * @return $this
     *
     * @throws \InvalidArgumentException
     */
    public function thenInvalid($message)
    {
        $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); };

        return $this;
    }

    /**
     * Sets a closure unsetting this key of the array at validation time.
     *
     * @return $this
     *
     * @throws UnsetKeyException
     */
    public function thenUnset()
    {
        $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key'); };

        return $this;
    }

    /**
     * Returns the related node.
     *
     * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition
     *
     * @throws \RuntimeException
     */
    public function end()
    {
        if (null === $this->ifPart) {
            throw new \RuntimeException('You must specify an if part.');
        }
        if (null === $this->thenPart) {
            throw new \RuntimeException('You must specify a then part.');
        }

        return $this->node;
    }

    /**
     * Builds the expressions.
     *
     * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build
     *
     * @return array
     */
    public static function buildExpressions(array $expressions)
    {
        foreach ($expressions as $k => $expr) {
            if ($expr instanceof self) {
                $if = $expr->ifPart;
                $then = $expr->thenPart;
                $expressions[$k] = function ($v) use ($if, $then) {
                    return $if($v) ? $then($v) : $v;
                };
            }
        }

        return $expressions;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\EnumNode;

/**
 * Enum Node Definition.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class EnumNodeDefinition extends ScalarNodeDefinition
{
    private $values;

    /**
     * @return $this
     */
    public function values(array $values)
    {
        $values = array_unique($values);

        if (empty($values)) {
            throw new \InvalidArgumentException('->values() must be called with at least one value.');
        }

        $this->values = $values;

        return $this;
    }

    /**
     * Instantiate a Node.
     *
     * @return EnumNode The node
     *
     * @throws \RuntimeException
     */
    protected function instantiateNode()
    {
        if (null === $this->values) {
            throw new \RuntimeException('You must call ->values() on enum nodes.');
        }

        return new EnumNode($this->name, $this->parent, $this->values);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

/**
 * This class builds merge conditions.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class MergeBuilder
{
    protected $node;
    public $allowFalse = false;
    public $allowOverwrite = true;

    public function __construct(NodeDefinition $node)
    {
        $this->node = $node;
    }

    /**
     * Sets whether the node can be unset.
     *
     * @param bool $allow
     *
     * @return $this
     */
    public function allowUnset($allow = true)
    {
        $this->allowFalse = $allow;

        return $this;
    }

    /**
     * Sets whether the node can be overwritten.
     *
     * @param bool $deny Whether the overwriting is forbidden or not
     *
     * @return $this
     */
    public function denyOverwrite($deny = true)
    {
        $this->allowOverwrite = !$deny;

        return $this;
    }

    /**
     * Returns the related node.
     *
     * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition
     */
    public function end()
    {
        return $this->node;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;

/**
 * Abstract class that contains common code of integer and float node definitions.
 *
 * @author David Jeanmonod <david.jeanmonod@gmail.com>
 */
abstract class NumericNodeDefinition extends ScalarNodeDefinition
{
    protected $min;
    protected $max;

    /**
     * Ensures that the value is smaller than the given reference.
     *
     * @param mixed $max
     *
     * @return $this
     *
     * @throws \InvalidArgumentException when the constraint is inconsistent
     */
    public function max($max)
    {
        if (isset($this->min) && $this->min > $max) {
            throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min));
        }
        $this->max = $max;

        return $this;
    }

    /**
     * Ensures that the value is bigger than the given reference.
     *
     * @param mixed $min
     *
     * @return $this
     *
     * @throws \InvalidArgumentException when the constraint is inconsistent
     */
    public function min($min)
    {
        if (isset($this->max) && $this->max < $min) {
            throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max));
        }
        $this->min = $min;

        return $this;
    }

    /**
     * {@inheritdoc}
     *
     * @throws InvalidDefinitionException
     */
    public function cannotBeEmpty()
    {
        throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.');
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

/**
 * This class provides a fluent interface for building a node.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class NodeBuilder implements NodeParentInterface
{
    protected $parent;
    protected $nodeMapping;

    public function __construct()
    {
        $this->nodeMapping = array(
            'variable' => __NAMESPACE__.'\\VariableNodeDefinition',
            'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition',
            'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition',
            'integer' => __NAMESPACE__.'\\IntegerNodeDefinition',
            'float' => __NAMESPACE__.'\\FloatNodeDefinition',
            'array' => __NAMESPACE__.'\\ArrayNodeDefinition',
            'enum' => __NAMESPACE__.'\\EnumNodeDefinition',
        );
    }

    /**
     * Set the parent node.
     *
     * @return $this
     */
    public function setParent(ParentNodeDefinitionInterface $parent = null)
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * Creates a child array node.
     *
     * @param string $name The name of the node
     *
     * @return ArrayNodeDefinition The child node
     */
    public function arrayNode($name)
    {
        return $this->node($name, 'array');
    }

    /**
     * Creates a child scalar node.
     *
     * @param string $name The name of the node
     *
     * @return ScalarNodeDefinition The child node
     */
    public function scalarNode($name)
    {
        return $this->node($name, 'scalar');
    }

    /**
     * Creates a child Boolean node.
     *
     * @param string $name The name of the node
     *
     * @return BooleanNodeDefinition The child node
     */
    public function booleanNode($name)
    {
        return $this->node($name, 'boolean');
    }

    /**
     * Creates a child integer node.
     *
     * @param string $name The name of the node
     *
     * @return IntegerNodeDefinition The child node
     */
    public function integerNode($name)
    {
        return $this->node($name, 'integer');
    }

    /**
     * Creates a child float node.
     *
     * @param string $name The name of the node
     *
     * @return FloatNodeDefinition The child node
     */
    public function floatNode($name)
    {
        return $this->node($name, 'float');
    }

    /**
     * Creates a child EnumNode.
     *
     * @param string $name
     *
     * @return EnumNodeDefinition
     */
    public function enumNode($name)
    {
        return $this->node($name, 'enum');
    }

    /**
     * Creates a child variable node.
     *
     * @param string $name The name of the node
     *
     * @return VariableNodeDefinition The builder of the child node
     */
    public function variableNode($name)
    {
        return $this->node($name, 'variable');
    }

    /**
     * Returns the parent node.
     *
     * @return ParentNodeDefinitionInterface|NodeDefinition The parent node
     */
    public function end()
    {
        return $this->parent;
    }

    /**
     * Creates a child node.
     *
     * @param string $name The name of the node
     * @param string $type The type of the node
     *
     * @return NodeDefinition The child node
     *
     * @throws \RuntimeException When the node type is not registered
     * @throws \RuntimeException When the node class is not found
     */
    public function node($name, $type)
    {
        $class = $this->getNodeClass($type);

        $node = new $class($name);

        $this->append($node);

        return $node;
    }

    /**
     * Appends a node definition.
     *
     * Usage:
     *
     *     $node = new ArrayNodeDefinition('name')
     *         ->children()
     *             ->scalarNode('foo')->end()
     *             ->scalarNode('baz')->end()
     *             ->append($this->getBarNodeDefinition())
     *         ->end()
     *     ;
     *
     * @return $this
     */
    public function append(NodeDefinition $node)
    {
        if ($node instanceof ParentNodeDefinitionInterface) {
            $builder = clone $this;
            $builder->setParent(null);
            $node->setBuilder($builder);
        }

        if (null !== $this->parent) {
            $this->parent->append($node);
            // Make this builder the node parent to allow for a fluid interface
            $node->setParent($this);
        }

        return $this;
    }

    /**
     * Adds or overrides a node Type.
     *
     * @param string $type  The name of the type
     * @param string $class The fully qualified name the node definition class
     *
     * @return $this
     */
    public function setNodeClass($type, $class)
    {
        $this->nodeMapping[strtolower($type)] = $class;

        return $this;
    }

    /**
     * Returns the class name of the node definition.
     *
     * @param string $type The node type
     *
     * @return string The node definition class name
     *
     * @throws \RuntimeException When the node type is not registered
     * @throws \RuntimeException When the node class is not found
     */
    protected function getNodeClass($type)
    {
        $type = strtolower($type);

        if (!isset($this->nodeMapping[$type])) {
            throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type));
        }

        $class = $this->nodeMapping[$type];

        if (!class_exists($class)) {
            throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class));
        }

        return $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\Config\Definition\Builder;

use Symfony\Component\Config\Definition\VariableNode;

/**
 * This class provides a fluent interface for defining a node.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class VariableNodeDefinition extends NodeDefinition
{
    /**
     * Instantiate a Node.
     *
     * @return VariableNode The node
     */
    protected function instantiateNode()
    {
        return new VariableNode($this->name, $this->parent);
    }

    /**
     * {@inheritdoc}
     */
    protected function createNode()
    {
        $node = $this->instantiateNode();

        if (null !== $this->normalization) {
            $node->setNormalizationClosures($this->normalization->before);
        }

        if (null !== $this->merge) {
            $node->setAllowOverwrite($this->merge->allowOverwrite);
        }

        if (true === $this->default) {
            $node->setDefaultValue($this->defaultValue);
        }

        $node->setAllowEmptyValue($this->allowEmptyValue);
        $node->addEquivalentValue(null, $this->nullEquivalent);
        $node->addEquivalentValue(true, $this->trueEquivalent);
        $node->addEquivalentValue(false, $this->falseEquivalent);
        $node->setRequired($this->required);
        $node->setDeprecated($this->deprecationMessage);

        if (null !== $this->validation) {
            $node->setFinalValidationClosures($this->validation->rules);
        }

        return $node;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\PrototypedArrayNode;
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;

/**
 * This class provides a fluent interface for defining an array node.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface
{
    protected $performDeepMerging = true;
    protected $ignoreExtraKeys = false;
    protected $removeExtraKeys = true;
    protected $children = array();
    protected $prototype;
    protected $atLeastOne = false;
    protected $allowNewKeys = true;
    protected $key;
    protected $removeKeyItem;
    protected $addDefaults = false;
    protected $addDefaultChildren = false;
    protected $nodeBuilder;
    protected $normalizeKeys = true;

    /**
     * {@inheritdoc}
     */
    public function __construct($name, NodeParentInterface $parent = null)
    {
        parent::__construct($name, $parent);

        $this->nullEquivalent = array();
        $this->trueEquivalent = array();
    }

    /**
     * Sets a custom children builder.
     */
    public function setBuilder(NodeBuilder $builder)
    {
        $this->nodeBuilder = $builder;
    }

    /**
     * Returns a builder to add children nodes.
     *
     * @return NodeBuilder
     */
    public function children()
    {
        return $this->getNodeBuilder();
    }

    /**
     * Sets a prototype for child nodes.
     *
     * @param string $type The type of node
     *
     * @return NodeDefinition
     */
    public function prototype($type)
    {
        return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this);
    }

    /**
     * @return VariableNodeDefinition
     */
    public function variablePrototype()
    {
        return $this->prototype('variable');
    }

    /**
     * @return ScalarNodeDefinition
     */
    public function scalarPrototype()
    {
        return $this->prototype('scalar');
    }

    /**
     * @return BooleanNodeDefinition
     */
    public function booleanPrototype()
    {
        return $this->prototype('boolean');
    }

    /**
     * @return IntegerNodeDefinition
     */
    public function integerPrototype()
    {
        return $this->prototype('integer');
    }

    /**
     * @return FloatNodeDefinition
     */
    public function floatPrototype()
    {
        return $this->prototype('float');
    }

    /**
     * @return ArrayNodeDefinition
     */
    public function arrayPrototype()
    {
        return $this->prototype('array');
    }

    /**
     * @return EnumNodeDefinition
     */
    public function enumPrototype()
    {
        return $this->prototype('enum');
    }

    /**
     * Adds the default value if the node is not set in the configuration.
     *
     * This method is applicable to concrete nodes only (not to prototype nodes).
     * If this function has been called and the node is not set during the finalization
     * phase, it's default value will be derived from its children default values.
     *
     * @return $this
     */
    public function addDefaultsIfNotSet()
    {
        $this->addDefaults = true;

        return $this;
    }

    /**
     * Adds children with a default value when none are defined.
     *
     * This method is applicable to prototype nodes only.
     *
     * @param int|string|array|null $children The number of children|The child name|The children names to be added
     *
     * @return $this
     */
    public function addDefaultChildrenIfNoneSet($children = null)
    {
        $this->addDefaultChildren = $children;

        return $this;
    }

    /**
     * Requires the node to have at least one element.
     *
     * This method is applicable to prototype nodes only.
     *
     * @return $this
     */
    public function requiresAtLeastOneElement()
    {
        $this->atLeastOne = true;

        return $this;
    }

    /**
     * Disallows adding news keys in a subsequent configuration.
     *
     * If used all keys have to be defined in the same configuration file.
     *
     * @return $this
     */
    public function disallowNewKeysInSubsequentConfigs()
    {
        $this->allowNewKeys = false;

        return $this;
    }

    /**
     * Sets a normalization rule for XML configurations.
     *
     * @param string $singular The key to remap
     * @param string $plural   The plural of the key for irregular plurals
     *
     * @return $this
     */
    public function fixXmlConfig($singular, $plural = null)
    {
        $this->normalization()->remap($singular, $plural);

        return $this;
    }

    /**
     * Sets the attribute which value is to be used as key.
     *
     * This is useful when you have an indexed array that should be an
     * associative array. You can select an item from within the array
     * to be the key of the particular item. For example, if "id" is the
     * "key", then:
     *
     *     array(
     *         array('id' => 'my_name', 'foo' => 'bar'),
     *     );
     *
     *   becomes
     *
     *     array(
     *         'my_name' => array('foo' => 'bar'),
     *     );
     *
     * If you'd like "'id' => 'my_name'" to still be present in the resulting
     * array, then you can set the second argument of this method to false.
     *
     * This method is applicable to prototype nodes only.
     *
     * @param string $name          The name of the key
     * @param bool   $removeKeyItem Whether or not the key item should be removed
     *
     * @return $this
     */
    public function useAttributeAsKey($name, $removeKeyItem = true)
    {
        $this->key = $name;
        $this->removeKeyItem = $removeKeyItem;

        return $this;
    }

    /**
     * Sets whether the node can be unset.
     *
     * @param bool $allow
     *
     * @return $this
     */
    public function canBeUnset($allow = true)
    {
        $this->merge()->allowUnset($allow);

        return $this;
    }

    /**
     * Adds an "enabled" boolean to enable the current section.
     *
     * By default, the section is disabled. If any configuration is specified then
     * the node will be automatically enabled:
     *
     * enableableArrayNode: {enabled: true, ...}   # The config is enabled & default values get overridden
     * enableableArrayNode: ~                      # The config is enabled & use the default values
     * enableableArrayNode: true                   # The config is enabled & use the default values
     * enableableArrayNode: {other: value, ...}    # The config is enabled & default values get overridden
     * enableableArrayNode: {enabled: false, ...}  # The config is disabled
     * enableableArrayNode: false                  # The config is disabled
     *
     * @return $this
     */
    public function canBeEnabled()
    {
        $this
            ->addDefaultsIfNotSet()
            ->treatFalseLike(array('enabled' => false))
            ->treatTrueLike(array('enabled' => true))
            ->treatNullLike(array('enabled' => true))
            ->beforeNormalization()
                ->ifArray()
                ->then(function ($v) {
                    $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true;

                    return $v;
                })
            ->end()
            ->children()
                ->booleanNode('enabled')
                    ->defaultFalse()
        ;

        return $this;
    }

    /**
     * Adds an "enabled" boolean to enable the current section.
     *
     * By default, the section is enabled.
     *
     * @return $this
     */
    public function canBeDisabled()
    {
        $this
            ->addDefaultsIfNotSet()
            ->treatFalseLike(array('enabled' => false))
            ->treatTrueLike(array('enabled' => true))
            ->treatNullLike(array('enabled' => true))
            ->children()
                ->booleanNode('enabled')
                    ->defaultTrue()
        ;

        return $this;
    }

    /**
     * Disables the deep merging of the node.
     *
     * @return $this
     */
    public function performNoDeepMerging()
    {
        $this->performDeepMerging = false;

        return $this;
    }

    /**
     * Allows extra config keys to be specified under an array without
     * throwing an exception.
     *
     * Those config values are simply ignored and removed from the
     * resulting array. This should be used only in special cases where
     * you want to send an entire configuration array through a special
     * tree that processes only part of the array.
     *
     * @param bool $remove Whether to remove the extra keys
     *
     * @return $this
     */
    public function ignoreExtraKeys($remove = true)
    {
        $this->ignoreExtraKeys = true;
        $this->removeExtraKeys = $remove;

        return $this;
    }

    /**
     * Sets key normalization.
     *
     * @param bool $bool Whether to enable key normalization
     *
     * @return $this
     */
    public function normalizeKeys($bool)
    {
        $this->normalizeKeys = (bool) $bool;

        return $this;
    }

    /**
     * Appends a node definition.
     *
     *     $node = new ArrayNodeDefinition()
     *         ->children()
     *             ->scalarNode('foo')->end()
     *             ->scalarNode('baz')->end()
     *         ->end()
     *         ->append($this->getBarNodeDefinition())
     *     ;
     *
     * @return $this
     */
    public function append(NodeDefinition $node)
    {
        $this->children[$node->name] = $node->setParent($this);

        return $this;
    }

    /**
     * Returns a node builder to be used to add children and prototype.
     *
     * @return NodeBuilder The node builder
     */
    protected function getNodeBuilder()
    {
        if (null === $this->nodeBuilder) {
            $this->nodeBuilder = new NodeBuilder();
        }

        return $this->nodeBuilder->setParent($this);
    }

    /**
     * {@inheritdoc}
     */
    protected function createNode()
    {
        if (null === $this->prototype) {
            $node = new ArrayNode($this->name, $this->parent);

            $this->validateConcreteNode($node);

            $node->setAddIfNotSet($this->addDefaults);

            foreach ($this->children as $child) {
                $child->parent = $node;
                $node->addChild($child->getNode());
            }
        } else {
            $node = new PrototypedArrayNode($this->name, $this->parent);

            $this->validatePrototypeNode($node);

            if (null !== $this->key) {
                $node->setKeyAttribute($this->key, $this->removeKeyItem);
            }

            if (false === $this->allowEmptyValue) {
                @trigger_error(sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), E_USER_DEPRECATED);
            }

            if (true === $this->atLeastOne) {
                $node->setMinNumberOfElements(1);
            }

            if ($this->default) {
                $node->setDefaultValue($this->defaultValue);
            }

            if (false !== $this->addDefaultChildren) {
                $node->setAddChildrenIfNoneSet($this->addDefaultChildren);
                if ($this->prototype instanceof static && null === $this->prototype->prototype) {
                    $this->prototype->addDefaultsIfNotSet();
                }
            }

            $this->prototype->parent = $node;
            $node->setPrototype($this->prototype->getNode());
        }

        $node->setAllowNewKeys($this->allowNewKeys);
        $node->addEquivalentValue(null, $this->nullEquivalent);
        $node->addEquivalentValue(true, $this->trueEquivalent);
        $node->addEquivalentValue(false, $this->falseEquivalent);
        $node->setPerformDeepMerging($this->performDeepMerging);
        $node->setRequired($this->required);
        $node->setDeprecated($this->deprecationMessage);
        $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys);
        $node->setNormalizeKeys($this->normalizeKeys);

        if (null !== $this->normalization) {
            $node->setNormalizationClosures($this->normalization->before);
            $node->setXmlRemappings($this->normalization->remappings);
        }

        if (null !== $this->merge) {
            $node->setAllowOverwrite($this->merge->allowOverwrite);
            $node->setAllowFalse($this->merge->allowFalse);
        }

        if (null !== $this->validation) {
            $node->setFinalValidationClosures($this->validation->rules);
        }

        return $node;
    }

    /**
     * Validate the configuration of a concrete node.
     *
     * @throws InvalidDefinitionException
     */
    protected function validateConcreteNode(ArrayNode $node)
    {
        $path = $node->getPath();

        if (null !== $this->key) {
            throw new InvalidDefinitionException(
                sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path)
            );
        }

        if (false === $this->allowEmptyValue) {
            @trigger_error(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), E_USER_DEPRECATED);
        }

        if (true === $this->atLeastOne) {
            throw new InvalidDefinitionException(
                sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path)
            );
        }

        if ($this->default) {
            throw new InvalidDefinitionException(
                sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path)
            );
        }

        if (false !== $this->addDefaultChildren) {
            throw new InvalidDefinitionException(
                sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path)
            );
        }
    }

    /**
     * Validate the configuration of a prototype node.
     *
     * @throws InvalidDefinitionException
     */
    protected function validatePrototypeNode(PrototypedArrayNode $node)
    {
        $path = $node->getPath();

        if ($this->addDefaults) {
            throw new InvalidDefinitionException(
                sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path)
            );
        }

        if (false !== $this->addDefaultChildren) {
            if ($this->default) {
                throw new InvalidDefinitionException(
                    sprintf('A default value and default children might not be used together at path "%s"', $path)
                );
            }

            if (null !== $this->key && (null === $this->addDefaultChildren || is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) {
                throw new InvalidDefinitionException(
                    sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)
                );
            }

            if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) {
                throw new InvalidDefinitionException(
                    sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path)
                );
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

/**
 * This class builds validation conditions.
 *
 * @author Christophe Coevoet <stof@notk.org>
 */
class ValidationBuilder
{
    protected $node;
    public $rules = array();

    public function __construct(NodeDefinition $node)
    {
        $this->node = $node;
    }

    /**
     * Registers a closure to run as normalization or an expression builder to build it if null is provided.
     *
     * @return ExprBuilder|$this
     */
    public function rule(\Closure $closure = null)
    {
        if (null !== $closure) {
            $this->rules[] = $closure;

            return $this;
        }

        return $this->rules[] = new ExprBuilder($this->node);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\NodeInterface;

/**
 * This is the entry class for building a config tree.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class TreeBuilder implements NodeParentInterface
{
    protected $tree;
    protected $root;

    /**
     * @deprecated since 3.4. To be removed in 4.0
     */
    protected $builder;

    /**
     * Creates the root node.
     *
     * @param string      $name    The name of the root node
     * @param string      $type    The type of the root node
     * @param NodeBuilder $builder A custom node builder instance
     *
     * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array')
     *
     * @throws \RuntimeException When the node type is not supported
     */
    public function root($name, $type = 'array', NodeBuilder $builder = null)
    {
        $builder = $builder ?: new NodeBuilder();

        return $this->root = $builder->node($name, $type)->setParent($this);
    }

    /**
     * Builds the tree.
     *
     * @return NodeInterface
     *
     * @throws \RuntimeException
     */
    public function buildTree()
    {
        if (null === $this->root) {
            throw new \RuntimeException('The configuration tree has no root node.');
        }
        if (null !== $this->tree) {
            return $this->tree;
        }

        return $this->tree = $this->root->getNode(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\Config\Definition\Builder;

use Symfony\Component\Config\Definition\IntegerNode;

/**
 * This class provides a fluent interface for defining an integer node.
 *
 * @author Jeanmonod David <david.jeanmonod@gmail.com>
 */
class IntegerNodeDefinition extends NumericNodeDefinition
{
    /**
     * Instantiates a Node.
     *
     * @return IntegerNode The node
     */
    protected function instantiateNode()
    {
        return new IntegerNode($this->name, $this->parent, $this->min, $this->max);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

/**
 * An interface that must be implemented by all node parents.
 *
 * @author Victor Berchet <victor@suumit.com>
 */
interface NodeParentInterface
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

/**
 * This class builds normalization conditions.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class NormalizationBuilder
{
    protected $node;
    public $before = array();
    public $remappings = array();

    public function __construct(NodeDefinition $node)
    {
        $this->node = $node;
    }

    /**
     * Registers a key to remap to its plural form.
     *
     * @param string $key    The key to remap
     * @param string $plural The plural of the key in case of irregular plural
     *
     * @return $this
     */
    public function remap($key, $plural = null)
    {
        $this->remappings[] = array($key, null === $plural ? $key.'s' : $plural);

        return $this;
    }

    /**
     * Registers a closure to run before the normalization or an expression builder to build it if null is provided.
     *
     * @return ExprBuilder|$this
     */
    public function before(\Closure $closure = null)
    {
        if (null !== $closure) {
            $this->before[] = $closure;

            return $this;
        }

        return $this->before[] = new ExprBuilder($this->node);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\BooleanNode;
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;

/**
 * This class provides a fluent interface for defining a node.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class BooleanNodeDefinition extends ScalarNodeDefinition
{
    /**
     * {@inheritdoc}
     */
    public function __construct($name, NodeParentInterface $parent = null)
    {
        parent::__construct($name, $parent);

        $this->nullEquivalent = true;
    }

    /**
     * Instantiate a Node.
     *
     * @return BooleanNode The node
     */
    protected function instantiateNode()
    {
        return new BooleanNode($this->name, $this->parent);
    }

    /**
     * {@inheritdoc}
     *
     * @throws InvalidDefinitionException
     */
    public function cannotBeEmpty()
    {
        throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.');
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

/**
 * An interface that must be implemented by nodes which can have children.
 *
 * @author Victor Berchet <victor@suumit.com>
 */
interface ParentNodeDefinitionInterface
{
    public function children();

    public function append(NodeDefinition $node);

    public function setBuilder(NodeBuilder $builder);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\ScalarNode;

/**
 * This class provides a fluent interface for defining a node.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class ScalarNodeDefinition extends VariableNodeDefinition
{
    /**
     * Instantiate a Node.
     *
     * @return ScalarNode The node
     */
    protected function instantiateNode()
    {
        return new ScalarNode($this->name, $this->parent);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\FloatNode;

/**
 * This class provides a fluent interface for defining a float node.
 *
 * @author Jeanmonod David <david.jeanmonod@gmail.com>
 */
class FloatNodeDefinition extends NumericNodeDefinition
{
    /**
     * Instantiates a Node.
     *
     * @return FloatNode The node
     */
    protected function instantiateNode()
    {
        return new FloatNode($this->name, $this->parent, $this->min, $this->max);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Builder;

use Symfony\Component\Config\Definition\NodeInterface;
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;

/**
 * This class provides a fluent interface for defining a node.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
abstract class NodeDefinition implements NodeParentInterface
{
    protected $name;
    protected $normalization;
    protected $validation;
    protected $defaultValue;
    protected $default = false;
    protected $required = false;
    protected $deprecationMessage = null;
    protected $merge;
    protected $allowEmptyValue = true;
    protected $nullEquivalent;
    protected $trueEquivalent = true;
    protected $falseEquivalent = false;
    protected $parent;
    protected $attributes = array();

    /**
     * @param string                   $name   The name of the node
     * @param NodeParentInterface|null $parent The parent
     */
    public function __construct($name, NodeParentInterface $parent = null)
    {
        $this->parent = $parent;
        $this->name = $name;
    }

    /**
     * Sets the parent node.
     *
     * @return $this
     */
    public function setParent(NodeParentInterface $parent)
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * Sets info message.
     *
     * @param string $info The info text
     *
     * @return $this
     */
    public function info($info)
    {
        return $this->attribute('info', $info);
    }

    /**
     * Sets example configuration.
     *
     * @param string|array $example
     *
     * @return $this
     */
    public function example($example)
    {
        return $this->attribute('example', $example);
    }

    /**
     * Sets an attribute on the node.
     *
     * @param string $key
     * @param mixed  $value
     *
     * @return $this
     */
    public function attribute($key, $value)
    {
        $this->attributes[$key] = $value;

        return $this;
    }

    /**
     * Returns the parent node.
     *
     * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null The builder of the parent node
     */
    public function end()
    {
        return $this->parent;
    }

    /**
     * Creates the node.
     *
     * @param bool $forceRootNode Whether to force this node as the root node
     *
     * @return NodeInterface
     */
    public function getNode($forceRootNode = false)
    {
        if ($forceRootNode) {
            $this->parent = null;
        }

        if (null !== $this->normalization) {
            $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before);
        }

        if (null !== $this->validation) {
            $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules);
        }

        $node = $this->createNode();
        $node->setAttributes($this->attributes);

        return $node;
    }

    /**
     * Sets the default value.
     *
     * @param mixed $value The default value
     *
     * @return $this
     */
    public function defaultValue($value)
    {
        $this->default = true;
        $this->defaultValue = $value;

        return $this;
    }

    /**
     * Sets the node as required.
     *
     * @return $this
     */
    public function isRequired()
    {
        $this->required = true;

        return $this;
    }

    /**
     * Sets the node as deprecated.
     *
     * You can use %node% and %path% placeholders in your message to display,
     * respectively, the node name and its complete path.
     *
     * @param string $message Deprecation message
     *
     * @return $this
     */
    public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.')
    {
        $this->deprecationMessage = $message;

        return $this;
    }

    /**
     * Sets the equivalent value used when the node contains null.
     *
     * @param mixed $value
     *
     * @return $this
     */
    public function treatNullLike($value)
    {
        $this->nullEquivalent = $value;

        return $this;
    }

    /**
     * Sets the equivalent value used when the node contains true.
     *
     * @param mixed $value
     *
     * @return $this
     */
    public function treatTrueLike($value)
    {
        $this->trueEquivalent = $value;

        return $this;
    }

    /**
     * Sets the equivalent value used when the node contains false.
     *
     * @param mixed $value
     *
     * @return $this
     */
    public function treatFalseLike($value)
    {
        $this->falseEquivalent = $value;

        return $this;
    }

    /**
     * Sets null as the default value.
     *
     * @return $this
     */
    public function defaultNull()
    {
        return $this->defaultValue(null);
    }

    /**
     * Sets true as the default value.
     *
     * @return $this
     */
    public function defaultTrue()
    {
        return $this->defaultValue(true);
    }

    /**
     * Sets false as the default value.
     *
     * @return $this
     */
    public function defaultFalse()
    {
        return $this->defaultValue(false);
    }

    /**
     * Sets an expression to run before the normalization.
     *
     * @return ExprBuilder
     */
    public function beforeNormalization()
    {
        return $this->normalization()->before();
    }

    /**
     * Denies the node value being empty.
     *
     * @return $this
     */
    public function cannotBeEmpty()
    {
        $this->allowEmptyValue = false;

        return $this;
    }

    /**
     * Sets an expression to run for the validation.
     *
     * The expression receives the value of the node and must return it. It can
     * modify it.
     * An exception should be thrown when the node is not valid.
     *
     * @return ExprBuilder
     */
    public function validate()
    {
        return $this->validation()->rule();
    }

    /**
     * Sets whether the node can be overwritten.
     *
     * @param bool $deny Whether the overwriting is forbidden or not
     *
     * @return $this
     */
    public function cannotBeOverwritten($deny = true)
    {
        $this->merge()->denyOverwrite($deny);

        return $this;
    }

    /**
     * Gets the builder for validation rules.
     *
     * @return ValidationBuilder
     */
    protected function validation()
    {
        if (null === $this->validation) {
            $this->validation = new ValidationBuilder($this);
        }

        return $this->validation;
    }

    /**
     * Gets the builder for merging rules.
     *
     * @return MergeBuilder
     */
    protected function merge()
    {
        if (null === $this->merge) {
            $this->merge = new MergeBuilder($this);
        }

        return $this->merge;
    }

    /**
     * Gets the builder for normalization rules.
     *
     * @return NormalizationBuilder
     */
    protected function normalization()
    {
        if (null === $this->normalization) {
            $this->normalization = new NormalizationBuilder($this);
        }

        return $this->normalization;
    }

    /**
     * Instantiate and configure the node according to this definition.
     *
     * @return NodeInterface $node The node instance
     *
     * @throws InvalidDefinitionException When the definition is invalid
     */
    abstract protected function createNode();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Exception;

/**
 * This exception is thrown whenever the key of an array is not unique. This can
 * only be the case if the configuration is coming from an XML file.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class DuplicateKeyException extends InvalidConfigurationException
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Exception;

/**
 * Thrown when an error is detected in a node Definition.
 *
 * @author Victor Berchet <victor.berchet@suumit.com>
 */
class InvalidDefinitionException extends Exception
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Exception;

/**
 * This exception is thrown if an invalid type is encountered.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class InvalidTypeException extends InvalidConfigurationException
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Exception;

/**
 * This exception is usually not encountered by the end-user, but only used
 * internally to signal the parent scope to unset a key.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class UnsetKeyException extends Exception
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Exception;

/**
 * A very general exception which can be thrown whenever non of the more specific
 * exceptions is suitable.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class InvalidConfigurationException extends Exception
{
    private $path;
    private $containsHints = false;

    public function setPath($path)
    {
        $this->path = $path;
    }

    public function getPath()
    {
        return $this->path;
    }

    /**
     * Adds extra information that is suffixed to the original exception message.
     *
     * @param string $hint
     */
    public function addHint($hint)
    {
        if (!$this->containsHints) {
            $this->message .= "\nHint: ".$hint;
            $this->containsHints = true;
        } else {
            $this->message .= ', '.$hint;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Exception;

/**
 * This exception is thrown when a configuration path is overwritten from a
 * subsequent configuration file, but the entry node specifically forbids this.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class ForbiddenOverwriteException extends InvalidConfigurationException
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Exception;

/**
 * Base exception for all configuration exceptions.
 *
 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
 */
class Exception 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\Config\Definition;

use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

/**
 * This node represents a value of variable type in the config tree.
 *
 * This node is intended for values of arbitrary type.
 * Any PHP type is accepted as a value.
 *
 * @author Jeremy Mikola <jmikola@gmail.com>
 */
class VariableNode extends BaseNode implements PrototypeNodeInterface
{
    protected $defaultValueSet = false;
    protected $defaultValue;
    protected $allowEmptyValue = true;

    /**
     * {@inheritdoc}
     */
    public function setDefaultValue($value)
    {
        $this->defaultValueSet = true;
        $this->defaultValue = $value;
    }

    /**
     * {@inheritdoc}
     */
    public function hasDefaultValue()
    {
        return $this->defaultValueSet;
    }

    /**
     * {@inheritdoc}
     */
    public function getDefaultValue()
    {
        $v = $this->defaultValue;

        return $v instanceof \Closure ? $v() : $v;
    }

    /**
     * Sets if this node is allowed to have an empty value.
     *
     * @param bool $boolean True if this entity will accept empty values
     */
    public function setAllowEmptyValue($boolean)
    {
        $this->allowEmptyValue = (bool) $boolean;
    }

    /**
     * {@inheritdoc}
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * {@inheritdoc}
     */
    protected function validateType($value)
    {
    }

    /**
     * {@inheritdoc}
     */
    protected function finalizeValue($value)
    {
        if (!$this->allowEmptyValue && $this->isValueEmpty($value)) {
            $ex = new InvalidConfigurationException(sprintf(
                'The path "%s" cannot contain an empty value, but got %s.',
                $this->getPath(),
                json_encode($value)
            ));
            if ($hint = $this->getInfo()) {
                $ex->addHint($hint);
            }
            $ex->setPath($this->getPath());

            throw $ex;
        }

        return $value;
    }

    /**
     * {@inheritdoc}
     */
    protected function normalizeValue($value)
    {
        return $value;
    }

    /**
     * {@inheritdoc}
     */
    protected function mergeValues($leftSide, $rightSide)
    {
        return $rightSide;
    }

    /**
     * Evaluates if the given value is to be treated as empty.
     *
     * By default, PHP's empty() function is used to test for emptiness. This
     * method may be overridden by subtypes to better match their understanding
     * of empty data.
     *
     * @param mixed $value
     *
     * @return bool
     */
    protected function isValueEmpty($value)
    {
        return empty($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\Config\Definition\Dumper;

use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\NodeInterface;
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\EnumNode;
use Symfony\Component\Config\Definition\PrototypedArrayNode;
use Symfony\Component\Config\Definition\ScalarNode;
use Symfony\Component\Yaml\Inline;

/**
 * Dumps a Yaml reference configuration for the given configuration/node instance.
 *
 * @author Kevin Bond <kevinbond@gmail.com>
 */
class YamlReferenceDumper
{
    private $reference;

    public function dump(ConfigurationInterface $configuration)
    {
        return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree());
    }

    public function dumpAtPath(ConfigurationInterface $configuration, $path)
    {
        $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree();

        foreach (explode('.', $path) as $step) {
            if (!$node instanceof ArrayNode) {
                throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path));
            }

            /** @var NodeInterface[] $children */
            $children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren();

            foreach ($children as $child) {
                if ($child->getName() === $step) {
                    $node = $child;

                    continue 2;
                }
            }

            throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path));
        }

        return $this->dumpNode($node);
    }

    public function dumpNode(NodeInterface $node)
    {
        $this->reference = '';
        $this->writeNode($node);
        $ref = $this->reference;
        $this->reference = null;

        return $ref;
    }

    /**
     * @param NodeInterface      $node
     * @param NodeInterface|null $parentNode
     * @param int                $depth
     * @param bool               $prototypedArray
     */
    private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, $depth = 0, $prototypedArray = false)
    {
        $comments = array();
        $default = '';
        $defaultArray = null;
        $children = null;
        $example = $node->getExample();

        // defaults
        if ($node instanceof ArrayNode) {
            $children = $node->getChildren();

            if ($node instanceof PrototypedArrayNode) {
                $children = $this->getPrototypeChildren($node);
            }

            if (!$children) {
                if ($node->hasDefaultValue() && count($defaultArray = $node->getDefaultValue())) {
                    $default = '';
                } elseif (!is_array($example)) {
                    $default = '[]';
                }
            }
        } elseif ($node instanceof EnumNode) {
            $comments[] = 'One of '.implode('; ', array_map('json_encode', $node->getValues()));
            $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~';
        } else {
            $default = '~';

            if ($node->hasDefaultValue()) {
                $default = $node->getDefaultValue();

                if (is_array($default)) {
                    if (count($defaultArray = $node->getDefaultValue())) {
                        $default = '';
                    } elseif (!is_array($example)) {
                        $default = '[]';
                    }
                } else {
                    $default = Inline::dump($default);
                }
            }
        }

        // required?
        if ($node->isRequired()) {
            $comments[] = 'Required';
        }

        // deprecated?
        if ($node->isDeprecated()) {
            $comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath()));
        }

        // example
        if ($example && !is_array($example)) {
            $comments[] = 'Example: '.$example;
        }

        $default = '' != (string) $default ? ' '.$default : '';
        $comments = count($comments) ? '# '.implode(', ', $comments) : '';

        $key = $prototypedArray ? '-' : $node->getName().':';
        $text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' ');

        if ($info = $node->getInfo()) {
            $this->writeLine('');
            // indenting multi-line info
            $info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info);
            $this->writeLine('# '.$info, $depth * 4);
        }

        $this->writeLine($text, $depth * 4);

        // output defaults
        if ($defaultArray) {
            $this->writeLine('');

            $message = count($defaultArray) > 1 ? 'Defaults' : 'Default';

            $this->writeLine('# '.$message.':', $depth * 4 + 4);

            $this->writeArray($defaultArray, $depth + 1);
        }

        if (is_array($example)) {
            $this->writeLine('');

            $message = count($example) > 1 ? 'Examples' : 'Example';

            $this->writeLine('# '.$message.':', $depth * 4 + 4);

            $this->writeArray($example, $depth + 1);
        }

        if ($children) {
            foreach ($children as $childNode) {
                $this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute());
            }
        }
    }

    /**
     * Outputs a single config reference line.
     *
     * @param string $text
     * @param int    $indent
     */
    private function writeLine($text, $indent = 0)
    {
        $indent = strlen($text) + $indent;
        $format = '%'.$indent.'s';

        $this->reference .= sprintf($format, $text)."\n";
    }

    private function writeArray(array $array, $depth)
    {
        $isIndexed = array_values($array) === $array;

        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $val = '';
            } else {
                $val = $value;
            }

            if ($isIndexed) {
                $this->writeLine('- '.$val, $depth * 4);
            } else {
                $this->writeLine(sprintf('%-20s %s', $key.':', $val), $depth * 4);
            }

            if (is_array($value)) {
                $this->writeArray($value, $depth + 1);
            }
        }
    }

    /**
     * @param PrototypedArrayNode $node
     *
     * @return array
     */
    private function getPrototypeChildren(PrototypedArrayNode $node)
    {
        $prototype = $node->getPrototype();
        $key = $node->getKeyAttribute();

        // Do not expand prototype if it isn't an array node nor uses attribute as key
        if (!$key && !$prototype instanceof ArrayNode) {
            return $node->getChildren();
        }

        if ($prototype instanceof ArrayNode) {
            $keyNode = new ArrayNode($key, $node);
            $children = $prototype->getChildren();

            if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) {
                $children = $this->getPrototypeChildren($prototype);
            }

            // add children
            foreach ($children as $childNode) {
                $keyNode->addChild($childNode);
            }
        } else {
            $keyNode = new ScalarNode($key, $node);
        }

        $info = 'Prototype';
        if (null !== $prototype->getInfo()) {
            $info .= ': '.$prototype->getInfo();
        }
        $keyNode->setInfo($info);

        return array($key => $keyNode);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Definition\Dumper;

use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\NodeInterface;
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\EnumNode;
use Symfony\Component\Config\Definition\PrototypedArrayNode;

/**
 * Dumps a XML reference configuration for the given configuration/node instance.
 *
 * @author Wouter J <waldio.webdesign@gmail.com>
 */
class XmlReferenceDumper
{
    private $reference;

    public function dump(ConfigurationInterface $configuration, $namespace = null)
    {
        return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace);
    }

    public function dumpNode(NodeInterface $node, $namespace = null)
    {
        $this->reference = '';
        $this->writeNode($node, 0, true, $namespace);
        $ref = $this->reference;
        $this->reference = null;

        return $ref;
    }

    /**
     * @param NodeInterface $node
     * @param int           $depth
     * @param bool          $root      If the node is the root node
     * @param string        $namespace The namespace of the node
     */
    private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null)
    {
        $rootName = ($root ? 'config' : $node->getName());
        $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null));

        // xml remapping
        if ($node->getParent()) {
            $remapping = array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use ($rootName) {
                return $rootName === $mapping[1];
            });

            if (count($remapping)) {
                list($singular) = current($remapping);
                $rootName = $singular;
            }
        }
        $rootName = str_replace('_', '-', $rootName);

        $rootAttributes = array();
        $rootAttributeComments = array();
        $rootChildren = array();
        $rootComments = array();

        if ($node instanceof ArrayNode) {
            $children = $node->getChildren();

            // comments about the root node
            if ($rootInfo = $node->getInfo()) {
                $rootComments[] = $rootInfo;
            }

            if ($rootNamespace) {
                $rootComments[] = 'Namespace: '.$rootNamespace;
            }

            // render prototyped nodes
            if ($node instanceof PrototypedArrayNode) {
                $prototype = $node->getPrototype();

                $info = 'prototype';
                if (null !== $prototype->getInfo()) {
                    $info .= ': '.$prototype->getInfo();
                }
                array_unshift($rootComments, $info);

                if ($key = $node->getKeyAttribute()) {
                    $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key;
                }

                if ($prototype instanceof PrototypedArrayNode) {
                    $prototype->setName($key);
                    $children = array($key => $prototype);
                } elseif ($prototype instanceof ArrayNode) {
                    $children = $prototype->getChildren();
                } else {
                    if ($prototype->hasDefaultValue()) {
                        $prototypeValue = $prototype->getDefaultValue();
                    } else {
                        switch (get_class($prototype)) {
                            case 'Symfony\Component\Config\Definition\ScalarNode':
                                $prototypeValue = 'scalar value';
                                break;

                            case 'Symfony\Component\Config\Definition\FloatNode':
                            case 'Symfony\Component\Config\Definition\IntegerNode':
                                $prototypeValue = 'numeric value';
                                break;

                            case 'Symfony\Component\Config\Definition\BooleanNode':
                                $prototypeValue = 'true|false';
                                break;

                            case 'Symfony\Component\Config\Definition\EnumNode':
                                $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues()));
                                break;

                            default:
                                $prototypeValue = 'value';
                        }
                    }
                }
            }

            // get attributes and elements
            foreach ($children as $child) {
                if (!$child instanceof ArrayNode) {
                    // get attributes

                    // metadata
                    $name = str_replace('_', '-', $child->getName());
                    $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world

                    // comments
                    $comments = array();
                    if ($info = $child->getInfo()) {
                        $comments[] = $info;
                    }

                    if ($example = $child->getExample()) {
                        $comments[] = 'Example: '.$example;
                    }

                    if ($child->isRequired()) {
                        $comments[] = 'Required';
                    }

                    if ($child->isDeprecated()) {
                        $comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath()));
                    }

                    if ($child instanceof EnumNode) {
                        $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues()));
                    }

                    if (count($comments)) {
                        $rootAttributeComments[$name] = implode(";\n", $comments);
                    }

                    // default values
                    if ($child->hasDefaultValue()) {
                        $value = $child->getDefaultValue();
                    }

                    // append attribute
                    $rootAttributes[$name] = $value;
                } else {
                    // get elements
                    $rootChildren[] = $child;
                }
            }
        }

        // render comments

        // root node comment
        if (count($rootComments)) {
            foreach ($rootComments as $comment) {
                $this->writeLine('<!-- '.$comment.' -->', $depth);
            }
        }

        // attribute comments
        if (count($rootAttributeComments)) {
            foreach ($rootAttributeComments as $attrName => $comment) {
                $commentDepth = $depth + 4 + strlen($attrName) + 2;
                $commentLines = explode("\n", $comment);
                $multiline = (count($commentLines) > 1);
                $comment = implode(PHP_EOL.str_repeat(' ', $commentDepth), $commentLines);

                if ($multiline) {
                    $this->writeLine('<!--', $depth);
                    $this->writeLine($attrName.': '.$comment, $depth + 4);
                    $this->writeLine('-->', $depth);
                } else {
                    $this->writeLine('<!-- '.$attrName.': '.$comment.' -->', $depth);
                }
            }
        }

        // render start tag + attributes
        $rootIsVariablePrototype = isset($prototypeValue);
        $rootIsEmptyTag = (0 === count($rootChildren) && !$rootIsVariablePrototype);
        $rootOpenTag = '<'.$rootName;
        if (1 >= ($attributesCount = count($rootAttributes))) {
            if (1 === $attributesCount) {
                $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes)));
            }

            $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>';

            if ($rootIsVariablePrototype) {
                $rootOpenTag .= $prototypeValue.'</'.$rootName.'>';
            }

            $this->writeLine($rootOpenTag, $depth);
        } else {
            $this->writeLine($rootOpenTag, $depth);

            $i = 1;

            foreach ($rootAttributes as $attrName => $attrValue) {
                $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue));

                $this->writeLine($attr, $depth + 4);

                if ($attributesCount === $i++) {
                    $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth);

                    if ($rootIsVariablePrototype) {
                        $rootOpenTag .= $prototypeValue.'</'.$rootName.'>';
                    }
                }
            }
        }

        // render children tags
        foreach ($rootChildren as $child) {
            $this->writeLine('');
            $this->writeNode($child, $depth + 4);
        }

        // render end tag
        if (!$rootIsEmptyTag && !$rootIsVariablePrototype) {
            $this->writeLine('');

            $rootEndTag = '</'.$rootName.'>';
            $this->writeLine($rootEndTag, $depth);
        }
    }

    /**
     * Outputs a single config reference line.
     *
     * @param string $text
     * @param int    $indent
     */
    private function writeLine($text, $indent = 0)
    {
        $indent = strlen($text) + $indent;
        $format = '%'.$indent.'s';

        $this->reference .= sprintf($format, $text).PHP_EOL;
    }

    /**
     * Renders the string conversion of the value.
     *
     * @param mixed $value
     *
     * @return string
     */
    private function writeValue($value)
    {
        if ('%%%%not_defined%%%%' === $value) {
            return '';
        }

        if (is_string($value) || is_numeric($value)) {
            return $value;
        }

        if (false === $value) {
            return 'false';
        }

        if (true === $value) {
            return 'true';
        }

        if (null === $value) {
            return 'null';
        }

        if (empty($value)) {
            return '';
        }

        if (is_array($value)) {
            return implode(',', $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\Config\Resource;

/**
 * Interface for Resources that can check for freshness autonomously,
 * without special support from external services.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 */
interface SelfCheckingResourceInterface extends ResourceInterface
{
    /**
     * Returns true if the resource has not been updated since the given timestamp.
     *
     * @param int $timestamp The last time the resource was loaded
     *
     * @return bool True if the resource has not been updated, false otherwise
     */
    public function isFresh($timestamp);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

/**
 * FileResource represents a resource stored on the filesystem.
 *
 * The resource can be a file or a directory.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FileResource implements SelfCheckingResourceInterface, \Serializable
{
    /**
     * @var string|false
     */
    private $resource;

    /**
     * @param string $resource The file path to the resource
     *
     * @throws \InvalidArgumentException
     */
    public function __construct($resource)
    {
        $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false);

        if (false === $this->resource) {
            throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return $this->resource;
    }

    /**
     * @return string The canonicalized, absolute path to the resource
     */
    public function getResource()
    {
        return $this->resource;
    }

    /**
     * {@inheritdoc}
     */
    public function isFresh($timestamp)
    {
        return file_exists($this->resource) && @filemtime($this->resource) <= $timestamp;
    }

    public function serialize()
    {
        return serialize($this->resource);
    }

    public function unserialize($serialized)
    {
        $this->resource = unserialize($serialized);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

/**
 * @author Nicolas Grekas <p@tchwork.com>
 */
class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable
{
    private $files = array();
    private $className;
    private $classReflector;
    private $excludedVendors = array();
    private $hash;

    public function __construct(\ReflectionClass $classReflector, $excludedVendors = array())
    {
        $this->className = $classReflector->name;
        $this->classReflector = $classReflector;
        $this->excludedVendors = $excludedVendors;
    }

    public function isFresh($timestamp)
    {
        if (null === $this->hash) {
            $this->hash = $this->computeHash();
            $this->loadFiles($this->classReflector);
        }

        foreach ($this->files as $file => $v) {
            if (!file_exists($file)) {
                return false;
            }

            if (@filemtime($file) > $timestamp) {
                return $this->hash === $this->computeHash();
            }
        }

        return true;
    }

    public function __toString()
    {
        return 'reflection.'.$this->className;
    }

    public function serialize()
    {
        if (null === $this->hash) {
            $this->hash = $this->computeHash();
            $this->loadFiles($this->classReflector);
        }

        return serialize(array($this->files, $this->className, $this->hash));
    }

    public function unserialize($serialized)
    {
        list($this->files, $this->className, $this->hash) = unserialize($serialized);
    }

    private function loadFiles(\ReflectionClass $class)
    {
        foreach ($class->getInterfaces() as $v) {
            $this->loadFiles($v);
        }
        do {
            $file = $class->getFileName();
            if (false !== $file && file_exists($file)) {
                foreach ($this->excludedVendors as $vendor) {
                    if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, strlen($vendor), 1), '/'.DIRECTORY_SEPARATOR)) {
                        $file = false;
                        break;
                    }
                }
                if ($file) {
                    $this->files[$file] = null;
                }
            }
            foreach ($class->getTraits() as $v) {
                $this->loadFiles($v);
            }
        } while ($class = $class->getParentClass());
    }

    private function computeHash()
    {
        if (null === $this->classReflector) {
            try {
                $this->classReflector = new \ReflectionClass($this->className);
            } catch (\ReflectionException $e) {
                // the class does not exist anymore
                return false;
            }
        }
        $hash = hash_init('md5');

        foreach ($this->generateSignature($this->classReflector) as $info) {
            hash_update($hash, $info);
        }

        return hash_final($hash);
    }

    private function generateSignature(\ReflectionClass $class)
    {
        yield $class->getDocComment().$class->getModifiers();

        if ($class->isTrait()) {
            yield print_r(class_uses($class->name), true);
        } else {
            yield print_r(class_parents($class->name), true);
            yield print_r(class_implements($class->name), true);
            yield print_r($class->getConstants(), true);
        }

        if (!$class->isInterface()) {
            $defaults = $class->getDefaultProperties();

            foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) {
                yield $p->getDocComment().$p;
                yield print_r($defaults[$p->name], true);
            }
        }

        if (defined('HHVM_VERSION')) {
            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
                // workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762
                yield preg_replace('/^  @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name));
            }
        } else {
            foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
                yield preg_replace('/^  @@.*/m', '', $m);

                $defaults = array();
                foreach ($m->getParameters() as $p) {
                    $defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null;
                }
                yield print_r($defaults, true);
            }
        }
    }
}

/**
 * @internal
 */
class ReflectionMethodHhvmWrapper extends \ReflectionMethod
{
    public function getParameters()
    {
        $params = array();

        foreach (parent::getParameters() as $i => $p) {
            $params[] = new ReflectionParameterHhvmWrapper(array($this->class, $this->name), $i);
        }

        return $params;
    }
}

/**
 * @internal
 */
class ReflectionParameterHhvmWrapper extends \ReflectionParameter
{
    public function getDefaultValue()
    {
        return array($this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : 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\Config\Resource;

use Symfony\Component\Config\ResourceCheckerInterface;

/**
 * Resource checker for instances of SelfCheckingResourceInterface.
 *
 * As these resources perform the actual check themselves, we can provide
 * this class as a standard way of validating them.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 */
class SelfCheckingResourceChecker implements ResourceCheckerInterface
{
    public function supports(ResourceInterface $metadata)
    {
        return $metadata instanceof SelfCheckingResourceInterface;
    }

    public function isFresh(ResourceInterface $resource, $timestamp)
    {
        /* @var SelfCheckingResourceInterface $resource */
        return $resource->isFresh($timestamp);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

/**
 * ComposerResource tracks the PHP version and Composer dependencies.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 */
class ComposerResource implements SelfCheckingResourceInterface, \Serializable
{
    private $vendors;

    private static $runtimeVendors;

    public function __construct()
    {
        self::refresh();
        $this->vendors = self::$runtimeVendors;
    }

    public function getVendors()
    {
        return array_keys($this->vendors);
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return __CLASS__;
    }

    /**
     * {@inheritdoc}
     */
    public function isFresh($timestamp)
    {
        self::refresh();

        return self::$runtimeVendors === $this->vendors;
    }

    public function serialize()
    {
        return serialize($this->vendors);
    }

    public function unserialize($serialized)
    {
        $this->vendors = unserialize($serialized);
    }

    private static function refresh()
    {
        self::$runtimeVendors = array();

        foreach (get_declared_classes() as $class) {
            if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
                $r = new \ReflectionClass($class);
                $v = dirname(dirname($r->getFileName()));
                if (file_exists($v.'/composer/installed.json')) {
                    self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json');
                }
            }
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

/**
 * FileExistenceResource represents a resource stored on the filesystem.
 * Freshness is only evaluated against resource creation or deletion.
 *
 * The resource can be a file or a directory.
 *
 * @author Charles-Henri Bruyand <charleshenri.bruyand@gmail.com>
 */
class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable
{
    private $resource;

    private $exists;

    /**
     * @param string $resource The file path to the resource
     */
    public function __construct($resource)
    {
        $this->resource = (string) $resource;
        $this->exists = file_exists($resource);
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return $this->resource;
    }

    /**
     * @return string The file path to the resource
     */
    public function getResource()
    {
        return $this->resource;
    }

    /**
     * {@inheritdoc}
     */
    public function isFresh($timestamp)
    {
        return file_exists($this->resource) === $this->exists;
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        return serialize(array($this->resource, $this->exists));
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($serialized)
    {
        list($this->resource, $this->exists) = unserialize($serialized);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

/**
 * ResourceInterface is the interface that must be implemented by all Resource classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface ResourceInterface
{
    /**
     * Returns a string representation of the Resource.
     *
     * This method is necessary to allow for resource de-duplication, for example by means
     * of array_unique(). The string returned need not have a particular meaning, but has
     * to be identical for different ResourceInterface instances referring to the same
     * resource; and it should be unlikely to collide with that of other, unrelated
     * resource instances.
     *
     * @return string A string representation unique to the underlying Resource
     */
    public function __toString();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

/**
 * ClassExistenceResource represents a class existence.
 * Freshness is only evaluated against resource existence.
 *
 * The resource must be a fully-qualified class name.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable
{
    private $resource;
    private $exists;

    private static $autoloadLevel = 0;
    private static $autoloadedClass;
    private static $existsCache = array();

    /**
     * @param string    $resource The fully-qualified class name
     * @param bool|null $exists   Boolean when the existency check has already been done
     */
    public function __construct($resource, $exists = null)
    {
        $this->resource = $resource;
        if (null !== $exists) {
            $this->exists = (bool) $exists;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return $this->resource;
    }

    /**
     * @return string The file path to the resource
     */
    public function getResource()
    {
        return $this->resource;
    }

    /**
     * {@inheritdoc}
     *
     * @throws \ReflectionException when a parent class/interface/trait is not found
     */
    public function isFresh($timestamp)
    {
        $loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);

        if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) {
            $exists = $exists || $loaded;
        } elseif (!$exists = $loaded) {
            if (!self::$autoloadLevel++) {
                spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
            }
            $autoloadedClass = self::$autoloadedClass;
            self::$autoloadedClass = $this->resource;

            try {
                $exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
            } catch (\ReflectionException $e) {
                if (0 >= $timestamp) {
                    unset(self::$existsCache[1][$this->resource]);
                    throw $e;
                }
            } finally {
                self::$autoloadedClass = $autoloadedClass;
                if (!--self::$autoloadLevel) {
                    spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass');
                }
            }
        }

        if (null === $this->exists) {
            $this->exists = $exists;
        }

        return $this->exists xor !$exists;
    }

    /**
     * {@inheritdoc}
     */
    public function serialize()
    {
        if (null === $this->exists) {
            $this->isFresh(0);
        }

        return serialize(array($this->resource, $this->exists));
    }

    /**
     * {@inheritdoc}
     */
    public function unserialize($serialized)
    {
        list($this->resource, $this->exists) = unserialize($serialized);
    }

    /**
     * @throws \ReflectionException When $class is not found and is required
     */
    private static function throwOnRequiredClass($class)
    {
        if (self::$autoloadedClass === $class) {
            return;
        }
        $e = new \ReflectionException("Class $class not found");
        $trace = $e->getTrace();
        $autoloadFrame = array(
            'function' => 'spl_autoload_call',
            'args' => array($class),
        );
        $i = 1 + array_search($autoloadFrame, $trace, true);

        if (isset($trace[$i]['function']) && !isset($trace[$i]['class'])) {
            switch ($trace[$i]['function']) {
                case 'get_class_methods':
                case 'get_class_vars':
                case 'get_parent_class':
                case 'is_a':
                case 'is_subclass_of':
                case 'class_exists':
                case 'class_implements':
                case 'class_parents':
                case 'trait_exists':
                case 'defined':
                case 'interface_exists':
                case 'method_exists':
                case 'property_exists':
                case 'is_callable':
                    return;
            }

            $props = array(
                'file' => $trace[$i]['file'],
                'line' => $trace[$i]['line'],
                'trace' => array_slice($trace, 1 + $i),
            );

            foreach ($props as $p => $v) {
                $r = new \ReflectionProperty('Exception', $p);
                $r->setAccessible(true);
                $r->setValue($e, $v);
            }
        }

        throw $e;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

/**
 * DirectoryResource represents a resources stored in a subdirectory tree.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
{
    private $resource;
    private $pattern;

    /**
     * @param string      $resource The file path to the resource
     * @param string|null $pattern  A pattern to restrict monitored files
     *
     * @throws \InvalidArgumentException
     */
    public function __construct($resource, $pattern = null)
    {
        $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false);
        $this->pattern = $pattern;

        if (false === $this->resource || !is_dir($this->resource)) {
            throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return md5(serialize(array($this->resource, $this->pattern)));
    }

    /**
     * @return string The file path to the resource
     */
    public function getResource()
    {
        return $this->resource;
    }

    /**
     * Returns the pattern to restrict monitored files.
     *
     * @return string|null
     */
    public function getPattern()
    {
        return $this->pattern;
    }

    /**
     * {@inheritdoc}
     */
    public function isFresh($timestamp)
    {
        if (!is_dir($this->resource)) {
            return false;
        }

        if ($timestamp < filemtime($this->resource)) {
            return false;
        }

        foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) {
            // if regex filtering is enabled only check matching files
            if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) {
                continue;
            }

            // always monitor directories for changes, except the .. entries
            // (otherwise deleted files wouldn't get detected)
            if ($file->isDir() && '/..' === substr($file, -3)) {
                continue;
            }

            // for broken links
            try {
                $fileMTime = $file->getMTime();
            } catch (\RuntimeException $e) {
                continue;
            }

            // early return if a file's mtime exceeds the passed timestamp
            if ($timestamp < $fileMTime) {
                return false;
            }
        }

        return true;
    }

    public function serialize()
    {
        return serialize(array($this->resource, $this->pattern));
    }

    public function unserialize($serialized)
    {
        list($this->resource, $this->pattern) = unserialize($serialized);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Resource;

use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\Glob;

/**
 * GlobResource represents a set of resources stored on the filesystem.
 *
 * Only existence/removal is tracked (not mtimes.)
 *
 * @author Nicolas Grekas <p@tchwork.com>
 */
class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable
{
    private $prefix;
    private $pattern;
    private $recursive;
    private $hash;

    /**
     * @param string $prefix    A directory prefix
     * @param string $pattern   A glob pattern
     * @param bool   $recursive Whether directories should be scanned recursively or not
     *
     * @throws \InvalidArgumentException
     */
    public function __construct($prefix, $pattern, $recursive)
    {
        $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false);
        $this->pattern = $pattern;
        $this->recursive = $recursive;

        if (false === $this->prefix) {
            throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix));
        }
    }

    public function getPrefix()
    {
        return $this->prefix;
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive;
    }

    /**
     * {@inheritdoc}
     */
    public function isFresh($timestamp)
    {
        $hash = $this->computeHash();

        if (null === $this->hash) {
            $this->hash = $hash;
        }

        return $this->hash === $hash;
    }

    public function serialize()
    {
        if (null === $this->hash) {
            $this->hash = $this->computeHash();
        }

        return serialize(array($this->prefix, $this->pattern, $this->recursive, $this->hash));
    }

    public function unserialize($serialized)
    {
        list($this->prefix, $this->pattern, $this->recursive, $this->hash) = unserialize($serialized);
    }

    public function getIterator()
    {
        if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
            return;
        }

        if (false === strpos($this->pattern, '/**/') && (defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) {
            foreach (glob($this->prefix.$this->pattern, defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) {
                if ($this->recursive && is_dir($path)) {
                    $files = iterator_to_array(new \RecursiveIteratorIterator(
                        new \RecursiveCallbackFilterIterator(
                            new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
                            function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; }
                        ),
                        \RecursiveIteratorIterator::LEAVES_ONLY
                    ));
                    uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
                        return (string) $a > (string) $b ? 1 : -1;
                    });

                    foreach ($files as $path => $info) {
                        if ($info->isFile()) {
                            yield $path => $info;
                        }
                    }
                } elseif (is_file($path)) {
                    yield $path => new \SplFileInfo($path);
                }
            }

            return;
        }

        if (!class_exists(Finder::class)) {
            throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern));
        }

        $finder = new Finder();
        $regex = Glob::toRegex($this->pattern);
        if ($this->recursive) {
            $regex = substr_replace($regex, '(/|$)', -2, 1);
        }

        $prefixLen = strlen($this->prefix);
        foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) {
            if (preg_match($regex, substr('\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path, $prefixLen)) && $info->isFile()) {
                yield $path => $info;
            }
        }
    }

    private function computeHash()
    {
        $hash = hash_init('md5');

        foreach ($this->getIterator() as $path => $info) {
            hash_update($hash, $path."\n");
        }

        return hash_final($hash);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\DependencyInjection;

@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED);

use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 * @author Benjamin Klotz <bk@webfactory.de>
 *
 * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead.
 */
class ConfigCachePass implements CompilerPassInterface
{
    use PriorityTaggedServiceTrait;

    private $factoryServiceId;
    private $resourceCheckerTag;

    public function __construct($factoryServiceId = 'config_cache_factory', $resourceCheckerTag = 'config_cache.resource_checker')
    {
        $this->factoryServiceId = $factoryServiceId;
        $this->resourceCheckerTag = $resourceCheckerTag;
    }

    public function process(ContainerBuilder $container)
    {
        $resourceCheckers = $this->findAndSortTaggedServices($this->resourceCheckerTag, $container);

        if (empty($resourceCheckers)) {
            return;
        }

        $container->getDefinition($this->factoryServiceId)->replaceArgument(0, new IteratorArgument($resourceCheckers));
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Exception;

/**
 * Exception class for when a resource cannot be loaded or imported.
 *
 * @author Ryan Weaver <ryan@thatsquality.com>
 */
class FileLoaderLoadException extends \Exception
{
    /**
     * @param string     $resource       The resource that could not be imported
     * @param string     $sourceResource The original resource importing the new resource
     * @param int        $code           The error code
     * @param \Exception $previous       A previous exception
     * @param string     $type           The type of resource
     */
    public function __construct($resource, $sourceResource = null, $code = null, $previous = null, $type = null)
    {
        $message = '';
        if ($previous) {
            // Include the previous exception, to help the user see what might be the underlying cause

            // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim...
            if ('.' === substr($previous->getMessage(), -1)) {
                $trimmedMessage = substr($previous->getMessage(), 0, -1);
                $message .= sprintf('%s', $trimmedMessage).' in ';
            } else {
                $message .= sprintf('%s', $previous->getMessage()).' in ';
            }
            $message .= $resource.' ';

            // show tweaked trace to complete the human readable sentence
            if (null === $sourceResource) {
                $message .= sprintf('(which is loaded in resource "%s")', $this->varToString($resource));
            } else {
                $message .= sprintf('(which is being imported from "%s")', $this->varToString($sourceResource));
            }
            $message .= '.';

        // if there's no previous message, present it the default way
        } elseif (null === $sourceResource) {
            $message .= sprintf('Cannot load resource "%s".', $this->varToString($resource));
        } else {
            $message .= sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource));
        }

        // Is the resource located inside a bundle?
        if ('@' === $resource[0]) {
            $parts = explode(DIRECTORY_SEPARATOR, $resource);
            $bundle = substr($parts[0], 1);
            $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle);
            $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource);
        } elseif (null !== $type) {
            // maybe there is no loader for this specific type
            if ('annotation' === $type) {
                $message .= ' Make sure annotations are installed and enabled.';
            } else {
                $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type);
            }
        }

        parent::__construct($message, $code, $previous);
    }

    protected function varToString($var)
    {
        if (is_object($var)) {
            return sprintf('Object(%s)', get_class($var));
        }

        if (is_array($var)) {
            $a = array();
            foreach ($var as $k => $v) {
                $a[] = sprintf('%s => %s', $k, $this->varToString($v));
            }

            return sprintf('Array(%s)', implode(', ', $a));
        }

        if (is_resource($var)) {
            return sprintf('Resource(%s)', get_resource_type($var));
        }

        if (null === $var) {
            return 'null';
        }

        if (false === $var) {
            return 'false';
        }

        if (true === $var) {
            return 'true';
        }

        return (string) $var;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Exception;

/**
 * Exception class for when a circular reference is detected when importing resources.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FileLoaderImportCircularReferenceException extends FileLoaderLoadException
{
    public function __construct(array $resources, $code = null, $previous = null)
    {
        $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]);

        \Exception::__construct($message, $code, $previous);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config\Exception;

/**
 * File locator exception if a file does not exist.
 *
 * @author Leo Feyer <https://github.com/leofeyer>
 */
class FileLocatorFileNotFoundException extends \InvalidArgumentException
{
    private $paths;

    public function __construct($message = '', $code = 0, $previous = null, array $paths = array())
    {
        parent::__construct($message, $code, $previous);

        $this->paths = $paths;
    }

    public function getPaths()
    {
        return $this->paths;
    }
}
{
    "name": "symfony/config",
    "type": "library",
    "description": "Symfony Config Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": "^5.5.9|>=7.0.8",
        "symfony/filesystem": "~2.8|~3.0|~4.0"
    },
    "require-dev": {
        "symfony/finder": "~3.3|~4.0",
        "symfony/yaml": "~3.0|~4.0",
        "symfony/dependency-injection": "~3.3|~4.0"
    },
    "conflict": {
        "symfony/finder": "<3.3",
        "symfony/dependency-injection": "<3.3"
    },
    "suggest": {
        "symfony/yaml": "To use the yaml reference dumper"
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\Config\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "3.4-dev"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config;

/**
 * Basic implementation of ConfigCacheFactoryInterface that
 * creates an instance of the default ConfigCache.
 *
 * This factory and/or cache <em>do not</em> support cache validation
 * by means of ResourceChecker instances (that is, service-based).
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 */
class ConfigCacheFactory implements ConfigCacheFactoryInterface
{
    private $debug;

    /**
     * @param bool $debug The debug flag to pass to ConfigCache
     */
    public function __construct($debug)
    {
        $this->debug = $debug;
    }

    /**
     * {@inheritdoc}
     */
    public function cache($file, $callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback)));
        }

        $cache = new ConfigCache($file, $this->debug);
        if (!$cache->isFresh()) {
            call_user_func($callback, $cache);
        }

        return $cache;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Config;

/**
 * A ConfigCacheFactory implementation that validates the
 * cache with an arbitrary set of ResourceCheckers.
 *
 * @author Matthias Pigulla <mp@webfactory.de>
 */
class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface
{
    private $resourceCheckers = array();

    /**
     * @param iterable|ResourceCheckerInterface[] $resourceCheckers
     */
    public function __construct($resourceCheckers = array())
    {
        $this->resourceCheckers = $resourceCheckers;
    }

    /**
     * {@inheritdoc}
     */
    public function cache($file, $callback)
    {
        if (!is_callable($callback)) {
            throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback)));
        }

        $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers);
        if (!$cache->isFresh()) {
            call_user_func($callback, $cache);
        }

        return $cache;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Filesystem;

use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;

/**
 * Provides basic utility to manipulate the file system.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Filesystem
{
    /**
     * Copies a file.
     *
     * If the target file is older than the origin file, it's always overwritten.
     * If the target file is newer, it is overwritten only when the
     * $overwriteNewerFiles option is set to true.
     *
     * @param string $originFile          The original filename
     * @param string $targetFile          The target filename
     * @param bool   $overwriteNewerFiles If true, target files newer than origin files are overwritten
     *
     * @throws FileNotFoundException When originFile doesn't exist
     * @throws IOException           When copy fails
     */
    public function copy($originFile, $targetFile, $overwriteNewerFiles = false)
    {
        $originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://');
        if ($originIsLocal && !is_file($originFile)) {
            throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile);
        }

        $this->mkdir(dirname($targetFile));

        $doCopy = true;
        if (!$overwriteNewerFiles && null === parse_url($originFile, PHP_URL_HOST) && is_file($targetFile)) {
            $doCopy = filemtime($originFile) > filemtime($targetFile);
        }

        if ($doCopy) {
            // https://bugs.php.net/bug.php?id=64634
            if (false === $source = @fopen($originFile, 'r')) {
                throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading.', $originFile, $targetFile), 0, null, $originFile);
            }

            // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default
            if (false === $target = @fopen($targetFile, 'w', null, stream_context_create(array('ftp' => array('overwrite' => true))))) {
                throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing.', $originFile, $targetFile), 0, null, $originFile);
            }

            $bytesCopied = stream_copy_to_stream($source, $target);
            fclose($source);
            fclose($target);
            unset($source, $target);

            if (!is_file($targetFile)) {
                throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile);
            }

            if ($originIsLocal) {
                // Like `cp`, preserve executable permission bits
                @chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111));

                if ($bytesCopied !== $bytesOrigin = filesize($originFile)) {
                    throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile);
                }
            }
        }
    }

    /**
     * Creates a directory recursively.
     *
     * @param string|iterable $dirs The directory path
     * @param int             $mode The directory mode
     *
     * @throws IOException On any directory creation failure
     */
    public function mkdir($dirs, $mode = 0777)
    {
        foreach ($this->toIterable($dirs) as $dir) {
            if (is_dir($dir)) {
                continue;
            }

            if (true !== @mkdir($dir, $mode, true)) {
                $error = error_get_last();
                if (!is_dir($dir)) {
                    // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
                    if ($error) {
                        throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']), 0, null, $dir);
                    }
                    throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir);
                }
            }
        }
    }

    /**
     * Checks the existence of files or directories.
     *
     * @param string|iterable $files A filename, an array of files, or a \Traversable instance to check
     *
     * @return bool true if the file exists, false otherwise
     */
    public function exists($files)
    {
        $maxPathLength = PHP_MAXPATHLEN - 2;

        foreach ($this->toIterable($files) as $file) {
            if (strlen($file) > $maxPathLength) {
                throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file);
            }

            if (!file_exists($file)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Sets access and modification time of file.
     *
     * @param string|iterable $files A filename, an array of files, or a \Traversable instance to create
     * @param int             $time  The touch time as a Unix timestamp
     * @param int             $atime The access time as a Unix timestamp
     *
     * @throws IOException When touch fails
     */
    public function touch($files, $time = null, $atime = null)
    {
        foreach ($this->toIterable($files) as $file) {
            $touch = $time ? @touch($file, $time, $atime) : @touch($file);
            if (true !== $touch) {
                throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file);
            }
        }
    }

    /**
     * Removes files or directories.
     *
     * @param string|iterable $files A filename, an array of files, or a \Traversable instance to remove
     *
     * @throws IOException When removal fails
     */
    public function remove($files)
    {
        if ($files instanceof \Traversable) {
            $files = iterator_to_array($files, false);
        } elseif (!is_array($files)) {
            $files = array($files);
        }
        $files = array_reverse($files);
        foreach ($files as $file) {
            if (is_link($file)) {
                // See https://bugs.php.net/52176
                if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) {
                    $error = error_get_last();
                    throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message']));
                }
            } elseif (is_dir($file)) {
                $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));

                if (!@rmdir($file) && file_exists($file)) {
                    $error = error_get_last();
                    throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message']));
                }
            } elseif (!@unlink($file) && file_exists($file)) {
                $error = error_get_last();
                throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message']));
            }
        }
    }

    /**
     * Change mode for an array of files or directories.
     *
     * @param string|iterable $files     A filename, an array of files, or a \Traversable instance to change mode
     * @param int             $mode      The new mode (octal)
     * @param int             $umask     The mode mask (octal)
     * @param bool            $recursive Whether change the mod recursively or not
     *
     * @throws IOException When the change fail
     */
    public function chmod($files, $mode, $umask = 0000, $recursive = false)
    {
        foreach ($this->toIterable($files) as $file) {
            if (true !== @chmod($file, $mode & ~$umask)) {
                throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file);
            }
            if ($recursive && is_dir($file) && !is_link($file)) {
                $this->chmod(new \FilesystemIterator($file), $mode, $umask, true);
            }
        }
    }

    /**
     * Change the owner of an array of files or directories.
     *
     * @param string|iterable $files     A filename, an array of files, or a \Traversable instance to change owner
     * @param string          $user      The new owner user name
     * @param bool            $recursive Whether change the owner recursively or not
     *
     * @throws IOException When the change fail
     */
    public function chown($files, $user, $recursive = false)
    {
        foreach ($this->toIterable($files) as $file) {
            if ($recursive && is_dir($file) && !is_link($file)) {
                $this->chown(new \FilesystemIterator($file), $user, true);
            }
            if (is_link($file) && function_exists('lchown')) {
                if (true !== @lchown($file, $user)) {
                    throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file);
                }
            } else {
                if (true !== @chown($file, $user)) {
                    throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file);
                }
            }
        }
    }

    /**
     * Change the group of an array of files or directories.
     *
     * @param string|iterable $files     A filename, an array of files, or a \Traversable instance to change group
     * @param string          $group     The group name
     * @param bool            $recursive Whether change the group recursively or not
     *
     * @throws IOException When the change fail
     */
    public function chgrp($files, $group, $recursive = false)
    {
        foreach ($this->toIterable($files) as $file) {
            if ($recursive && is_dir($file) && !is_link($file)) {
                $this->chgrp(new \FilesystemIterator($file), $group, true);
            }
            if (is_link($file) && function_exists('lchgrp')) {
                if (true !== @lchgrp($file, $group) || (defined('HHVM_VERSION') && !posix_getgrnam($group))) {
                    throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file);
                }
            } else {
                if (true !== @chgrp($file, $group)) {
                    throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file);
                }
            }
        }
    }

    /**
     * Renames a file or a directory.
     *
     * @param string $origin    The origin filename or directory
     * @param string $target    The new filename or directory
     * @param bool   $overwrite Whether to overwrite the target if it already exists
     *
     * @throws IOException When target file or directory already exists
     * @throws IOException When origin cannot be renamed
     */
    public function rename($origin, $target, $overwrite = false)
    {
        // we check that target does not exist
        if (!$overwrite && $this->isReadable($target)) {
            throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target);
        }

        if (true !== @rename($origin, $target)) {
            if (is_dir($origin)) {
                // See https://bugs.php.net/bug.php?id=54097 & http://php.net/manual/en/function.rename.php#113943
                $this->mirror($origin, $target, null, array('override' => $overwrite, 'delete' => $overwrite));
                $this->remove($origin);

                return;
            }
            throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target);
        }
    }

    /**
     * Tells whether a file exists and is readable.
     *
     * @param string $filename Path to the file
     *
     * @return bool
     *
     * @throws IOException When windows path is longer than 258 characters
     */
    private function isReadable($filename)
    {
        $maxPathLength = PHP_MAXPATHLEN - 2;

        if (strlen($filename) > $maxPathLength) {
            throw new IOException(sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename);
        }

        return is_readable($filename);
    }

    /**
     * Creates a symbolic link or copy a directory.
     *
     * @param string $originDir     The origin directory path
     * @param string $targetDir     The symbolic link name
     * @param bool   $copyOnWindows Whether to copy files if on Windows
     *
     * @throws IOException When symlink fails
     */
    public function symlink($originDir, $targetDir, $copyOnWindows = false)
    {
        if ('\\' === DIRECTORY_SEPARATOR) {
            $originDir = strtr($originDir, '/', '\\');
            $targetDir = strtr($targetDir, '/', '\\');

            if ($copyOnWindows) {
                $this->mirror($originDir, $targetDir);

                return;
            }
        }

        $this->mkdir(dirname($targetDir));

        $ok = false;
        if (is_link($targetDir)) {
            if (readlink($targetDir) != $originDir) {
                $this->remove($targetDir);
            } else {
                $ok = true;
            }
        }

        if (!$ok && true !== @symlink($originDir, $targetDir)) {
            $this->linkException($originDir, $targetDir, 'symbolic');
        }
    }

    /**
     * Creates a hard link, or several hard links to a file.
     *
     * @param string          $originFile  The original file
     * @param string|string[] $targetFiles The target file(s)
     *
     * @throws FileNotFoundException When original file is missing or not a file
     * @throws IOException           When link fails, including if link already exists
     */
    public function hardlink($originFile, $targetFiles)
    {
        if (!$this->exists($originFile)) {
            throw new FileNotFoundException(null, 0, null, $originFile);
        }

        if (!is_file($originFile)) {
            throw new FileNotFoundException(sprintf('Origin file "%s" is not a file', $originFile));
        }

        foreach ($this->toIterable($targetFiles) as $targetFile) {
            if (is_file($targetFile)) {
                if (fileinode($originFile) === fileinode($targetFile)) {
                    continue;
                }
                $this->remove($targetFile);
            }

            if (true !== @link($originFile, $targetFile)) {
                $this->linkException($originFile, $targetFile, 'hard');
            }
        }
    }

    /**
     * @param string $origin
     * @param string $target
     * @param string $linkType Name of the link type, typically 'symbolic' or 'hard'
     */
    private function linkException($origin, $target, $linkType)
    {
        $report = error_get_last();
        if (is_array($report)) {
            if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) {
                throw new IOException(sprintf('Unable to create %s link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target);
            }
        }
        throw new IOException(sprintf('Failed to create %s link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target);
    }

    /**
     * Resolves links in paths.
     *
     * With $canonicalize = false (default)
     *      - if $path does not exist or is not a link, returns null
     *      - if $path is a link, returns the next direct target of the link without considering the existence of the target
     *
     * With $canonicalize = true
     *      - if $path does not exist, returns null
     *      - if $path exists, returns its absolute fully resolved final version
     *
     * @param string $path         A filesystem path
     * @param bool   $canonicalize Whether or not to return a canonicalized path
     *
     * @return string|null
     */
    public function readlink($path, $canonicalize = false)
    {
        if (!$canonicalize && !is_link($path)) {
            return;
        }

        if ($canonicalize) {
            if (!$this->exists($path)) {
                return;
            }

            if ('\\' === DIRECTORY_SEPARATOR) {
                $path = readlink($path);
            }

            return realpath($path);
        }

        if ('\\' === DIRECTORY_SEPARATOR) {
            return realpath($path);
        }

        return readlink($path);
    }

    /**
     * Given an existing path, convert it to a path relative to a given starting path.
     *
     * @param string $endPath   Absolute path of target
     * @param string $startPath Absolute path where traversal begins
     *
     * @return string Path of target relative to starting path
     */
    public function makePathRelative($endPath, $startPath)
    {
        if (!$this->isAbsolutePath($endPath) || !$this->isAbsolutePath($startPath)) {
            @trigger_error(sprintf('Support for passing relative paths to %s() is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
        }

        // Normalize separators on Windows
        if ('\\' === DIRECTORY_SEPARATOR) {
            $endPath = str_replace('\\', '/', $endPath);
            $startPath = str_replace('\\', '/', $startPath);
        }

        $stripDriveLetter = function ($path) {
            if (strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) {
                return substr($path, 2);
            }

            return $path;
        };

        $endPath = $stripDriveLetter($endPath);
        $startPath = $stripDriveLetter($startPath);

        // Split the paths into arrays
        $startPathArr = explode('/', trim($startPath, '/'));
        $endPathArr = explode('/', trim($endPath, '/'));

        $normalizePathArray = function ($pathSegments, $absolute) {
            $result = array();

            foreach ($pathSegments as $segment) {
                if ('..' === $segment && ($absolute || count($result))) {
                    array_pop($result);
                } elseif ('.' !== $segment) {
                    $result[] = $segment;
                }
            }

            return $result;
        };

        $startPathArr = $normalizePathArray($startPathArr, static::isAbsolutePath($startPath));
        $endPathArr = $normalizePathArray($endPathArr, static::isAbsolutePath($endPath));

        // Find for which directory the common path stops
        $index = 0;
        while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
            ++$index;
        }

        // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
        if (1 === count($startPathArr) && '' === $startPathArr[0]) {
            $depth = 0;
        } else {
            $depth = count($startPathArr) - $index;
        }

        // Repeated "../" for each level need to reach the common path
        $traverser = str_repeat('../', $depth);

        $endPathRemainder = implode('/', array_slice($endPathArr, $index));

        // Construct $endPath from traversing to the common path, then to the remaining $endPath
        $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : '');

        return '' === $relativePath ? './' : $relativePath;
    }

    /**
     * Mirrors a directory to another.
     *
     * @param string       $originDir The origin directory
     * @param string       $targetDir The target directory
     * @param \Traversable $iterator  A Traversable instance
     * @param array        $options   An array of boolean options
     *                                Valid options are:
     *                                - $options['override'] Whether to override an existing file on copy or not (see copy())
     *                                - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
     *                                - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
     *
     * @throws IOException When file type is unknown
     */
    public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array())
    {
        $targetDir = rtrim($targetDir, '/\\');
        $originDir = rtrim($originDir, '/\\');
        $originDirLen = strlen($originDir);

        // Iterate in destination folder to remove obsolete entries
        if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
            $deleteIterator = $iterator;
            if (null === $deleteIterator) {
                $flags = \FilesystemIterator::SKIP_DOTS;
                $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST);
            }
            $targetDirLen = strlen($targetDir);
            foreach ($deleteIterator as $file) {
                $origin = $originDir.substr($file->getPathname(), $targetDirLen);
                if (!$this->exists($origin)) {
                    $this->remove($file);
                }
            }
        }

        $copyOnWindows = false;
        if (isset($options['copy_on_windows'])) {
            $copyOnWindows = $options['copy_on_windows'];
        }

        if (null === $iterator) {
            $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS;
            $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
        }

        if ($this->exists($originDir)) {
            $this->mkdir($targetDir);
        }

        foreach ($iterator as $file) {
            $target = $targetDir.substr($file->getPathname(), $originDirLen);

            if ($copyOnWindows) {
                if (is_file($file)) {
                    $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
                } elseif (is_dir($file)) {
                    $this->mkdir($target);
                } else {
                    throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
                }
            } else {
                if (is_link($file)) {
                    $this->symlink($file->getLinkTarget(), $target);
                } elseif (is_dir($file)) {
                    $this->mkdir($target);
                } elseif (is_file($file)) {
                    $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
                } else {
                    throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
                }
            }
        }
    }

    /**
     * Returns whether the file path is an absolute path.
     *
     * @param string $file A file path
     *
     * @return bool
     */
    public function isAbsolutePath($file)
    {
        return strspn($file, '/\\', 0, 1)
            || (strlen($file) > 3 && ctype_alpha($file[0])
                && ':' === $file[1]
                && strspn($file, '/\\', 2, 1)
            )
            || null !== parse_url($file, PHP_URL_SCHEME)
        ;
    }

    /**
     * Creates a temporary file with support for custom stream wrappers.
     *
     * @param string $dir    The directory where the temporary filename will be created
     * @param string $prefix The prefix of the generated temporary filename
     *                       Note: Windows uses only the first three characters of prefix
     *
     * @return string The new temporary filename (with path), or throw an exception on failure
     */
    public function tempnam($dir, $prefix)
    {
        list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir);

        // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem
        if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) {
            $tmpFile = @tempnam($hierarchy, $prefix);

            // If tempnam failed or no scheme return the filename otherwise prepend the scheme
            if (false !== $tmpFile) {
                if (null !== $scheme && 'gs' !== $scheme) {
                    return $scheme.'://'.$tmpFile;
                }

                return $tmpFile;
            }

            throw new IOException('A temporary file could not be created.');
        }

        // Loop until we create a valid temp file or have reached 10 attempts
        for ($i = 0; $i < 10; ++$i) {
            // Create a unique filename
            $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true);

            // Use fopen instead of file_exists as some streams do not support stat
            // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability
            $handle = @fopen($tmpFile, 'x+');

            // If unsuccessful restart the loop
            if (false === $handle) {
                continue;
            }

            // Close the file if it was successfully opened
            @fclose($handle);

            return $tmpFile;
        }

        throw new IOException('A temporary file could not be created.');
    }

    /**
     * Atomically dumps content into a file.
     *
     * @param string $filename The file to be written to
     * @param string $content  The data to write into the file
     *
     * @throws IOException if the file cannot be written to
     */
    public function dumpFile($filename, $content)
    {
        $dir = dirname($filename);

        if (!is_dir($dir)) {
            $this->mkdir($dir);
        }

        if (!is_writable($dir)) {
            throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir);
        }

        // Will create a temp file with 0600 access rights
        // when the filesystem supports chmod.
        $tmpFile = $this->tempnam($dir, basename($filename));

        if (false === @file_put_contents($tmpFile, $content)) {
            throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
        }

        @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask());

        $this->rename($tmpFile, $filename, true);
    }

    /**
     * Appends content to an existing file.
     *
     * @param string $filename The file to which to append content
     * @param string $content  The content to append
     *
     * @throws IOException If the file is not writable
     */
    public function appendToFile($filename, $content)
    {
        $dir = dirname($filename);

        if (!is_dir($dir)) {
            $this->mkdir($dir);
        }

        if (!is_writable($dir)) {
            throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir);
        }

        if (false === @file_put_contents($filename, $content, FILE_APPEND)) {
            throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
        }
    }

    /**
     * @param mixed $files
     *
     * @return array|\Traversable
     */
    private function toIterable($files)
    {
        return is_array($files) || $files instanceof \Traversable ? $files : array($files);
    }

    /**
     * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> array(file, tmp)).
     *
     * @param string $filename The filename to be parsed
     *
     * @return array The filename scheme and hierarchical part
     */
    private function getSchemeAndHierarchy($filename)
    {
        $components = explode('://', $filename, 2);

        return 2 === count($components) ? array($components[0], $components[1]) : array(null, $components[0]);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Filesystem;

use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Lock\Store\FlockStore;
use Symfony\Component\Lock\Store\SemaphoreStore;

@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use %s or %s instead.', LockHandler::class, SemaphoreStore::class, FlockStore::class), E_USER_DEPRECATED);

/**
 * LockHandler class provides a simple abstraction to lock anything by means of
 * a file lock.
 *
 * A locked file is created based on the lock name when calling lock(). Other
 * lock handlers will not be able to lock the same name until it is released
 * (explicitly by calling release() or implicitly when the instance holding the
 * lock is destroyed).
 *
 * @author GrÃ©goire Pineau <lyrixx@lyrixx.info>
 * @author Romain Neutron <imprec@gmail.com>
 * @author Nicolas Grekas <p@tchwork.com>
 *
 * @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\Lock\Store\SemaphoreStore or Symfony\Component\Lock\Store\FlockStore instead.
 */
class LockHandler
{
    private $file;
    private $handle;

    /**
     * @param string      $name     The lock name
     * @param string|null $lockPath The directory to store the lock. Default values will use temporary directory
     *
     * @throws IOException If the lock directory could not be created or is not writable
     */
    public function __construct($name, $lockPath = null)
    {
        $lockPath = $lockPath ?: sys_get_temp_dir();

        if (!is_dir($lockPath)) {
            $fs = new Filesystem();
            $fs->mkdir($lockPath);
        }

        if (!is_writable($lockPath)) {
            throw new IOException(sprintf('The directory "%s" is not writable.', $lockPath), 0, null, $lockPath);
        }

        $this->file = sprintf('%s/sf.%s.%s.lock', $lockPath, preg_replace('/[^a-z0-9\._-]+/i', '-', $name), hash('sha256', $name));
    }

    /**
     * Lock the resource.
     *
     * @param bool $blocking Wait until the lock is released
     *
     * @return bool Returns true if the lock was acquired, false otherwise
     *
     * @throws IOException If the lock file could not be created or opened
     */
    public function lock($blocking = false)
    {
        if ($this->handle) {
            return true;
        }

        $error = null;

        // Silence error reporting
        set_error_handler(function ($errno, $msg) use (&$error) {
            $error = $msg;
        });

        if (!$this->handle = fopen($this->file, 'r')) {
            if ($this->handle = fopen($this->file, 'x')) {
                chmod($this->file, 0444);
            } elseif (!$this->handle = fopen($this->file, 'r')) {
                usleep(100); // Give some time for chmod() to complete
                $this->handle = fopen($this->file, 'r');
            }
        }
        restore_error_handler();

        if (!$this->handle) {
            throw new IOException($error, 0, null, $this->file);
        }

        // On Windows, even if PHP doc says the contrary, LOCK_NB works, see
        // https://bugs.php.net/54129
        if (!flock($this->handle, LOCK_EX | ($blocking ? 0 : LOCK_NB))) {
            fclose($this->handle);
            $this->handle = null;

            return false;
        }

        return true;
    }

    /**
     * Release the resource.
     */
    public function release()
    {
        if ($this->handle) {
            flock($this->handle, LOCK_UN | LOCK_NB);
            fclose($this->handle);
            $this->handle = 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\Filesystem\Exception;

/**
 * IOException interface for file and input/output stream related exceptions thrown by the component.
 *
 * @author Christian GÃ¤rtner <christiangaertner.film@googlemail.com>
 */
interface IOExceptionInterface extends ExceptionInterface
{
    /**
     * Returns the associated path for the exception.
     *
     * @return string The path
     */
    public function getPath();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Filesystem\Exception;

/**
 * Exception interface for all exceptions thrown by the component.
 *
 * @author Romain Neutron <imprec@gmail.com>
 */
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\Filesystem\Exception;

/**
 * Exception class thrown when a filesystem operation failure happens.
 *
 * @author Romain Neutron <imprec@gmail.com>
 * @author Christian GÃ¤rtner <christiangaertner.film@googlemail.com>
 * @author Fabien Potencier <fabien@symfony.com>
 */
class IOException extends \RuntimeException implements IOExceptionInterface
{
    private $path;

    public function __construct($message, $code = 0, \Exception $previous = null, $path = null)
    {
        $this->path = $path;

        parent::__construct($message, $code, $previous);
    }

    /**
     * {@inheritdoc}
     */
    public function getPath()
    {
        return $this->path;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Filesystem\Exception;

/**
 * Exception class thrown when a file couldn't be found.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Christian GÃ¤rtner <christiangaertner.film@googlemail.com>
 */
class FileNotFoundException extends IOException
{
    public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null)
    {
        if (null === $message) {
            if (null === $path) {
                $message = 'File could not be found.';
            } else {
                $message = sprintf('File "%s" could not be found.', $path);
            }
        }

        parent::__construct($message, $code, $previous, $path);
    }
}
{
    "name": "symfony/filesystem",
    "type": "library",
    "description": "Symfony Filesystem Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": "^5.5.9|>=7.0.8"
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\Filesystem\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "3.4-dev"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@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>
 *
 * @final since version 3.4
 */
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 $filename;
    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 file into a PHP value.
     *
     * @param string $filename The path to the YAML file to be parsed
     * @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 file could not be read or the YAML is not valid
     */
    public function parseFile($filename, $flags = 0)
    {
        if (!is_file($filename)) {
            throw new ParseException(sprintf('File "%s" does not exist.', $filename));
        }

        if (!is_readable($filename)) {
            throw new ParseException(sprintf('File "%s" cannot be read.', $filename));
        }

        $this->filename = $filename;

        try {
            return $this->parse(file_get_contents($filename), $flags);
        } finally {
            $this->filename = null;
        }
    }

    /**
     * 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 Symfony 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 Symfony 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 Symfony 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 (Yaml::PARSE_KEYS_AS_STRINGS & $flags) {
            @trigger_error('Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.', E_USER_DEPRECATED);
        }

        if (false === preg_match('//u', $value)) {
            throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename);
        }

        $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, $this->filename);
            }

            Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename);

            $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, $this->filename);
                }
                $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($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 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>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\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, $this->filename);
                }
                $context = 'mapping';

                try {
                    $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 (!is_string($key) && !is_int($key)) {
                    $keyType = is_numeric($key) ? 'numeric key' : 'non-string key';
                    @trigger_error($this->getDeprecationMessage(sprintf('Implicit casting of %s to string is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.', $keyType)), 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 && (!isset($values['value']) || !self::preg_match('#^&(?P<ref>[^ ]+)#u', $values['value'], $refMatches))) {
                    $mergeNode = true;
                    $allowOverwrite = true;
                    if (isset($values['value'][0]) && '*' === $values['value'][0]) {
                        $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, $this->filename);
                        }

                        $refValue = $this->refs[$refName];

                        if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $refValue instanceof \stdClass) {
                            $refValue = (array) $refValue;
                        }

                        if (!is_array($refValue)) {
                            throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
                        }

                        $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 (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsed instanceof \stdClass) {
                            $parsed = (array) $parsed;
                        }

                        if (!is_array($parsed)) {
                            throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
                        }

                        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 (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $parsedItem instanceof \stdClass) {
                                    $parsedItem = (array) $parsedItem;
                                }

                                if (!is_array($parsedItem)) {
                                    throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename);
                                }

                                $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 ('<<' !== $key && 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)) || '<<' === $key) {
                    // 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($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
                        }
                    } else {
                        $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $flags);
                        if ('<<' === $key) {
                            $this->refs[$refMatches['ref']] = $value;

                            if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && $value instanceof \stdClass) {
                                $value = (array) $value;
                            }

                            $data += $value;
                        } elseif ($allowOverwrite || !isset($data[$key])) {
                            // Spec: Keys MUST be unique; first one wins.
                            // But overwriting is allowed when a merge node is used in current block.
                            if (null !== $subTag) {
                                $data[$key] = new TaggedValue($subTag, $value);
                            } else {
                                $data[$key] = $value;
                            }
                        } else {
                            @trigger_error($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), 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($this->getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), 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, $this->filename);
                }

                if (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1]) {
                    @trigger_error($this->getDeprecationMessage('Starting an unquoted string with a question mark followed by a space is deprecated since Symfony 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 {
                        $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;
                    $previousLineWasTerminatedWithBackslash = 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";
                            } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
                                $value .= ' ';
                            }

                            if ('' !== trim($parsedLine) && '\\' === substr($parsedLine, -1)) {
                                $value .= ltrim(substr($parsedLine, 0, -1));
                            } elseif ('' !== trim($parsedLine)) {
                                $value .= trim($parsedLine);
                            }

                            if ('' === trim($parsedLine)) {
                                $previousLineWasNewline = true;
                                $previousLineWasTerminatedWithBackslash = false;
                            } elseif ('\\' === substr($parsedLine, -1)) {
                                $previousLineWasNewline = false;
                                $previousLineWasTerminatedWithBackslash = true;
                            } else {
                                $previousLineWasNewline = false;
                                $previousLineWasTerminatedWithBackslash = false;
                            }
                        } catch (ParseException $e) {
                            $parseError = true;
                            break;
                        }
                    }

                    if (!$parseError) {
                        return Inline::parse(trim($value));
                    }
                }

                throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
            }
        } 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).
     *
     * @internal
     *
     * @return int The current line number
     */
    public 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 = null;
            $movements = 0;

            do {
                $EOF = false;

                // empty and comment-like lines do not influence the indentation depth
                if ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
                    $EOF = !$this->moveToNextLine();

                    if (!$EOF) {
                        ++$movements;
                    }
                } else {
                    $newIndent = $this->getCurrentLineIndentation();
                }
            } while (!$EOF && null === $newIndent);

            for ($i = 0; $i < $movements; ++$i) {
                $this->moveToPreviousLine();
            }

            $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem();

            if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
                throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
            }
        } else {
            $newIndent = $indentation;
        }

        $data = array();
        if ($this->getCurrentLineIndentation() >= $newIndent) {
            $data[] = substr($this->currentLine, $newIndent);
        } elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
            $data[] = $this->currentLine;
        } 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;
            }

            if ($indent >= $newIndent) {
                $data[] = substr($this->currentLine, $newIndent);
            } elseif ($this->isCurrentLineComment()) {
                $data[] = $this->currentLine;
            } elseif (0 == $indent) {
                $this->moveToPreviousLine();

                break;
            } else {
                throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
            }
        }

        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, $this->filename);
            }

            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 ('tagged' === $matches['tag']) {
                    return new TaggedValue(substr($matches['tag'], 1), $data);
                } elseif ('!' !== $matches['tag']) {
                    @trigger_error($this->getDeprecationMessage(sprintf('Using the custom tag "%s" for the value "%s" is deprecated since Symfony 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);
            }

            $lines = array();

            while ($this->moveToNextLine()) {
                // unquoted strings end before the first unindented line
                if (null === $quotation && 0 === $this->getCurrentLineIndentation()) {
                    $this->moveToPreviousLine();

                    break;
                }

                $lines[] = 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;
                }
            }

            for ($i = 0, $linesCount = count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
                if ('' === $lines[$i]) {
                    $value .= "\n";
                    $previousLineBlank = true;
                } elseif ($previousLineBlank) {
                    $value .= $lines[$i];
                    $previousLineBlank = false;
                } else {
                    $value .= ' '.$lines[$i];
                    $previousLineBlank = false;
                }
            }

            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.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
            }

            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();
        $movements = 0;

        do {
            $EOF = !$this->moveToNextLine();

            if (!$EOF) {
                ++$movements;
            }
        } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));

        if ($EOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() > $currentIndentation;

        for ($i = 0; $i < $movements; ++$i) {
            $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 (1 === $count) {
            // 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 (1 === $count) {
            // 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();
        $movements = 0;

        do {
            $EOF = !$this->moveToNextLine();

            if (!$EOF) {
                ++$movements;
            }
        } while (!$EOF && ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()));

        if ($EOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem();

        for ($i = 0; $i < $movements; ++$i) {
            $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), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
        }

        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']), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
    }

    private function getDeprecationMessage($message)
    {
        $message = rtrim($message, '.');

        if (null !== $this->filename) {
            $message .= ' in '.$this->filename;
        }

        $message .= ' on line '.($this->getRealCurrentLineNb() + 1);

        return $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;

/**
 * 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;

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;

/**
 * Dumper dumps PHP variables to YAML strings.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final since version 3.4
 */
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 Symfony 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 Symfony 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 Symfony 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") && false === strpos($value, "\r\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;

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 = -1;
    public static $parsedFilename;

    private static $exceptionOnInvalidType = false;
    private static $objectSupport = false;
    private static $objectForMap = false;
    private static $constantSupport = false;

    /**
     * @param int         $flags
     * @param int|null    $parsedLineNumber
     * @param string|null $parsedFilename
     */
    public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null)
    {
        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);
        self::$parsedFilename = $parsedFilename;

        if (null !== $parsedLineNumber) {
            self::$parsedLineNumber = $parsedLineNumber;
        }
    }

    /**
     * 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 Symfony 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 Symfony 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 Symfony 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::initialize($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)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
        }

        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 Symfony 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 Symfony 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 '.self::dump(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)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                }
            }
        } 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), self::$parsedLineNumber + 1, null, self::$parsedFilename);
            }

            // 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]), self::$parsedLineNumber + 1, $output, self::$parsedFilename);
            }

            if ($output && '%' === $output[0]) {
                @trigger_error(self::getDeprecationMessage(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)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
        }

        $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), self::$parsedLineNumber + 1, null, self::$parsedFilename);
    }

    /**
     * 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;
        $allowOverwrite = false;

        // {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(self::getDeprecationMessage('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0.'), E_USER_DEPRECATED);
            }

            if (!$isKeyQuoted) {
                $evaluatedKey = self::evaluateScalar($key, $flags, $references);

                if ('' !== $key && $evaluatedKey !== $key && !is_string($evaluatedKey) && !is_int($evaluatedKey)) {
                    @trigger_error(self::getDeprecationMessage('Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.'), E_USER_DEPRECATED);
                }
            }

            if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', ',', '[', ']', '{', '}'), true))) {
                @trigger_error(self::getDeprecationMessage('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since Symfony 3.2 and will throw a ParseException in 4.0.'), E_USER_DEPRECATED);
            }

            if ('<<' === $key) {
                $allowOverwrite = true;
            }

            while ($i < $len) {
                if (':' === $mapping[$i] || ' ' === $mapping[$i]) {
                    ++$i;

                    continue;
                }

                $tag = self::parseTag($mapping, $i, $flags);
                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.
                        // But overwriting is allowed when a merge node is used in current block.
                        if ('<<' === $key) {
                            foreach ($value as $parsedValue) {
                                $output += $parsedValue;
                            }
                        } elseif ($allowOverwrite || !isset($output[$key])) {
                            if (null !== $tag) {
                                $output[$key] = new TaggedValue($tag, $value);
                            } else {
                                $output[$key] = $value;
                            }
                        } elseif (isset($output[$key])) {
                            @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
                        }
                        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.
                        // But overwriting is allowed when a merge node is used in current block.
                        if ('<<' === $key) {
                            $output += $value;
                        } elseif ($allowOverwrite || !isset($output[$key])) {
                            if (null !== $tag) {
                                $output[$key] = new TaggedValue($tag, $value);
                            } else {
                                $output[$key] = $value;
                            }
                        } elseif (isset($output[$key])) {
                            @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
                        }
                        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.
                        // But overwriting is allowed when a merge node is used in current block.
                        if ('<<' === $key) {
                            $output += $value;
                        } elseif ($allowOverwrite || !isset($output[$key])) {
                            if (null !== $tag) {
                                $output[$key] = new TaggedValue($tag, $value);
                            } else {
                                $output[$key] = $value;
                            }
                        } elseif (isset($output[$key])) {
                            @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.', $key)), E_USER_DEPRECATED);
                        }
                        --$i;
                }
                ++$i;

                continue 2;
            }
        }

        throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping), self::$parsedLineNumber + 1, null, self::$parsedFilename);
    }

    /**
     * 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.', self::$parsedLineNumber + 1, $value, self::$parsedFilename);
            }

            if (!array_key_exists($value, $references)) {
                throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
            }

            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'):
                        @trigger_error(self::getDeprecationMessage('Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead.'), E_USER_DEPRECATED);

                        return (string) substr($scalar, 5);
                    case 0 === strpos($scalar, '!!str '):
                        return (string) substr($scalar, 6);
                    case 0 === strpos($scalar, '! '):
                        @trigger_error(self::getDeprecationMessage('Using the non-specific tag "!" is deprecated since Symfony 3.4 as its behavior will change in 4.0. It will force non-evaluating your values in 4.0. Use plain integers or !!float instead.'), E_USER_DEPRECATED);

                        return (int) self::parseScalar(substr($scalar, 2), $flags);
                    case 0 === strpos($scalar, '!php/object:'):
                        if (self::$objectSupport) {
                            @trigger_error(self::getDeprecationMessage('The !php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), E_USER_DEPRECATED);

                            return unserialize(substr($scalar, 12));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                        }

                        return;
                    case 0 === strpos($scalar, '!!php/object:'):
                        if (self::$objectSupport) {
                            @trigger_error(self::getDeprecationMessage('The !!php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.1 and will be removed in 4.0. Use the !php/object (without the colon) 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.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                        }

                        return;
                    case 0 === strpos($scalar, '!php/object'):
                        if (self::$objectSupport) {
                            return unserialize(self::parseScalar(substr($scalar, 12)));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                        }

                        return;
                    case 0 === strpos($scalar, '!php/const:'):
                        if (self::$constantSupport) {
                            @trigger_error(self::getDeprecationMessage('The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead.'), E_USER_DEPRECATED);

                            if (defined($const = substr($scalar, 11))) {
                                return constant($const);
                            }

                            throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                        }
                        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), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                        }

                        return;
                    case 0 === strpos($scalar, '!php/const'):
                        if (self::$constantSupport) {
                            $i = 0;
                            if (defined($const = self::parseScalar(substr($scalar, 11), 0, null, $i, false))) {
                                return constant($const);
                            }

                            throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                        }
                        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), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
                        }

                        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(self::getDeprecationMessage(sprintf('Using the unquoted scalar value "%s" is deprecated since Symfony 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.
            // no break
            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
                        // no break
                    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(self::getDeprecationMessage('Using the comma as a group separator for floats is deprecated since Symfony 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)) && 'tagged' !== $tag) {
            // Manage non-whitelisted 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), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
        }

        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), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
    }

    /**
     * @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)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
        }

        if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
            throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
        }

        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';
    }

    private static function getDeprecationMessage($message)
    {
        $message = rtrim($message, '.');

        if (null !== self::$parsedFilename) {
            $message .= ' in '.self::$parsedFilename;
        }

        if (-1 !== self::$parsedLineNumber) {
            $message .= ' on line '.(self::$parsedLineNumber + 1);
        }

        return $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;

use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Yaml offers convenience methods to load and dump YAML.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final since version 3.4
 */
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;

    /**
     * @deprecated since version 3.4, to be removed in 4.0. Quote your evaluable keys instead.
     */
    const PARSE_KEYS_AS_STRINGS = 2048;

    /**
     * Parses a YAML file into a PHP value.
     *
     *  Usage:
     *  <code>
     *   $array = Yaml::parseFile('config.yml');
     *   print_r($array);
     *  </code>
     *
     * @param string $filename The path to the YAML file to be parsed
     * @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 file could not be read or the YAML is not valid
     */
    public static function parseFile($filename, $flags = 0)
    {
        $yaml = new Parser();

        return $yaml->parseFile($filename, $flags);
    }

    /**
     * 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 Symfony 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 Symfony 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 Symfony 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 Symfony 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 Symfony 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 Symfony package.
 *
 * (c) Fabien Potencier <fabien@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;
use Symfony\Component\Yaml\Yaml;

/**
 * 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
{
    protected static $defaultName = 'lint:yaml';

    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
            ->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')
            ->addOption('parse-tags', null, InputOption::VALUE_NONE, 'Parse custom tags')
            ->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();
        $flags = $input->getOption('parse-tags') ? Yaml::PARSE_CUSTOM_TAGS : 0;

        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, $flags)));
        }

        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), $flags, $file);
        }

        return $this->display($io, $filesInfo);
    }

    private function validate($content, $flags, $file = null)
    {
        $prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
            if (E_USER_DEPRECATED === $level) {
                throw new ParseException($message, $this->getParser()->getRealCurrentLineNb() + 1);
            }

            return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
        });

        try {
            $this->getParser()->parse($content, Yaml::PARSE_CONSTANT | $flags);
        } catch (ParseException $e) {
            return array('file' => $file, 'line' => $e->getParsedLine(), '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 (0 === $erroredFiles) {
            $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\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 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\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;

    /**
     * @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\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;
    }
}
{
    "name": "symfony/yaml",
    "type": "library",
    "description": "Symfony Yaml Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": "^5.5.9|>=7.0.8"
    },
    "require-dev": {
        "symfony/console": "~3.4|~4.0"
    },
    "conflict": {
        "symfony/console": "<3.4"
    },
    "suggest": {
        "symfony/console": "For validating YAML files using the lint command"
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\Yaml\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "3.4-dev"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug;

/**
 * Registers all the debug tools.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Debug
{
    private static $enabled = false;

    /**
     * Enables the debug tools.
     *
     * This method registers an error handler and an exception handler.
     *
     * If the Symfony ClassLoader component is available, a special
     * class loader is also registered.
     *
     * @param int  $errorReportingLevel The level of error reporting you want
     * @param bool $displayErrors       Whether to display errors (for development) or just log them (for production)
     */
    public static function enable($errorReportingLevel = E_ALL, $displayErrors = true)
    {
        if (static::$enabled) {
            return;
        }

        static::$enabled = true;

        if (null !== $errorReportingLevel) {
            error_reporting($errorReportingLevel);
        } else {
            error_reporting(E_ALL);
        }

        if ('cli' !== PHP_SAPI) {
            ini_set('display_errors', 0);
            ExceptionHandler::register();
        } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) {
            // CLI - display errors only if they're not already logged to STDERR
            ini_set('display_errors', 1);
        }
        if ($displayErrors) {
            ErrorHandler::register(new ErrorHandler(new BufferingLogger()));
        } else {
            ErrorHandler::register()->throwAt(0, true);
        }

        DebugClassLoader::enable();
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\FatalErrorHandler;

use Symfony\Component\Debug\Exception\UndefinedFunctionException;
use Symfony\Component\Debug\Exception\FatalErrorException;

/**
 * ErrorHandler for undefined functions.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handleError(array $error, FatalErrorException $exception)
    {
        $messageLen = strlen($error['message']);
        $notFoundSuffix = '()';
        $notFoundSuffixLen = strlen($notFoundSuffix);
        if ($notFoundSuffixLen > $messageLen) {
            return;
        }

        if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
            return;
        }

        $prefix = 'Call to undefined function ';
        $prefixLen = strlen($prefix);
        if (0 !== strpos($error['message'], $prefix)) {
            return;
        }

        $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
        if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) {
            $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1);
            $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex);
            $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix);
        } else {
            $functionName = $fullyQualifiedFunctionName;
            $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName);
        }

        $candidates = array();
        foreach (get_defined_functions() as $type => $definedFunctionNames) {
            foreach ($definedFunctionNames as $definedFunctionName) {
                if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) {
                    $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1);
                } else {
                    $definedFunctionNameBasename = $definedFunctionName;
                }

                if ($definedFunctionNameBasename === $functionName) {
                    $candidates[] = '\\'.$definedFunctionName;
                }
            }
        }

        if ($candidates) {
            sort($candidates);
            $last = array_pop($candidates).'"?';
            if ($candidates) {
                $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last;
            } else {
                $candidates = '"'.$last;
            }
            $message .= "\nDid you mean to call ".$candidates;
        }

        return new UndefinedFunctionException($message, $exception);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\FatalErrorHandler;

use Symfony\Component\Debug\Exception\FatalErrorException;

/**
 * Attempts to convert fatal errors to exceptions.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface FatalErrorHandlerInterface
{
    /**
     * Attempts to convert an error into an exception.
     *
     * @param array               $error     An array as returned by error_get_last()
     * @param FatalErrorException $exception A FatalErrorException instance
     *
     * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise
     */
    public function handleError(array $error, FatalErrorException $exception);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\FatalErrorHandler;

use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\Exception\UndefinedMethodException;

/**
 * ErrorHandler for undefined methods.
 *
 * @author GrÃ©goire Pineau <lyrixx@lyrixx.info>
 */
class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handleError(array $error, FatalErrorException $exception)
    {
        preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches);
        if (!$matches) {
            return;
        }

        $className = $matches[1];
        $methodName = $matches[2];

        $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className);

        if (!class_exists($className) || null === $methods = get_class_methods($className)) {
            // failed to get the class or its methods on which an unknown method was called (for example on an anonymous class)
            return new UndefinedMethodException($message, $exception);
        }

        $candidates = array();
        foreach ($methods as $definedMethodName) {
            $lev = levenshtein($methodName, $definedMethodName);
            if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) {
                $candidates[] = $definedMethodName;
            }
        }

        if ($candidates) {
            sort($candidates);
            $last = array_pop($candidates).'"?';
            if ($candidates) {
                $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last;
            } else {
                $candidates = '"'.$last;
            }

            $message .= "\nDid you mean to call ".$candidates;
        }

        return new UndefinedMethodException($message, $exception);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\FatalErrorHandler;

use Symfony\Component\Debug\Exception\ClassNotFoundException;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\DebugClassLoader;
use Composer\Autoload\ClassLoader as ComposerClassLoader;
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;

/**
 * ErrorHandler for classes that do not exist.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handleError(array $error, FatalErrorException $exception)
    {
        $messageLen = strlen($error['message']);
        $notFoundSuffix = '\' not found';
        $notFoundSuffixLen = strlen($notFoundSuffix);
        if ($notFoundSuffixLen > $messageLen) {
            return;
        }

        if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
            return;
        }

        foreach (array('class', 'interface', 'trait') as $typeName) {
            $prefix = ucfirst($typeName).' \'';
            $prefixLen = strlen($prefix);
            if (0 !== strpos($error['message'], $prefix)) {
                continue;
            }

            $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
            if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) {
                $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1);
                $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex);
                $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix);
                $tail = ' for another namespace?';
            } else {
                $className = $fullyQualifiedClassName;
                $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className);
                $tail = '?';
            }

            if ($candidates = $this->getClassCandidates($className)) {
                $tail = array_pop($candidates).'"?';
                if ($candidates) {
                    $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail;
                } else {
                    $tail = ' for "'.$tail;
                }
            }
            $message .= "\nDid you forget a \"use\" statement".$tail;

            return new ClassNotFoundException($message, $exception);
        }
    }

    /**
     * Tries to guess the full namespace for a given class name.
     *
     * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer
     * autoloader (that should cover all common cases).
     *
     * @param string $class A class name (without its namespace)
     *
     * @return array An array of possible fully qualified class names
     */
    private function getClassCandidates($class)
    {
        if (!is_array($functions = spl_autoload_functions())) {
            return array();
        }

        // find Symfony and Composer autoloaders
        $classes = array();

        foreach ($functions as $function) {
            if (!is_array($function)) {
                continue;
            }
            // get class loaders wrapped by DebugClassLoader
            if ($function[0] instanceof DebugClassLoader) {
                $function = $function[0]->getClassLoader();

                if (!is_array($function)) {
                    continue;
                }
            }

            if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
                foreach ($function[0]->getPrefixes() as $prefix => $paths) {
                    foreach ($paths as $path) {
                        $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
                    }
                }
            }
            if ($function[0] instanceof ComposerClassLoader) {
                foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) {
                    foreach ($paths as $path) {
                        $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
                    }
                }
            }
        }

        return array_unique($classes);
    }

    /**
     * @param string $path
     * @param string $class
     * @param string $prefix
     *
     * @return array
     */
    private function findClassInPath($path, $class, $prefix)
    {
        if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) {
            return array();
        }

        $classes = array();
        $filename = $class.'.php';
        foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
            if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) {
                $classes[] = $class;
            }
        }

        return $classes;
    }

    /**
     * @param string $path
     * @param string $file
     * @param string $prefix
     *
     * @return string|null
     */
    private function convertFileToClass($path, $file, $prefix)
    {
        $candidates = array(
            // namespaced class
            $namespacedClass = str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file),
            // namespaced class (with target dir)
            $prefix.$namespacedClass,
            // namespaced class (with target dir and separator)
            $prefix.'\\'.$namespacedClass,
            // PEAR class
            str_replace('\\', '_', $namespacedClass),
            // PEAR class (with target dir)
            str_replace('\\', '_', $prefix.$namespacedClass),
            // PEAR class (with target dir and separator)
            str_replace('\\', '_', $prefix.'\\'.$namespacedClass),
        );

        if ($prefix) {
            $candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); });
        }

        // We cannot use the autoloader here as most of them use require; but if the class
        // is not found, the new autoloader call will require the file again leading to a
        // "cannot redeclare class" error.
        foreach ($candidates as $candidate) {
            if ($this->classExists($candidate)) {
                return $candidate;
            }
        }

        require_once $file;

        foreach ($candidates as $candidate) {
            if ($this->classExists($candidate)) {
                return $candidate;
            }
        }
    }

    /**
     * @param string $class
     *
     * @return bool
     */
    private function classExists($class)
    {
        return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug;

use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\Debug\Exception\OutOfMemoryException;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;

/**
 * ExceptionHandler converts an exception to a Response object.
 *
 * It is mostly useful in debug mode to replace the default PHP/XDebug
 * output with something prettier and more useful.
 *
 * As this class is mainly used during Kernel boot, where nothing is yet
 * available, the Response content is always HTML.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Nicolas Grekas <p@tchwork.com>
 */
class ExceptionHandler
{
    private $debug;
    private $charset;
    private $handler;
    private $caughtBuffer;
    private $caughtLength;
    private $fileLinkFormat;

    public function __construct($debug = true, $charset = null, $fileLinkFormat = null)
    {
        $this->debug = $debug;
        $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8';
        $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
    }

    /**
     * Registers the exception handler.
     *
     * @param bool        $debug          Enable/disable debug mode, where the stack trace is displayed
     * @param string|null $charset        The charset used by exception messages
     * @param string|null $fileLinkFormat The IDE link template
     *
     * @return static
     */
    public static function register($debug = true, $charset = null, $fileLinkFormat = null)
    {
        $handler = new static($debug, $charset, $fileLinkFormat);

        $prev = set_exception_handler(array($handler, 'handle'));
        if (is_array($prev) && $prev[0] instanceof ErrorHandler) {
            restore_exception_handler();
            $prev[0]->setExceptionHandler(array($handler, 'handle'));
        }

        return $handler;
    }

    /**
     * Sets a user exception handler.
     *
     * @param callable $handler An handler that will be called on Exception
     *
     * @return callable|null The previous exception handler if any
     */
    public function setHandler(callable $handler = null)
    {
        $old = $this->handler;
        $this->handler = $handler;

        return $old;
    }

    /**
     * Sets the format for links to source files.
     *
     * @param string|FileLinkFormatter $fileLinkFormat The format for links to source files
     *
     * @return string The previous file link format
     */
    public function setFileLinkFormat($fileLinkFormat)
    {
        $old = $this->fileLinkFormat;
        $this->fileLinkFormat = $fileLinkFormat;

        return $old;
    }

    /**
     * Sends a response for the given Exception.
     *
     * To be as fail-safe as possible, the exception is first handled
     * by our simple exception handler, then by the user exception handler.
     * The latter takes precedence and any output from the former is cancelled,
     * if and only if nothing bad happens in this handling path.
     */
    public function handle(\Exception $exception)
    {
        if (null === $this->handler || $exception instanceof OutOfMemoryException) {
            $this->sendPhpResponse($exception);

            return;
        }

        $caughtLength = $this->caughtLength = 0;

        ob_start(function ($buffer) {
            $this->caughtBuffer = $buffer;

            return '';
        });

        $this->sendPhpResponse($exception);
        while (null === $this->caughtBuffer && ob_end_flush()) {
            // Empty loop, everything is in the condition
        }
        if (isset($this->caughtBuffer[0])) {
            ob_start(function ($buffer) {
                if ($this->caughtLength) {
                    // use substr_replace() instead of substr() for mbstring overloading resistance
                    $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength);
                    if (isset($cleanBuffer[0])) {
                        $buffer = $cleanBuffer;
                    }
                }

                return $buffer;
            });

            echo $this->caughtBuffer;
            $caughtLength = ob_get_length();
        }
        $this->caughtBuffer = null;

        try {
            call_user_func($this->handler, $exception);
            $this->caughtLength = $caughtLength;
        } catch (\Exception $e) {
            if (!$caughtLength) {
                // All handlers failed. Let PHP handle that now.
                throw $exception;
            }
        }
    }

    /**
     * Sends the error associated with the given Exception as a plain PHP response.
     *
     * This method uses plain PHP functions like header() and echo to output
     * the response.
     *
     * @param \Exception|FlattenException $exception An \Exception or FlattenException instance
     */
    public function sendPhpResponse($exception)
    {
        if (!$exception instanceof FlattenException) {
            $exception = FlattenException::create($exception);
        }

        if (!headers_sent()) {
            header(sprintf('HTTP/1.0 %s', $exception->getStatusCode()));
            foreach ($exception->getHeaders() as $name => $value) {
                header($name.': '.$value, false);
            }
            header('Content-Type: text/html; charset='.$this->charset);
        }

        echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
    }

    /**
     * Gets the full HTML content associated with the given exception.
     *
     * @param \Exception|FlattenException $exception An \Exception or FlattenException instance
     *
     * @return string The HTML content as a string
     */
    public function getHtml($exception)
    {
        if (!$exception instanceof FlattenException) {
            $exception = FlattenException::create($exception);
        }

        return $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
    }

    /**
     * Gets the HTML content associated with the given exception.
     *
     * @return string The content as a string
     */
    public function getContent(FlattenException $exception)
    {
        switch ($exception->getStatusCode()) {
            case 404:
                $title = 'Sorry, the page you are looking for could not be found.';
                break;
            default:
                $title = 'Whoops, looks like something went wrong.';
        }

        $content = '';
        if ($this->debug) {
            try {
                $count = count($exception->getAllPrevious());
                $total = $count + 1;
                foreach ($exception->toArray() as $position => $e) {
                    $ind = $count - $position + 1;
                    $class = $this->formatClass($e['class']);
                    $message = nl2br($this->escapeHtml($e['message']));
                    $content .= sprintf(<<<'EOF'
                        <div class="trace trace-as-html">
                            <table class="trace-details">
                                <thead class="trace-head"><tr><th>
                                    <h3 class="trace-class">
                                        <span class="text-muted">(%d/%d)</span>
                                        <span class="exception_title">%s</span>
                                    </h3>
                                    <p class="break-long-words trace-message">%s</p>
                                </th></tr></thead>
                                <tbody>
EOF
                        , $ind, $total, $class, $message);
                    foreach ($e['trace'] as $trace) {
                        $content .= '<tr><td>';
                        if ($trace['function']) {
                            $content .= sprintf('at <span class="trace-class">%s</span><span class="trace-type">%s</span><span class="trace-method">%s</span>(<span class="trace-arguments">%s</span>)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
                        }
                        if (isset($trace['file']) && isset($trace['line'])) {
                            $content .= $this->formatPath($trace['file'], $trace['line']);
                        }
                        $content .= "</td></tr>\n";
                    }

                    $content .= "</tbody>\n</table>\n</div>\n";
                }
            } catch (\Exception $e) {
                // something nasty happened and we cannot throw an exception anymore
                if ($this->debug) {
                    $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage()));
                } else {
                    $title = 'Whoops, looks like something went wrong.';
                }
            }
        }

        $symfonyGhostImageContents = $this->getSymfonyGhostAsSvg();

        return <<<EOF
            <div class="exception-summary">
                <div class="container">
                    <div class="exception-message-wrapper">
                        <h1 class="break-long-words exception-message">$title</h1>
                        <div class="exception-illustration hidden-xs-down">$symfonyGhostImageContents</div>
                    </div>
                </div>
            </div>

            <div class="container">
                $content
            </div>
EOF;
    }

    /**
     * Gets the stylesheet associated with the given exception.
     *
     * @return string The stylesheet as a string
     */
    public function getStylesheet(FlattenException $exception)
    {
        return <<<'EOF'
            body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; }

            a { cursor: pointer; text-decoration: none; }
            a:hover { text-decoration: underline; }
            abbr[title] { border-bottom: none; cursor: help; text-decoration: none; }

            code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; }

            table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; }
            table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; }
            table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; }
            table th { background-color: #E0E0E0; font-weight: bold; text-align: left; }

            .hidden-xs-down { display: none; }
            .block { display: block; }
            .break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; }
            .text-muted { color: #999; }

            .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; }
            .container::after { content: ""; display: table; clear: both; }

            .exception-summary { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; }

            .exception-message-wrapper { display: flex; align-items: center; min-height: 70px; }
            .exception-message { flex-grow: 1; padding: 30px 0; }
            .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; }
            .exception-message.long { font-size: 18px; }
            .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; }
            .exception-message a:hover { border-bottom-color: #ffffff; }

            .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; }

            .trace + .trace { margin-top: 30px; }
            .trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; }

            .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; }

            .trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; }
            .trace-class { color: #B0413E; }
            .trace-type { padding: 0 2px; }
            .trace-method { color: #B0413E; font-weight: bold; }
            .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; }

            @media (min-width: 575px) {
                .hidden-xs-down { display: initial; }
            }
EOF;
    }

    private function decorate($content, $css)
    {
        return <<<EOF
<!DOCTYPE html>
<html>
    <head>
        <meta charset="{$this->charset}" />
        <meta name="robots" content="noindex,nofollow" />
        <style>$css</style>
    </head>
    <body>
        $content
    </body>
</html>
EOF;
    }

    private function formatClass($class)
    {
        $parts = explode('\\', $class);

        return sprintf('<abbr title="%s">%s</abbr>', $class, array_pop($parts));
    }

    private function formatPath($path, $line)
    {
        $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path);
        $fmt = $this->fileLinkFormat;

        if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $path, '%l' => $line)) : $fmt->format($path, $line)) {
            return sprintf('<span class="block trace-file-path">in <a href="%s" title="Go to source">%s (line %d)</a></span>', $this->escapeHtml($link), $file, $line);
        }

        return sprintf('<span class="block trace-file-path">in <a title="%s line %3$d"><strong>%s</strong> (line %d)</a></span>', $this->escapeHtml($path), $file, $line);
    }

    /**
     * Formats an array as a string.
     *
     * @param array $args The argument array
     *
     * @return string
     */
    private function formatArgs(array $args)
    {
        $result = array();
        foreach ($args as $key => $item) {
            if ('object' === $item[0]) {
                $formattedValue = sprintf('<em>object</em>(%s)', $this->formatClass($item[1]));
            } elseif ('array' === $item[0]) {
                $formattedValue = sprintf('<em>array</em>(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
            } elseif ('null' === $item[0]) {
                $formattedValue = '<em>null</em>';
            } elseif ('boolean' === $item[0]) {
                $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
            } elseif ('resource' === $item[0]) {
                $formattedValue = '<em>resource</em>';
            } else {
                $formattedValue = str_replace("\n", '', $this->escapeHtml(var_export($item[1], true)));
            }

            $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escapeHtml($key), $formattedValue);
        }

        return implode(', ', $result);
    }

    /**
     * HTML-encodes a string.
     */
    private function escapeHtml($str)
    {
        return htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset);
    }

    private function getSymfonyGhostAsSvg()
    {
        return '<svg viewBox="0 0 136 81" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M92.358 20.403a23.168 23.168 0 0 1 9.003 1.881 23.67 23.67 0 0 1 5.197 3.079 24.257 24.257 0 0 1 3.457 3.296 24.771 24.771 0 0 1 5.042 9.396c.486 1.72.78 3.492.895 5.28l.008.142.028.158.015.246v13.875c.116.034.232.065.348.098.193.054.383.116.577.168.487.125.989.191 1.49.215.338.016.689.023 1.021-.059.021-.005.032-.029.048-.044l.095-.1c.243-.265.461-.552.663-.851.277-.408.523-.837.746-1.279l.042-.087c-.066-.012-.131-.026-.197-.04l-.099-.023a5.536 5.536 0 0 1-.694-.242 5.649 5.649 0 0 1-2.374-1.845 5.694 5.694 0 0 1-.824-1.594 6.514 6.514 0 0 1-.267-2.781c.045-.394.126-.779.233-1.159.079-.278.162-.562.307-.812.094-.163.129-.196.247-.341l.79-.882c.143-.143.174-.186.34-.303.249-.174.536-.289.834-.333.074-.011.15-.014.224-.02l1.188-.037c.173.004.217-.002.388.028s.211.05.375.105l.018.007c.059.026.119.05.176.079.151.076.179.104.313.2l.006-.021c.073-.187.084-.238.187-.41.077-.129.167-.249.27-.357.051-.054.108-.103.162-.154l1.124-.95c.14-.107.172-.14.327-.224.155-.085.199-.094.363-.154l.019-.006c.169-.043.211-.06.385-.077.174-.016.218-.007.392.003l1.446.158c.193.033.244.033.43.098.278.097.534.259.744.47.053.053.1.112.149.167l.923 1.158.149.213.028.054.017-.014.184-.125c.196-.104.196-.104.402-.184l1.386-.451c.064-.018.126-.038.19-.052.129-.028.259-.042.39-.043.16-.002.321.017.478.047.364.069.711.21 1.032.396.162.094.316.199.469.308.088.063.176.132.27.188l.021.011c.19.123.245.146.409.305.185.178.336.393.443.63.035.079.061.162.091.243l.439 1.428c.045.175.062.219.081.4.02.193.006.381-.015.573a7.79 7.79 0 0 1-.101.645c-.09.455-.212.901-.365 1.339-.128.366-.273.73-.445 1.077-.658 1.335-1.652 2.512-2.917 3.265a6.399 6.399 0 0 1-1.019.489 6.097 6.097 0 0 1-.631.203c-.226.058-.455.1-.686.134l-.096.012-.061.007c-.01.176-.022.352-.036.528-.034.39-.082.778-.153 1.163a14.258 14.258 0 0 1-.574 2.114c-.229.654-.484 1.306-.806 1.918a9.16 9.16 0 0 1-.386.656c-.219.348-.451.686-.697 1.013-.448.594-.946 1.148-1.521 1.614-.255.207-.52.397-.808.553-.9.489-1.919.648-2.921.735-.493.038-.986.059-1.478.099-.162.015-.324.033-.486.049-.145.011-.289.022-.434.03a15.768 15.768 0 0 1-2.778-.118c0 1.416.007 2.832-.001 4.248a9.737 9.737 0 0 1-.684 3.479 9.615 9.615 0 0 1-1.72 2.804 9.326 9.326 0 0 1-3.04 2.279 9.046 9.046 0 0 1-5.33.715 9.064 9.064 0 0 1-2.988-1.079 9.363 9.363 0 0 1-2.761-2.429 10.078 10.078 0 0 1-1.05 1.16 9.281 9.281 0 0 1-1.871 1.358 9.033 9.033 0 0 1-2.495.926 9.04 9.04 0 0 1-6.462-1.072 9.395 9.395 0 0 1-2.602-2.292l-.062-.08a10.896 10.896 0 0 1-.53.635 9.266 9.266 0 0 1-2.671 2.032 9.028 9.028 0 0 1-6.044.751 9.048 9.048 0 0 1-2.436-.934 9.343 9.343 0 0 1-2.286-1.803 9.572 9.572 0 0 1-1.783-2.757 9.705 9.705 0 0 1-.773-3.693V67.244c-.157.024-.314.047-.472.067-.487.06-.977.103-1.469.109-.313.004-.627-.009-.94-.028-.426-.025-.85-.065-1.273-.125-1.833-.264-3.65-.92-5.109-2.117a8.172 8.172 0 0 1-1.064-1.049 10.155 10.155 0 0 1-.878-1.236 15.277 15.277 0 0 1-.7-1.274 20.835 20.835 0 0 1-1.889-6.194l-.018-.142-.008-.061a6.47 6.47 0 0 1-.99-.297 6.135 6.135 0 0 1-.61-.285 6.587 6.587 0 0 1-.889-.562c-1.228-.924-2.124-2.259-2.668-3.711a9.947 9.947 0 0 1-.307-.99 10.288 10.288 0 0 1-.318-1.923c-.009-.147-.011-.293-.015-.44v-.037c.008-.175.004-.22.037-.393.033-.173.053-.213.11-.378l.561-1.417c.031-.068.06-.139.095-.206a2.028 2.028 0 0 1 .771-.803c.093-.054.194-.095.289-.145l.311-.179c.352-.194.714-.358 1.107-.44.213-.044.426-.061.643-.061l.034.001c.177.014.223.01.396.052.174.041.214.065.379.132l1.347.635c.073.04.15.076.221.121.142.091.272.2.388.325.154.166.176.222.297.414l.022.047.722-.762.168-.158c.165-.122.202-.161.385-.253.206-.102.429-.168.656-.193.076-.008.152-.008.228-.011l1.46.013c.177.011.223.007.397.046.175.038.215.061.381.126l.018.008c.154.08.196.094.338.196.142.102.169.137.294.259l.853.912.152-.067.191-.063.019-.005.196-.042c.177-.019.222-.031.401-.022.066.003.133.013.199.02l1.185.182c.073.016.147.027.219.047.288.08.558.227.784.428.151.135.177.181.303.339l.714 1.004c.097.152.127.187.201.352.077.172.123.352.164.536.029.134.056.269.08.404.063.361.102.725.112 1.091.021.78-.08 1.566-.321 2.307a5.906 5.906 0 0 1-.532 1.183 5.463 5.463 0 0 1-3.257 2.489l-.03.008c.195.584.433 1.155.712 1.701.215.422.453.833.735 1.211.026.035.026.034.053.068l.058.072c.056.024.113.042.171.06.319.09.653.121.982.14.488.027.978.013 1.461-.06.167-.028.333-.062.499-.089.134-.022.267-.042.401-.066l.28-.056c.154-.023.308-.049.462-.076l.115-.021V43.881c.011-.203.006-.203.042-.404a26.66 26.66 0 0 1 .226-2.241 24.737 24.737 0 0 1 5.72-12.577 24.204 24.204 0 0 1 3.457-3.296 23.653 23.653 0 0 1 4.937-2.966 23.215 23.215 0 0 1 5.604-1.681 23.703 23.703 0 0 1 3.958-.313zm-.287 2.042a21.169 21.169 0 0 0-8.012 1.622 21.636 21.636 0 0 0-4.799 2.766 22.233 22.233 0 0 0-3.205 2.985 22.705 22.705 0 0 0-4.897 9.196 23.383 23.383 0 0 0-.737 4.867h-.025v15.744c-.258.053-.258.052-.517.101-.28.051-.56.1-.841.144-.211.04-.421.079-.632.115l-.232.037-.411.078c-.116.02-.233.035-.348.057-.305.056-.609.11-.917.14a9.929 9.929 0 0 1-1.883-.017c-.514-.056-1.044-.155-1.51-.397a1.762 1.762 0 0 1-.33-.218 1.925 1.925 0 0 1-.234-.252 5.248 5.248 0 0 1-.174-.22 8.97 8.97 0 0 1-.582-.883 13.806 13.806 0 0 1-.941-1.971 14.348 14.348 0 0 1-.608-1.954 14.04 14.04 0 0 1-.169-.86l-.015-.11-.015-.109c.161-.007.16-.007.321-.016a12.793 12.793 0 0 0 1.413-.182 4.43 4.43 0 0 0 .28-.074 3.56 3.56 0 0 0 1.199-.616c.309-.244.576-.543.786-.88.163-.261.292-.544.387-.838.123-.378.192-.774.214-1.172a5.102 5.102 0 0 0-.024-.865 7.192 7.192 0 0 0-.145-.799l-.714-1.005-1.184-.182-.019.005-.946.758-.12 1.229a4.953 4.953 0 0 1 .111.455c.032.181.052.36.043.544a1.04 1.04 0 0 1-.056.303c-.11.301-.419.451-.696.548-.402.142-.813.25-1.229.339l.07-.648c.022-.191.047-.381.08-.57.036-.207.079-.413.152-.61.077-.211.182-.412.296-.605.044-.074.092-.146.135-.222.029-.048.031-.047.055-.098.016-.033.031-.064.045-.098l-.026-1.551-1.042-1.116-.018-.008-1.459-.014-1.022 1.079c-.049.128-.08.258-.111.393a5.274 5.274 0 0 0-.1.651 5.55 5.55 0 0 0-.031.466c-.009.687.104 1.37.294 2.028.11.382.262.753.402 1.123-.115-.029-.228-.06-.342-.092a9.526 9.526 0 0 1-1.176-.446c-.108-.05-.111-.048-.191-.097a1.921 1.921 0 0 1-.327-.249c-.416-.4-.589-.986-.671-1.55a5.643 5.643 0 0 1-.057-.549c-.007-.143-.006-.286-.007-.429-.001-.186.005-.372.011-.558l.001-.039-.567-1.446-1.347-.634c-.316-.008-.599.144-.867.299-.109.063-.218.126-.33.185a2.058 2.058 0 0 1-.125.061l-.042.019-.561 1.416c0 .209.014.416.036.624.04.377.106.75.196 1.118.076.309.164.616.275.913.415 1.109 1.093 2.146 2.043 2.838.234.171.485.317.746.442.183.088.371.161.565.22.263.079.532.13.803.17.296.045.594.075.892.095l.108.007c.004.151.01.302.017.453.011.177.023.353.038.529a18.13 18.13 0 0 0 .762 3.752c.239.76.522 1.505.857 2.225.23.494.483.977.767 1.44.288.469.608.915.989 1.308 1.001 1.028 2.324 1.648 3.687 1.976.643.155 1.298.243 1.955.287.311.021.622.036.933.033.418-.006.835-.041 1.25-.094.238-.03.477-.064.713-.11.117-.023.232-.053.348-.081.196-.048.392-.097.586-.151.147-.041.291-.094.436-.144.204-.069.408-.139.608-.217l.006-.003c0 2.207-.013 4.414.001 6.62a7.942 7.942 0 0 0 .13 1.32 7.545 7.545 0 0 0 2.383 4.243 7.23 7.23 0 0 0 2.258 1.372 7.094 7.094 0 0 0 7.012-1.164 7.504 7.504 0 0 0 2.035-2.613 7.727 7.727 0 0 0 .676-2.401l.009-.088.038-.765a8.16 8.16 0 0 0 .113 1.324c.121.694.338 1.37.643 2.001a7.49 7.49 0 0 0 1.692 2.275 7.266 7.266 0 0 0 2.24 1.399 7.11 7.11 0 0 0 4.615.19 7.212 7.212 0 0 0 2.351-1.218 7.501 7.501 0 0 0 2.128-2.64 7.763 7.763 0 0 0 .702-2.39l.01-.088.009-.088.038-.765a9.339 9.339 0 0 0 .021.575 7.626 7.626 0 0 0 .621 2.504 7.507 7.507 0 0 0 2.35 2.972 7.1 7.1 0 0 0 7.026.881 7.275 7.275 0 0 0 2.268-1.515 7.525 7.525 0 0 0 1.612-2.338 7.58 7.58 0 0 0 .572-2.033c.048-.347.069-.696.071-1.046v-6.721c.136.051.271.101.408.148a12.153 12.153 0 0 0 1.976.443c.264.035.529.055.794.071.33.02.66.031.991.027.245-.002.49-.012.735-.031.245-.018.49-.048.735-.068.407-.03.814-.051 1.221-.079a9.493 9.493 0 0 0 1.384-.188c.315-.073.626-.174.912-.329a3.53 3.53 0 0 0 .586-.418c.46-.386.85-.85 1.205-1.337a12.178 12.178 0 0 0 .801-1.246c.122-.232.229-.471.33-.712a15.873 15.873 0 0 0 .681-1.988c.136-.525.23-1.058.282-1.598.035-.41.052-.822.088-1.232.03-.317.078-.632.121-.947l.018-.145.016-.145c.144.009.287.016.431.021.459.009.924.007 1.378-.07a4.456 4.456 0 0 0 1.353-.482c.989-.55 1.752-1.466 2.258-2.488.116-.235.214-.48.304-.727a7.58 7.58 0 0 0 .377-1.43c.016-.109.027-.218.039-.328l.001-.009-.438-1.428a5.206 5.206 0 0 1-.16-.096c-.158-.105-.311-.219-.467-.326a3.829 3.829 0 0 0-.159-.1 1.356 1.356 0 0 0-.509-.18l-.01-.001-1.386.452-.681 1.323c-.016.212-.023.424-.043.636a5.66 5.66 0 0 1-.139.873c-.118.494-.316.999-.702 1.338a1.865 1.865 0 0 1-.496.301l-.272.087a9.57 9.57 0 0 1-.83.205 8.797 8.797 0 0 1-.582.091l.229-.462c.079-.163.158-.325.229-.492.051-.118.096-.239.139-.36.036-.103.076-.209.103-.315.019-.075.031-.153.041-.229.017-.132.031-.263.043-.395.035-.368.06-.737.094-1.104.02-.187.048-.372.067-.559.015-.167.015-.336.012-.505a4.76 4.76 0 0 0-.074-.826c-.012-.065-.03-.13-.045-.194l-.003-.009-.923-1.157-1.446-.159-.019.006-1.124.95-.154 1.489c.011.034.024.066.037.099.044.115.107.221.161.331.046.096.088.193.13.29l.031.076c.013.033.017.07.023.105.012.096.022.191.031.287.031.364.047.73.081 1.093.013.102.028.202.04.303.014.145.027.29.033.435.014.28.016.561.023.841a9.588 9.588 0 0 1-.862-.323c-.063-.027-.128-.062-.193-.084a1.325 1.325 0 0 0-.067-.013c-.081-.01-.162-.017-.243-.025-.245-.02-.49-.037-.734-.061-.066-.007-.132-.014-.198-.028l-.017-.005c-.03-.013-.029-.014-.067-.038a1.614 1.614 0 0 1-.161-.108.863.863 0 0 1-.22-.242c-.089-.155-.102-.34-.09-.517.02-.299.117-.591.228-.866l.004-.009-.018-1.197-.874-.84-.018-.007-1.188.036-.79.882c-.037.112-.074.224-.106.338a4.756 4.756 0 0 0-.171 1.906c.039.329.115.654.233.963a3.542 3.542 0 0 0 1.263 1.636c.313.222.659.393 1.019.517.237.082.487.111.734.145.479.06.959.106 1.438.166.121.017.241.037.362.058l.158.026a12.12 12.12 0 0 1-.923 2.565 13.221 13.221 0 0 1-.829 1.474 9.474 9.474 0 0 1-.984 1.286c-.08.087-.163.17-.248.252a1.655 1.655 0 0 1-.329.262 2.376 2.376 0 0 1-.722.247c-.457.089-.927.093-1.39.071-.391-.018-.781-.06-1.168-.123a7.817 7.817 0 0 1-.609-.124c-.226-.056-.448-.124-.671-.191-.065-.019-.131-.035-.197-.054a14.75 14.75 0 0 1-.543-.165 23.384 23.384 0 0 1-.453-.128c-.196-.059-.195-.059-.39-.12l-.276-.077V43.881h-.025a34.633 34.633 0 0 0-.031-.557 23.606 23.606 0 0 0-.4-2.994 22.743 22.743 0 0 0-1.492-4.708 22.567 22.567 0 0 0-4.593-6.748 21.865 21.865 0 0 0-6.882-4.706 21.175 21.175 0 0 0-8.115-1.722l-.411-.001zm9.15 33.69c.109.015.214.038.315.085a1.012 1.012 0 0 1 .574.771c.021.132.013.268.009.4a8.38 8.38 0 0 1-.026.476 8.767 8.767 0 0 1-1.564 4.282c-.306.437-.65.846-1.024 1.222a10.09 10.09 0 0 1-4.612 2.627c-1.32.343-2.704.427-4.055.254a10.422 10.422 0 0 1-2.67-.709 9.917 9.917 0 0 1-3.57-2.503 9.312 9.312 0 0 1-.775-.984 8.933 8.933 0 0 1-.731-1.288 8.648 8.648 0 0 1-.795-3.377c-.003-.104-.008-.211 0-.316a1.042 1.042 0 0 1 .254-.609.98.98 0 0 1 1.337-.125 1.023 1.023 0 0 1 .385.719c.007.151.006.303.014.454a6.547 6.547 0 0 0 .524 2.217c.257.595.599 1.15 1.006 1.648.325.398.691.759 1.087 1.081.312.253.642.482.987.684 2.592 1.522 5.945 1.538 8.553.047a7.982 7.982 0 0 0 1.069-.731 7.619 7.619 0 0 0 1.142-1.15 6.949 6.949 0 0 0 1.018-1.741 6.538 6.538 0 0 0 .467-2.425l.004-.084a1.012 1.012 0 0 1 .672-.876c.08-.028.158-.04.241-.05.082-.003.082-.003.164.001zm-70.51-12.426c-15.5.93-28.544-5.922-30.126-16.443C-1.156 15.689 11.64 4.024 29.14 1.235c17.501-2.79 33.123 4.345 34.864 15.922 1.575 10.475-8.749 21.021-23.691 25.001l.001.099a31.185 31.185 0 0 0 .042.833c.007.094.019.188.021.282.006.178.013.356.024.534.011.16.024.32.039.48.017.154.038.306.058.459.036.273.077.544.144.811a4.723 4.723 0 0 0 .449 1.128c.192.332.434.628.702.898l.047.05c.151.139.302.275.461.403.24.192.492.367.748.537.474.314.962.6 1.457.877l.041.023.588.735-.729.586c-.376.112-.755.216-1.135.309a11.193 11.193 0 0 1-2.562.355 8.575 8.575 0 0 1-2.995-.486 8.461 8.461 0 0 1-.96-.413 11.194 11.194 0 0 1-1.836-1.152 13.345 13.345 0 0 1-1.07-.934c-.23-.221-.454-.448-.672-.681-.121-.129-.246-.258-.36-.395a23.448 23.448 0 0 1-1.328-1.773c-.051-.076-.049-.077-.095-.155l-.277-.477-.072-.13c-.081-.177-.159-.357-.238-.535l-.003-.01-.092-.707zm52.409-7.804c3.557 0 6.444 3.201 6.444 7.145 0 3.944-2.887 7.146-6.444 7.146s-6.444-3.202-6.444-7.146 2.887-7.145 6.444-7.145zm18.062 0c3.557 0 6.444 3.201 6.444 7.145 0 3.944-2.887 7.146-6.444 7.146s-6.444-3.202-6.444-7.146 2.887-7.145 6.444-7.145zM83.12 42.029c1.915 0 3.47 1.601 3.47 3.573s-1.555 3.573-3.47 3.573c-1.915 0-3.47-1.601-3.47-3.573s1.555-3.573 3.47-3.573zm17.846 0c1.915 0 3.47 1.601 3.47 3.573s-1.555 3.573-3.47 3.573c-1.915 0-3.47-1.601-3.47-3.573s1.555-3.573 3.47-3.573zM17.019 28c-.368 1.65-1.848 5.008-5.178 5.799-2.572.611-4.153-.815-4.544-2.559-.424-1.891.722-3.532 2.121-4.575a3.473 3.473 0 0 1-1.446-2.099c-.421-1.875.867-3.637 3.184-4.187 1.917-.455 3.185.248 3.462 1.482.265 1.184-.534 2.275-1.828 2.582-.878.209-1.574-.042-1.718-.683a1.4 1.4 0 0 1 .044-.704s.287.227.894.083c.751-.179 1.086-.709.972-1.219-.14-.625-.892-.827-1.739-.626-1.054.25-2.06 1.096-1.713 2.642.232 1.036.871 1.56 1.483 1.813.245-.11.481-.183.688-.233.943-.224 1.48-.005 1.587.472.092.411-.144.935-1.166 1.178a3.255 3.255 0 0 1-1.548.004c-.837.771-1.58 1.883-1.27 3.264.276 1.234 1.267 2.125 2.944 1.726 2.598-.617 3.861-3.638 4.277-4.883-.353-.574-.615-1.153-.732-1.676-.107-.477.145-1.005.863-1.175.48-.114.702.127.846.769a2.77 2.77 0 0 1-.03.995c.209.331.443.622.735.951.616-1.983 1.369-3.877 1.737-3.964.591-.141 1.492.65 1.492.65-.815.644-1.689 2.376-2.333 4.158.804.658 1.627 1.103 2.139.982.43-.102.735-.577.95-1.151-.323-2.226.975-4.331 2.31-4.648.703-.167 1.257.204 1.39.796.114.51-.044 1.379-.854 1.745-.236-1.053-.672-1.348-.944-1.283-.495.117-.844 1.413-.538 2.778.232 1.037.712 1.529 1.351 1.377.756-.179 1.333-1.176 1.699-2.128-.265-2.095.877-4.166 2.221-4.486.671-.159 1.214.162 1.391.952.332 1.48-.986 2.885-2.173 3.444.265.734.673 1.053 1.281.909.96-.229 1.578-1.465 1.923-2.506-.125-1.267-.26-2.385-.406-3.035l-.055-.247s1.568-.286 1.778.652l.019.082c.238-.663.67-1.216 1.309-1.368.83-.197 1.526.504 1.755 1.524.497 2.22-.556 4.428-1.834 4.732-.368.087-.642.066-.883-.033.121 1.288.292 2.651.542 3.77.126.559.272 1.061.448 1.47-.464.11-1.797.392-1.978-.414-.16-.716-.342-3.206-.554-5.612-.504 1.107-1.311 2.192-2.441 2.46-1.008.24-1.685-.303-2.055-1.182-.491 1.082-1.281 2.148-2.381 2.409-.817.194-1.554-.117-1.988-1.013-.36.843-.875 1.555-1.54 1.713-.639.152-1.53-.295-2.4-1.024-.239.888-.384 1.668-.39 2.241 0 0-.701.028-.804-.433-.096-.427.065-1.436.341-2.61a10.315 10.315 0 0 1-.713-.848zm38.163-17.803c.068.157.185.527.266.889.424 1.892.37 4.451.739 6.42-.065.61-.387 3.077-1.352 3.307-.192.045-.333-.06-.422-.454-.14-.626-.091-1.607-.293-2.512-.258-1.152-.782-1.686-1.517-1.511-.767.182-1.287 1.016-1.643 2.054-.022-.099-.053-.386-.093-.567-.211-.938-1.779-.652-1.779-.652a6.2 6.2 0 0 1 .457 1.364c.07.31.119.618.155.921-.246.495-.637.996-1.225 1.135-.064.015-.128.031-.195.029a6.977 6.977 0 0 0-.126-.784c-.258-1.152-.871-2.011-1.526-1.855a.712.712 0 0 0-.423.291c-1.337.317-2.358 2.107-2.118 3.919-.214.889-.551 1.757-1.059 1.877-.415.099-.724-.452-1.03-1.817-.059-.263-.09-.706-.122-1.149.142-.64.177-1.237.081-1.665-.107-.477-.417-.733-.816-.638-.715.17-.909 1.75-.52 3.801-.238.92-.639 1.915-1.278 2.067-.464.11-.835-.27-1.012-1.059-.158-.708-.196-1.929-.236-3.08 1.201-.424 1.911-1.009 1.775-1.617-.114-.51-.739-.743-.739-.743s-.124.722-1.064 1.258c-.029-.582-.064-1.111-.137-1.44-.137-.609-.458-.914-1.688-.622.158.327.274.698.359 1.076.103.46.162.949.189 1.445-.611.128-.947.052-.947.052s-.1.457-.041.72c.078.345.432.348 1.026.224.02 1.364-.067 2.701.143 3.639.306 1.365 1.231 1.89 2.046 1.697.907-.216 1.539-1.275 1.914-2.36.407 1.245 1.031 1.955 1.951 1.736.731-.174 1.261-1.142 1.587-2.195.431.765 1.15 1.129 1.983.931 1.214-.289 1.742-1.54 1.835-2.775 0 0 .147-.018.243-.04.526-.125.949-.488 1.26-.915.04.788.053 1.518.194 2.146.111.493.339.612.595.552.495-.118 1.081-.881 1.081-.881a3.93 3.93 0 0 1-.383-1.035c-.284-1.267.317-3.541.988-3.7.208-.049.377.257.492.767.057.255.092.504.115.751l.098 1.469c.024.246.059.496.116.751.158.707.63 1.236 1.381 1.058 1.317-.313 2.07-2.634 2.178-3.956.228.157.536.175.909.086-.505-2.253.089-6.136-.298-7.864-.1-.444-1.001-.58-1.607-.583l-.467.037zM33.729 22.293c.415-.099.711.246.885 1.02.287 1.283-.222 2.616-.797 2.753-.191.045-.695-.025-.961-1.21-.025-.115-.051-.23-.061-.349.05-1.277.439-2.097.934-2.214zm-5.187.955c.271-.065.511.104.588.449.137.609-.338 1.345-1.275 1.966-.255-1.36.159-2.29.687-2.415zm18.032-.403c-.607.144-1.062-.458-1.239-1.248-.217-.97.001-2.097.644-2.457.001.155.038.32.075.484.147.658.554 1.497 1.268 1.83-.017.749-.253 1.273-.748 1.391zm9.877-1.654c.103.461.496.714 1.039.585.799-.19.973-.993.847-1.553-.125-.559-.461-.93-.988-.805-.543.13-1.108.836-.898 1.773zm-14.21-5.442c-.104-.461-.497-.714-1.056-.581-.783.186-.972.993-.847 1.552.126.56.461.93.908.824.56-.133 1.172-1.006.995-1.795z" fill="#fff" fill-opacity=".6"></path></svg>';
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug;

/**
 * Autoloader checking if the class is really defined in the file found.
 *
 * The ClassLoader will wrap all registered autoloaders
 * and will throw an exception if a file is found but does
 * not declare the class.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Christophe Coevoet <stof@notk.org>
 * @author Nicolas Grekas <p@tchwork.com>
 */
class DebugClassLoader
{
    private $classLoader;
    private $isFinder;
    private $loaded = array();
    private static $caseCheck;
    private static $checkedClasses = array();
    private static $final = array();
    private static $finalMethods = array();
    private static $deprecated = array();
    private static $internal = array();
    private static $internalMethods = array();
    private static $php7Reserved = array('int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1);
    private static $darwinCache = array('/' => array('/', array()));

    public function __construct(callable $classLoader)
    {
        $this->classLoader = $classLoader;
        $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile');

        if (!isset(self::$caseCheck)) {
            $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR);
            $i = strrpos($file, DIRECTORY_SEPARATOR);
            $dir = substr($file, 0, 1 + $i);
            $file = substr($file, 1 + $i);
            $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file);
            $test = realpath($dir.$test);

            if (false === $test || false === $i) {
                // filesystem is case sensitive
                self::$caseCheck = 0;
            } elseif (substr($test, -strlen($file)) === $file) {
                // filesystem is case insensitive and realpath() normalizes the case of characters
                self::$caseCheck = 1;
            } elseif (false !== stripos(PHP_OS, 'darwin')) {
                // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters
                self::$caseCheck = 2;
            } else {
                // filesystem case checks failed, fallback to disabling them
                self::$caseCheck = 0;
            }
        }
    }

    /**
     * Gets the wrapped class loader.
     *
     * @return callable The wrapped class loader
     */
    public function getClassLoader()
    {
        return $this->classLoader;
    }

    /**
     * Wraps all autoloaders.
     */
    public static function enable()
    {
        // Ensures we don't hit https://bugs.php.net/42098
        class_exists('Symfony\Component\Debug\ErrorHandler');
        class_exists('Psr\Log\LogLevel');

        if (!is_array($functions = spl_autoload_functions())) {
            return;
        }

        foreach ($functions as $function) {
            spl_autoload_unregister($function);
        }

        foreach ($functions as $function) {
            if (!is_array($function) || !$function[0] instanceof self) {
                $function = array(new static($function), 'loadClass');
            }

            spl_autoload_register($function);
        }
    }

    /**
     * Disables the wrapping.
     */
    public static function disable()
    {
        if (!is_array($functions = spl_autoload_functions())) {
            return;
        }

        foreach ($functions as $function) {
            spl_autoload_unregister($function);
        }

        foreach ($functions as $function) {
            if (is_array($function) && $function[0] instanceof self) {
                $function = $function[0]->getClassLoader();
            }

            spl_autoload_register($function);
        }
    }

    /**
     * Loads the given class or interface.
     *
     * @param string $class The name of the class
     *
     * @return bool|null True, if loaded
     *
     * @throws \RuntimeException
     */
    public function loadClass($class)
    {
        $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);

        try {
            if ($this->isFinder && !isset($this->loaded[$class])) {
                $this->loaded[$class] = true;
                if ($file = $this->classLoader[0]->findFile($class) ?: false) {
                    $wasCached = \function_exists('opcache_is_script_cached') && opcache_is_script_cached($file);

                    require $file;

                    if ($wasCached) {
                        return;
                    }
                }
            } else {
                call_user_func($this->classLoader, $class);
                $file = false;
            }
        } finally {
            error_reporting($e);
        }

        $this->checkClass($class, $file);
    }

    private function checkClass($class, $file = null)
    {
        $exists = null === $file || \class_exists($class, false) || \interface_exists($class, false) || \trait_exists($class, false);

        if (null !== $file && $class && '\\' === $class[0]) {
            $class = substr($class, 1);
        }

        if ($exists) {
            if (isset(self::$checkedClasses[$class])) {
                return;
            }
            self::$checkedClasses[$class] = true;

            $refl = new \ReflectionClass($class);
            if (null === $file && $refl->isInternal()) {
                return;
            }
            $name = $refl->getName();

            if ($name !== $class && 0 === \strcasecmp($name, $class)) {
                throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
            }

            // Don't trigger deprecations for classes in the same vendor
            if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) {
                $len = 0;
                $ns = '';
            } else {
                $ns = \substr($name, 0, $len);
            }

            // Detect annotations on the class
            if (false !== $doc = $refl->getDocComment()) {
                foreach (array('final', 'deprecated', 'internal') as $annotation) {
                    if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) {
                        self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
                    }
                }
            }

            $parentAndTraits = \class_uses($name, false);
            if ($parent = \get_parent_class($class)) {
                $parentAndTraits[] = $parent;

                if (!isset(self::$checkedClasses[$parent])) {
                    $this->checkClass($parent);
                }

                if (isset(self::$final[$parent])) {
                    @trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED);
                }
            }

            // Detect if the parent is annotated
            foreach ($parentAndTraits + $this->getOwnInterfaces($name, $parent) as $use) {
                if (!isset(self::$checkedClasses[$use])) {
                    $this->checkClass($use);
                }
                if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) {
                    $type = class_exists($name, false) ? 'class' : (interface_exists($name, false) ? 'interface' : 'trait');
                    $verb = class_exists($use, false) || interface_exists($name, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');

                    @trigger_error(sprintf('The "%s" %s %s "%s" that is deprecated%s.', $name, $type, $verb, $use, self::$deprecated[$use]), E_USER_DEPRECATED);
                }
                if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) {
                    @trigger_error(sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $name), E_USER_DEPRECATED);
                }
            }

            // Inherit @final and @internal annotations for methods
            self::$finalMethods[$name] = array();
            self::$internalMethods[$name] = array();
            foreach ($parentAndTraits as $use) {
                foreach (array('finalMethods', 'internalMethods') as $property) {
                    if (isset(self::${$property}[$use])) {
                        self::${$property}[$name] = self::${$property}[$name] ? self::${$property}[$use] + self::${$property}[$name] : self::${$property}[$use];
                    }
                }
            }

            $isClass = \class_exists($name, false);
            foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
                if ($method->class !== $name) {
                    continue;
                }

                // Method from a trait
                if ($method->getFilename() !== $refl->getFileName()) {
                    continue;
                }

                if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) {
                    list($declaringClass, $message) = self::$finalMethods[$parent][$method->name];
                    @trigger_error(sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED);
                }

                foreach ($parentAndTraits as $use) {
                    if (isset(self::$internalMethods[$use][$method->name])) {
                        list($declaringClass, $message) = self::$internalMethods[$use][$method->name];
                        if (\strncmp($ns, $declaringClass, $len)) {
                            @trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED);
                        }
                    }
                }

                // Detect method annotations
                if (false === $doc = $method->getDocComment()) {
                    continue;
                }

                foreach (array('final', 'internal') as $annotation) {
                    if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) {
                        $message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
                        self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message);
                    }
                }
            }

            if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) {
                @trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
            }
        }

        if ($file) {
            if (!$exists) {
                if (false !== strpos($class, '/')) {
                    throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class));
                }

                throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
            }
            if (self::$caseCheck) {
                $real = explode('\\', $class.strrchr($file, '.'));
                $tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file));

                $i = count($tail) - 1;
                $j = count($real) - 1;

                while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
                    --$i;
                    --$j;
                }

                array_splice($tail, 0, $i + 1);
            }
            if (self::$caseCheck && $tail) {
                $tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail);
                $tailLen = strlen($tail);
                $real = $refl->getFileName();

                if (2 === self::$caseCheck) {
                    // realpath() on MacOSX doesn't normalize the case of characters

                    $i = 1 + strrpos($real, '/');
                    $file = substr($real, $i);
                    $real = substr($real, 0, $i);

                    if (isset(self::$darwinCache[$real])) {
                        $kDir = $real;
                    } else {
                        $kDir = strtolower($real);

                        if (isset(self::$darwinCache[$kDir])) {
                            $real = self::$darwinCache[$kDir][0];
                        } else {
                            $dir = getcwd();
                            chdir($real);
                            $real = getcwd().'/';
                            chdir($dir);

                            $dir = $real;
                            $k = $kDir;
                            $i = strlen($dir) - 1;
                            while (!isset(self::$darwinCache[$k])) {
                                self::$darwinCache[$k] = array($dir, array());
                                self::$darwinCache[$dir] = &self::$darwinCache[$k];

                                while ('/' !== $dir[--$i]) {
                                }
                                $k = substr($k, 0, ++$i);
                                $dir = substr($dir, 0, $i--);
                            }
                        }
                    }

                    $dirFiles = self::$darwinCache[$kDir][1];

                    if (isset($dirFiles[$file])) {
                        $kFile = $file;
                    } else {
                        $kFile = strtolower($file);

                        if (!isset($dirFiles[$kFile])) {
                            foreach (scandir($real, 2) as $f) {
                                if ('.' !== $f[0]) {
                                    $dirFiles[$f] = $f;
                                    if ($f === $file) {
                                        $kFile = $k = $file;
                                    } elseif ($f !== $k = strtolower($f)) {
                                        $dirFiles[$k] = $f;
                                    }
                                }
                            }
                            self::$darwinCache[$kDir][1] = $dirFiles;
                        }
                    }

                    $real .= $dirFiles[$kFile];
                }

                if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
                  && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
                ) {
                    throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)));
                }
            }
        }
    }

    /**
     * `class_implements` includes interfaces from the parents so we have to manually exclude them.
     *
     * @param string       $class
     * @param string|false $parent
     *
     * @return string[]
     */
    private function getOwnInterfaces($class, $parent)
    {
        $ownInterfaces = class_implements($class, false);

        if ($parent) {
            foreach (class_implements($parent, false) as $interface) {
                unset($ownInterfaces[$interface]);
            }
        }

        foreach ($ownInterfaces as $interface) {
            foreach (class_implements($interface) as $interface) {
                unset($ownInterfaces[$interface]);
            }
        }

        return $ownInterfaces;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug;

use Psr\Log\AbstractLogger;

/**
 * A buffering logger that stacks logs for later.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 */
class BufferingLogger extends AbstractLogger
{
    private $logs = array();

    public function log($level, $message, array $context = array())
    {
        $this->logs[] = array($level, $message, $context);
    }

    public function cleanLogs()
    {
        $logs = $this->logs;
        $this->logs = array();

        return $logs;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug;

use Psr\Log\LogLevel;
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\ContextErrorException;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\Debug\Exception\OutOfMemoryException;
use Symfony\Component\Debug\Exception\SilencedErrorContext;
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;

/**
 * A generic ErrorHandler for the PHP engine.
 *
 * Provides five bit fields that control how errors are handled:
 * - thrownErrors: errors thrown as \ErrorException
 * - loggedErrors: logged errors, when not @-silenced
 * - scopedErrors: errors thrown or logged with their local context
 * - tracedErrors: errors logged with their stack trace
 * - screamedErrors: never @-silenced errors
 *
 * Each error level can be logged by a dedicated PSR-3 logger object.
 * Screaming only applies to logging.
 * Throwing takes precedence over logging.
 * Uncaught exceptions are logged as E_ERROR.
 * E_DEPRECATED and E_USER_DEPRECATED levels never throw.
 * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
 * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
 * As errors have a performance cost, repeated errors are all logged, so that the developer
 * can see them and weight them as more important to fix than others of the same level.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 * @author GrÃ©goire Pineau <lyrixx@lyrixx.info>
 */
class ErrorHandler
{
    private $levels = array(
        E_DEPRECATED => 'Deprecated',
        E_USER_DEPRECATED => 'User Deprecated',
        E_NOTICE => 'Notice',
        E_USER_NOTICE => 'User Notice',
        E_STRICT => 'Runtime Notice',
        E_WARNING => 'Warning',
        E_USER_WARNING => 'User Warning',
        E_COMPILE_WARNING => 'Compile Warning',
        E_CORE_WARNING => 'Core Warning',
        E_USER_ERROR => 'User Error',
        E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
        E_COMPILE_ERROR => 'Compile Error',
        E_PARSE => 'Parse Error',
        E_ERROR => 'Error',
        E_CORE_ERROR => 'Core Error',
    );

    private $loggers = array(
        E_DEPRECATED => array(null, LogLevel::INFO),
        E_USER_DEPRECATED => array(null, LogLevel::INFO),
        E_NOTICE => array(null, LogLevel::WARNING),
        E_USER_NOTICE => array(null, LogLevel::WARNING),
        E_STRICT => array(null, LogLevel::WARNING),
        E_WARNING => array(null, LogLevel::WARNING),
        E_USER_WARNING => array(null, LogLevel::WARNING),
        E_COMPILE_WARNING => array(null, LogLevel::WARNING),
        E_CORE_WARNING => array(null, LogLevel::WARNING),
        E_USER_ERROR => array(null, LogLevel::CRITICAL),
        E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
        E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
        E_PARSE => array(null, LogLevel::CRITICAL),
        E_ERROR => array(null, LogLevel::CRITICAL),
        E_CORE_ERROR => array(null, LogLevel::CRITICAL),
    );

    private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
    private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
    private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE
    private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE
    private $loggedErrors = 0;
    private $traceReflector;

    private $isRecursive = 0;
    private $isRoot = false;
    private $exceptionHandler;
    private $bootstrappingLogger;

    private static $reservedMemory;
    private static $stackedErrors = array();
    private static $stackedErrorLevels = array();
    private static $toStringException = null;
    private static $silencedErrorCache = array();
    private static $silencedErrorCount = 0;
    private static $exitCode = 0;

    /**
     * Registers the error handler.
     *
     * @param self|null $handler The handler to register
     * @param bool      $replace Whether to replace or not any existing handler
     *
     * @return self The registered error handler
     */
    public static function register(self $handler = null, $replace = true)
    {
        if (null === self::$reservedMemory) {
            self::$reservedMemory = str_repeat('x', 10240);
            register_shutdown_function(__CLASS__.'::handleFatalError');
        }

        if ($handlerIsNew = null === $handler) {
            $handler = new static();
        }

        if (null === $prev = set_error_handler(array($handler, 'handleError'))) {
            restore_error_handler();
            // Specifying the error types earlier would expose us to https://bugs.php.net/63206
            set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors);
            $handler->isRoot = true;
        }

        if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) {
            $handler = $prev[0];
            $replace = false;
        }
        if (!$replace && $prev) {
            restore_error_handler();
        }
        if (is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] === $handler) {
            restore_exception_handler();
        } else {
            $handler->setExceptionHandler($prev);
        }

        $handler->throwAt(E_ALL & $handler->thrownErrors, true);

        return $handler;
    }

    public function __construct(BufferingLogger $bootstrappingLogger = null)
    {
        if ($bootstrappingLogger) {
            $this->bootstrappingLogger = $bootstrappingLogger;
            $this->setDefaultLogger($bootstrappingLogger);
        }
        $this->traceReflector = new \ReflectionProperty('Exception', 'trace');
        $this->traceReflector->setAccessible(true);
    }

    /**
     * Sets a logger to non assigned errors levels.
     *
     * @param LoggerInterface $logger  A PSR-3 logger to put as default for the given levels
     * @param array|int       $levels  An array map of E_* to LogLevel::* or an integer bit field of E_* constants
     * @param bool            $replace Whether to replace or not any existing logger
     */
    public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
    {
        $loggers = array();

        if (is_array($levels)) {
            foreach ($levels as $type => $logLevel) {
                if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
                    $loggers[$type] = array($logger, $logLevel);
                }
            }
        } else {
            if (null === $levels) {
                $levels = E_ALL;
            }
            foreach ($this->loggers as $type => $log) {
                if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
                    $log[0] = $logger;
                    $loggers[$type] = $log;
                }
            }
        }

        $this->setLoggers($loggers);
    }

    /**
     * Sets a logger for each error level.
     *
     * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
     *
     * @return array The previous map
     *
     * @throws \InvalidArgumentException
     */
    public function setLoggers(array $loggers)
    {
        $prevLogged = $this->loggedErrors;
        $prev = $this->loggers;
        $flush = array();

        foreach ($loggers as $type => $log) {
            if (!isset($prev[$type])) {
                throw new \InvalidArgumentException('Unknown error type: '.$type);
            }
            if (!is_array($log)) {
                $log = array($log);
            } elseif (!array_key_exists(0, $log)) {
                throw new \InvalidArgumentException('No logger provided');
            }
            if (null === $log[0]) {
                $this->loggedErrors &= ~$type;
            } elseif ($log[0] instanceof LoggerInterface) {
                $this->loggedErrors |= $type;
            } else {
                throw new \InvalidArgumentException('Invalid logger provided');
            }
            $this->loggers[$type] = $log + $prev[$type];

            if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
                $flush[$type] = $type;
            }
        }
        $this->reRegister($prevLogged | $this->thrownErrors);

        if ($flush) {
            foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
                $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR;
                if (!isset($flush[$type])) {
                    $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
                } elseif ($this->loggers[$type][0]) {
                    $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
                }
            }
        }

        return $prev;
    }

    /**
     * Sets a user exception handler.
     *
     * @param callable $handler A handler that will be called on Exception
     *
     * @return callable|null The previous exception handler
     */
    public function setExceptionHandler(callable $handler = null)
    {
        $prev = $this->exceptionHandler;
        $this->exceptionHandler = $handler;

        return $prev;
    }

    /**
     * Sets the PHP error levels that throw an exception when a PHP error occurs.
     *
     * @param int  $levels  A bit field of E_* constants for thrown errors
     * @param bool $replace Replace or amend the previous value
     *
     * @return int The previous value
     */
    public function throwAt($levels, $replace = false)
    {
        $prev = $this->thrownErrors;
        $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
        if (!$replace) {
            $this->thrownErrors |= $prev;
        }
        $this->reRegister($prev | $this->loggedErrors);

        return $prev;
    }

    /**
     * Sets the PHP error levels for which local variables are preserved.
     *
     * @param int  $levels  A bit field of E_* constants for scoped errors
     * @param bool $replace Replace or amend the previous value
     *
     * @return int The previous value
     */
    public function scopeAt($levels, $replace = false)
    {
        $prev = $this->scopedErrors;
        $this->scopedErrors = (int) $levels;
        if (!$replace) {
            $this->scopedErrors |= $prev;
        }

        return $prev;
    }

    /**
     * Sets the PHP error levels for which the stack trace is preserved.
     *
     * @param int  $levels  A bit field of E_* constants for traced errors
     * @param bool $replace Replace or amend the previous value
     *
     * @return int The previous value
     */
    public function traceAt($levels, $replace = false)
    {
        $prev = $this->tracedErrors;
        $this->tracedErrors = (int) $levels;
        if (!$replace) {
            $this->tracedErrors |= $prev;
        }

        return $prev;
    }

    /**
     * Sets the error levels where the @-operator is ignored.
     *
     * @param int  $levels  A bit field of E_* constants for screamed errors
     * @param bool $replace Replace or amend the previous value
     *
     * @return int The previous value
     */
    public function screamAt($levels, $replace = false)
    {
        $prev = $this->screamedErrors;
        $this->screamedErrors = (int) $levels;
        if (!$replace) {
            $this->screamedErrors |= $prev;
        }

        return $prev;
    }

    /**
     * Re-registers as a PHP error handler if levels changed.
     */
    private function reRegister($prev)
    {
        if ($prev !== $this->thrownErrors | $this->loggedErrors) {
            $handler = set_error_handler('var_dump');
            $handler = is_array($handler) ? $handler[0] : null;
            restore_error_handler();
            if ($handler === $this) {
                restore_error_handler();
                if ($this->isRoot) {
                    set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors);
                } else {
                    set_error_handler(array($this, 'handleError'));
                }
            }
        }
    }

    /**
     * Handles errors by filtering then logging them according to the configured bit fields.
     *
     * @param int    $type    One of the E_* constants
     * @param string $message
     * @param string $file
     * @param int    $line
     *
     * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
     *
     * @throws \ErrorException When $this->thrownErrors requests so
     *
     * @internal
     */
    public function handleError($type, $message, $file, $line)
    {
        // Level is the current error reporting level to manage silent error.
        // Strong errors are not authorized to be silenced.
        $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
        $log = $this->loggedErrors & $type;
        $throw = $this->thrownErrors & $type & $level;
        $type &= $level | $this->screamedErrors;

        if (!$type || (!$log && !$throw)) {
            return $type && $log;
        }
        $scope = $this->scopedErrors & $type;

        if (4 < $numArgs = func_num_args()) {
            $context = $scope ? (func_get_arg(4) ?: array()) : array();
            $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM
        } else {
            $context = array();
            $backtrace = null;
        }

        if (isset($context['GLOBALS']) && $scope) {
            $e = $context;                  // Whatever the signature of the method,
            unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
            $context = $e;
        }

        if (null !== $backtrace && $type & E_ERROR) {
            // E_ERROR fatal errors are triggered on HHVM when
            // hhvm.error_handling.call_user_handler_on_fatals=1
            // which is the way to get their backtrace.
            $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace'));

            return true;
        }

        $logMessage = $this->levels[$type].': '.$message;

        if (null !== self::$toStringException) {
            $errorAsException = self::$toStringException;
            self::$toStringException = null;
        } elseif (!$throw && !($type & $level)) {
            if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
                $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : array();
                $errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace);
            } elseif (isset(self::$silencedErrorCache[$id][$message])) {
                $lightTrace = null;
                $errorAsException = self::$silencedErrorCache[$id][$message];
                ++$errorAsException->count;
            } else {
                $lightTrace = array();
                $errorAsException = null;
            }

            if (100 < ++self::$silencedErrorCount) {
                self::$silencedErrorCache = $lightTrace = array();
                self::$silencedErrorCount = 1;
            }
            if ($errorAsException) {
                self::$silencedErrorCache[$id][$message] = $errorAsException;
            }
            if (null === $lightTrace) {
                return;
            }
        } else {
            if ($scope) {
                $errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context);
            } else {
                $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
            }

            // Clean the trace by removing function arguments and the first frames added by the error handler itself.
            if ($throw || $this->tracedErrors & $type) {
                $backtrace = $backtrace ?: $errorAsException->getTrace();
                $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
                $this->traceReflector->setValue($errorAsException, $lightTrace);
            } else {
                $this->traceReflector->setValue($errorAsException, array());
            }
        }

        if ($throw) {
            if (E_USER_ERROR & $type) {
                for ($i = 1; isset($backtrace[$i]); ++$i) {
                    if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
                        && '__toString' === $backtrace[$i]['function']
                        && '->' === $backtrace[$i]['type']
                        && !isset($backtrace[$i - 1]['class'])
                        && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
                    ) {
                        // Here, we know trigger_error() has been called from __toString().
                        // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead.
                        // A small convention allows working around the limitation:
                        // given a caught $e exception in __toString(), quitting the method with
                        // `return trigger_error($e, E_USER_ERROR);` allows this error handler
                        // to make $e get through the __toString() barrier.

                        foreach ($context as $e) {
                            if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) {
                                if (1 === $i) {
                                    // On HHVM
                                    $errorAsException = $e;
                                    break;
                                }
                                self::$toStringException = $e;

                                return true;
                            }
                        }

                        if (1 < $i) {
                            // On PHP (not on HHVM), display the original error message instead of the default one.
                            $this->handleException($errorAsException);

                            // Stop the process by giving back the error to the native handler.
                            return false;
                        }
                    }
                }
            }

            throw $errorAsException;
        }

        if ($this->isRecursive) {
            $log = 0;
        } elseif (self::$stackedErrorLevels) {
            self::$stackedErrors[] = array(
                $this->loggers[$type][0],
                ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG,
                $logMessage,
                $errorAsException ? array('exception' => $errorAsException) : array(),
            );
        } else {
            try {
                $this->isRecursive = true;
                $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
                $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? array('exception' => $errorAsException) : array());
            } finally {
                $this->isRecursive = false;
            }
        }

        return $type && $log;
    }

    /**
     * Handles an exception by logging then forwarding it to another handler.
     *
     * @param \Exception|\Throwable $exception An exception to handle
     * @param array                 $error     An array as returned by error_get_last()
     *
     * @internal
     */
    public function handleException($exception, array $error = null)
    {
        if (null === $error) {
            self::$exitCode = 255;
        }
        if (!$exception instanceof \Exception) {
            $exception = new FatalThrowableError($exception);
        }
        $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
        $handlerException = null;

        if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
            if ($exception instanceof FatalErrorException) {
                if ($exception instanceof FatalThrowableError) {
                    $error = array(
                        'type' => $type,
                        'message' => $message = $exception->getMessage(),
                        'file' => $exception->getFile(),
                        'line' => $exception->getLine(),
                    );
                } else {
                    $message = 'Fatal '.$exception->getMessage();
                }
            } elseif ($exception instanceof \ErrorException) {
                $message = 'Uncaught '.$exception->getMessage();
            } else {
                $message = 'Uncaught Exception: '.$exception->getMessage();
            }
        }
        if ($this->loggedErrors & $type) {
            try {
                $this->loggers[$type][0]->log($this->loggers[$type][1], $message, array('exception' => $exception));
            } catch (\Exception $handlerException) {
            } catch (\Throwable $handlerException) {
            }
        }
        if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
            foreach ($this->getFatalErrorHandlers() as $handler) {
                if ($e = $handler->handleError($error, $exception)) {
                    $exception = $e;
                    break;
                }
            }
        }
        try {
            if (null !== $this->exceptionHandler) {
                return \call_user_func($this->exceptionHandler, $exception);
            }
            $handlerException = $handlerException ?: $exception;
        } catch (\Exception $handlerException) {
        } catch (\Throwable $handlerException) {
        }
        $this->exceptionHandler = null;
        if ($exception === $handlerException) {
            self::$reservedMemory = null; // Disable the fatal error handler
            throw $exception; // Give back $exception to the native handler
        }
        $this->handleException($handlerException);
    }

    /**
     * Shutdown registered function for handling PHP fatal errors.
     *
     * @param array $error An array as returned by error_get_last()
     *
     * @internal
     */
    public static function handleFatalError(array $error = null)
    {
        if (null === self::$reservedMemory) {
            return;
        }

        $handler = self::$reservedMemory = null;
        $handlers = array();
        $previousHandler = null;
        $sameHandlerLimit = 10;

        while (!is_array($handler) || !$handler[0] instanceof self) {
            $handler = set_exception_handler('var_dump');
            restore_exception_handler();

            if (!$handler) {
                break;
            }
            restore_exception_handler();

            if ($handler !== $previousHandler) {
                array_unshift($handlers, $handler);
                $previousHandler = $handler;
            } elseif (0 === --$sameHandlerLimit) {
                $handler = null;
                break;
            }
        }
        foreach ($handlers as $h) {
            set_exception_handler($h);
        }
        if (!$handler) {
            return;
        }
        if ($handler !== $h) {
            $handler[0]->setExceptionHandler($h);
        }
        $handler = $handler[0];
        $handlers = array();

        if ($exit = null === $error) {
            $error = error_get_last();
        }

        try {
            while (self::$stackedErrorLevels) {
                static::unstackErrors();
            }
        } catch (\Exception $exception) {
            // Handled below
        } catch (\Throwable $exception) {
            // Handled below
        }

        if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
            // Let's not throw anymore but keep logging
            $handler->throwAt(0, true);
            $trace = isset($error['backtrace']) ? $error['backtrace'] : null;

            if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
                $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace);
            } else {
                $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
            }
        }

        try {
            if (isset($exception)) {
                self::$exitCode = 255;
                $handler->handleException($exception, $error);
            }
        } catch (FatalErrorException $e) {
            // Ignore this re-throw
        }

        if ($exit && self::$exitCode) {
            $exitCode = self::$exitCode;
            register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
        }
    }

    /**
     * Configures the error handler for delayed handling.
     * Ensures also that non-catchable fatal errors are never silenced.
     *
     * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724
     * PHP has a compile stage where it behaves unusually. To workaround it,
     * we plug an error handler that only stacks errors for later.
     *
     * The most important feature of this is to prevent
     * autoloading until unstackErrors() is called.
     *
     * @deprecated since version 3.4, to be removed in 4.0.
     */
    public static function stackErrors()
    {
        @trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);

        self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
    }

    /**
     * Unstacks stacked errors and forwards to the logger.
     *
     * @deprecated since version 3.4, to be removed in 4.0.
     */
    public static function unstackErrors()
    {
        @trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);

        $level = array_pop(self::$stackedErrorLevels);

        if (null !== $level) {
            $errorReportingLevel = error_reporting($level);
            if ($errorReportingLevel !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
                // If the user changed the error level, do not overwrite it
                error_reporting($errorReportingLevel);
            }
        }

        if (empty(self::$stackedErrorLevels)) {
            $errors = self::$stackedErrors;
            self::$stackedErrors = array();

            foreach ($errors as $error) {
                $error[0]->log($error[1], $error[2], $error[3]);
            }
        }
    }

    /**
     * Gets the fatal error handlers.
     *
     * Override this method if you want to define more fatal error handlers.
     *
     * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
     */
    protected function getFatalErrorHandlers()
    {
        return array(
            new UndefinedFunctionFatalErrorHandler(),
            new UndefinedMethodFatalErrorHandler(),
            new ClassNotFoundFatalErrorHandler(),
        );
    }

    private function cleanTrace($backtrace, $type, $file, $line, $throw)
    {
        $lightTrace = $backtrace;

        for ($i = 0; isset($backtrace[$i]); ++$i) {
            if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
                $lightTrace = array_slice($lightTrace, 1 + $i);
                break;
            }
        }
        if (!($throw || $this->scopedErrors & $type)) {
            for ($i = 0; isset($lightTrace[$i]); ++$i) {
                unset($lightTrace[$i]['args'], $lightTrace[$i]['object']);
            }
        }

        return $lightTrace;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

/**
 * Fatal Throwable Error.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 */
class FatalThrowableError extends FatalErrorException
{
    public function __construct(\Throwable $e)
    {
        if ($e instanceof \ParseError) {
            $message = 'Parse error: '.$e->getMessage();
            $severity = E_PARSE;
        } elseif ($e instanceof \TypeError) {
            $message = 'Type error: '.$e->getMessage();
            $severity = E_RECOVERABLE_ERROR;
        } else {
            $message = $e->getMessage();
            $severity = E_ERROR;
        }

        \ErrorException::__construct(
            $message,
            $e->getCode(),
            $severity,
            $e->getFile(),
            $e->getLine()
        );

        $this->setTrace($e->getTrace());
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

/**
 * Out of memory exception.
 *
 * @author Nicolas Grekas <p@tchwork.com>
 */
class OutOfMemoryException extends FatalErrorException
{
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

/**
 * FlattenException wraps a PHP Exception to be able to serialize it.
 *
 * Basically, this class removes all objects from the trace.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FlattenException
{
    private $message;
    private $code;
    private $previous;
    private $trace;
    private $class;
    private $statusCode;
    private $headers;
    private $file;
    private $line;

    public static function create(\Exception $exception, $statusCode = null, array $headers = array())
    {
        $e = new static();
        $e->setMessage($exception->getMessage());
        $e->setCode($exception->getCode());

        if ($exception instanceof HttpExceptionInterface) {
            $statusCode = $exception->getStatusCode();
            $headers = array_merge($headers, $exception->getHeaders());
        } elseif ($exception instanceof RequestExceptionInterface) {
            $statusCode = 400;
        }

        if (null === $statusCode) {
            $statusCode = 500;
        }

        $e->setStatusCode($statusCode);
        $e->setHeaders($headers);
        $e->setTraceFromException($exception);
        $e->setClass(get_class($exception));
        $e->setFile($exception->getFile());
        $e->setLine($exception->getLine());

        $previous = $exception->getPrevious();

        if ($previous instanceof \Exception) {
            $e->setPrevious(static::create($previous));
        } elseif ($previous instanceof \Throwable) {
            $e->setPrevious(static::create(new FatalThrowableError($previous)));
        }

        return $e;
    }

    public function toArray()
    {
        $exceptions = array();
        foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) {
            $exceptions[] = array(
                'message' => $exception->getMessage(),
                'class' => $exception->getClass(),
                'trace' => $exception->getTrace(),
            );
        }

        return $exceptions;
    }

    public function getStatusCode()
    {
        return $this->statusCode;
    }

    public function setStatusCode($code)
    {
        $this->statusCode = $code;
    }

    public function getHeaders()
    {
        return $this->headers;
    }

    public function setHeaders(array $headers)
    {
        $this->headers = $headers;
    }

    public function getClass()
    {
        return $this->class;
    }

    public function setClass($class)
    {
        $this->class = $class;
    }

    public function getFile()
    {
        return $this->file;
    }

    public function setFile($file)
    {
        $this->file = $file;
    }

    public function getLine()
    {
        return $this->line;
    }

    public function setLine($line)
    {
        $this->line = $line;
    }

    public function getMessage()
    {
        return $this->message;
    }

    public function setMessage($message)
    {
        $this->message = $message;
    }

    public function getCode()
    {
        return $this->code;
    }

    public function setCode($code)
    {
        $this->code = $code;
    }

    public function getPrevious()
    {
        return $this->previous;
    }

    public function setPrevious(FlattenException $previous)
    {
        $this->previous = $previous;
    }

    public function getAllPrevious()
    {
        $exceptions = array();
        $e = $this;
        while ($e = $e->getPrevious()) {
            $exceptions[] = $e;
        }

        return $exceptions;
    }

    public function getTrace()
    {
        return $this->trace;
    }

    public function setTraceFromException(\Exception $exception)
    {
        $this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine());
    }

    public function setTrace($trace, $file, $line)
    {
        $this->trace = array();
        $this->trace[] = array(
            'namespace' => '',
            'short_class' => '',
            'class' => '',
            'type' => '',
            'function' => '',
            'file' => $file,
            'line' => $line,
            'args' => array(),
        );
        foreach ($trace as $entry) {
            $class = '';
            $namespace = '';
            if (isset($entry['class'])) {
                $parts = explode('\\', $entry['class']);
                $class = array_pop($parts);
                $namespace = implode('\\', $parts);
            }

            $this->trace[] = array(
                'namespace' => $namespace,
                'short_class' => $class,
                'class' => isset($entry['class']) ? $entry['class'] : '',
                'type' => isset($entry['type']) ? $entry['type'] : '',
                'function' => isset($entry['function']) ? $entry['function'] : null,
                'file' => isset($entry['file']) ? $entry['file'] : null,
                'line' => isset($entry['line']) ? $entry['line'] : null,
                'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
            );
        }
    }

    private function flattenArgs($args, $level = 0, &$count = 0)
    {
        $result = array();
        foreach ($args as $key => $value) {
            if (++$count > 1e4) {
                return array('array', '*SKIPPED over 10000 entries*');
            }
            if ($value instanceof \__PHP_Incomplete_Class) {
                // is_object() returns false on PHP<=7.1
                $result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value));
            } elseif (is_object($value)) {
                $result[$key] = array('object', get_class($value));
            } elseif (is_array($value)) {
                if ($level > 10) {
                    $result[$key] = array('array', '*DEEP NESTED ARRAY*');
                } else {
                    $result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count));
                }
            } elseif (null === $value) {
                $result[$key] = array('null', null);
            } elseif (is_bool($value)) {
                $result[$key] = array('boolean', $value);
            } elseif (is_int($value)) {
                $result[$key] = array('integer', $value);
            } elseif (is_float($value)) {
                $result[$key] = array('float', $value);
            } elseif (is_resource($value)) {
                $result[$key] = array('resource', get_resource_type($value));
            } else {
                $result[$key] = array('string', (string) $value);
            }
        }

        return $result;
    }

    private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
    {
        $array = new \ArrayObject($value);

        return $array['__PHP_Incomplete_Class_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\Debug\Exception;

/**
 * Undefined Method Exception.
 *
 * @author GrÃ©goire Pineau <lyrixx@lyrixx.info>
 */
class UndefinedMethodException extends FatalErrorException
{
    public function __construct($message, \ErrorException $previous)
    {
        parent::__construct(
            $message,
            $previous->getCode(),
            $previous->getSeverity(),
            $previous->getFile(),
            $previous->getLine(),
            $previous->getPrevious()
        );
        $this->setTrace($previous->getTrace());
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

/**
 * Undefined Function Exception.
 *
 * @author Konstanton Myakshin <koc-dp@yandex.ru>
 */
class UndefinedFunctionException extends FatalErrorException
{
    public function __construct($message, \ErrorException $previous)
    {
        parent::__construct(
            $message,
            $previous->getCode(),
            $previous->getSeverity(),
            $previous->getFile(),
            $previous->getLine(),
            $previous->getPrevious()
        );
        $this->setTrace($previous->getTrace());
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

/**
 * Fatal Error Exception.
 *
 * @author Konstanton Myakshin <koc-dp@yandex.ru>
 */
class FatalErrorException extends \ErrorException
{
    public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null)
    {
        parent::__construct($message, $code, $severity, $filename, $lineno);

        if (null !== $trace) {
            if (!$traceArgs) {
                foreach ($trace as &$frame) {
                    unset($frame['args'], $frame['this'], $frame);
                }
            }

            $this->setTrace($trace);
        } elseif (null !== $traceOffset) {
            if (function_exists('xdebug_get_function_stack')) {
                $trace = xdebug_get_function_stack();
                if (0 < $traceOffset) {
                    array_splice($trace, -$traceOffset);
                }

                foreach ($trace as &$frame) {
                    if (!isset($frame['type'])) {
                        // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
                        if (isset($frame['class'])) {
                            $frame['type'] = '::';
                        }
                    } elseif ('dynamic' === $frame['type']) {
                        $frame['type'] = '->';
                    } elseif ('static' === $frame['type']) {
                        $frame['type'] = '::';
                    }

                    // XDebug also has a different name for the parameters array
                    if (!$traceArgs) {
                        unset($frame['params'], $frame['args']);
                    } elseif (isset($frame['params']) && !isset($frame['args'])) {
                        $frame['args'] = $frame['params'];
                        unset($frame['params']);
                    }
                }

                unset($frame);
                $trace = array_reverse($trace);
            } elseif (function_exists('symfony_debug_backtrace')) {
                $trace = symfony_debug_backtrace();
                if (0 < $traceOffset) {
                    array_splice($trace, 0, $traceOffset);
                }
            } else {
                $trace = array();
            }

            $this->setTrace($trace);
        }
    }

    protected function setTrace($trace)
    {
        $traceReflector = new \ReflectionProperty('Exception', 'trace');
        $traceReflector->setAccessible(true);
        $traceReflector->setValue($this, $trace);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

/**
 * Data Object that represents a Silenced Error.
 *
 * @author GrÃ©goire Pineau <lyrixx@lyrixx.info>
 */
class SilencedErrorContext implements \JsonSerializable
{
    public $count = 1;

    private $severity;
    private $file;
    private $line;
    private $trace;

    public function __construct($severity, $file, $line, array $trace = array(), $count = 1)
    {
        $this->severity = $severity;
        $this->file = $file;
        $this->line = $line;
        $this->trace = $trace;
        $this->count = $count;
    }

    public function getSeverity()
    {
        return $this->severity;
    }

    public function getFile()
    {
        return $this->file;
    }

    public function getLine()
    {
        return $this->line;
    }

    public function getTrace()
    {
        return $this->trace;
    }

    public function JsonSerialize()
    {
        return array(
            'severity' => $this->severity,
            'file' => $this->file,
            'line' => $this->line,
            'trace' => $this->trace,
            'count' => $this->count,
        );
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

/**
 * Class (or Trait or Interface) Not Found Exception.
 *
 * @author Konstanton Myakshin <koc-dp@yandex.ru>
 */
class ClassNotFoundException extends FatalErrorException
{
    public function __construct($message, \ErrorException $previous)
    {
        parent::__construct(
            $message,
            $previous->getCode(),
            $previous->getSeverity(),
            $previous->getFile(),
            $previous->getLine(),
            $previous->getPrevious()
        );
        $this->setTrace($previous->getTrace());
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Debug\Exception;

/**
 * Error Exception with Variable Context.
 *
 * @author Christian Sciberras <uuf6429@gmail.com>
 *
 * @deprecated since version 3.3. Instead, \ErrorException will be used directly in 4.0.
 */
class ContextErrorException extends \ErrorException
{
    private $context = array();

    public function __construct($message, $code, $severity, $filename, $lineno, $context = array())
    {
        parent::__construct($message, $code, $severity, $filename, $lineno);
        $this->context = $context;
    }

    /**
     * @return array Array of variables that existed when the exception occurred
     */
    public function getContext()
    {
        @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED);

        return $this->context;
    }
}
{
    "name": "symfony/debug",
    "type": "library",
    "description": "Symfony Debug Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": "^5.5.9|>=7.0.8",
        "psr/log": "~1.0"
    },
    "conflict": {
        "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
    },
    "require-dev": {
        "symfony/http-kernel": "~2.8|~3.0|~4.0"
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\Debug\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "3.4-dev"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console;

/**
 * Contains all events dispatched by an Application.
 *
 * @author Francesco Levorato <git@flevour.net>
 */
final class ConsoleEvents
{
    /**
     * The COMMAND event allows you to attach listeners before any command is
     * executed by the console. It also allows you to modify the command, input and output
     * before they are handled to the command.
     *
     * @Event("Symfony\Component\Console\Event\ConsoleCommandEvent")
     */
    const COMMAND = 'console.command';

    /**
     * The TERMINATE event allows you to attach listeners after a command is
     * executed by the console.
     *
     * @Event("Symfony\Component\Console\Event\ConsoleTerminateEvent")
     */
    const TERMINATE = 'console.terminate';

    /**
     * The EXCEPTION event occurs when an uncaught exception appears
     * while executing Command#run().
     *
     * This event allows you to deal with the exception or
     * to modify the thrown exception.
     *
     * @Event("Symfony\Component\Console\Event\ConsoleExceptionEvent")
     *
     * @deprecated The console.exception event is deprecated since version 3.3 and will be removed in 4.0. Use the console.error event instead.
     */
    const EXCEPTION = 'console.exception';

    /**
     * The ERROR event occurs when an uncaught exception or error appears.
     *
     * This event allows you to deal with the exception/error or
     * to modify the thrown exception.
     *
     * @Event("Symfony\Component\Console\Event\ConsoleErrorEvent")
     */
    const ERROR = 'console.error';
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console;

use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\DebugFormatterHelper;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StreamableInputInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputAwareInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * An Application is the container for a collection of commands.
 *
 * It is the main entry point of a Console application.
 *
 * This class is optimized for a standard CLI environment.
 *
 * Usage:
 *
 *     $app = new Application('myapp', '1.0 (stable)');
 *     $app->add(new SimpleCommand());
 *     $app->run();
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Application
{
    private $commands = array();
    private $wantHelps = false;
    private $runningCommand;
    private $name;
    private $version;
    private $commandLoader;
    private $catchExceptions = true;
    private $autoExit = true;
    private $definition;
    private $helperSet;
    private $dispatcher;
    private $terminal;
    private $defaultCommand;
    private $singleCommand;
    private $initialized;

    /**
     * @param string $name    The name of the application
     * @param string $version The version of the application
     */
    public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
    {
        $this->name = $name;
        $this->version = $version;
        $this->terminal = new Terminal();
        $this->defaultCommand = 'list';
    }

    public function setDispatcher(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function setCommandLoader(CommandLoaderInterface $commandLoader)
    {
        $this->commandLoader = $commandLoader;
    }

    /**
     * Runs the current application.
     *
     * @return int 0 if everything went fine, or an error code
     *
     * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}.
     */
    public function run(InputInterface $input = null, OutputInterface $output = null)
    {
        putenv('LINES='.$this->terminal->getHeight());
        putenv('COLUMNS='.$this->terminal->getWidth());

        if (null === $input) {
            $input = new ArgvInput();
        }

        if (null === $output) {
            $output = new ConsoleOutput();
        }

        $renderException = function ($e) use ($output) {
            if (!$e instanceof \Exception) {
                $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
            }
            if ($output instanceof ConsoleOutputInterface) {
                $this->renderException($e, $output->getErrorOutput());
            } else {
                $this->renderException($e, $output);
            }
        };
        if ($phpHandler = set_exception_handler($renderException)) {
            restore_exception_handler();
            if (!is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
                $debugHandler = true;
            } elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
                $phpHandler[0]->setExceptionHandler($debugHandler);
            }
        }

        if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) {
            @trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED);
        }

        $this->configureIO($input, $output);

        try {
            $exitCode = $this->doRun($input, $output);
        } catch (\Exception $e) {
            if (!$this->catchExceptions) {
                throw $e;
            }

            $renderException($e);

            $exitCode = $e->getCode();
            if (is_numeric($exitCode)) {
                $exitCode = (int) $exitCode;
                if (0 === $exitCode) {
                    $exitCode = 1;
                }
            } else {
                $exitCode = 1;
            }
        } finally {
            // if the exception handler changed, keep it
            // otherwise, unregister $renderException
            if (!$phpHandler) {
                if (set_exception_handler($renderException) === $renderException) {
                    restore_exception_handler();
                }
                restore_exception_handler();
            } elseif (!$debugHandler) {
                $finalHandler = $phpHandler[0]->setExceptionHandler(null);
                if ($finalHandler !== $renderException) {
                    $phpHandler[0]->setExceptionHandler($finalHandler);
                }
            }
        }

        if ($this->autoExit) {
            if ($exitCode > 255) {
                $exitCode = 255;
            }

            exit($exitCode);
        }

        return $exitCode;
    }

    /**
     * Runs the current application.
     *
     * @return int 0 if everything went fine, or an error code
     */
    public function doRun(InputInterface $input, OutputInterface $output)
    {
        if (true === $input->hasParameterOption(array('--version', '-V'), true)) {
            $output->writeln($this->getLongVersion());

            return 0;
        }

        $name = $this->getCommandName($input);
        if (true === $input->hasParameterOption(array('--help', '-h'), true)) {
            if (!$name) {
                $name = 'help';
                $input = new ArrayInput(array('command_name' => $this->defaultCommand));
            } else {
                $this->wantHelps = true;
            }
        }

        if (!$name) {
            $name = $this->defaultCommand;
            $definition = $this->getDefinition();
            $definition->setArguments(array_merge(
                $definition->getArguments(),
                array(
                    'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
                )
            ));
        }

        try {
            $e = $this->runningCommand = null;
            // the command name MUST be the first element of the input
            $command = $this->find($name);
        } catch (\Exception $e) {
        } catch (\Throwable $e) {
        }
        if (null !== $e) {
            if (null !== $this->dispatcher) {
                $event = new ConsoleErrorEvent($input, $output, $e);
                $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
                $e = $event->getError();

                if (0 === $event->getExitCode()) {
                    return 0;
                }
            }

            throw $e;
        }

        $this->runningCommand = $command;
        $exitCode = $this->doRunCommand($command, $input, $output);
        $this->runningCommand = null;

        return $exitCode;
    }

    public function setHelperSet(HelperSet $helperSet)
    {
        $this->helperSet = $helperSet;
    }

    /**
     * Get the helper set associated with the command.
     *
     * @return HelperSet The HelperSet instance associated with this command
     */
    public function getHelperSet()
    {
        if (!$this->helperSet) {
            $this->helperSet = $this->getDefaultHelperSet();
        }

        return $this->helperSet;
    }

    public function setDefinition(InputDefinition $definition)
    {
        $this->definition = $definition;
    }

    /**
     * Gets the InputDefinition related to this Application.
     *
     * @return InputDefinition The InputDefinition instance
     */
    public function getDefinition()
    {
        if (!$this->definition) {
            $this->definition = $this->getDefaultInputDefinition();
        }

        if ($this->singleCommand) {
            $inputDefinition = $this->definition;
            $inputDefinition->setArguments();

            return $inputDefinition;
        }

        return $this->definition;
    }

    /**
     * Gets the help message.
     *
     * @return string A help message
     */
    public function getHelp()
    {
        return $this->getLongVersion();
    }

    /**
     * Gets whether to catch exceptions or not during commands execution.
     *
     * @return bool Whether to catch exceptions or not during commands execution
     */
    public function areExceptionsCaught()
    {
        return $this->catchExceptions;
    }

    /**
     * Sets whether to catch exceptions or not during commands execution.
     *
     * @param bool $boolean Whether to catch exceptions or not during commands execution
     */
    public function setCatchExceptions($boolean)
    {
        $this->catchExceptions = (bool) $boolean;
    }

    /**
     * Gets whether to automatically exit after a command execution or not.
     *
     * @return bool Whether to automatically exit after a command execution or not
     */
    public function isAutoExitEnabled()
    {
        return $this->autoExit;
    }

    /**
     * Sets whether to automatically exit after a command execution or not.
     *
     * @param bool $boolean Whether to automatically exit after a command execution or not
     */
    public function setAutoExit($boolean)
    {
        $this->autoExit = (bool) $boolean;
    }

    /**
     * Gets the name of the application.
     *
     * @return string The application name
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Sets the application name.
     *
     * @param string $name The application name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * Gets the application version.
     *
     * @return string The application version
     */
    public function getVersion()
    {
        return $this->version;
    }

    /**
     * Sets the application version.
     *
     * @param string $version The application version
     */
    public function setVersion($version)
    {
        $this->version = $version;
    }

    /**
     * Returns the long version of the application.
     *
     * @return string The long application version
     */
    public function getLongVersion()
    {
        if ('UNKNOWN' !== $this->getName()) {
            if ('UNKNOWN' !== $this->getVersion()) {
                return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
            }

            return $this->getName();
        }

        return 'Console Tool';
    }

    /**
     * Registers a new command.
     *
     * @param string $name The command name
     *
     * @return Command The newly created command
     */
    public function register($name)
    {
        return $this->add(new Command($name));
    }

    /**
     * Adds an array of command objects.
     *
     * If a Command is not enabled it will not be added.
     *
     * @param Command[] $commands An array of commands
     */
    public function addCommands(array $commands)
    {
        foreach ($commands as $command) {
            $this->add($command);
        }
    }

    /**
     * Adds a command object.
     *
     * If a command with the same name already exists, it will be overridden.
     * If the command is not enabled it will not be added.
     *
     * @return Command|null The registered command if enabled or null
     */
    public function add(Command $command)
    {
        $this->init();

        $command->setApplication($this);

        if (!$command->isEnabled()) {
            $command->setApplication(null);

            return;
        }

        if (null === $command->getDefinition()) {
            throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
        }

        if (!$command->getName()) {
            throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($command)));
        }

        $this->commands[$command->getName()] = $command;

        foreach ($command->getAliases() as $alias) {
            $this->commands[$alias] = $command;
        }

        return $command;
    }

    /**
     * Returns a registered command by name or alias.
     *
     * @param string $name The command name or alias
     *
     * @return Command A Command object
     *
     * @throws CommandNotFoundException When given command name does not exist
     */
    public function get($name)
    {
        $this->init();

        if (!$this->has($name)) {
            throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
        }

        $command = $this->commands[$name];

        if ($this->wantHelps) {
            $this->wantHelps = false;

            $helpCommand = $this->get('help');
            $helpCommand->setCommand($command);

            return $helpCommand;
        }

        return $command;
    }

    /**
     * Returns true if the command exists, false otherwise.
     *
     * @param string $name The command name or alias
     *
     * @return bool true if the command exists, false otherwise
     */
    public function has($name)
    {
        $this->init();

        return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name)));
    }

    /**
     * Returns an array of all unique namespaces used by currently registered commands.
     *
     * It does not return the global namespace which always exists.
     *
     * @return string[] An array of namespaces
     */
    public function getNamespaces()
    {
        $namespaces = array();
        foreach ($this->all() as $command) {
            $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));

            foreach ($command->getAliases() as $alias) {
                $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
            }
        }

        return array_values(array_unique(array_filter($namespaces)));
    }

    /**
     * Finds a registered namespace by a name or an abbreviation.
     *
     * @param string $namespace A namespace or abbreviation to search for
     *
     * @return string A registered namespace
     *
     * @throws CommandNotFoundException When namespace is incorrect or ambiguous
     */
    public function findNamespace($namespace)
    {
        $allNamespaces = $this->getNamespaces();
        $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
        $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);

        if (empty($namespaces)) {
            $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);

            if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
                if (1 == count($alternatives)) {
                    $message .= "\n\nDid you mean this?\n    ";
                } else {
                    $message .= "\n\nDid you mean one of these?\n    ";
                }

                $message .= implode("\n    ", $alternatives);
            }

            throw new CommandNotFoundException($message, $alternatives);
        }

        $exact = in_array($namespace, $namespaces, true);
        if (count($namespaces) > 1 && !$exact) {
            throw new CommandNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
        }

        return $exact ? $namespace : reset($namespaces);
    }

    /**
     * Finds a command by name or alias.
     *
     * Contrary to get, this command tries to find the best
     * match if you give it an abbreviation of a name or alias.
     *
     * @param string $name A command name or a command alias
     *
     * @return Command A Command instance
     *
     * @throws CommandNotFoundException When command name is incorrect or ambiguous
     */
    public function find($name)
    {
        $this->init();

        $aliases = array();
        $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
        $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
        $commands = preg_grep('{^'.$expr.'}', $allCommands);

        if (empty($commands)) {
            $commands = preg_grep('{^'.$expr.'}i', $allCommands);
        }

        // if no commands matched or we just matched namespaces
        if (empty($commands) || count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) {
            if (false !== $pos = strrpos($name, ':')) {
                // check if a namespace exists and contains commands
                $this->findNamespace(substr($name, 0, $pos));
            }

            $message = sprintf('Command "%s" is not defined.', $name);

            if ($alternatives = $this->findAlternatives($name, $allCommands)) {
                if (1 == count($alternatives)) {
                    $message .= "\n\nDid you mean this?\n    ";
                } else {
                    $message .= "\n\nDid you mean one of these?\n    ";
                }
                $message .= implode("\n    ", $alternatives);
            }

            throw new CommandNotFoundException($message, $alternatives);
        }

        // filter out aliases for commands which are already on the list
        if (count($commands) > 1) {
            $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
            $commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands, &$aliases) {
                $commandName = $commandList[$nameOrAlias] instanceof Command ? $commandList[$nameOrAlias]->getName() : $nameOrAlias;
                $aliases[$nameOrAlias] = $commandName;

                return $commandName === $nameOrAlias || !in_array($commandName, $commands);
            }));
        }

        $exact = in_array($name, $commands, true) || isset($aliases[$name]);
        if (count($commands) > 1 && !$exact) {
            $usableWidth = $this->terminal->getWidth() - 10;
            $abbrevs = array_values($commands);
            $maxLen = 0;
            foreach ($abbrevs as $abbrev) {
                $maxLen = max(Helper::strlen($abbrev), $maxLen);
            }
            $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) {
                if (!$commandList[$cmd] instanceof Command) {
                    return $cmd;
                }
                $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();

                return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
            }, array_values($commands));
            $suggestions = $this->getAbbreviationSuggestions($abbrevs);

            throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands));
        }

        return $this->get($exact ? $name : reset($commands));
    }

    /**
     * Gets the commands (registered in the given namespace if provided).
     *
     * The array keys are the full names and the values the command instances.
     *
     * @param string $namespace A namespace name
     *
     * @return Command[] An array of Command instances
     */
    public function all($namespace = null)
    {
        $this->init();

        if (null === $namespace) {
            if (!$this->commandLoader) {
                return $this->commands;
            }

            $commands = $this->commands;
            foreach ($this->commandLoader->getNames() as $name) {
                if (!isset($commands[$name]) && $this->has($name)) {
                    $commands[$name] = $this->get($name);
                }
            }

            return $commands;
        }

        $commands = array();
        foreach ($this->commands as $name => $command) {
            if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
                $commands[$name] = $command;
            }
        }

        if ($this->commandLoader) {
            foreach ($this->commandLoader->getNames() as $name) {
                if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) {
                    $commands[$name] = $this->get($name);
                }
            }
        }

        return $commands;
    }

    /**
     * Returns an array of possible abbreviations given a set of names.
     *
     * @param array $names An array of names
     *
     * @return array An array of abbreviations
     */
    public static function getAbbreviations($names)
    {
        $abbrevs = array();
        foreach ($names as $name) {
            for ($len = strlen($name); $len > 0; --$len) {
                $abbrev = substr($name, 0, $len);
                $abbrevs[$abbrev][] = $name;
            }
        }

        return $abbrevs;
    }

    /**
     * Renders a caught exception.
     */
    public function renderException(\Exception $e, OutputInterface $output)
    {
        $output->writeln('', OutputInterface::VERBOSITY_QUIET);

        $this->doRenderException($e, $output);

        if (null !== $this->runningCommand) {
            $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
            $output->writeln('', OutputInterface::VERBOSITY_QUIET);
        }
    }

    protected function doRenderException(\Exception $e, OutputInterface $output)
    {
        do {
            $message = trim($e->getMessage());
            if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
                $title = sprintf('  [%s%s]  ', get_class($e), 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
                $len = Helper::strlen($title);
            } else {
                $len = 0;
            }

            $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX;
            // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
            if (defined('HHVM_VERSION') && $width > 1 << 31) {
                $width = 1 << 31;
            }
            $lines = array();
            foreach ('' !== $message ? preg_split('/\r?\n/', $message) : array() as $line) {
                foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
                    // pre-format lines to get the right string length
                    $lineLength = Helper::strlen($line) + 4;
                    $lines[] = array($line, $lineLength);

                    $len = max($lineLength, $len);
                }
            }

            $messages = array();
            if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
                $messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
            }
            $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
            if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
                $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::strlen($title))));
            }
            foreach ($lines as $line) {
                $messages[] = sprintf('<error>  %s  %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
            }
            $messages[] = $emptyLine;
            $messages[] = '';

            $output->writeln($messages, OutputInterface::VERBOSITY_QUIET);

            if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
                $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);

                // exception related properties
                $trace = $e->getTrace();

                for ($i = 0, $count = count($trace); $i < $count; ++$i) {
                    $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
                    $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
                    $function = $trace[$i]['function'];
                    $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
                    $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';

                    $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET);
                }

                $output->writeln('', OutputInterface::VERBOSITY_QUIET);
            }
        } while ($e = $e->getPrevious());
    }

    /**
     * Tries to figure out the terminal width in which this application runs.
     *
     * @return int|null
     *
     * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
     */
    protected function getTerminalWidth()
    {
        @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED);

        return $this->terminal->getWidth();
    }

    /**
     * Tries to figure out the terminal height in which this application runs.
     *
     * @return int|null
     *
     * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
     */
    protected function getTerminalHeight()
    {
        @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED);

        return $this->terminal->getHeight();
    }

    /**
     * Tries to figure out the terminal dimensions based on the current environment.
     *
     * @return array Array containing width and height
     *
     * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
     */
    public function getTerminalDimensions()
    {
        @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED);

        return array($this->terminal->getWidth(), $this->terminal->getHeight());
    }

    /**
     * Sets terminal dimensions.
     *
     * Can be useful to force terminal dimensions for functional tests.
     *
     * @param int $width  The width
     * @param int $height The height
     *
     * @return $this
     *
     * @deprecated since version 3.2, to be removed in 4.0. Set the COLUMNS and LINES env vars instead.
     */
    public function setTerminalDimensions($width, $height)
    {
        @trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Set the COLUMNS and LINES env vars instead.', __METHOD__), E_USER_DEPRECATED);

        putenv('COLUMNS='.$width);
        putenv('LINES='.$height);

        return $this;
    }

    /**
     * Configures the input and output instances based on the user arguments and options.
     */
    protected function configureIO(InputInterface $input, OutputInterface $output)
    {
        if (true === $input->hasParameterOption(array('--ansi'), true)) {
            $output->setDecorated(true);
        } elseif (true === $input->hasParameterOption(array('--no-ansi'), true)) {
            $output->setDecorated(false);
        }

        if (true === $input->hasParameterOption(array('--no-interaction', '-n'), true)) {
            $input->setInteractive(false);
        } elseif (function_exists('posix_isatty')) {
            $inputStream = null;

            if ($input instanceof StreamableInputInterface) {
                $inputStream = $input->getStream();
            }

            // This check ensures that calling QuestionHelper::setInputStream() works
            // To be removed in 4.0 (in the same time as QuestionHelper::setInputStream)
            if (!$inputStream && $this->getHelperSet()->has('question')) {
                $inputStream = $this->getHelperSet()->get('question')->getInputStream(false);
            }

            if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
                $input->setInteractive(false);
            }
        }

        switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
            case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break;
            case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break;
            case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break;
            case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break;
            default: $shellVerbosity = 0; break;
        }

        if (true === $input->hasParameterOption(array('--quiet', '-q'), true)) {
            $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
            $shellVerbosity = -1;
        } else {
            if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) {
                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
                $shellVerbosity = 3;
            } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) {
                $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
                $shellVerbosity = 2;
            } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) {
                $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
                $shellVerbosity = 1;
            }
        }

        if (-1 === $shellVerbosity) {
            $input->setInteractive(false);
        }

        putenv('SHELL_VERBOSITY='.$shellVerbosity);
        $_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
        $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
    }

    /**
     * Runs the current command.
     *
     * If an event dispatcher has been attached to the application,
     * events are also dispatched during the life-cycle of the command.
     *
     * @return int 0 if everything went fine, or an error code
     */
    protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
    {
        foreach ($command->getHelperSet() as $helper) {
            if ($helper instanceof InputAwareInterface) {
                $helper->setInput($input);
            }
        }

        if (null === $this->dispatcher) {
            return $command->run($input, $output);
        }

        // bind before the console.command event, so the listeners have access to input options/arguments
        try {
            $command->mergeApplicationDefinition();
            $input->bind($command->getDefinition());
        } catch (ExceptionInterface $e) {
            // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
        }

        $event = new ConsoleCommandEvent($command, $input, $output);
        $e = null;

        try {
            $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);

            if ($event->commandShouldRun()) {
                $exitCode = $command->run($input, $output);
            } else {
                $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
            }
        } catch (\Exception $e) {
        } catch (\Throwable $e) {
        }
        if (null !== $e) {
            if ($this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) {
                $x = $e instanceof \Exception ? $e : new FatalThrowableError($e);
                $event = new ConsoleExceptionEvent($command, $input, $output, $x, $x->getCode());
                $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);

                if ($x !== $event->getException()) {
                    $e = $event->getException();
                }
            }
            $event = new ConsoleErrorEvent($input, $output, $e, $command);
            $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
            $e = $event->getError();

            if (0 === $exitCode = $event->getExitCode()) {
                $e = null;
            }
        }

        $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
        $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);

        if (null !== $e) {
            throw $e;
        }

        return $event->getExitCode();
    }

    /**
     * Gets the name of the command based on input.
     *
     * @return string The command name
     */
    protected function getCommandName(InputInterface $input)
    {
        return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument();
    }

    /**
     * Gets the default input definition.
     *
     * @return InputDefinition An InputDefinition instance
     */
    protected function getDefaultInputDefinition()
    {
        return new InputDefinition(array(
            new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),

            new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
            new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
            new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
            new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
            new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
            new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
            new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
        ));
    }

    /**
     * Gets the default commands that should always be available.
     *
     * @return Command[] An array of default Command instances
     */
    protected function getDefaultCommands()
    {
        return array(new HelpCommand(), new ListCommand());
    }

    /**
     * Gets the default helper set with the helpers that should always be available.
     *
     * @return HelperSet A HelperSet instance
     */
    protected function getDefaultHelperSet()
    {
        return new HelperSet(array(
            new FormatterHelper(),
            new DebugFormatterHelper(),
            new ProcessHelper(),
            new QuestionHelper(),
        ));
    }

    /**
     * Returns abbreviated suggestions in string format.
     *
     * @param array $abbrevs Abbreviated suggestions to convert
     *
     * @return string A formatted string of abbreviated suggestions
     */
    private function getAbbreviationSuggestions($abbrevs)
    {
        return '    '.implode("\n    ", $abbrevs);
    }

    /**
     * Returns the namespace part of the command name.
     *
     * This method is not part of public API and should not be used directly.
     *
     * @param string $name  The full name of the command
     * @param string $limit The maximum number of parts of the namespace
     *
     * @return string The namespace of the command
     */
    public function extractNamespace($name, $limit = null)
    {
        $parts = explode(':', $name);
        array_pop($parts);

        return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
    }

    /**
     * Finds alternative of $name among $collection,
     * if nothing is found in $collection, try in $abbrevs.
     *
     * @param string   $name       The string
     * @param iterable $collection The collection
     *
     * @return string[] A sorted array of similar string
     */
    private function findAlternatives($name, $collection)
    {
        $threshold = 1e3;
        $alternatives = array();

        $collectionParts = array();
        foreach ($collection as $item) {
            $collectionParts[$item] = explode(':', $item);
        }

        foreach (explode(':', $name) as $i => $subname) {
            foreach ($collectionParts as $collectionName => $parts) {
                $exists = isset($alternatives[$collectionName]);
                if (!isset($parts[$i]) && $exists) {
                    $alternatives[$collectionName] += $threshold;
                    continue;
                } elseif (!isset($parts[$i])) {
                    continue;
                }

                $lev = levenshtein($subname, $parts[$i]);
                if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
                    $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
                } elseif ($exists) {
                    $alternatives[$collectionName] += $threshold;
                }
            }
        }

        foreach ($collection as $item) {
            $lev = levenshtein($name, $item);
            if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
                $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
            }
        }

        $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
        ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE);

        return array_keys($alternatives);
    }

    /**
     * Sets the default Command name.
     *
     * @param string $commandName     The Command name
     * @param bool   $isSingleCommand Set to true if there is only one command in this application
     *
     * @return self
     */
    public function setDefaultCommand($commandName, $isSingleCommand = false)
    {
        $this->defaultCommand = $commandName;

        if ($isSingleCommand) {
            // Ensure the command exist
            $this->find($commandName);

            $this->singleCommand = true;
        }

        return $this;
    }

    private function splitStringByWidth($string, $width)
    {
        // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
        // additionally, array_slice() is not enough as some character has doubled width.
        // we need a function to split string not by character count but by string width
        if (false === $encoding = mb_detect_encoding($string, null, true)) {
            return str_split($string, $width);
        }

        $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
        $lines = array();
        $line = '';
        foreach (preg_split('//u', $utf8String) as $char) {
            // test if $char could be appended to current line
            if (mb_strwidth($line.$char, 'utf8') <= $width) {
                $line .= $char;
                continue;
            }
            // if not, push current line to array and make new line
            $lines[] = str_pad($line, $width);
            $line = $char;
        }

        $lines[] = count($lines) ? str_pad($line, $width) : $line;

        mb_convert_variables($encoding, 'utf8', $lines);

        return $lines;
    }

    /**
     * Returns all namespaces of the command name.
     *
     * @param string $name The full name of the command
     *
     * @return string[] The namespaces of the command
     */
    private function extractAllNamespaces($name)
    {
        // -1 as third argument is needed to skip the command short name when exploding
        $parts = explode(':', $name, -1);
        $namespaces = array();

        foreach ($parts as $part) {
            if (count($namespaces)) {
                $namespaces[] = end($namespaces).':'.$part;
            } else {
                $namespaces[] = $part;
            }
        }

        return $namespaces;
    }

    private function init()
    {
        if ($this->initialized) {
            return;
        }
        $this->initialized = true;

        foreach ($this->getDefaultCommands() as $command) {
            $this->add($command);
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Logger;

use Psr\Log\AbstractLogger;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;

/**
 * PSR-3 compliant console logger.
 *
 * @author KÃ©vin Dunglas <dunglas@gmail.com>
 *
 * @see http://www.php-fig.org/psr/psr-3/
 */
class ConsoleLogger extends AbstractLogger
{
    const INFO = 'info';
    const ERROR = 'error';

    private $output;
    private $verbosityLevelMap = array(
        LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL,
        LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL,
        LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL,
        LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL,
        LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL,
        LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE,
        LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE,
        LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG,
    );
    private $formatLevelMap = array(
        LogLevel::EMERGENCY => self::ERROR,
        LogLevel::ALERT => self::ERROR,
        LogLevel::CRITICAL => self::ERROR,
        LogLevel::ERROR => self::ERROR,
        LogLevel::WARNING => self::INFO,
        LogLevel::NOTICE => self::INFO,
        LogLevel::INFO => self::INFO,
        LogLevel::DEBUG => self::INFO,
    );
    private $errored = false;

    public function __construct(OutputInterface $output, array $verbosityLevelMap = array(), array $formatLevelMap = array())
    {
        $this->output = $output;
        $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap;
        $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap;
    }

    /**
     * {@inheritdoc}
     */
    public function log($level, $message, array $context = array())
    {
        if (!isset($this->verbosityLevelMap[$level])) {
            throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level));
        }

        $output = $this->output;

        // Write to the error output if necessary and available
        if (self::ERROR === $this->formatLevelMap[$level]) {
            if ($this->output instanceof ConsoleOutputInterface) {
                $output = $output->getErrorOutput();
            }
            $this->errored = true;
        }

        // the if condition check isn't necessary -- it's the same one that $output will do internally anyway.
        // We only do it for efficiency here as the message formatting is relatively expensive.
        if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) {
            $output->writeln(sprintf('<%1$s>[%2$s] %3$s</%1$s>', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]);
        }
    }

    /**
     * Returns true when any messages have been logged at error levels.
     *
     * @return bool
     */
    public function hasErrored()
    {
        return $this->errored;
    }

    /**
     * Interpolates context values into the message placeholders.
     *
     * @author PHP Framework Interoperability Group
     *
     * @param string $message
     * @param array  $context
     *
     * @return string
     */
    private function interpolate($message, array $context)
    {
        if (false === strpos($message, '{')) {
            return $message;
        }

        $replacements = array();
        foreach ($context as $key => $val) {
            if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) {
                $replacements["{{$key}}"] = $val;
            } elseif ($val instanceof \DateTimeInterface) {
                $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339);
            } elseif (\is_object($val)) {
                $replacements["{{$key}}"] = '[object '.\get_class($val).']';
            } else {
                $replacements["{{$key}}"] = '['.\gettype($val).']';
            }
        }

        return strtr($message, $replacements);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

/**
 * StreamableInputInterface is the interface implemented by all input classes
 * that have an input stream.
 *
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
interface StreamableInputInterface extends InputInterface
{
    /**
     * Sets the input stream to read from when interacting with the user.
     *
     * This is mainly useful for testing purpose.
     *
     * @param resource $stream The input stream
     */
    public function setStream($stream);

    /**
     * Returns the input stream.
     *
     * @return resource|null
     */
    public function getStream();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\InvalidOptionException;

/**
 * ArrayInput represents an input provided as an array.
 *
 * Usage:
 *
 *     $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar'));
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ArrayInput extends Input
{
    private $parameters;

    public function __construct(array $parameters, InputDefinition $definition = null)
    {
        $this->parameters = $parameters;

        parent::__construct($definition);
    }

    /**
     * {@inheritdoc}
     */
    public function getFirstArgument()
    {
        foreach ($this->parameters as $key => $value) {
            if ($key && '-' === $key[0]) {
                continue;
            }

            return $value;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasParameterOption($values, $onlyParams = false)
    {
        $values = (array) $values;

        foreach ($this->parameters as $k => $v) {
            if (!is_int($k)) {
                $v = $k;
            }

            if ($onlyParams && '--' === $v) {
                return false;
            }

            if (in_array($v, $values)) {
                return true;
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getParameterOption($values, $default = false, $onlyParams = false)
    {
        $values = (array) $values;

        foreach ($this->parameters as $k => $v) {
            if ($onlyParams && ('--' === $k || (is_int($k) && '--' === $v))) {
                return false;
            }

            if (is_int($k)) {
                if (in_array($v, $values)) {
                    return true;
                }
            } elseif (in_array($k, $values)) {
                return $v;
            }
        }

        return $default;
    }

    /**
     * Returns a stringified representation of the args passed to the command.
     *
     * @return string
     */
    public function __toString()
    {
        $params = array();
        foreach ($this->parameters as $param => $val) {
            if ($param && '-' === $param[0]) {
                if (is_array($val)) {
                    foreach ($val as $v) {
                        $params[] = $param.('' != $v ? '='.$this->escapeToken($v) : '');
                    }
                } else {
                    $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : '');
                }
            } else {
                $params[] = is_array($val) ? array_map(array($this, 'escapeToken'), $val) : $this->escapeToken($val);
            }
        }

        return implode(' ', $params);
    }

    /**
     * {@inheritdoc}
     */
    protected function parse()
    {
        foreach ($this->parameters as $key => $value) {
            if ('--' === $key) {
                return;
            }
            if (0 === strpos($key, '--')) {
                $this->addLongOption(substr($key, 2), $value);
            } elseif ('-' === $key[0]) {
                $this->addShortOption(substr($key, 1), $value);
            } else {
                $this->addArgument($key, $value);
            }
        }
    }

    /**
     * Adds a short option value.
     *
     * @param string $shortcut The short option key
     * @param mixed  $value    The value for the option
     *
     * @throws InvalidOptionException When option given doesn't exist
     */
    private function addShortOption($shortcut, $value)
    {
        if (!$this->definition->hasShortcut($shortcut)) {
            throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
        }

        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
    }

    /**
     * Adds a long option value.
     *
     * @param string $name  The long option key
     * @param mixed  $value The value for the option
     *
     * @throws InvalidOptionException When option given doesn't exist
     * @throws InvalidOptionException When a required value is missing
     */
    private function addLongOption($name, $value)
    {
        if (!$this->definition->hasOption($name)) {
            throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
        }

        $option = $this->definition->getOption($name);

        if (null === $value) {
            if ($option->isValueRequired()) {
                throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name));
            }

            if (!$option->isValueOptional()) {
                $value = true;
            }
        }

        $this->options[$name] = $value;
    }

    /**
     * Adds an argument value.
     *
     * @param string $name  The argument name
     * @param mixed  $value The value for the argument
     *
     * @throws InvalidArgumentException When argument given doesn't exist
     */
    private function addArgument($name, $value)
    {
        if (!$this->definition->hasArgument($name)) {
            throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
        }

        $this->arguments[$name] = $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\Console\Input;

use Symfony\Component\Console\Exception\RuntimeException;

/**
 * ArgvInput represents an input coming from the CLI arguments.
 *
 * Usage:
 *
 *     $input = new ArgvInput();
 *
 * By default, the `$_SERVER['argv']` array is used for the input values.
 *
 * This can be overridden by explicitly passing the input values in the constructor:
 *
 *     $input = new ArgvInput($_SERVER['argv']);
 *
 * If you pass it yourself, don't forget that the first element of the array
 * is the name of the running application.
 *
 * When passing an argument to the constructor, be sure that it respects
 * the same rules as the argv one. It's almost always better to use the
 * `StringInput` when you want to provide your own input.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
 * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
 */
class ArgvInput extends Input
{
    private $tokens;
    private $parsed;

    /**
     * @param array|null           $argv       An array of parameters from the CLI (in the argv format)
     * @param InputDefinition|null $definition A InputDefinition instance
     */
    public function __construct(array $argv = null, InputDefinition $definition = null)
    {
        if (null === $argv) {
            $argv = $_SERVER['argv'];
        }

        // strip the application name
        array_shift($argv);

        $this->tokens = $argv;

        parent::__construct($definition);
    }

    protected function setTokens(array $tokens)
    {
        $this->tokens = $tokens;
    }

    /**
     * {@inheritdoc}
     */
    protected function parse()
    {
        $parseOptions = true;
        $this->parsed = $this->tokens;
        while (null !== $token = array_shift($this->parsed)) {
            if ($parseOptions && '' == $token) {
                $this->parseArgument($token);
            } elseif ($parseOptions && '--' == $token) {
                $parseOptions = false;
            } elseif ($parseOptions && 0 === strpos($token, '--')) {
                $this->parseLongOption($token);
            } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
                $this->parseShortOption($token);
            } else {
                $this->parseArgument($token);
            }
        }
    }

    /**
     * Parses a short option.
     *
     * @param string $token The current token
     */
    private function parseShortOption($token)
    {
        $name = substr($token, 1);

        if (strlen($name) > 1) {
            if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
                // an option with a value (with no space)
                $this->addShortOption($name[0], substr($name, 1));
            } else {
                $this->parseShortOptionSet($name);
            }
        } else {
            $this->addShortOption($name, null);
        }
    }

    /**
     * Parses a short option set.
     *
     * @param string $name The current token
     *
     * @throws RuntimeException When option given doesn't exist
     */
    private function parseShortOptionSet($name)
    {
        $len = strlen($name);
        for ($i = 0; $i < $len; ++$i) {
            if (!$this->definition->hasShortcut($name[$i])) {
                throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
            }

            $option = $this->definition->getOptionForShortcut($name[$i]);
            if ($option->acceptValue()) {
                $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));

                break;
            } else {
                $this->addLongOption($option->getName(), null);
            }
        }
    }

    /**
     * Parses a long option.
     *
     * @param string $token The current token
     */
    private function parseLongOption($token)
    {
        $name = substr($token, 2);

        if (false !== $pos = strpos($name, '=')) {
            if (0 === strlen($value = substr($name, $pos + 1))) {
                // if no value after "=" then substr() returns "" since php7 only, false before
                // see http://php.net/manual/fr/migration70.incompatible.php#119151
                if (\PHP_VERSION_ID < 70000 && false === $value) {
                    $value = '';
                }
                array_unshift($this->parsed, $value);
            }
            $this->addLongOption(substr($name, 0, $pos), $value);
        } else {
            $this->addLongOption($name, null);
        }
    }

    /**
     * Parses an argument.
     *
     * @param string $token The current token
     *
     * @throws RuntimeException When too many arguments are given
     */
    private function parseArgument($token)
    {
        $c = count($this->arguments);

        // if input is expecting another argument, add it
        if ($this->definition->hasArgument($c)) {
            $arg = $this->definition->getArgument($c);
            $this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token;

        // if last argument isArray(), append token to last argument
        } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
            $arg = $this->definition->getArgument($c - 1);
            $this->arguments[$arg->getName()][] = $token;

        // unexpected argument
        } else {
            $all = $this->definition->getArguments();
            if (count($all)) {
                throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))));
            }

            throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token));
        }
    }

    /**
     * Adds a short option value.
     *
     * @param string $shortcut The short option key
     * @param mixed  $value    The value for the option
     *
     * @throws RuntimeException When option given doesn't exist
     */
    private function addShortOption($shortcut, $value)
    {
        if (!$this->definition->hasShortcut($shortcut)) {
            throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
        }

        $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
    }

    /**
     * Adds a long option value.
     *
     * @param string $name  The long option key
     * @param mixed  $value The value for the option
     *
     * @throws RuntimeException When option given doesn't exist
     */
    private function addLongOption($name, $value)
    {
        if (!$this->definition->hasOption($name)) {
            throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
        }

        $option = $this->definition->getOption($name);

        if (null !== $value && !$option->acceptValue()) {
            throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
        }

        if (in_array($value, array('', null), true) && $option->acceptValue() && count($this->parsed)) {
            // if option accepts an optional or mandatory argument
            // let's see if there is one provided
            $next = array_shift($this->parsed);
            if ((isset($next[0]) && '-' !== $next[0]) || in_array($next, array('', null), true)) {
                $value = $next;
            } else {
                array_unshift($this->parsed, $next);
            }
        }

        if (null === $value) {
            if ($option->isValueRequired()) {
                throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name));
            }

            if (!$option->isArray() && !$option->isValueOptional()) {
                $value = true;
            }
        }

        if ($option->isArray()) {
            $this->options[$name][] = $value;
        } else {
            $this->options[$name] = $value;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getFirstArgument()
    {
        foreach ($this->tokens as $token) {
            if ($token && '-' === $token[0]) {
                continue;
            }

            return $token;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasParameterOption($values, $onlyParams = false)
    {
        $values = (array) $values;

        foreach ($this->tokens as $token) {
            if ($onlyParams && '--' === $token) {
                return false;
            }
            foreach ($values as $value) {
                if ($token === $value || 0 === strpos($token, $value.'=')) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function getParameterOption($values, $default = false, $onlyParams = false)
    {
        $values = (array) $values;
        $tokens = $this->tokens;

        while (0 < count($tokens)) {
            $token = array_shift($tokens);
            if ($onlyParams && '--' === $token) {
                return false;
            }

            foreach ($values as $value) {
                if ($token === $value || 0 === strpos($token, $value.'=')) {
                    if (false !== $pos = strpos($token, '=')) {
                        return substr($token, $pos + 1);
                    }

                    return array_shift($tokens);
                }
            }
        }

        return $default;
    }

    /**
     * Returns a stringified representation of the args passed to the command.
     *
     * @return string
     */
    public function __toString()
    {
        $tokens = array_map(function ($token) {
            if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
                return $match[1].$this->escapeToken($match[2]);
            }

            if ($token && '-' !== $token[0]) {
                return $this->escapeToken($token);
            }

            return $token;
        }, $this->tokens);

        return implode(' ', $tokens);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;

/**
 * Represents a command line argument.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class InputArgument
{
    const REQUIRED = 1;
    const OPTIONAL = 2;
    const IS_ARRAY = 4;

    private $name;
    private $mode;
    private $default;
    private $description;

    /**
     * @param string $name        The argument name
     * @param int    $mode        The argument mode: self::REQUIRED or self::OPTIONAL
     * @param string $description A description text
     * @param mixed  $default     The default value (for self::OPTIONAL mode only)
     *
     * @throws InvalidArgumentException When argument mode is not valid
     */
    public function __construct($name, $mode = null, $description = '', $default = null)
    {
        if (null === $mode) {
            $mode = self::OPTIONAL;
        } elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
            throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
        }

        $this->name = $name;
        $this->mode = $mode;
        $this->description = $description;

        $this->setDefault($default);
    }

    /**
     * Returns the argument name.
     *
     * @return string The argument name
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Returns true if the argument is required.
     *
     * @return bool true if parameter mode is self::REQUIRED, false otherwise
     */
    public function isRequired()
    {
        return self::REQUIRED === (self::REQUIRED & $this->mode);
    }

    /**
     * Returns true if the argument can take multiple values.
     *
     * @return bool true if mode is self::IS_ARRAY, false otherwise
     */
    public function isArray()
    {
        return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
    }

    /**
     * Sets the default value.
     *
     * @param mixed $default The default value
     *
     * @throws LogicException When incorrect default value is given
     */
    public function setDefault($default = null)
    {
        if (self::REQUIRED === $this->mode && null !== $default) {
            throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
        }

        if ($this->isArray()) {
            if (null === $default) {
                $default = array();
            } elseif (!is_array($default)) {
                throw new LogicException('A default value for an array argument must be an array.');
            }
        }

        $this->default = $default;
    }

    /**
     * Returns the default value.
     *
     * @return mixed The default value
     */
    public function getDefault()
    {
        return $this->default;
    }

    /**
     * Returns the description text.
     *
     * @return string The description text
     */
    public function getDescription()
    {
        return $this->description;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

/**
 * InputAwareInterface should be implemented by classes that depends on the
 * Console Input.
 *
 * @author Wouter J <waldio.webdesign@gmail.com>
 */
interface InputAwareInterface
{
    /**
     * Sets the Console Input.
     *
     * @param InputInterface
     */
    public function setInput(InputInterface $input);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;

/**
 * Represents a command line option.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class InputOption
{
    const VALUE_NONE = 1;
    const VALUE_REQUIRED = 2;
    const VALUE_OPTIONAL = 4;
    const VALUE_IS_ARRAY = 8;

    private $name;
    private $shortcut;
    private $mode;
    private $default;
    private $description;

    /**
     * @param string       $name        The option name
     * @param string|array $shortcut    The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
     * @param int          $mode        The option mode: One of the VALUE_* constants
     * @param string       $description A description text
     * @param mixed        $default     The default value (must be null for self::VALUE_NONE)
     *
     * @throws InvalidArgumentException If option mode is invalid or incompatible
     */
    public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
    {
        if (0 === strpos($name, '--')) {
            $name = substr($name, 2);
        }

        if (empty($name)) {
            throw new InvalidArgumentException('An option name cannot be empty.');
        }

        if (empty($shortcut)) {
            $shortcut = null;
        }

        if (null !== $shortcut) {
            if (is_array($shortcut)) {
                $shortcut = implode('|', $shortcut);
            }
            $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
            $shortcuts = array_filter($shortcuts);
            $shortcut = implode('|', $shortcuts);

            if (empty($shortcut)) {
                throw new InvalidArgumentException('An option shortcut cannot be empty.');
            }
        }

        if (null === $mode) {
            $mode = self::VALUE_NONE;
        } elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
            throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
        }

        $this->name = $name;
        $this->shortcut = $shortcut;
        $this->mode = $mode;
        $this->description = $description;

        if ($this->isArray() && !$this->acceptValue()) {
            throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
        }

        $this->setDefault($default);
    }

    /**
     * Returns the option shortcut.
     *
     * @return string The shortcut
     */
    public function getShortcut()
    {
        return $this->shortcut;
    }

    /**
     * Returns the option name.
     *
     * @return string The name
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Returns true if the option accepts a value.
     *
     * @return bool true if value mode is not self::VALUE_NONE, false otherwise
     */
    public function acceptValue()
    {
        return $this->isValueRequired() || $this->isValueOptional();
    }

    /**
     * Returns true if the option requires a value.
     *
     * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise
     */
    public function isValueRequired()
    {
        return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
    }

    /**
     * Returns true if the option takes an optional value.
     *
     * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise
     */
    public function isValueOptional()
    {
        return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
    }

    /**
     * Returns true if the option can take multiple values.
     *
     * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise
     */
    public function isArray()
    {
        return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
    }

    /**
     * Sets the default value.
     *
     * @param mixed $default The default value
     *
     * @throws LogicException When incorrect default value is given
     */
    public function setDefault($default = null)
    {
        if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
            throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
        }

        if ($this->isArray()) {
            if (null === $default) {
                $default = array();
            } elseif (!is_array($default)) {
                throw new LogicException('A default value for an array option must be an array.');
            }
        }

        $this->default = $this->acceptValue() ? $default : false;
    }

    /**
     * Returns the default value.
     *
     * @return mixed The default value
     */
    public function getDefault()
    {
        return $this->default;
    }

    /**
     * Returns the description text.
     *
     * @return string The description text
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Checks whether the given option equals this one.
     *
     * @return bool
     */
    public function equals(InputOption $option)
    {
        return $option->getName() === $this->getName()
            && $option->getShortcut() === $this->getShortcut()
            && $option->getDefault() === $this->getDefault()
            && $option->isArray() === $this->isArray()
            && $option->isValueRequired() === $this->isValueRequired()
            && $option->isValueOptional() === $this->isValueOptional()
        ;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;

/**
 * A InputDefinition represents a set of valid command line arguments and options.
 *
 * Usage:
 *
 *     $definition = new InputDefinition(array(
 *       new InputArgument('name', InputArgument::REQUIRED),
 *       new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
 *     ));
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class InputDefinition
{
    private $arguments;
    private $requiredCount;
    private $hasAnArrayArgument = false;
    private $hasOptional;
    private $options;
    private $shortcuts;

    /**
     * @param array $definition An array of InputArgument and InputOption instance
     */
    public function __construct(array $definition = array())
    {
        $this->setDefinition($definition);
    }

    /**
     * Sets the definition of the input.
     */
    public function setDefinition(array $definition)
    {
        $arguments = array();
        $options = array();
        foreach ($definition as $item) {
            if ($item instanceof InputOption) {
                $options[] = $item;
            } else {
                $arguments[] = $item;
            }
        }

        $this->setArguments($arguments);
        $this->setOptions($options);
    }

    /**
     * Sets the InputArgument objects.
     *
     * @param InputArgument[] $arguments An array of InputArgument objects
     */
    public function setArguments($arguments = array())
    {
        $this->arguments = array();
        $this->requiredCount = 0;
        $this->hasOptional = false;
        $this->hasAnArrayArgument = false;
        $this->addArguments($arguments);
    }

    /**
     * Adds an array of InputArgument objects.
     *
     * @param InputArgument[] $arguments An array of InputArgument objects
     */
    public function addArguments($arguments = array())
    {
        if (null !== $arguments) {
            foreach ($arguments as $argument) {
                $this->addArgument($argument);
            }
        }
    }

    /**
     * @throws LogicException When incorrect argument is given
     */
    public function addArgument(InputArgument $argument)
    {
        if (isset($this->arguments[$argument->getName()])) {
            throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
        }

        if ($this->hasAnArrayArgument) {
            throw new LogicException('Cannot add an argument after an array argument.');
        }

        if ($argument->isRequired() && $this->hasOptional) {
            throw new LogicException('Cannot add a required argument after an optional one.');
        }

        if ($argument->isArray()) {
            $this->hasAnArrayArgument = true;
        }

        if ($argument->isRequired()) {
            ++$this->requiredCount;
        } else {
            $this->hasOptional = true;
        }

        $this->arguments[$argument->getName()] = $argument;
    }

    /**
     * Returns an InputArgument by name or by position.
     *
     * @param string|int $name The InputArgument name or position
     *
     * @return InputArgument An InputArgument object
     *
     * @throws InvalidArgumentException When argument given doesn't exist
     */
    public function getArgument($name)
    {
        if (!$this->hasArgument($name)) {
            throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
        }

        $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;

        return $arguments[$name];
    }

    /**
     * Returns true if an InputArgument object exists by name or position.
     *
     * @param string|int $name The InputArgument name or position
     *
     * @return bool true if the InputArgument object exists, false otherwise
     */
    public function hasArgument($name)
    {
        $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;

        return isset($arguments[$name]);
    }

    /**
     * Gets the array of InputArgument objects.
     *
     * @return InputArgument[] An array of InputArgument objects
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * Returns the number of InputArguments.
     *
     * @return int The number of InputArguments
     */
    public function getArgumentCount()
    {
        return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
    }

    /**
     * Returns the number of required InputArguments.
     *
     * @return int The number of required InputArguments
     */
    public function getArgumentRequiredCount()
    {
        return $this->requiredCount;
    }

    /**
     * Gets the default values.
     *
     * @return array An array of default values
     */
    public function getArgumentDefaults()
    {
        $values = array();
        foreach ($this->arguments as $argument) {
            $values[$argument->getName()] = $argument->getDefault();
        }

        return $values;
    }

    /**
     * Sets the InputOption objects.
     *
     * @param InputOption[] $options An array of InputOption objects
     */
    public function setOptions($options = array())
    {
        $this->options = array();
        $this->shortcuts = array();
        $this->addOptions($options);
    }

    /**
     * Adds an array of InputOption objects.
     *
     * @param InputOption[] $options An array of InputOption objects
     */
    public function addOptions($options = array())
    {
        foreach ($options as $option) {
            $this->addOption($option);
        }
    }

    /**
     * @throws LogicException When option given already exist
     */
    public function addOption(InputOption $option)
    {
        if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
            throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
        }

        if ($option->getShortcut()) {
            foreach (explode('|', $option->getShortcut()) as $shortcut) {
                if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) {
                    throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
                }
            }
        }

        $this->options[$option->getName()] = $option;
        if ($option->getShortcut()) {
            foreach (explode('|', $option->getShortcut()) as $shortcut) {
                $this->shortcuts[$shortcut] = $option->getName();
            }
        }
    }

    /**
     * Returns an InputOption by name.
     *
     * @param string $name The InputOption name
     *
     * @return InputOption A InputOption object
     *
     * @throws InvalidArgumentException When option given doesn't exist
     */
    public function getOption($name)
    {
        if (!$this->hasOption($name)) {
            throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
        }

        return $this->options[$name];
    }

    /**
     * Returns true if an InputOption object exists by name.
     *
     * This method can't be used to check if the user included the option when
     * executing the command (use getOption() instead).
     *
     * @param string $name The InputOption name
     *
     * @return bool true if the InputOption object exists, false otherwise
     */
    public function hasOption($name)
    {
        return isset($this->options[$name]);
    }

    /**
     * Gets the array of InputOption objects.
     *
     * @return InputOption[] An array of InputOption objects
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Returns true if an InputOption object exists by shortcut.
     *
     * @param string $name The InputOption shortcut
     *
     * @return bool true if the InputOption object exists, false otherwise
     */
    public function hasShortcut($name)
    {
        return isset($this->shortcuts[$name]);
    }

    /**
     * Gets an InputOption by shortcut.
     *
     * @param string $shortcut The Shortcut name
     *
     * @return InputOption An InputOption object
     */
    public function getOptionForShortcut($shortcut)
    {
        return $this->getOption($this->shortcutToName($shortcut));
    }

    /**
     * Gets an array of default values.
     *
     * @return array An array of all default values
     */
    public function getOptionDefaults()
    {
        $values = array();
        foreach ($this->options as $option) {
            $values[$option->getName()] = $option->getDefault();
        }

        return $values;
    }

    /**
     * Returns the InputOption name given a shortcut.
     *
     * @param string $shortcut The shortcut
     *
     * @return string The InputOption name
     *
     * @throws InvalidArgumentException When option given does not exist
     */
    private function shortcutToName($shortcut)
    {
        if (!isset($this->shortcuts[$shortcut])) {
            throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
        }

        return $this->shortcuts[$shortcut];
    }

    /**
     * Gets the synopsis.
     *
     * @param bool $short Whether to return the short version (with options folded) or not
     *
     * @return string The synopsis
     */
    public function getSynopsis($short = false)
    {
        $elements = array();

        if ($short && $this->getOptions()) {
            $elements[] = '[options]';
        } elseif (!$short) {
            foreach ($this->getOptions() as $option) {
                $value = '';
                if ($option->acceptValue()) {
                    $value = sprintf(
                        ' %s%s%s',
                        $option->isValueOptional() ? '[' : '',
                        strtoupper($option->getName()),
                        $option->isValueOptional() ? ']' : ''
                    );
                }

                $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
                $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value);
            }
        }

        if (count($elements) && $this->getArguments()) {
            $elements[] = '[--]';
        }

        foreach ($this->getArguments() as $argument) {
            $element = '<'.$argument->getName().'>';
            if (!$argument->isRequired()) {
                $element = '['.$element.']';
            } elseif ($argument->isArray()) {
                $element = $element.' ('.$element.')';
            }

            if ($argument->isArray()) {
                $element .= '...';
            }

            $elements[] = $element;
        }

        return implode(' ', $elements);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * StringInput represents an input provided as a string.
 *
 * Usage:
 *
 *     $input = new StringInput('foo --bar="foobar"');
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class StringInput extends ArgvInput
{
    const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
    const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';

    /**
     * @param string $input A string representing the parameters from the CLI
     */
    public function __construct($input)
    {
        parent::__construct(array());

        $this->setTokens($this->tokenize($input));
    }

    /**
     * Tokenizes a string.
     *
     * @param string $input The input to tokenize
     *
     * @return array An array of tokens
     *
     * @throws InvalidArgumentException When unable to parse input (should never happen)
     */
    private function tokenize($input)
    {
        $tokens = array();
        $length = strlen($input);
        $cursor = 0;
        while ($cursor < $length) {
            if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
            } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
                $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
            } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
                $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
            } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) {
                $tokens[] = stripcslashes($match[1]);
            } else {
                // should never happen
                throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
            }

            $cursor += strlen($match[0]);
        }

        return $tokens;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;

/**
 * Input is the base class for all concrete Input classes.
 *
 * Three concrete classes are provided by default:
 *
 *  * `ArgvInput`: The input comes from the CLI arguments (argv)
 *  * `StringInput`: The input is provided as a string
 *  * `ArrayInput`: The input is provided as an array
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class Input implements InputInterface, StreamableInputInterface
{
    protected $definition;
    protected $stream;
    protected $options = array();
    protected $arguments = array();
    protected $interactive = true;

    public function __construct(InputDefinition $definition = null)
    {
        if (null === $definition) {
            $this->definition = new InputDefinition();
        } else {
            $this->bind($definition);
            $this->validate();
        }
    }

    /**
     * {@inheritdoc}
     */
    public function bind(InputDefinition $definition)
    {
        $this->arguments = array();
        $this->options = array();
        $this->definition = $definition;

        $this->parse();
    }

    /**
     * Processes command line arguments.
     */
    abstract protected function parse();

    /**
     * {@inheritdoc}
     */
    public function validate()
    {
        $definition = $this->definition;
        $givenArguments = $this->arguments;

        $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
            return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
        });

        if (count($missingArguments) > 0) {
            throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function isInteractive()
    {
        return $this->interactive;
    }

    /**
     * {@inheritdoc}
     */
    public function setInteractive($interactive)
    {
        $this->interactive = (bool) $interactive;
    }

    /**
     * {@inheritdoc}
     */
    public function getArguments()
    {
        return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
    }

    /**
     * {@inheritdoc}
     */
    public function getArgument($name)
    {
        if (!$this->definition->hasArgument($name)) {
            throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
        }

        return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
    }

    /**
     * {@inheritdoc}
     */
    public function setArgument($name, $value)
    {
        if (!$this->definition->hasArgument($name)) {
            throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
        }

        $this->arguments[$name] = $value;
    }

    /**
     * {@inheritdoc}
     */
    public function hasArgument($name)
    {
        return $this->definition->hasArgument($name);
    }

    /**
     * {@inheritdoc}
     */
    public function getOptions()
    {
        return array_merge($this->definition->getOptionDefaults(), $this->options);
    }

    /**
     * {@inheritdoc}
     */
    public function getOption($name)
    {
        if (!$this->definition->hasOption($name)) {
            throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
        }

        return array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
    }

    /**
     * {@inheritdoc}
     */
    public function setOption($name, $value)
    {
        if (!$this->definition->hasOption($name)) {
            throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
        }

        $this->options[$name] = $value;
    }

    /**
     * {@inheritdoc}
     */
    public function hasOption($name)
    {
        return $this->definition->hasOption($name);
    }

    /**
     * Escapes a token through escapeshellarg if it contains unsafe chars.
     *
     * @param string $token
     *
     * @return string
     */
    public function escapeToken($token)
    {
        return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
    }

    /**
     * {@inheritdoc}
     */
    public function setStream($stream)
    {
        $this->stream = $stream;
    }

    /**
     * {@inheritdoc}
     */
    public function getStream()
    {
        return $this->stream;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Input;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;

/**
 * InputInterface is the interface implemented by all input classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface InputInterface
{
    /**
     * Returns the first argument from the raw parameters (not parsed).
     *
     * @return string|null The value of the first argument or null otherwise
     */
    public function getFirstArgument();

    /**
     * Returns true if the raw parameters (not parsed) contain a value.
     *
     * This method is to be used to introspect the input parameters
     * before they have been validated. It must be used carefully.
     *
     * @param string|array $values     The values to look for in the raw parameters (can be an array)
     * @param bool         $onlyParams Only check real parameters, skip those following an end of options (--) signal
     *
     * @return bool true if the value is contained in the raw parameters
     */
    public function hasParameterOption($values, $onlyParams = false);

    /**
     * Returns the value of a raw option (not parsed).
     *
     * This method is to be used to introspect the input parameters
     * before they have been validated. It must be used carefully.
     *
     * @param string|array $values     The value(s) to look for in the raw parameters (can be an array)
     * @param mixed        $default    The default value to return if no result is found
     * @param bool         $onlyParams Only check real parameters, skip those following an end of options (--) signal
     *
     * @return mixed The option value
     */
    public function getParameterOption($values, $default = false, $onlyParams = false);

    /**
     * Binds the current Input instance with the given arguments and options.
     */
    public function bind(InputDefinition $definition);

    /**
     * Validates the input.
     *
     * @throws RuntimeException When not enough arguments are given
     */
    public function validate();

    /**
     * Returns all the given arguments merged with the default values.
     *
     * @return array
     */
    public function getArguments();

    /**
     * Returns the argument value for a given argument name.
     *
     * @param string $name The argument name
     *
     * @return mixed The argument value
     *
     * @throws InvalidArgumentException When argument given doesn't exist
     */
    public function getArgument($name);

    /**
     * Sets an argument value by name.
     *
     * @param string $name  The argument name
     * @param string $value The argument value
     *
     * @throws InvalidArgumentException When argument given doesn't exist
     */
    public function setArgument($name, $value);

    /**
     * Returns true if an InputArgument object exists by name or position.
     *
     * @param string|int $name The InputArgument name or position
     *
     * @return bool true if the InputArgument object exists, false otherwise
     */
    public function hasArgument($name);

    /**
     * Returns all the given options merged with the default values.
     *
     * @return array
     */
    public function getOptions();

    /**
     * Returns the option value for a given option name.
     *
     * @param string $name The option name
     *
     * @return mixed The option value
     *
     * @throws InvalidArgumentException When option given doesn't exist
     */
    public function getOption($name);

    /**
     * Sets an option value by name.
     *
     * @param string      $name  The option name
     * @param string|bool $value The option value
     *
     * @throws InvalidArgumentException When option given doesn't exist
     */
    public function setOption($name, $value);

    /**
     * Returns true if an InputOption object exists by name.
     *
     * @param string $name The InputOption name
     *
     * @return bool true if the InputOption object exists, false otherwise
     */
    public function hasOption($name);

    /**
     * Is this input means interactive?
     *
     * @return bool
     */
    public function isInteractive();

    /**
     * Sets the input interactivity.
     *
     * @param bool $interactive If the input should be interactive
     */
    public function setInteractive($interactive);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console;

class Terminal
{
    private static $width;
    private static $height;

    /**
     * Gets the terminal width.
     *
     * @return int
     */
    public function getWidth()
    {
        $width = getenv('COLUMNS');
        if (false !== $width) {
            return (int) trim($width);
        }

        if (null === self::$width) {
            self::initDimensions();
        }

        return self::$width ?: 80;
    }

    /**
     * Gets the terminal height.
     *
     * @return int
     */
    public function getHeight()
    {
        $height = getenv('LINES');
        if (false !== $height) {
            return (int) trim($height);
        }

        if (null === self::$height) {
            self::initDimensions();
        }

        return self::$height ?: 50;
    }

    private static function initDimensions()
    {
        if ('\\' === DIRECTORY_SEPARATOR) {
            if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) {
                // extract [w, H] from "wxh (WxH)"
                // or [w, h] from "wxh"
                self::$width = (int) $matches[1];
                self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
            } elseif (null !== $dimensions = self::getConsoleMode()) {
                // extract [w, h] from "wxh"
                self::$width = (int) $dimensions[0];
                self::$height = (int) $dimensions[1];
            }
        } elseif ($sttyString = self::getSttyColumns()) {
            if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
                // extract [w, h] from "rows h; columns w;"
                self::$width = (int) $matches[2];
                self::$height = (int) $matches[1];
            } elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
                // extract [w, h] from "; h rows; w columns"
                self::$width = (int) $matches[2];
                self::$height = (int) $matches[1];
            }
        }
    }

    /**
     * Runs and parses mode CON if it's available, suppressing any error output.
     *
     * @return int[]|null An array composed of the width and the height or null if it could not be parsed
     */
    private static function getConsoleMode()
    {
        if (!function_exists('proc_open')) {
            return;
        }

        $descriptorspec = array(
            1 => array('pipe', 'w'),
            2 => array('pipe', 'w'),
        );
        $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('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)) {
                return array((int) $matches[2], (int) $matches[1]);
            }
        }
    }

    /**
     * Runs and parses stty -a if it's available, suppressing any error output.
     *
     * @return string|null
     */
    private static function getSttyColumns()
    {
        if (!function_exists('proc_open')) {
            return;
        }

        $descriptorspec = array(
            1 => array('pipe', 'w'),
            2 => array('pipe', 'w'),
        );

        $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
        if (is_resource($process)) {
            $info = stream_get_contents($pipes[1]);
            fclose($pipes[1]);
            fclose($pipes[2]);
            proc_close($process);

            return $info;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Output;

/**
 * ConsoleOutputInterface is the interface implemented by ConsoleOutput class.
 * This adds information about stderr output stream.
 *
 * @author Dariusz GÃ³recki <darek.krk@gmail.com>
 */
interface ConsoleOutputInterface extends OutputInterface
{
    /**
     * Gets the OutputInterface for errors.
     *
     * @return OutputInterface
     */
    public function getErrorOutput();

    public function setErrorOutput(OutputInterface $error);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Output;

/**
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 */
class BufferedOutput extends Output
{
    private $buffer = '';

    /**
     * Empties buffer and returns its content.
     *
     * @return string
     */
    public function fetch()
    {
        $content = $this->buffer;
        $this->buffer = '';

        return $content;
    }

    /**
     * {@inheritdoc}
     */
    protected function doWrite($message, $newline)
    {
        $this->buffer .= $message;

        if ($newline) {
            $this->buffer .= PHP_EOL;
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;

/**
 * OutputInterface is the interface implemented by all Output classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface OutputInterface
{
    const VERBOSITY_QUIET = 16;
    const VERBOSITY_NORMAL = 32;
    const VERBOSITY_VERBOSE = 64;
    const VERBOSITY_VERY_VERBOSE = 128;
    const VERBOSITY_DEBUG = 256;

    const OUTPUT_NORMAL = 1;
    const OUTPUT_RAW = 2;
    const OUTPUT_PLAIN = 4;

    /**
     * Writes a message to the output.
     *
     * @param string|array $messages The message as an array of lines or a single string
     * @param bool         $newline  Whether to add a newline
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
     */
    public function write($messages, $newline = false, $options = 0);

    /**
     * Writes a message to the output and adds a newline at the end.
     *
     * @param string|array $messages The message as an array of lines of a single string
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
     */
    public function writeln($messages, $options = 0);

    /**
     * Sets the verbosity of the output.
     *
     * @param int $level The level of verbosity (one of the VERBOSITY constants)
     */
    public function setVerbosity($level);

    /**
     * Gets the current verbosity of the output.
     *
     * @return int The current level of verbosity (one of the VERBOSITY constants)
     */
    public function getVerbosity();

    /**
     * Returns whether verbosity is quiet (-q).
     *
     * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
     */
    public function isQuiet();

    /**
     * Returns whether verbosity is verbose (-v).
     *
     * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
     */
    public function isVerbose();

    /**
     * Returns whether verbosity is very verbose (-vv).
     *
     * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
     */
    public function isVeryVerbose();

    /**
     * Returns whether verbosity is debug (-vvv).
     *
     * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
     */
    public function isDebug();

    /**
     * Sets the decorated flag.
     *
     * @param bool $decorated Whether to decorate the messages
     */
    public function setDecorated($decorated);

    /**
     * Gets the decorated flag.
     *
     * @return bool true if the output will decorate messages, false otherwise
     */
    public function isDecorated();

    public function setFormatter(OutputFormatterInterface $formatter);

    /**
     * Returns current output formatter instance.
     *
     * @return OutputFormatterInterface
     */
    public function getFormatter();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Formatter\OutputFormatter;

/**
 * Base class for output classes.
 *
 * There are five levels of verbosity:
 *
 *  * normal: no option passed (normal output)
 *  * verbose: -v (more output)
 *  * very verbose: -vv (highly extended output)
 *  * debug: -vvv (all debug output)
 *  * quiet: -q (no output)
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class Output implements OutputInterface
{
    private $verbosity;
    private $formatter;

    /**
     * @param int                           $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
     * @param bool                          $decorated Whether to decorate messages
     * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
     */
    public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null)
    {
        $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity;
        $this->formatter = $formatter ?: new OutputFormatter();
        $this->formatter->setDecorated($decorated);
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(OutputFormatterInterface $formatter)
    {
        $this->formatter = $formatter;
    }

    /**
     * {@inheritdoc}
     */
    public function getFormatter()
    {
        return $this->formatter;
    }

    /**
     * {@inheritdoc}
     */
    public function setDecorated($decorated)
    {
        $this->formatter->setDecorated($decorated);
    }

    /**
     * {@inheritdoc}
     */
    public function isDecorated()
    {
        return $this->formatter->isDecorated();
    }

    /**
     * {@inheritdoc}
     */
    public function setVerbosity($level)
    {
        $this->verbosity = (int) $level;
    }

    /**
     * {@inheritdoc}
     */
    public function getVerbosity()
    {
        return $this->verbosity;
    }

    /**
     * {@inheritdoc}
     */
    public function isQuiet()
    {
        return self::VERBOSITY_QUIET === $this->verbosity;
    }

    /**
     * {@inheritdoc}
     */
    public function isVerbose()
    {
        return self::VERBOSITY_VERBOSE <= $this->verbosity;
    }

    /**
     * {@inheritdoc}
     */
    public function isVeryVerbose()
    {
        return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
    }

    /**
     * {@inheritdoc}
     */
    public function isDebug()
    {
        return self::VERBOSITY_DEBUG <= $this->verbosity;
    }

    /**
     * {@inheritdoc}
     */
    public function writeln($messages, $options = self::OUTPUT_NORMAL)
    {
        $this->write($messages, true, $options);
    }

    /**
     * {@inheritdoc}
     */
    public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
    {
        $messages = (array) $messages;

        $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN;
        $type = $types & $options ?: self::OUTPUT_NORMAL;

        $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG;
        $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL;

        if ($verbosity > $this->getVerbosity()) {
            return;
        }

        foreach ($messages as $message) {
            switch ($type) {
                case OutputInterface::OUTPUT_NORMAL:
                    $message = $this->formatter->format($message);
                    break;
                case OutputInterface::OUTPUT_RAW:
                    break;
                case OutputInterface::OUTPUT_PLAIN:
                    $message = strip_tags($this->formatter->format($message));
                    break;
            }

            $this->doWrite($message, $newline);
        }
    }

    /**
     * Writes a message to the output.
     *
     * @param string $message A message to write to the output
     * @param bool   $newline Whether to add a newline or not
     */
    abstract protected function doWrite($message, $newline);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;

/**
 * ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR.
 *
 * This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR.
 *
 *     $output = new ConsoleOutput();
 *
 * This is equivalent to:
 *
 *     $output = new StreamOutput(fopen('php://stdout', 'w'));
 *     $stdErr = new StreamOutput(fopen('php://stderr', 'w'));
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
    private $stderr;

    /**
     * @param int                           $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
     * @param bool|null                     $decorated Whether to decorate messages (null for auto-guessing)
     * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
     */
    public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
    {
        parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);

        $actualDecorated = $this->isDecorated();
        $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());

        if (null === $decorated) {
            $this->setDecorated($actualDecorated && $this->stderr->isDecorated());
        }
    }

    /**
     * {@inheritdoc}
     */
    public function setDecorated($decorated)
    {
        parent::setDecorated($decorated);
        $this->stderr->setDecorated($decorated);
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(OutputFormatterInterface $formatter)
    {
        parent::setFormatter($formatter);
        $this->stderr->setFormatter($formatter);
    }

    /**
     * {@inheritdoc}
     */
    public function setVerbosity($level)
    {
        parent::setVerbosity($level);
        $this->stderr->setVerbosity($level);
    }

    /**
     * {@inheritdoc}
     */
    public function getErrorOutput()
    {
        return $this->stderr;
    }

    /**
     * {@inheritdoc}
     */
    public function setErrorOutput(OutputInterface $error)
    {
        $this->stderr = $error;
    }

    /**
     * Returns true if current environment supports writing console output to
     * STDOUT.
     *
     * @return bool
     */
    protected function hasStdoutSupport()
    {
        return false === $this->isRunningOS400();
    }

    /**
     * Returns true if current environment supports writing console output to
     * STDERR.
     *
     * @return bool
     */
    protected function hasStderrSupport()
    {
        return false === $this->isRunningOS400();
    }

    /**
     * Checks if current executing environment is IBM iSeries (OS400), which
     * doesn't properly convert character-encodings between ASCII to EBCDIC.
     *
     * @return bool
     */
    private function isRunningOS400()
    {
        $checks = array(
            function_exists('php_uname') ? php_uname('s') : '',
            getenv('OSTYPE'),
            PHP_OS,
        );

        return false !== stripos(implode(';', $checks), 'OS400');
    }

    /**
     * @return resource
     */
    private function openOutputStream()
    {
        if (!$this->hasStdoutSupport()) {
            return fopen('php://output', 'w');
        }

        return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
    }

    /**
     * @return resource
     */
    private function openErrorStream()
    {
        return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w');
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;

/**
 * NullOutput suppresses all output.
 *
 *     $output = new NullOutput();
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Tobias Schultze <http://tobion.de>
 */
class NullOutput implements OutputInterface
{
    /**
     * {@inheritdoc}
     */
    public function setFormatter(OutputFormatterInterface $formatter)
    {
        // do nothing
    }

    /**
     * {@inheritdoc}
     */
    public function getFormatter()
    {
        // to comply with the interface we must return a OutputFormatterInterface
        return new OutputFormatter();
    }

    /**
     * {@inheritdoc}
     */
    public function setDecorated($decorated)
    {
        // do nothing
    }

    /**
     * {@inheritdoc}
     */
    public function isDecorated()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function setVerbosity($level)
    {
        // do nothing
    }

    /**
     * {@inheritdoc}
     */
    public function getVerbosity()
    {
        return self::VERBOSITY_QUIET;
    }

    /**
     * {@inheritdoc}
     */
    public function isQuiet()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isVerbose()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function isVeryVerbose()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function isDebug()
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function writeln($messages, $options = self::OUTPUT_NORMAL)
    {
        // do nothing
    }

    /**
     * {@inheritdoc}
     */
    public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
    {
        // do nothing
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Output;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;

/**
 * StreamOutput writes the output to a given stream.
 *
 * Usage:
 *
 * $output = new StreamOutput(fopen('php://stdout', 'w'));
 *
 * As `StreamOutput` can use any stream, you can also use a file:
 *
 * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false));
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class StreamOutput extends Output
{
    private $stream;

    /**
     * @param resource                      $stream    A stream resource
     * @param int                           $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface)
     * @param bool|null                     $decorated Whether to decorate messages (null for auto-guessing)
     * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter)
     *
     * @throws InvalidArgumentException When first argument is not a real stream
     */
    public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
    {
        if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
            throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
        }

        $this->stream = $stream;

        if (null === $decorated) {
            $decorated = $this->hasColorSupport();
        }

        parent::__construct($verbosity, $decorated, $formatter);
    }

    /**
     * Gets the stream attached to this StreamOutput instance.
     *
     * @return resource A stream resource
     */
    public function getStream()
    {
        return $this->stream;
    }

    /**
     * {@inheritdoc}
     */
    protected function doWrite($message, $newline)
    {
        if (false === @fwrite($this->stream, $message) || ($newline && (false === @fwrite($this->stream, PHP_EOL)))) {
            // should never happen
            throw new RuntimeException('Unable to write output.');
        }

        fflush($this->stream);
    }

    /**
     * Returns true if the stream supports colorization.
     *
     * Colorization is disabled if not supported by the stream:
     *
     *  -  Windows != 10.0.10586 without Ansicon, ConEmu or Mintty
     *  -  non tty consoles
     *
     * @return bool true if the stream supports colorization, false otherwise
     */
    protected function hasColorSupport()
    {
        if (DIRECTORY_SEPARATOR === '\\') {
            return
                '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR.'.'.PHP_WINDOWS_VERSION_MINOR.'.'.PHP_WINDOWS_VERSION_BUILD
                || false !== getenv('ANSICON')
                || 'ON' === getenv('ConEmuANSI')
                || 'xterm' === getenv('TERM');
        }

        return function_exists('posix_isatty') && @posix_isatty($this->stream);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Style;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;

/**
 * Decorates output to add console style guide helpers.
 *
 * @author Kevin Bond <kevinbond@gmail.com>
 */
abstract class OutputStyle implements OutputInterface, StyleInterface
{
    private $output;

    public function __construct(OutputInterface $output)
    {
        $this->output = $output;
    }

    /**
     * {@inheritdoc}
     */
    public function newLine($count = 1)
    {
        $this->output->write(str_repeat(PHP_EOL, $count));
    }

    /**
     * @param int $max
     *
     * @return ProgressBar
     */
    public function createProgressBar($max = 0)
    {
        return new ProgressBar($this->output, $max);
    }

    /**
     * {@inheritdoc}
     */
    public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
    {
        $this->output->write($messages, $newline, $type);
    }

    /**
     * {@inheritdoc}
     */
    public function writeln($messages, $type = self::OUTPUT_NORMAL)
    {
        $this->output->writeln($messages, $type);
    }

    /**
     * {@inheritdoc}
     */
    public function setVerbosity($level)
    {
        $this->output->setVerbosity($level);
    }

    /**
     * {@inheritdoc}
     */
    public function getVerbosity()
    {
        return $this->output->getVerbosity();
    }

    /**
     * {@inheritdoc}
     */
    public function setDecorated($decorated)
    {
        $this->output->setDecorated($decorated);
    }

    /**
     * {@inheritdoc}
     */
    public function isDecorated()
    {
        return $this->output->isDecorated();
    }

    /**
     * {@inheritdoc}
     */
    public function setFormatter(OutputFormatterInterface $formatter)
    {
        $this->output->setFormatter($formatter);
    }

    /**
     * {@inheritdoc}
     */
    public function getFormatter()
    {
        return $this->output->getFormatter();
    }

    /**
     * {@inheritdoc}
     */
    public function isQuiet()
    {
        return $this->output->isQuiet();
    }

    /**
     * {@inheritdoc}
     */
    public function isVerbose()
    {
        return $this->output->isVerbose();
    }

    /**
     * {@inheritdoc}
     */
    public function isVeryVerbose()
    {
        return $this->output->isVeryVerbose();
    }

    /**
     * {@inheritdoc}
     */
    public function isDebug()
    {
        return $this->output->isDebug();
    }

    protected function getErrorOutput()
    {
        if (!$this->output instanceof ConsoleOutputInterface) {
            return $this->output;
        }

        return $this->output->getErrorOutput();
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Style;

use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;

/**
 * Output decorator helpers for the Symfony Style Guide.
 *
 * @author Kevin Bond <kevinbond@gmail.com>
 */
class SymfonyStyle extends OutputStyle
{
    const MAX_LINE_LENGTH = 120;

    private $input;
    private $questionHelper;
    private $progressBar;
    private $lineLength;
    private $bufferedOutput;

    public function __construct(InputInterface $input, OutputInterface $output)
    {
        $this->input = $input;
        $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter());
        // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
        $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
        $this->lineLength = min($width - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);

        parent::__construct($output);
    }

    /**
     * Formats a message as a block of text.
     *
     * @param string|array $messages The message to write in the block
     * @param string|null  $type     The block type (added in [] on first line)
     * @param string|null  $style    The style to apply to the whole block
     * @param string       $prefix   The prefix for the block
     * @param bool         $padding  Whether to add vertical padding
     * @param bool         $escape   Whether to escape the message
     */
    public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = true)
    {
        $messages = is_array($messages) ? array_values($messages) : array($messages);

        $this->autoPrependBlock();
        $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape));
        $this->newLine();
    }

    /**
     * {@inheritdoc}
     */
    public function title($message)
    {
        $this->autoPrependBlock();
        $this->writeln(array(
            sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
            sprintf('<comment>%s</>', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))),
        ));
        $this->newLine();
    }

    /**
     * {@inheritdoc}
     */
    public function section($message)
    {
        $this->autoPrependBlock();
        $this->writeln(array(
            sprintf('<comment>%s</>', OutputFormatter::escapeTrailingBackslash($message)),
            sprintf('<comment>%s</>', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))),
        ));
        $this->newLine();
    }

    /**
     * {@inheritdoc}
     */
    public function listing(array $elements)
    {
        $this->autoPrependText();
        $elements = array_map(function ($element) {
            return sprintf(' * %s', $element);
        }, $elements);

        $this->writeln($elements);
        $this->newLine();
    }

    /**
     * {@inheritdoc}
     */
    public function text($message)
    {
        $this->autoPrependText();

        $messages = is_array($message) ? array_values($message) : array($message);
        foreach ($messages as $message) {
            $this->writeln(sprintf(' %s', $message));
        }
    }

    /**
     * Formats a command comment.
     *
     * @param string|array $message
     */
    public function comment($message)
    {
        $this->block($message, null, null, '<fg=default;bg=default> // </>', false, false);
    }

    /**
     * {@inheritdoc}
     */
    public function success($message)
    {
        $this->block($message, 'OK', 'fg=black;bg=green', ' ', true);
    }

    /**
     * {@inheritdoc}
     */
    public function error($message)
    {
        $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true);
    }

    /**
     * {@inheritdoc}
     */
    public function warning($message)
    {
        $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true);
    }

    /**
     * {@inheritdoc}
     */
    public function note($message)
    {
        $this->block($message, 'NOTE', 'fg=yellow', ' ! ');
    }

    /**
     * {@inheritdoc}
     */
    public function caution($message)
    {
        $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true);
    }

    /**
     * {@inheritdoc}
     */
    public function table(array $headers, array $rows)
    {
        $style = clone Table::getStyleDefinition('symfony-style-guide');
        $style->setCellHeaderFormat('<info>%s</info>');

        $table = new Table($this);
        $table->setHeaders($headers);
        $table->setRows($rows);
        $table->setStyle($style);

        $table->render();
        $this->newLine();
    }

    /**
     * {@inheritdoc}
     */
    public function ask($question, $default = null, $validator = null)
    {
        $question = new Question($question, $default);
        $question->setValidator($validator);

        return $this->askQuestion($question);
    }

    /**
     * {@inheritdoc}
     */
    public function askHidden($question, $validator = null)
    {
        $question = new Question($question);

        $question->setHidden(true);
        $question->setValidator($validator);

        return $this->askQuestion($question);
    }

    /**
     * {@inheritdoc}
     */
    public function confirm($question, $default = true)
    {
        return $this->askQuestion(new ConfirmationQuestion($question, $default));
    }

    /**
     * {@inheritdoc}
     */
    public function choice($question, array $choices, $default = null)
    {
        if (null !== $default) {
            $values = array_flip($choices);
            $default = $values[$default];
        }

        return $this->askQuestion(new ChoiceQuestion($question, $choices, $default));
    }

    /**
     * {@inheritdoc}
     */
    public function progressStart($max = 0)
    {
        $this->progressBar = $this->createProgressBar($max);
        $this->progressBar->start();
    }

    /**
     * {@inheritdoc}
     */
    public function progressAdvance($step = 1)
    {
        $this->getProgressBar()->advance($step);
    }

    /**
     * {@inheritdoc}
     */
    public function progressFinish()
    {
        $this->getProgressBar()->finish();
        $this->newLine(2);
        $this->progressBar = null;
    }

    /**
     * {@inheritdoc}
     */
    public function createProgressBar($max = 0)
    {
        $progressBar = parent::createProgressBar($max);

        if ('\\' !== DIRECTORY_SEPARATOR) {
            $progressBar->setEmptyBarCharacter('â–‘'); // light shade character \u2591
            $progressBar->setProgressCharacter('');
            $progressBar->setBarCharacter('â–“'); // dark shade character \u2593
        }

        return $progressBar;
    }

    /**
     * @return mixed
     */
    public function askQuestion(Question $question)
    {
        if ($this->input->isInteractive()) {
            $this->autoPrependBlock();
        }

        if (!$this->questionHelper) {
            $this->questionHelper = new SymfonyQuestionHelper();
        }

        $answer = $this->questionHelper->ask($this->input, $this, $question);

        if ($this->input->isInteractive()) {
            $this->newLine();
            $this->bufferedOutput->write("\n");
        }

        return $answer;
    }

    /**
     * {@inheritdoc}
     */
    public function writeln($messages, $type = self::OUTPUT_NORMAL)
    {
        parent::writeln($messages, $type);
        $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type);
    }

    /**
     * {@inheritdoc}
     */
    public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
    {
        parent::write($messages, $newline, $type);
        $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type);
    }

    /**
     * {@inheritdoc}
     */
    public function newLine($count = 1)
    {
        parent::newLine($count);
        $this->bufferedOutput->write(str_repeat("\n", $count));
    }

    /**
     * Returns a new instance which makes use of stderr if available.
     *
     * @return self
     */
    public function getErrorStyle()
    {
        return new self($this->input, $this->getErrorOutput());
    }

    /**
     * @return ProgressBar
     */
    private function getProgressBar()
    {
        if (!$this->progressBar) {
            throw new RuntimeException('The ProgressBar is not started.');
        }

        return $this->progressBar;
    }

    private function autoPrependBlock()
    {
        $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);

        if (!isset($chars[0])) {
            return $this->newLine(); //empty history, so we should start with a new line.
        }
        //Prepend new line for each non LF chars (This means no blank line was output before)
        $this->newLine(2 - substr_count($chars, "\n"));
    }

    private function autoPrependText()
    {
        $fetched = $this->bufferedOutput->fetch();
        //Prepend new line if last char isn't EOL:
        if ("\n" !== substr($fetched, -1)) {
            $this->newLine();
        }
    }

    private function reduceBuffer($messages)
    {
        // We need to know if the two last chars are PHP_EOL
        // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer
        return array_map(function ($value) {
            return substr($value, -4);
        }, array_merge(array($this->bufferedOutput->fetch()), (array) $messages));
    }

    private function createBlock($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = false)
    {
        $indentLength = 0;
        $prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix);
        $lines = array();

        if (null !== $type) {
            $type = sprintf('[%s] ', $type);
            $indentLength = strlen($type);
            $lineIndentation = str_repeat(' ', $indentLength);
        }

        // wrap and add newlines for each element
        foreach ($messages as $key => $message) {
            if ($escape) {
                $message = OutputFormatter::escape($message);
            }

            $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - $prefixLength - $indentLength, PHP_EOL, true)));

            if (count($messages) > 1 && $key < count($messages) - 1) {
                $lines[] = '';
            }
        }

        $firstLineIndex = 0;
        if ($padding && $this->isDecorated()) {
            $firstLineIndex = 1;
            array_unshift($lines, '');
            $lines[] = '';
        }

        foreach ($lines as $i => &$line) {
            if (null !== $type) {
                $line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line;
            }

            $line = $prefix.$line;
            $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line));

            if ($style) {
                $line = sprintf('<%s>%s</>', $style, $line);
            }
        }

        return $lines;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Style;

/**
 * Output style helpers.
 *
 * @author Kevin Bond <kevinbond@gmail.com>
 */
interface StyleInterface
{
    /**
     * Formats a command title.
     *
     * @param string $message
     */
    public function title($message);

    /**
     * Formats a section title.
     *
     * @param string $message
     */
    public function section($message);

    /**
     * Formats a list.
     */
    public function listing(array $elements);

    /**
     * Formats informational text.
     *
     * @param string|array $message
     */
    public function text($message);

    /**
     * Formats a success result bar.
     *
     * @param string|array $message
     */
    public function success($message);

    /**
     * Formats an error result bar.
     *
     * @param string|array $message
     */
    public function error($message);

    /**
     * Formats an warning result bar.
     *
     * @param string|array $message
     */
    public function warning($message);

    /**
     * Formats a note admonition.
     *
     * @param string|array $message
     */
    public function note($message);

    /**
     * Formats a caution admonition.
     *
     * @param string|array $message
     */
    public function caution($message);

    /**
     * Formats a table.
     */
    public function table(array $headers, array $rows);

    /**
     * Asks a question.
     *
     * @param string        $question
     * @param string|null   $default
     * @param callable|null $validator
     *
     * @return mixed
     */
    public function ask($question, $default = null, $validator = null);

    /**
     * Asks a question with the user input hidden.
     *
     * @param string        $question
     * @param callable|null $validator
     *
     * @return mixed
     */
    public function askHidden($question, $validator = null);

    /**
     * Asks for confirmation.
     *
     * @param string $question
     * @param bool   $default
     *
     * @return bool
     */
    public function confirm($question, $default = true);

    /**
     * Asks a choice question.
     *
     * @param string          $question
     * @param array           $choices
     * @param string|int|null $default
     *
     * @return mixed
     */
    public function choice($question, array $choices, $default = null);

    /**
     * Add newline(s).
     *
     * @param int $count The number of newlines
     */
    public function newLine($count = 1);

    /**
     * Starts the progress output.
     *
     * @param int $max Maximum steps (0 if unknown)
     */
    public function progressStart($max = 0);

    /**
     * Advances the progress output X steps.
     *
     * @param int $step Number of steps to advance
     */
    public function progressAdvance($step = 1);

    /**
     * Finishes the progress output.
     */
    public function progressFinish();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Question;

use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * Represents a choice question.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ChoiceQuestion extends Question
{
    private $choices;
    private $multiselect = false;
    private $prompt = ' > ';
    private $errorMessage = 'Value "%s" is invalid';

    /**
     * @param string $question The question to ask to the user
     * @param array  $choices  The list of available choices
     * @param mixed  $default  The default answer to return
     */
    public function __construct($question, array $choices, $default = null)
    {
        if (!$choices) {
            throw new \LogicException('Choice question must have at least 1 choice available.');
        }

        parent::__construct($question, $default);

        $this->choices = $choices;
        $this->setValidator($this->getDefaultValidator());
        $this->setAutocompleterValues($choices);
    }

    /**
     * Returns available choices.
     *
     * @return array
     */
    public function getChoices()
    {
        return $this->choices;
    }

    /**
     * Sets multiselect option.
     *
     * When multiselect is set to true, multiple choices can be answered.
     *
     * @param bool $multiselect
     *
     * @return $this
     */
    public function setMultiselect($multiselect)
    {
        $this->multiselect = $multiselect;
        $this->setValidator($this->getDefaultValidator());

        return $this;
    }

    /**
     * Returns whether the choices are multiselect.
     *
     * @return bool
     */
    public function isMultiselect()
    {
        return $this->multiselect;
    }

    /**
     * Gets the prompt for choices.
     *
     * @return string
     */
    public function getPrompt()
    {
        return $this->prompt;
    }

    /**
     * Sets the prompt for choices.
     *
     * @param string $prompt
     *
     * @return $this
     */
    public function setPrompt($prompt)
    {
        $this->prompt = $prompt;

        return $this;
    }

    /**
     * Sets the error message for invalid values.
     *
     * The error message has a string placeholder (%s) for the invalid value.
     *
     * @param string $errorMessage
     *
     * @return $this
     */
    public function setErrorMessage($errorMessage)
    {
        $this->errorMessage = $errorMessage;
        $this->setValidator($this->getDefaultValidator());

        return $this;
    }

    /**
     * Returns the default answer validator.
     *
     * @return callable
     */
    private function getDefaultValidator()
    {
        $choices = $this->choices;
        $errorMessage = $this->errorMessage;
        $multiselect = $this->multiselect;
        $isAssoc = $this->isAssoc($choices);

        return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
            // Collapse all spaces.
            $selectedChoices = str_replace(' ', '', $selected);

            if ($multiselect) {
                // Check for a separated comma values
                if (!preg_match('/^[^,]+(?:,[^,]+)*$/', $selectedChoices, $matches)) {
                    throw new InvalidArgumentException(sprintf($errorMessage, $selected));
                }
                $selectedChoices = explode(',', $selectedChoices);
            } else {
                $selectedChoices = array($selected);
            }

            $multiselectChoices = array();
            foreach ($selectedChoices as $value) {
                $results = array();
                foreach ($choices as $key => $choice) {
                    if ($choice === $value) {
                        $results[] = $key;
                    }
                }

                if (count($results) > 1) {
                    throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results)));
                }

                $result = array_search($value, $choices);

                if (!$isAssoc) {
                    if (false !== $result) {
                        $result = $choices[$result];
                    } elseif (isset($choices[$value])) {
                        $result = $choices[$value];
                    }
                } elseif (false === $result && isset($choices[$value])) {
                    $result = $value;
                }

                if (false === $result) {
                    throw new InvalidArgumentException(sprintf($errorMessage, $value));
                }

                $multiselectChoices[] = (string) $result;
            }

            if ($multiselect) {
                return $multiselectChoices;
            }

            return current($multiselectChoices);
        };
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Question;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;

/**
 * Represents a Question.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Question
{
    private $question;
    private $attempts;
    private $hidden = false;
    private $hiddenFallback = true;
    private $autocompleterValues;
    private $validator;
    private $default;
    private $normalizer;

    /**
     * @param string $question The question to ask to the user
     * @param mixed  $default  The default answer to return if the user enters nothing
     */
    public function __construct($question, $default = null)
    {
        $this->question = $question;
        $this->default = $default;
    }

    /**
     * Returns the question.
     *
     * @return string
     */
    public function getQuestion()
    {
        return $this->question;
    }

    /**
     * Returns the default answer.
     *
     * @return mixed
     */
    public function getDefault()
    {
        return $this->default;
    }

    /**
     * Returns whether the user response must be hidden.
     *
     * @return bool
     */
    public function isHidden()
    {
        return $this->hidden;
    }

    /**
     * Sets whether the user response must be hidden or not.
     *
     * @param bool $hidden
     *
     * @return $this
     *
     * @throws LogicException In case the autocompleter is also used
     */
    public function setHidden($hidden)
    {
        if ($this->autocompleterValues) {
            throw new LogicException('A hidden question cannot use the autocompleter.');
        }

        $this->hidden = (bool) $hidden;

        return $this;
    }

    /**
     * In case the response can not be hidden, whether to fallback on non-hidden question or not.
     *
     * @return bool
     */
    public function isHiddenFallback()
    {
        return $this->hiddenFallback;
    }

    /**
     * Sets whether to fallback on non-hidden question if the response can not be hidden.
     *
     * @param bool $fallback
     *
     * @return $this
     */
    public function setHiddenFallback($fallback)
    {
        $this->hiddenFallback = (bool) $fallback;

        return $this;
    }

    /**
     * Gets values for the autocompleter.
     *
     * @return null|iterable
     */
    public function getAutocompleterValues()
    {
        return $this->autocompleterValues;
    }

    /**
     * Sets values for the autocompleter.
     *
     * @param null|iterable $values
     *
     * @return $this
     *
     * @throws InvalidArgumentException
     * @throws LogicException
     */
    public function setAutocompleterValues($values)
    {
        if (is_array($values)) {
            $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
        }

        if (null !== $values && !is_array($values) && !$values instanceof \Traversable) {
            throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or a `Traversable` object.');
        }

        if ($this->hidden) {
            throw new LogicException('A hidden question cannot use the autocompleter.');
        }

        $this->autocompleterValues = $values;

        return $this;
    }

    /**
     * Sets a validator for the question.
     *
     * @param null|callable $validator
     *
     * @return $this
     */
    public function setValidator(callable $validator = null)
    {
        $this->validator = $validator;

        return $this;
    }

    /**
     * Gets the validator for the question.
     *
     * @return null|callable
     */
    public function getValidator()
    {
        return $this->validator;
    }

    /**
     * Sets the maximum number of attempts.
     *
     * Null means an unlimited number of attempts.
     *
     * @param null|int $attempts
     *
     * @return $this
     *
     * @throws InvalidArgumentException in case the number of attempts is invalid
     */
    public function setMaxAttempts($attempts)
    {
        if (null !== $attempts && $attempts < 1) {
            throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
        }

        $this->attempts = $attempts;

        return $this;
    }

    /**
     * Gets the maximum number of attempts.
     *
     * Null means an unlimited number of attempts.
     *
     * @return null|int
     */
    public function getMaxAttempts()
    {
        return $this->attempts;
    }

    /**
     * Sets a normalizer for the response.
     *
     * The normalizer can be a callable (a string), a closure or a class implementing __invoke.
     *
     * @param callable $normalizer
     *
     * @return $this
     */
    public function setNormalizer(callable $normalizer)
    {
        $this->normalizer = $normalizer;

        return $this;
    }

    /**
     * Gets the normalizer for the response.
     *
     * The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
     *
     * @return callable
     */
    public function getNormalizer()
    {
        return $this->normalizer;
    }

    protected function isAssoc($array)
    {
        return (bool) count(array_filter(array_keys($array), 'is_string'));
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Question;

/**
 * Represents a yes/no question.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ConfirmationQuestion extends Question
{
    private $trueAnswerRegex;

    /**
     * @param string $question        The question to ask to the user
     * @param bool   $default         The default answer to return, true or false
     * @param string $trueAnswerRegex A regex to match the "yes" answer
     */
    public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i')
    {
        parent::__construct($question, (bool) $default);

        $this->trueAnswerRegex = $trueAnswerRegex;
        $this->setNormalizer($this->getDefaultNormalizer());
    }

    /**
     * Returns the default answer normalizer.
     *
     * @return callable
     */
    private function getDefaultNormalizer()
    {
        $default = $this->getDefault();
        $regex = $this->trueAnswerRegex;

        return function ($answer) use ($default, $regex) {
            if (is_bool($answer)) {
                return $answer;
            }

            $answerIsTrue = (bool) preg_match($regex, $answer);
            if (false === $default) {
                return $answer && $answerIsTrue;
            }

            return !$answer || $answerIsTrue;
        };
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Formatter;

use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * Formatter style class for defining styles.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class OutputFormatterStyle implements OutputFormatterStyleInterface
{
    private static $availableForegroundColors = array(
        'black' => array('set' => 30, 'unset' => 39),
        'red' => array('set' => 31, 'unset' => 39),
        'green' => array('set' => 32, 'unset' => 39),
        'yellow' => array('set' => 33, 'unset' => 39),
        'blue' => array('set' => 34, 'unset' => 39),
        'magenta' => array('set' => 35, 'unset' => 39),
        'cyan' => array('set' => 36, 'unset' => 39),
        'white' => array('set' => 37, 'unset' => 39),
        'default' => array('set' => 39, 'unset' => 39),
    );
    private static $availableBackgroundColors = array(
        'black' => array('set' => 40, 'unset' => 49),
        'red' => array('set' => 41, 'unset' => 49),
        'green' => array('set' => 42, 'unset' => 49),
        'yellow' => array('set' => 43, 'unset' => 49),
        'blue' => array('set' => 44, 'unset' => 49),
        'magenta' => array('set' => 45, 'unset' => 49),
        'cyan' => array('set' => 46, 'unset' => 49),
        'white' => array('set' => 47, 'unset' => 49),
        'default' => array('set' => 49, 'unset' => 49),
    );
    private static $availableOptions = array(
        'bold' => array('set' => 1, 'unset' => 22),
        'underscore' => array('set' => 4, 'unset' => 24),
        'blink' => array('set' => 5, 'unset' => 25),
        'reverse' => array('set' => 7, 'unset' => 27),
        'conceal' => array('set' => 8, 'unset' => 28),
    );

    private $foreground;
    private $background;
    private $options = array();

    /**
     * Initializes output formatter style.
     *
     * @param string|null $foreground The style foreground color name
     * @param string|null $background The style background color name
     * @param array       $options    The style options
     */
    public function __construct($foreground = null, $background = null, array $options = array())
    {
        if (null !== $foreground) {
            $this->setForeground($foreground);
        }
        if (null !== $background) {
            $this->setBackground($background);
        }
        if (count($options)) {
            $this->setOptions($options);
        }
    }

    /**
     * Sets style foreground color.
     *
     * @param string|null $color The color name
     *
     * @throws InvalidArgumentException When the color name isn't defined
     */
    public function setForeground($color = null)
    {
        if (null === $color) {
            $this->foreground = null;

            return;
        }

        if (!isset(static::$availableForegroundColors[$color])) {
            throw new InvalidArgumentException(sprintf(
                'Invalid foreground color specified: "%s". Expected one of (%s)',
                $color,
                implode(', ', array_keys(static::$availableForegroundColors))
            ));
        }

        $this->foreground = static::$availableForegroundColors[$color];
    }

    /**
     * Sets style background color.
     *
     * @param string|null $color The color name
     *
     * @throws InvalidArgumentException When the color name isn't defined
     */
    public function setBackground($color = null)
    {
        if (null === $color) {
            $this->background = null;

            return;
        }

        if (!isset(static::$availableBackgroundColors[$color])) {
            throw new InvalidArgumentException(sprintf(
                'Invalid background color specified: "%s". Expected one of (%s)',
                $color,
                implode(', ', array_keys(static::$availableBackgroundColors))
            ));
        }

        $this->background = static::$availableBackgroundColors[$color];
    }

    /**
     * Sets some specific style option.
     *
     * @param string $option The option name
     *
     * @throws InvalidArgumentException When the option name isn't defined
     */
    public function setOption($option)
    {
        if (!isset(static::$availableOptions[$option])) {
            throw new InvalidArgumentException(sprintf(
                'Invalid option specified: "%s". Expected one of (%s)',
                $option,
                implode(', ', array_keys(static::$availableOptions))
            ));
        }

        if (!in_array(static::$availableOptions[$option], $this->options)) {
            $this->options[] = static::$availableOptions[$option];
        }
    }

    /**
     * Unsets some specific style option.
     *
     * @param string $option The option name
     *
     * @throws InvalidArgumentException When the option name isn't defined
     */
    public function unsetOption($option)
    {
        if (!isset(static::$availableOptions[$option])) {
            throw new InvalidArgumentException(sprintf(
                'Invalid option specified: "%s". Expected one of (%s)',
                $option,
                implode(', ', array_keys(static::$availableOptions))
            ));
        }

        $pos = array_search(static::$availableOptions[$option], $this->options);
        if (false !== $pos) {
            unset($this->options[$pos]);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function setOptions(array $options)
    {
        $this->options = array();

        foreach ($options as $option) {
            $this->setOption($option);
        }
    }

    /**
     * Applies the style to a given text.
     *
     * @param string $text The text to style
     *
     * @return string
     */
    public function apply($text)
    {
        $setCodes = array();
        $unsetCodes = array();

        if (null !== $this->foreground) {
            $setCodes[] = $this->foreground['set'];
            $unsetCodes[] = $this->foreground['unset'];
        }
        if (null !== $this->background) {
            $setCodes[] = $this->background['set'];
            $unsetCodes[] = $this->background['unset'];
        }
        if (count($this->options)) {
            foreach ($this->options as $option) {
                $setCodes[] = $option['set'];
                $unsetCodes[] = $option['unset'];
            }
        }

        if (0 === count($setCodes)) {
            return $text;
        }

        return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Formatter;

/**
 * Formatter style interface for defining styles.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface OutputFormatterStyleInterface
{
    /**
     * Sets style foreground color.
     *
     * @param string $color The color name
     */
    public function setForeground($color = null);

    /**
     * Sets style background color.
     *
     * @param string $color The color name
     */
    public function setBackground($color = null);

    /**
     * Sets some specific style option.
     *
     * @param string $option The option name
     */
    public function setOption($option);

    /**
     * Unsets some specific style option.
     *
     * @param string $option The option name
     */
    public function unsetOption($option);

    /**
     * Sets multiple style options at once.
     */
    public function setOptions(array $options);

    /**
     * Applies the style to a given text.
     *
     * @param string $text The text to style
     *
     * @return string
     */
    public function apply($text);
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Formatter;

/**
 * Formatter interface for console output.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
interface OutputFormatterInterface
{
    /**
     * Sets the decorated flag.
     *
     * @param bool $decorated Whether to decorate the messages or not
     */
    public function setDecorated($decorated);

    /**
     * Gets the decorated flag.
     *
     * @return bool true if the output will decorate messages, false otherwise
     */
    public function isDecorated();

    /**
     * Sets a new style.
     *
     * @param string                        $name  The style name
     * @param OutputFormatterStyleInterface $style The style instance
     */
    public function setStyle($name, OutputFormatterStyleInterface $style);

    /**
     * Checks if output formatter has style with specified name.
     *
     * @param string $name
     *
     * @return bool
     */
    public function hasStyle($name);

    /**
     * Gets style options from style with specified name.
     *
     * @param string $name
     *
     * @return OutputFormatterStyleInterface
     *
     * @throws \InvalidArgumentException When style isn't defined
     */
    public function getStyle($name);

    /**
     * Formats a message according to the given styles.
     *
     * @param string $message The message to style
     *
     * @return string The styled message
     */
    public function format($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\Console\Formatter;

use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * Formatter class for console output.
 *
 * @author Konstantin Kudryashov <ever.zet@gmail.com>
 */
class OutputFormatter implements OutputFormatterInterface
{
    private $decorated;
    private $styles = array();
    private $styleStack;

    /**
     * Escapes "<" special char in given text.
     *
     * @param string $text Text to escape
     *
     * @return string Escaped text
     */
    public static function escape($text)
    {
        $text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);

        return self::escapeTrailingBackslash($text);
    }

    /**
     * Escapes trailing "\" in given text.
     *
     * @param string $text Text to escape
     *
     * @return string Escaped text
     *
     * @internal
     */
    public static function escapeTrailingBackslash($text)
    {
        if ('\\' === substr($text, -1)) {
            $len = strlen($text);
            $text = rtrim($text, '\\');
            $text = str_replace("\0", '', $text);
            $text .= str_repeat("\0", $len - strlen($text));
        }

        return $text;
    }

    /**
     * Initializes console output formatter.
     *
     * @param bool                            $decorated Whether this formatter should actually decorate strings
     * @param OutputFormatterStyleInterface[] $styles    Array of "name => FormatterStyle" instances
     */
    public function __construct($decorated = false, array $styles = array())
    {
        $this->decorated = (bool) $decorated;

        $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
        $this->setStyle('info', new OutputFormatterStyle('green'));
        $this->setStyle('comment', new OutputFormatterStyle('yellow'));
        $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));

        foreach ($styles as $name => $style) {
            $this->setStyle($name, $style);
        }

        $this->styleStack = new OutputFormatterStyleStack();
    }

    /**
     * {@inheritdoc}
     */
    public function setDecorated($decorated)
    {
        $this->decorated = (bool) $decorated;
    }

    /**
     * {@inheritdoc}
     */
    public function isDecorated()
    {
        return $this->decorated;
    }

    /**
     * {@inheritdoc}
     */
    public function setStyle($name, OutputFormatterStyleInterface $style)
    {
        $this->styles[strtolower($name)] = $style;
    }

    /**
     * {@inheritdoc}
     */
    public function hasStyle($name)
    {
        return isset($this->styles[strtolower($name)]);
    }

    /**
     * {@inheritdoc}
     */
    public function getStyle($name)
    {
        if (!$this->hasStyle($name)) {
            throw new InvalidArgumentException(sprintf('Undefined style: %s', $name));
        }

        return $this->styles[strtolower($name)];
    }

    /**
     * {@inheritdoc}
     */
    public function format($message)
    {
        $message = (string) $message;
        $offset = 0;
        $output = '';
        $tagRegex = '[a-z][a-z0-9,_=;-]*+';
        preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
        foreach ($matches[0] as $i => $match) {
            $pos = $match[1];
            $text = $match[0];

            if (0 != $pos && '\\' == $message[$pos - 1]) {
                continue;
            }

            // add the text up to the next tag
            $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
            $offset = $pos + strlen($text);

            // opening tag?
            if ($open = '/' != $text[1]) {
                $tag = $matches[1][$i][0];
            } else {
                $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
            }

            if (!$open && !$tag) {
                // </>
                $this->styleStack->pop();
            } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
                $output .= $this->applyCurrentStyle($text);
            } elseif ($open) {
                $this->styleStack->push($style);
            } else {
                $this->styleStack->pop($style);
            }
        }

        $output .= $this->applyCurrentStyle(substr($message, $offset));

        if (false !== strpos($output, "\0")) {
            return strtr($output, array("\0" => '\\', '\\<' => '<'));
        }

        return str_replace('\\<', '<', $output);
    }

    /**
     * @return OutputFormatterStyleStack
     */
    public function getStyleStack()
    {
        return $this->styleStack;
    }

    /**
     * Tries to create new style instance from string.
     *
     * @param string $string
     *
     * @return OutputFormatterStyle|false false if string is not format string
     */
    private function createStyleFromString($string)
    {
        if (isset($this->styles[$string])) {
            return $this->styles[$string];
        }

        if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, PREG_SET_ORDER)) {
            return false;
        }

        $style = new OutputFormatterStyle();
        foreach ($matches as $match) {
            array_shift($match);

            if ('fg' == $match[0]) {
                $style->setForeground($match[1]);
            } elseif ('bg' == $match[0]) {
                $style->setBackground($match[1]);
            } elseif ('options' === $match[0]) {
                preg_match_all('([^,;]+)', $match[1], $options);
                $options = array_shift($options);
                foreach ($options as $option) {
                    try {
                        $style->setOption($option);
                    } catch (\InvalidArgumentException $e) {
                        @trigger_error(sprintf('Unknown style options are deprecated since Symfony 3.2 and will be removed in 4.0. Exception "%s".', $e->getMessage()), E_USER_DEPRECATED);

                        return false;
                    }
                }
            } else {
                return false;
            }
        }

        return $style;
    }

    /**
     * Applies current style from stack to text, if must be applied.
     *
     * @param string $text Input text
     *
     * @return string Styled text
     */
    private function applyCurrentStyle($text)
    {
        return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Formatter;

use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 */
class OutputFormatterStyleStack
{
    /**
     * @var OutputFormatterStyleInterface[]
     */
    private $styles;

    private $emptyStyle;

    public function __construct(OutputFormatterStyleInterface $emptyStyle = null)
    {
        $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle();
        $this->reset();
    }

    /**
     * Resets stack (ie. empty internal arrays).
     */
    public function reset()
    {
        $this->styles = array();
    }

    /**
     * Pushes a style in the stack.
     */
    public function push(OutputFormatterStyleInterface $style)
    {
        $this->styles[] = $style;
    }

    /**
     * Pops a style from the stack.
     *
     * @return OutputFormatterStyleInterface
     *
     * @throws InvalidArgumentException When style tags incorrectly nested
     */
    public function pop(OutputFormatterStyleInterface $style = null)
    {
        if (empty($this->styles)) {
            return $this->emptyStyle;
        }

        if (null === $style) {
            return array_pop($this->styles);
        }

        foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
            if ($style->apply('') === $stackedStyle->apply('')) {
                $this->styles = array_slice($this->styles, 0, $index);

                return $stackedStyle;
            }
        }

        throw new InvalidArgumentException('Incorrectly nested style tag found.');
    }

    /**
     * Computes current style with stacks top codes.
     *
     * @return OutputFormatterStyle
     */
    public function getCurrent()
    {
        if (empty($this->styles)) {
            return $this->emptyStyle;
        }

        return $this->styles[count($this->styles) - 1];
    }

    /**
     * @return $this
     */
    public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle)
    {
        $this->emptyStyle = $emptyStyle;

        return $this;
    }

    /**
     * @return OutputFormatterStyleInterface
     */
    public function getEmptyStyle()
    {
        return $this->emptyStyle;
    }
}
<?php

namespace Symfony\Component\Console\CommandLoader;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;

/**
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
interface CommandLoaderInterface
{
    /**
     * Loads a command.
     *
     * @param string $name
     *
     * @return Command
     *
     * @throws CommandNotFoundException
     */
    public function get($name);

    /**
     * Checks if a command exists.
     *
     * @param string $name
     *
     * @return bool
     */
    public function has($name);

    /**
     * @return string[] All registered command names
     */
    public function getNames();
}
<?php

namespace Symfony\Component\Console\CommandLoader;

use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Exception\CommandNotFoundException;

/**
 * Loads commands from a PSR-11 container.
 *
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
class ContainerCommandLoader implements CommandLoaderInterface
{
    private $container;
    private $commandMap;

    /**
     * @param ContainerInterface $container  A container from which to load command services
     * @param array              $commandMap An array with command names as keys and service ids as values
     */
    public function __construct(ContainerInterface $container, array $commandMap)
    {
        $this->container = $container;
        $this->commandMap = $commandMap;
    }

    /**
     * {@inheritdoc}
     */
    public function get($name)
    {
        if (!$this->has($name)) {
            throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
        }

        return $this->container->get($this->commandMap[$name]);
    }

    /**
     * {@inheritdoc}
     */
    public function has($name)
    {
        return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]);
    }

    /**
     * {@inheritdoc}
     */
    public function getNames()
    {
        return array_keys($this->commandMap);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\CommandLoader;

use Symfony\Component\Console\Exception\CommandNotFoundException;

/**
 * A simple command loader using factories to instantiate commands lazily.
 *
 * @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
 */
class FactoryCommandLoader implements CommandLoaderInterface
{
    private $factories;

    /**
     * @param callable[] $factories Indexed by command names
     */
    public function __construct(array $factories)
    {
        $this->factories = $factories;
    }

    /**
     * {@inheritdoc}
     */
    public function has($name)
    {
        return isset($this->factories[$name]);
    }

    /**
     * {@inheritdoc}
     */
    public function get($name)
    {
        if (!isset($this->factories[$name])) {
            throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name));
        }

        $factory = $this->factories[$name];

        return $factory();
    }

    /**
     * {@inheritdoc}
     */
    public function getNames()
    {
        return array_keys($this->factories);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;

/**
 * Text descriptor.
 *
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 *
 * @internal
 */
class TextDescriptor extends Descriptor
{
    /**
     * {@inheritdoc}
     */
    protected function describeInputArgument(InputArgument $argument, array $options = array())
    {
        if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) {
            $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
        } else {
            $default = '';
        }

        $totalWidth = isset($options['total_width']) ? $options['total_width'] : Helper::strlen($argument->getName());
        $spacingWidth = $totalWidth - strlen($argument->getName());

        $this->writeText(sprintf('  <info>%s</info>  %s%s%s',
            $argument->getName(),
            str_repeat(' ', $spacingWidth),
            // + 4 = 2 spaces before <info>, 2 spaces after </info>
            preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()),
            $default
        ), $options);
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputOption(InputOption $option, array $options = array())
    {
        if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) {
            $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
        } else {
            $default = '';
        }

        $value = '';
        if ($option->acceptValue()) {
            $value = '='.strtoupper($option->getName());

            if ($option->isValueOptional()) {
                $value = '['.$value.']';
            }
        }

        $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option));
        $synopsis = sprintf('%s%s',
            $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : '    ',
            sprintf('--%s%s', $option->getName(), $value)
        );

        $spacingWidth = $totalWidth - Helper::strlen($synopsis);

        $this->writeText(sprintf('  <info>%s</info>  %s%s%s%s',
            $synopsis,
            str_repeat(' ', $spacingWidth),
            // + 4 = 2 spaces before <info>, 2 spaces after </info>
            preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()),
            $default,
            $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
        ), $options);
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputDefinition(InputDefinition $definition, array $options = array())
    {
        $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
        foreach ($definition->getArguments() as $argument) {
            $totalWidth = max($totalWidth, Helper::strlen($argument->getName()));
        }

        if ($definition->getArguments()) {
            $this->writeText('<comment>Arguments:</comment>', $options);
            $this->writeText("\n");
            foreach ($definition->getArguments() as $argument) {
                $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth)));
                $this->writeText("\n");
            }
        }

        if ($definition->getArguments() && $definition->getOptions()) {
            $this->writeText("\n");
        }

        if ($definition->getOptions()) {
            $laterOptions = array();

            $this->writeText('<comment>Options:</comment>', $options);
            foreach ($definition->getOptions() as $option) {
                if (strlen($option->getShortcut()) > 1) {
                    $laterOptions[] = $option;
                    continue;
                }
                $this->writeText("\n");
                $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth)));
            }
            foreach ($laterOptions as $option) {
                $this->writeText("\n");
                $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth)));
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function describeCommand(Command $command, array $options = array())
    {
        $command->getSynopsis(true);
        $command->getSynopsis(false);
        $command->mergeApplicationDefinition(false);

        $this->writeText('<comment>Usage:</comment>', $options);
        foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) {
            $this->writeText("\n");
            $this->writeText('  '.OutputFormatter::escape($usage), $options);
        }
        $this->writeText("\n");

        $definition = $command->getNativeDefinition();
        if ($definition->getOptions() || $definition->getArguments()) {
            $this->writeText("\n");
            $this->describeInputDefinition($definition, $options);
            $this->writeText("\n");
        }

        if ($help = $command->getProcessedHelp()) {
            $this->writeText("\n");
            $this->writeText('<comment>Help:</comment>', $options);
            $this->writeText("\n");
            $this->writeText('  '.str_replace("\n", "\n  ", $help), $options);
            $this->writeText("\n");
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function describeApplication(Application $application, array $options = array())
    {
        $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
        $description = new ApplicationDescription($application, $describedNamespace);

        if (isset($options['raw_text']) && $options['raw_text']) {
            $width = $this->getColumnWidth($description->getCommands());

            foreach ($description->getCommands() as $command) {
                $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
                $this->writeText("\n");
            }
        } else {
            if ('' != $help = $application->getHelp()) {
                $this->writeText("$help\n\n", $options);
            }

            $this->writeText("<comment>Usage:</comment>\n", $options);
            $this->writeText("  command [options] [arguments]\n\n", $options);

            $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options);

            $this->writeText("\n");
            $this->writeText("\n");

            $commands = $description->getCommands();
            $namespaces = $description->getNamespaces();
            if ($describedNamespace && $namespaces) {
                // make sure all alias commands are included when describing a specific namespace
                $describedNamespaceInfo = reset($namespaces);
                foreach ($describedNamespaceInfo['commands'] as $name) {
                    $commands[$name] = $description->getCommand($name);
                }
            }

            // calculate max. width based on available commands per namespace
            $width = $this->getColumnWidth(call_user_func_array('array_merge', array_map(function ($namespace) use ($commands) {
                return array_intersect($namespace['commands'], array_keys($commands));
            }, $namespaces)));

            if ($describedNamespace) {
                $this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
            } else {
                $this->writeText('<comment>Available commands:</comment>', $options);
            }

            foreach ($namespaces as $namespace) {
                $namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) {
                    return isset($commands[$name]);
                });

                if (!$namespace['commands']) {
                    continue;
                }

                if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
                    $this->writeText("\n");
                    $this->writeText(' <comment>'.$namespace['id'].'</comment>', $options);
                }

                foreach ($namespace['commands'] as $name) {
                    $this->writeText("\n");
                    $spacingWidth = $width - Helper::strlen($name);
                    $command = $commands[$name];
                    $commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : '';
                    $this->writeText(sprintf('  <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options);
                }
            }

            $this->writeText("\n");
        }
    }

    /**
     * {@inheritdoc}
     */
    private function writeText($content, array $options = array())
    {
        $this->write(
            isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
            isset($options['raw_output']) ? !$options['raw_output'] : true
        );
    }

    /**
     * Formats command aliases to show them in the command description.
     *
     * @return string
     */
    private function getCommandAliasesText(Command $command)
    {
        $text = '';
        $aliases = $command->getAliases();

        if ($aliases) {
            $text = '['.implode('|', $aliases).'] ';
        }

        return $text;
    }

    /**
     * Formats input option/argument default value.
     *
     * @param mixed $default
     *
     * @return string
     */
    private function formatDefaultValue($default)
    {
        if (INF === $default) {
            return 'INF';
        }

        if (is_string($default)) {
            $default = OutputFormatter::escape($default);
        } elseif (is_array($default)) {
            foreach ($default as $key => $value) {
                if (is_string($value)) {
                    $default[$key] = OutputFormatter::escape($value);
                }
            }
        }

        return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
    }

    /**
     * @param (Command|string)[] $commands
     *
     * @return int
     */
    private function getColumnWidth(array $commands)
    {
        $widths = array();

        foreach ($commands as $command) {
            if ($command instanceof Command) {
                $widths[] = Helper::strlen($command->getName());
                foreach ($command->getAliases() as $alias) {
                    $widths[] = Helper::strlen($alias);
                }
            } else {
                $widths[] = Helper::strlen($command);
            }
        }

        return $widths ? max($widths) + 2 : 0;
    }

    /**
     * @param InputOption[] $options
     *
     * @return int
     */
    private function calculateTotalWidthForOptions(array $options)
    {
        $totalWidth = 0;
        foreach ($options as $option) {
            // "-" + shortcut + ", --" + name
            $nameLength = 1 + max(Helper::strlen($option->getShortcut()), 1) + 4 + Helper::strlen($option->getName());

            if ($option->acceptValue()) {
                $valueLength = 1 + Helper::strlen($option->getName()); // = + value
                $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ]

                $nameLength += $valueLength;
            }
            $totalWidth = max($totalWidth, $nameLength);
        }

        return $totalWidth;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\CommandNotFoundException;

/**
 * @author Jean-FranÃ§ois Simon <jeanfrancois.simon@sensiolabs.com>
 *
 * @internal
 */
class ApplicationDescription
{
    const GLOBAL_NAMESPACE = '_global';

    private $application;
    private $namespace;
    private $showHidden;

    /**
     * @var array
     */
    private $namespaces;

    /**
     * @var Command[]
     */
    private $commands;

    /**
     * @var Command[]
     */
    private $aliases;

    /**
     * @param Application $application
     * @param string|null $namespace
     * @param bool        $showHidden
     */
    public function __construct(Application $application, $namespace = null, $showHidden = false)
    {
        $this->application = $application;
        $this->namespace = $namespace;
        $this->showHidden = $showHidden;
    }

    /**
     * @return array
     */
    public function getNamespaces()
    {
        if (null === $this->namespaces) {
            $this->inspectApplication();
        }

        return $this->namespaces;
    }

    /**
     * @return Command[]
     */
    public function getCommands()
    {
        if (null === $this->commands) {
            $this->inspectApplication();
        }

        return $this->commands;
    }

    /**
     * @param string $name
     *
     * @return Command
     *
     * @throws CommandNotFoundException
     */
    public function getCommand($name)
    {
        if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
            throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name));
        }

        return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];
    }

    private function inspectApplication()
    {
        $this->commands = array();
        $this->namespaces = array();

        $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null);
        foreach ($this->sortCommands($all) as $namespace => $commands) {
            $names = array();

            /** @var Command $command */
            foreach ($commands as $name => $command) {
                if (!$command->getName() || (!$this->showHidden && $command->isHidden())) {
                    continue;
                }

                if ($command->getName() === $name) {
                    $this->commands[$name] = $command;
                } else {
                    $this->aliases[$name] = $command;
                }

                $names[] = $name;
            }

            $this->namespaces[$namespace] = array('id' => $namespace, 'commands' => $names);
        }
    }

    /**
     * @return array
     */
    private function sortCommands(array $commands)
    {
        $namespacedCommands = array();
        $globalCommands = array();
        foreach ($commands as $name => $command) {
            $key = $this->application->extractNamespace($name, 1);
            if (!$key) {
                $globalCommands['_global'][$name] = $command;
            } else {
                $namespacedCommands[$key][$name] = $command;
            }
        }
        ksort($namespacedCommands);
        $namespacedCommands = array_merge($globalCommands, $namespacedCommands);

        foreach ($namespacedCommands as &$commandsSet) {
            ksort($commandsSet);
        }
        // unset reference to keep scope clear
        unset($commandsSet);

        return $namespacedCommands;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;

/**
 * XML descriptor.
 *
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 *
 * @internal
 */
class XmlDescriptor extends Descriptor
{
    /**
     * @return \DOMDocument
     */
    public function getInputDefinitionDocument(InputDefinition $definition)
    {
        $dom = new \DOMDocument('1.0', 'UTF-8');
        $dom->appendChild($definitionXML = $dom->createElement('definition'));

        $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
        foreach ($definition->getArguments() as $argument) {
            $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument));
        }

        $definitionXML->appendChild($optionsXML = $dom->createElement('options'));
        foreach ($definition->getOptions() as $option) {
            $this->appendDocument($optionsXML, $this->getInputOptionDocument($option));
        }

        return $dom;
    }

    /**
     * @return \DOMDocument
     */
    public function getCommandDocument(Command $command)
    {
        $dom = new \DOMDocument('1.0', 'UTF-8');
        $dom->appendChild($commandXML = $dom->createElement('command'));

        $command->getSynopsis();
        $command->mergeApplicationDefinition(false);

        $commandXML->setAttribute('id', $command->getName());
        $commandXML->setAttribute('name', $command->getName());
        $commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0);

        $commandXML->appendChild($usagesXML = $dom->createElement('usages'));

        foreach (array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()) as $usage) {
            $usagesXML->appendChild($dom->createElement('usage', $usage));
        }

        $commandXML->appendChild($descriptionXML = $dom->createElement('description'));
        $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription())));

        $commandXML->appendChild($helpXML = $dom->createElement('help'));
        $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp())));

        $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition());
        $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0));

        return $dom;
    }

    /**
     * @param Application $application
     * @param string|null $namespace
     *
     * @return \DOMDocument
     */
    public function getApplicationDocument(Application $application, $namespace = null)
    {
        $dom = new \DOMDocument('1.0', 'UTF-8');
        $dom->appendChild($rootXml = $dom->createElement('symfony'));

        if ('UNKNOWN' !== $application->getName()) {
            $rootXml->setAttribute('name', $application->getName());
            if ('UNKNOWN' !== $application->getVersion()) {
                $rootXml->setAttribute('version', $application->getVersion());
            }
        }

        $rootXml->appendChild($commandsXML = $dom->createElement('commands'));

        $description = new ApplicationDescription($application, $namespace, true);

        if ($namespace) {
            $commandsXML->setAttribute('namespace', $namespace);
        }

        foreach ($description->getCommands() as $command) {
            $this->appendDocument($commandsXML, $this->getCommandDocument($command));
        }

        if (!$namespace) {
            $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces'));

            foreach ($description->getNamespaces() as $namespaceDescription) {
                $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace'));
                $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']);

                foreach ($namespaceDescription['commands'] as $name) {
                    $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command'));
                    $commandXML->appendChild($dom->createTextNode($name));
                }
            }
        }

        return $dom;
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputArgument(InputArgument $argument, array $options = array())
    {
        $this->writeDocument($this->getInputArgumentDocument($argument));
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputOption(InputOption $option, array $options = array())
    {
        $this->writeDocument($this->getInputOptionDocument($option));
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputDefinition(InputDefinition $definition, array $options = array())
    {
        $this->writeDocument($this->getInputDefinitionDocument($definition));
    }

    /**
     * {@inheritdoc}
     */
    protected function describeCommand(Command $command, array $options = array())
    {
        $this->writeDocument($this->getCommandDocument($command));
    }

    /**
     * {@inheritdoc}
     */
    protected function describeApplication(Application $application, array $options = array())
    {
        $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null));
    }

    /**
     * Appends document children to parent node.
     */
    private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
    {
        foreach ($importedParent->childNodes as $childNode) {
            $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
        }
    }

    /**
     * Writes DOM document.
     *
     * @return \DOMDocument|string
     */
    private function writeDocument(\DOMDocument $dom)
    {
        $dom->formatOutput = true;
        $this->write($dom->saveXML());
    }

    /**
     * @return \DOMDocument
     */
    private function getInputArgumentDocument(InputArgument $argument)
    {
        $dom = new \DOMDocument('1.0', 'UTF-8');

        $dom->appendChild($objectXML = $dom->createElement('argument'));
        $objectXML->setAttribute('name', $argument->getName());
        $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
        $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
        $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
        $descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));

        $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));
        $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array()));
        foreach ($defaults as $default) {
            $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
            $defaultXML->appendChild($dom->createTextNode($default));
        }

        return $dom;
    }

    /**
     * @return \DOMDocument
     */
    private function getInputOptionDocument(InputOption $option)
    {
        $dom = new \DOMDocument('1.0', 'UTF-8');

        $dom->appendChild($objectXML = $dom->createElement('option'));
        $objectXML->setAttribute('name', '--'.$option->getName());
        $pos = strpos($option->getShortcut(), '|');
        if (false !== $pos) {
            $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos));
            $objectXML->setAttribute('shortcuts', '-'.implode('|-', explode('|', $option->getShortcut())));
        } else {
            $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
        }
        $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
        $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
        $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
        $objectXML->appendChild($descriptionXML = $dom->createElement('description'));
        $descriptionXML->appendChild($dom->createTextNode($option->getDescription()));

        if ($option->acceptValue()) {
            $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array()));
            $objectXML->appendChild($defaultsXML = $dom->createElement('defaults'));

            if (!empty($defaults)) {
                foreach ($defaults as $default) {
                    $defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
                    $defaultXML->appendChild($dom->createTextNode($default));
                }
            }
        }

        return $dom;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Output\OutputInterface;

/**
 * Descriptor interface.
 *
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 */
interface DescriptorInterface
{
    /**
     * Describes an InputArgument instance.
     *
     * @param OutputInterface $output
     * @param object          $object
     * @param array           $options
     */
    public function describe(OutputInterface $output, $object, array $options = array());
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * @author Jean-FranÃ§ois Simon <jeanfrancois.simon@sensiolabs.com>
 *
 * @internal
 */
abstract class Descriptor implements DescriptorInterface
{
    /**
     * @var OutputInterface
     */
    protected $output;

    /**
     * {@inheritdoc}
     */
    public function describe(OutputInterface $output, $object, array $options = array())
    {
        $this->output = $output;

        switch (true) {
            case $object instanceof InputArgument:
                $this->describeInputArgument($object, $options);
                break;
            case $object instanceof InputOption:
                $this->describeInputOption($object, $options);
                break;
            case $object instanceof InputDefinition:
                $this->describeInputDefinition($object, $options);
                break;
            case $object instanceof Command:
                $this->describeCommand($object, $options);
                break;
            case $object instanceof Application:
                $this->describeApplication($object, $options);
                break;
            default:
                throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
        }
    }

    /**
     * Writes content to output.
     *
     * @param string $content
     * @param bool   $decorated
     */
    protected function write($content, $decorated = false)
    {
        $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
    }

    /**
     * Describes an InputArgument instance.
     *
     * @return string|mixed
     */
    abstract protected function describeInputArgument(InputArgument $argument, array $options = array());

    /**
     * Describes an InputOption instance.
     *
     * @return string|mixed
     */
    abstract protected function describeInputOption(InputOption $option, array $options = array());

    /**
     * Describes an InputDefinition instance.
     *
     * @return string|mixed
     */
    abstract protected function describeInputDefinition(InputDefinition $definition, array $options = array());

    /**
     * Describes a Command instance.
     *
     * @return string|mixed
     */
    abstract protected function describeCommand(Command $command, array $options = array());

    /**
     * Describes an Application instance.
     *
     * @return string|mixed
     */
    abstract protected function describeApplication(Application $application, array $options = array());
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;

/**
 * JSON descriptor.
 *
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 *
 * @internal
 */
class JsonDescriptor extends Descriptor
{
    /**
     * {@inheritdoc}
     */
    protected function describeInputArgument(InputArgument $argument, array $options = array())
    {
        $this->writeData($this->getInputArgumentData($argument), $options);
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputOption(InputOption $option, array $options = array())
    {
        $this->writeData($this->getInputOptionData($option), $options);
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputDefinition(InputDefinition $definition, array $options = array())
    {
        $this->writeData($this->getInputDefinitionData($definition), $options);
    }

    /**
     * {@inheritdoc}
     */
    protected function describeCommand(Command $command, array $options = array())
    {
        $this->writeData($this->getCommandData($command), $options);
    }

    /**
     * {@inheritdoc}
     */
    protected function describeApplication(Application $application, array $options = array())
    {
        $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
        $description = new ApplicationDescription($application, $describedNamespace, true);
        $commands = array();

        foreach ($description->getCommands() as $command) {
            $commands[] = $this->getCommandData($command);
        }

        $data = array();
        if ('UNKNOWN' !== $application->getName()) {
            $data['application']['name'] = $application->getName();
            if ('UNKNOWN' !== $application->getVersion()) {
                $data['application']['version'] = $application->getVersion();
            }
        }

        $data['commands'] = $commands;

        if ($describedNamespace) {
            $data['namespace'] = $describedNamespace;
        } else {
            $data['namespaces'] = array_values($description->getNamespaces());
        }

        $this->writeData($data, $options);
    }

    /**
     * Writes data as json.
     *
     * @return array|string
     */
    private function writeData(array $data, array $options)
    {
        $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0));
    }

    /**
     * @return array
     */
    private function getInputArgumentData(InputArgument $argument)
    {
        return array(
            'name' => $argument->getName(),
            'is_required' => $argument->isRequired(),
            'is_array' => $argument->isArray(),
            'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()),
            'default' => INF === $argument->getDefault() ? 'INF' : $argument->getDefault(),
        );
    }

    /**
     * @return array
     */
    private function getInputOptionData(InputOption $option)
    {
        return array(
            'name' => '--'.$option->getName(),
            'shortcut' => $option->getShortcut() ? '-'.implode('|-', explode('|', $option->getShortcut())) : '',
            'accept_value' => $option->acceptValue(),
            'is_value_required' => $option->isValueRequired(),
            'is_multiple' => $option->isArray(),
            'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
            'default' => INF === $option->getDefault() ? 'INF' : $option->getDefault(),
        );
    }

    /**
     * @return array
     */
    private function getInputDefinitionData(InputDefinition $definition)
    {
        $inputArguments = array();
        foreach ($definition->getArguments() as $name => $argument) {
            $inputArguments[$name] = $this->getInputArgumentData($argument);
        }

        $inputOptions = array();
        foreach ($definition->getOptions() as $name => $option) {
            $inputOptions[$name] = $this->getInputOptionData($option);
        }

        return array('arguments' => $inputArguments, 'options' => $inputOptions);
    }

    /**
     * @return array
     */
    private function getCommandData(Command $command)
    {
        $command->getSynopsis();
        $command->mergeApplicationDefinition(false);

        return array(
            'name' => $command->getName(),
            'usage' => array_merge(array($command->getSynopsis()), $command->getUsages(), $command->getAliases()),
            'description' => $command->getDescription(),
            'help' => $command->getProcessedHelp(),
            'definition' => $this->getInputDefinitionData($command->getNativeDefinition()),
            'hidden' => $command->isHidden(),
        );
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Descriptor;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Markdown descriptor.
 *
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 *
 * @internal
 */
class MarkdownDescriptor extends Descriptor
{
    /**
     * {@inheritdoc}
     */
    public function describe(OutputInterface $output, $object, array $options = array())
    {
        $decorated = $output->isDecorated();
        $output->setDecorated(false);

        parent::describe($output, $object, $options);

        $output->setDecorated($decorated);
    }

    /**
     * {@inheritdoc}
     */
    protected function write($content, $decorated = true)
    {
        parent::write($content, $decorated);
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputArgument(InputArgument $argument, array $options = array())
    {
        $this->write(
            '#### `'.($argument->getName() ?: '<none>')."`\n\n"
            .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
            .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n"
            .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n"
            .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`'
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputOption(InputOption $option, array $options = array())
    {
        $name = '--'.$option->getName();
        if ($option->getShortcut()) {
            $name .= '|-'.implode('|-', explode('|', $option->getShortcut())).'';
        }

        $this->write(
            '#### `'.$name.'`'."\n\n"
            .($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '')
            .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n"
            .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
            .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n"
            .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`'
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function describeInputDefinition(InputDefinition $definition, array $options = array())
    {
        if ($showArguments = count($definition->getArguments()) > 0) {
            $this->write('### Arguments');
            foreach ($definition->getArguments() as $argument) {
                $this->write("\n\n");
                $this->write($this->describeInputArgument($argument));
            }
        }

        if (count($definition->getOptions()) > 0) {
            if ($showArguments) {
                $this->write("\n\n");
            }

            $this->write('### Options');
            foreach ($definition->getOptions() as $option) {
                $this->write("\n\n");
                $this->write($this->describeInputOption($option));
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function describeCommand(Command $command, array $options = array())
    {
        $command->getSynopsis();
        $command->mergeApplicationDefinition(false);

        $this->write(
            '`'.$command->getName()."`\n"
            .str_repeat('-', Helper::strlen($command->getName()) + 2)."\n\n"
            .($command->getDescription() ? $command->getDescription()."\n\n" : '')
            .'### Usage'."\n\n"
            .array_reduce(array_merge(array($command->getSynopsis()), $command->getAliases(), $command->getUsages()), function ($carry, $usage) {
                return $carry.'* `'.$usage.'`'."\n";
            })
        );

        if ($help = $command->getProcessedHelp()) {
            $this->write("\n");
            $this->write($help);
        }

        if ($command->getNativeDefinition()) {
            $this->write("\n\n");
            $this->describeInputDefinition($command->getNativeDefinition());
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function describeApplication(Application $application, array $options = array())
    {
        $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
        $description = new ApplicationDescription($application, $describedNamespace);
        $title = $this->getApplicationTitle($application);

        $this->write($title."\n".str_repeat('=', Helper::strlen($title)));

        foreach ($description->getNamespaces() as $namespace) {
            if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
                $this->write("\n\n");
                $this->write('**'.$namespace['id'].':**');
            }

            $this->write("\n\n");
            $this->write(implode("\n", array_map(function ($commandName) use ($description) {
                return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName()));
            }, $namespace['commands'])));
        }

        foreach ($description->getCommands() as $command) {
            $this->write("\n\n");
            $this->write($this->describeCommand($command));
        }
    }

    private function getApplicationTitle(Application $application)
    {
        if ('UNKNOWN' !== $application->getName()) {
            if ('UNKNOWN' !== $application->getVersion()) {
                return sprintf('%s %s', $application->getName(), $application->getVersion());
            }

            return $application->getName();
        }

        return 'Console Tool';
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Tester;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Eases the testing of console commands.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
class CommandTester
{
    private $command;
    private $input;
    private $output;
    private $inputs = array();
    private $statusCode;

    public function __construct(Command $command)
    {
        $this->command = $command;
    }

    /**
     * Executes the command.
     *
     * Available execution options:
     *
     *  * interactive: Sets the input interactive flag
     *  * decorated:   Sets the output decorated flag
     *  * verbosity:   Sets the output verbosity flag
     *
     * @param array $input   An array of command arguments and options
     * @param array $options An array of execution options
     *
     * @return int The command exit code
     */
    public function execute(array $input, array $options = array())
    {
        // set the command name automatically if the application requires
        // this argument and no command name was passed
        if (!isset($input['command'])
            && (null !== $application = $this->command->getApplication())
            && $application->getDefinition()->hasArgument('command')
        ) {
            $input = array_merge(array('command' => $this->command->getName()), $input);
        }

        $this->input = new ArrayInput($input);
        if ($this->inputs) {
            $this->input->setStream(self::createStream($this->inputs));
        }

        if (isset($options['interactive'])) {
            $this->input->setInteractive($options['interactive']);
        }

        $this->output = new StreamOutput(fopen('php://memory', 'w', false));
        $this->output->setDecorated(isset($options['decorated']) ? $options['decorated'] : false);
        if (isset($options['verbosity'])) {
            $this->output->setVerbosity($options['verbosity']);
        }

        return $this->statusCode = $this->command->run($this->input, $this->output);
    }

    /**
     * Gets the display returned by the last execution of the command.
     *
     * @param bool $normalize Whether to normalize end of lines to \n or not
     *
     * @return string The display
     */
    public function getDisplay($normalize = false)
    {
        rewind($this->output->getStream());

        $display = stream_get_contents($this->output->getStream());

        if ($normalize) {
            $display = str_replace(PHP_EOL, "\n", $display);
        }

        return $display;
    }

    /**
     * Gets the input instance used by the last execution of the command.
     *
     * @return InputInterface The current input instance
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * Gets the output instance used by the last execution of the command.
     *
     * @return OutputInterface The current output instance
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * Gets the status code returned by the last execution of the application.
     *
     * @return int The status code
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }

    /**
     * Sets the user inputs.
     *
     * @param array $inputs An array of strings representing each input
     *                      passed to the command input stream
     *
     * @return CommandTester
     */
    public function setInputs(array $inputs)
    {
        $this->inputs = $inputs;

        return $this;
    }

    private static function createStream(array $inputs)
    {
        $stream = fopen('php://memory', 'r+', false);

        fwrite($stream, implode(PHP_EOL, $inputs));
        rewind($stream);

        return $stream;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Tester;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;

/**
 * Eases the testing of console applications.
 *
 * When testing an application, don't forget to disable the auto exit flag:
 *
 *     $application = new Application();
 *     $application->setAutoExit(false);
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ApplicationTester
{
    private $application;
    private $input;
    private $statusCode;
    /**
     * @var OutputInterface
     */
    private $output;
    private $captureStreamsIndependently = false;

    public function __construct(Application $application)
    {
        $this->application = $application;
    }

    /**
     * Executes the application.
     *
     * Available options:
     *
     *  * interactive:               Sets the input interactive flag
     *  * decorated:                 Sets the output decorated flag
     *  * verbosity:                 Sets the output verbosity flag
     *  * capture_stderr_separately: Make output of stdOut and stdErr separately available
     *
     * @param array $input   An array of arguments and options
     * @param array $options An array of options
     *
     * @return int The command exit code
     */
    public function run(array $input, $options = array())
    {
        $this->input = new ArrayInput($input);
        if (isset($options['interactive'])) {
            $this->input->setInteractive($options['interactive']);
        }

        $this->captureStreamsIndependently = array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
        if (!$this->captureStreamsIndependently) {
            $this->output = new StreamOutput(fopen('php://memory', 'w', false));
            if (isset($options['decorated'])) {
                $this->output->setDecorated($options['decorated']);
            }
            if (isset($options['verbosity'])) {
                $this->output->setVerbosity($options['verbosity']);
            }
        } else {
            $this->output = new ConsoleOutput(
                isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL,
                isset($options['decorated']) ? $options['decorated'] : null
            );

            $errorOutput = new StreamOutput(fopen('php://memory', 'w', false));
            $errorOutput->setFormatter($this->output->getFormatter());
            $errorOutput->setVerbosity($this->output->getVerbosity());
            $errorOutput->setDecorated($this->output->isDecorated());

            $reflectedOutput = new \ReflectionObject($this->output);
            $strErrProperty = $reflectedOutput->getProperty('stderr');
            $strErrProperty->setAccessible(true);
            $strErrProperty->setValue($this->output, $errorOutput);

            $reflectedParent = $reflectedOutput->getParentClass();
            $streamProperty = $reflectedParent->getProperty('stream');
            $streamProperty->setAccessible(true);
            $streamProperty->setValue($this->output, fopen('php://memory', 'w', false));
        }

        return $this->statusCode = $this->application->run($this->input, $this->output);
    }

    /**
     * Gets the display returned by the last execution of the application.
     *
     * @param bool $normalize Whether to normalize end of lines to \n or not
     *
     * @return string The display
     */
    public function getDisplay($normalize = false)
    {
        rewind($this->output->getStream());

        $display = stream_get_contents($this->output->getStream());

        if ($normalize) {
            $display = str_replace(PHP_EOL, "\n", $display);
        }

        return $display;
    }

    /**
     * Gets the output written to STDERR by the application.
     *
     * @param bool $normalize Whether to normalize end of lines to \n or not
     *
     * @return string
     */
    public function getErrorOutput($normalize = false)
    {
        if (!$this->captureStreamsIndependently) {
            throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.');
        }

        rewind($this->output->getErrorOutput()->getStream());

        $display = stream_get_contents($this->output->getErrorOutput()->getStream());

        if ($normalize) {
            $display = str_replace(PHP_EOL, "\n", $display);
        }

        return $display;
    }

    /**
     * Gets the input instance used by the last execution of the application.
     *
     * @return InputInterface The current input instance
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * Gets the output instance used by the last execution of the application.
     *
     * @return OutputInterface The current output instance
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * Gets the status code returned by the last execution of the application.
     *
     * @return int The status code
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Lock\Factory;
use Symfony\Component\Lock\Lock;
use Symfony\Component\Lock\Store\FlockStore;
use Symfony\Component\Lock\Store\SemaphoreStore;

/**
 * Basic lock feature for commands.
 *
 * @author Geoffrey Brier <geoffrey.brier@gmail.com>
 */
trait LockableTrait
{
    /** @var Lock */
    private $lock;

    /**
     * Locks a command.
     *
     * @return bool
     */
    private function lock($name = null, $blocking = false)
    {
        if (!class_exists(SemaphoreStore::class)) {
            throw new RuntimeException('To enable the locking feature you must install the symfony/lock component.');
        }

        if (null !== $this->lock) {
            throw new LogicException('A lock is already in place.');
        }

        if (SemaphoreStore::isSupported($blocking)) {
            $store = new SemaphoreStore();
        } else {
            $store = new FlockStore();
        }

        $this->lock = (new Factory($store))->createLock($name ?: $this->getName());
        if (!$this->lock->acquire($blocking)) {
            $this->lock = null;

            return false;
        }

        return true;
    }

    /**
     * Releases the command lock if there is one.
     */
    private function release()
    {
        if ($this->lock) {
            $this->lock->release();
            $this->lock = 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\Console\Command;

use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * HelpCommand displays the help for a given command.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class HelpCommand extends Command
{
    private $command;

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->ignoreValidationErrors();

        $this
            ->setName('help')
            ->setDefinition(array(
                new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
                new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
                new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
            ))
            ->setDescription('Displays help for a command')
            ->setHelp(<<<'EOF'
The <info>%command.name%</info> command displays help for a given command:

  <info>php %command.full_name% list</info>

You can also output the help in other formats by using the <comment>--format</comment> option:

  <info>php %command.full_name% --format=xml list</info>

To display the list of available commands, please use the <info>list</info> command.
EOF
            )
        ;
    }

    public function setCommand(Command $command)
    {
        $this->command = $command;
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if (null === $this->command) {
            $this->command = $this->getApplication()->find($input->getArgument('command_name'));
        }

        $helper = new DescriptorHelper();
        $helper->describe($output, $this->command, array(
            'format' => $input->getOption('format'),
            'raw_text' => $input->getOption('raw'),
        ));

        $this->command = 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\Console\Command;

use Symfony\Component\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputDefinition;

/**
 * ListCommand displays the list of all available commands for the application.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ListCommand extends Command
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setName('list')
            ->setDefinition($this->createDefinition())
            ->setDescription('Lists commands')
            ->setHelp(<<<'EOF'
The <info>%command.name%</info> command lists all commands:

  <info>php %command.full_name%</info>

You can also display the commands for a specific namespace:

  <info>php %command.full_name% test</info>

You can also output the information in other formats by using the <comment>--format</comment> option:

  <info>php %command.full_name% --format=xml</info>

It's also possible to get raw list of commands (useful for embedding command runner):

  <info>php %command.full_name% --raw</info>
EOF
            )
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function getNativeDefinition()
    {
        return $this->createDefinition();
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $helper = new DescriptorHelper();
        $helper->describe($output, $this->getApplication(), array(
            'format' => $input->getOption('format'),
            'raw_text' => $input->getOption('raw'),
            'namespace' => $input->getArgument('namespace'),
        ));
    }

    /**
     * {@inheritdoc}
     */
    private function createDefinition()
    {
        return new InputDefinition(array(
            new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
            new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
            new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
        ));
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;

/**
 * Base class for all commands.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Command
{
    /**
     * @var string|null The default command name
     */
    protected static $defaultName;

    private $application;
    private $name;
    private $processTitle;
    private $aliases = array();
    private $definition;
    private $hidden = false;
    private $help;
    private $description;
    private $ignoreValidationErrors = false;
    private $applicationDefinitionMerged = false;
    private $applicationDefinitionMergedWithArgs = false;
    private $code;
    private $synopsis = array();
    private $usages = array();
    private $helperSet;

    /**
     * @return string|null The default command name or null when no default name is set
     */
    public static function getDefaultName()
    {
        $class = get_called_class();
        $r = new \ReflectionProperty($class, 'defaultName');

        return $class === $r->class ? static::$defaultName : null;
    }

    /**
     * @param string|null $name The name of the command; passing null means it must be set in configure()
     *
     * @throws LogicException When the command name is empty
     */
    public function __construct($name = null)
    {
        $this->definition = new InputDefinition();

        if (null !== $name || null !== $name = static::getDefaultName()) {
            $this->setName($name);
        }

        $this->configure();
    }

    /**
     * Ignores validation errors.
     *
     * This is mainly useful for the help command.
     */
    public function ignoreValidationErrors()
    {
        $this->ignoreValidationErrors = true;
    }

    public function setApplication(Application $application = null)
    {
        $this->application = $application;
        if ($application) {
            $this->setHelperSet($application->getHelperSet());
        } else {
            $this->helperSet = null;
        }
    }

    public function setHelperSet(HelperSet $helperSet)
    {
        $this->helperSet = $helperSet;
    }

    /**
     * Gets the helper set.
     *
     * @return HelperSet A HelperSet instance
     */
    public function getHelperSet()
    {
        return $this->helperSet;
    }

    /**
     * Gets the application instance for this command.
     *
     * @return Application An Application instance
     */
    public function getApplication()
    {
        return $this->application;
    }

    /**
     * Checks whether the command is enabled or not in the current environment.
     *
     * Override this to check for x or y and return false if the command can not
     * run properly under the current conditions.
     *
     * @return bool
     */
    public function isEnabled()
    {
        return true;
    }

    /**
     * Configures the current command.
     */
    protected function configure()
    {
    }

    /**
     * Executes the current command.
     *
     * This method is not abstract because you can use this class
     * as a concrete class. In this case, instead of defining the
     * execute() method, you set the code to execute by passing
     * a Closure to the setCode() method.
     *
     * @return null|int null or 0 if everything went fine, or an error code
     *
     * @throws LogicException When this abstract method is not implemented
     *
     * @see setCode()
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        throw new LogicException('You must override the execute() method in the concrete command class.');
    }

    /**
     * Interacts with the user.
     *
     * This method is executed before the InputDefinition is validated.
     * This means that this is the only place where the command can
     * interactively ask for values of missing required arguments.
     */
    protected function interact(InputInterface $input, OutputInterface $output)
    {
    }

    /**
     * Initializes the command just after the input has been validated.
     *
     * This is mainly useful when a lot of commands extends one main command
     * where some things need to be initialized based on the input arguments and options.
     */
    protected function initialize(InputInterface $input, OutputInterface $output)
    {
    }

    /**
     * Runs the command.
     *
     * The code to execute is either defined directly with the
     * setCode() method or by overriding the execute() method
     * in a sub-class.
     *
     * @return int The command exit code
     *
     * @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}.
     *
     * @see setCode()
     * @see execute()
     */
    public function run(InputInterface $input, OutputInterface $output)
    {
        // force the creation of the synopsis before the merge with the app definition
        $this->getSynopsis(true);
        $this->getSynopsis(false);

        // add the application arguments and options
        $this->mergeApplicationDefinition();

        // bind the input against the command specific arguments/options
        try {
            $input->bind($this->definition);
        } catch (ExceptionInterface $e) {
            if (!$this->ignoreValidationErrors) {
                throw $e;
            }
        }

        $this->initialize($input, $output);

        if (null !== $this->processTitle) {
            if (function_exists('cli_set_process_title')) {
                if (false === @cli_set_process_title($this->processTitle)) {
                    if ('Darwin' === PHP_OS) {
                        $output->writeln('<comment>Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.</comment>');
                    } else {
                        $error = error_get_last();
                        trigger_error($error['message'], E_USER_WARNING);
                    }
                }
            } elseif (function_exists('setproctitle')) {
                setproctitle($this->processTitle);
            } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
                $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
            }
        }

        if ($input->isInteractive()) {
            $this->interact($input, $output);
        }

        // The command name argument is often omitted when a command is executed directly with its run() method.
        // It would fail the validation if we didn't make sure the command argument is present,
        // since it's required by the application.
        if ($input->hasArgument('command') && null === $input->getArgument('command')) {
            $input->setArgument('command', $this->getName());
        }

        $input->validate();

        if ($this->code) {
            $statusCode = call_user_func($this->code, $input, $output);
        } else {
            $statusCode = $this->execute($input, $output);
        }

        return is_numeric($statusCode) ? (int) $statusCode : 0;
    }

    /**
     * Sets the code to execute when running this command.
     *
     * If this method is used, it overrides the code defined
     * in the execute() method.
     *
     * @param callable $code A callable(InputInterface $input, OutputInterface $output)
     *
     * @return $this
     *
     * @throws InvalidArgumentException
     *
     * @see execute()
     */
    public function setCode(callable $code)
    {
        if ($code instanceof \Closure) {
            $r = new \ReflectionFunction($code);
            if (null === $r->getClosureThis()) {
                if (\PHP_VERSION_ID < 70000) {
                    // Bug in PHP5: https://bugs.php.net/bug.php?id=64761
                    // This means that we cannot bind static closures and therefore we must
                    // ignore any errors here.  There is no way to test if the closure is
                    // bindable.
                    $code = @\Closure::bind($code, $this);
                } else {
                    $code = \Closure::bind($code, $this);
                }
            }
        }

        $this->code = $code;

        return $this;
    }

    /**
     * Merges the application definition with the command definition.
     *
     * This method is not part of public API and should not be used directly.
     *
     * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
     */
    public function mergeApplicationDefinition($mergeArgs = true)
    {
        if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
            return;
        }

        $this->definition->addOptions($this->application->getDefinition()->getOptions());

        if ($mergeArgs) {
            $currentArguments = $this->definition->getArguments();
            $this->definition->setArguments($this->application->getDefinition()->getArguments());
            $this->definition->addArguments($currentArguments);
        }

        $this->applicationDefinitionMerged = true;
        if ($mergeArgs) {
            $this->applicationDefinitionMergedWithArgs = true;
        }
    }

    /**
     * Sets an array of argument and option instances.
     *
     * @param array|InputDefinition $definition An array of argument and option instances or a definition instance
     *
     * @return $this
     */
    public function setDefinition($definition)
    {
        if ($definition instanceof InputDefinition) {
            $this->definition = $definition;
        } else {
            $this->definition->setDefinition($definition);
        }

        $this->applicationDefinitionMerged = false;

        return $this;
    }

    /**
     * Gets the InputDefinition attached to this Command.
     *
     * @return InputDefinition An InputDefinition instance
     */
    public function getDefinition()
    {
        return $this->definition;
    }

    /**
     * Gets the InputDefinition to be used to create representations of this Command.
     *
     * Can be overridden to provide the original command representation when it would otherwise
     * be changed by merging with the application InputDefinition.
     *
     * This method is not part of public API and should not be used directly.
     *
     * @return InputDefinition An InputDefinition instance
     */
    public function getNativeDefinition()
    {
        return $this->getDefinition();
    }

    /**
     * Adds an argument.
     *
     * @param string $name        The argument name
     * @param int    $mode        The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
     * @param string $description A description text
     * @param mixed  $default     The default value (for InputArgument::OPTIONAL mode only)
     *
     * @return $this
     */
    public function addArgument($name, $mode = null, $description = '', $default = null)
    {
        $this->definition->addArgument(new InputArgument($name, $mode, $description, $default));

        return $this;
    }

    /**
     * Adds an option.
     *
     * @param string $name        The option name
     * @param string $shortcut    The shortcut (can be null)
     * @param int    $mode        The option mode: One of the InputOption::VALUE_* constants
     * @param string $description A description text
     * @param mixed  $default     The default value (must be null for InputOption::VALUE_NONE)
     *
     * @return $this
     */
    public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
    {
        $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));

        return $this;
    }

    /**
     * Sets the name of the command.
     *
     * This method can set both the namespace and the name if
     * you separate them by a colon (:)
     *
     *     $command->setName('foo:bar');
     *
     * @param string $name The command name
     *
     * @return $this
     *
     * @throws InvalidArgumentException When the name is invalid
     */
    public function setName($name)
    {
        $this->validateName($name);

        $this->name = $name;

        return $this;
    }

    /**
     * Sets the process title of the command.
     *
     * This feature should be used only when creating a long process command,
     * like a daemon.
     *
     * PHP 5.5+ or the proctitle PECL library is required
     *
     * @param string $title The process title
     *
     * @return $this
     */
    public function setProcessTitle($title)
    {
        $this->processTitle = $title;

        return $this;
    }

    /**
     * Returns the command name.
     *
     * @return string The command name
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param bool $hidden Whether or not the command should be hidden from the list of commands
     *
     * @return Command The current instance
     */
    public function setHidden($hidden)
    {
        $this->hidden = (bool) $hidden;

        return $this;
    }

    /**
     * @return bool whether the command should be publicly shown or not
     */
    public function isHidden()
    {
        return $this->hidden;
    }

    /**
     * Sets the description for the command.
     *
     * @param string $description The description for the command
     *
     * @return $this
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Returns the description for the command.
     *
     * @return string The description for the command
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Sets the help for the command.
     *
     * @param string $help The help for the command
     *
     * @return $this
     */
    public function setHelp($help)
    {
        $this->help = $help;

        return $this;
    }

    /**
     * Returns the help for the command.
     *
     * @return string The help for the command
     */
    public function getHelp()
    {
        return $this->help;
    }

    /**
     * Returns the processed help for the command replacing the %command.name% and
     * %command.full_name% patterns with the real values dynamically.
     *
     * @return string The processed help for the command
     */
    public function getProcessedHelp()
    {
        $name = $this->name;

        $placeholders = array(
            '%command.name%',
            '%command.full_name%',
        );
        $replacements = array(
            $name,
            $_SERVER['PHP_SELF'].' '.$name,
        );

        return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
    }

    /**
     * Sets the aliases for the command.
     *
     * @param string[] $aliases An array of aliases for the command
     *
     * @return $this
     *
     * @throws InvalidArgumentException When an alias is invalid
     */
    public function setAliases($aliases)
    {
        if (!is_array($aliases) && !$aliases instanceof \Traversable) {
            throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
        }

        foreach ($aliases as $alias) {
            $this->validateName($alias);
        }

        $this->aliases = $aliases;

        return $this;
    }

    /**
     * Returns the aliases for the command.
     *
     * @return array An array of aliases for the command
     */
    public function getAliases()
    {
        return $this->aliases;
    }

    /**
     * Returns the synopsis for the command.
     *
     * @param bool $short Whether to show the short version of the synopsis (with options folded) or not
     *
     * @return string The synopsis
     */
    public function getSynopsis($short = false)
    {
        $key = $short ? 'short' : 'long';

        if (!isset($this->synopsis[$key])) {
            $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
        }

        return $this->synopsis[$key];
    }

    /**
     * Add a command usage example.
     *
     * @param string $usage The usage, it'll be prefixed with the command name
     *
     * @return $this
     */
    public function addUsage($usage)
    {
        if (0 !== strpos($usage, $this->name)) {
            $usage = sprintf('%s %s', $this->name, $usage);
        }

        $this->usages[] = $usage;

        return $this;
    }

    /**
     * Returns alternative usages of the command.
     *
     * @return array
     */
    public function getUsages()
    {
        return $this->usages;
    }

    /**
     * Gets a helper instance by name.
     *
     * @param string $name The helper name
     *
     * @return mixed The helper value
     *
     * @throws LogicException           if no HelperSet is defined
     * @throws InvalidArgumentException if the helper is not defined
     */
    public function getHelper($name)
    {
        if (null === $this->helperSet) {
            throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name));
        }

        return $this->helperSet->get($name);
    }

    /**
     * Validates a command name.
     *
     * It must be non-empty and parts can optionally be separated by ":".
     *
     * @param string $name
     *
     * @throws InvalidArgumentException When the name is invalid
     */
    private function validateName($name)
    {
        if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
            throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $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\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;

/**
 * Defines the styles for a Table.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Ð¡Ð°ÑˆÐ° Ð¡Ñ‚Ð°Ð¼ÐµÐ½ÐºÐ¾Ð²Ð¸Ñ› <umpirsky@gmail.com>
 */
class TableStyle
{
    private $paddingChar = ' ';
    private $horizontalBorderChar = '-';
    private $verticalBorderChar = '|';
    private $crossingChar = '+';
    private $cellHeaderFormat = '<info>%s</info>';
    private $cellRowFormat = '%s';
    private $cellRowContentFormat = ' %s ';
    private $borderFormat = '%s';
    private $padType = STR_PAD_RIGHT;

    /**
     * Sets padding character, used for cell padding.
     *
     * @param string $paddingChar
     *
     * @return $this
     */
    public function setPaddingChar($paddingChar)
    {
        if (!$paddingChar) {
            throw new LogicException('The padding char must not be empty');
        }

        $this->paddingChar = $paddingChar;

        return $this;
    }

    /**
     * Gets padding character, used for cell padding.
     *
     * @return string
     */
    public function getPaddingChar()
    {
        return $this->paddingChar;
    }

    /**
     * Sets horizontal border character.
     *
     * @param string $horizontalBorderChar
     *
     * @return $this
     */
    public function setHorizontalBorderChar($horizontalBorderChar)
    {
        $this->horizontalBorderChar = $horizontalBorderChar;

        return $this;
    }

    /**
     * Gets horizontal border character.
     *
     * @return string
     */
    public function getHorizontalBorderChar()
    {
        return $this->horizontalBorderChar;
    }

    /**
     * Sets vertical border character.
     *
     * @param string $verticalBorderChar
     *
     * @return $this
     */
    public function setVerticalBorderChar($verticalBorderChar)
    {
        $this->verticalBorderChar = $verticalBorderChar;

        return $this;
    }

    /**
     * Gets vertical border character.
     *
     * @return string
     */
    public function getVerticalBorderChar()
    {
        return $this->verticalBorderChar;
    }

    /**
     * Sets crossing character.
     *
     * @param string $crossingChar
     *
     * @return $this
     */
    public function setCrossingChar($crossingChar)
    {
        $this->crossingChar = $crossingChar;

        return $this;
    }

    /**
     * Gets crossing character.
     *
     * @return string $crossingChar
     */
    public function getCrossingChar()
    {
        return $this->crossingChar;
    }

    /**
     * Sets header cell format.
     *
     * @param string $cellHeaderFormat
     *
     * @return $this
     */
    public function setCellHeaderFormat($cellHeaderFormat)
    {
        $this->cellHeaderFormat = $cellHeaderFormat;

        return $this;
    }

    /**
     * Gets header cell format.
     *
     * @return string
     */
    public function getCellHeaderFormat()
    {
        return $this->cellHeaderFormat;
    }

    /**
     * Sets row cell format.
     *
     * @param string $cellRowFormat
     *
     * @return $this
     */
    public function setCellRowFormat($cellRowFormat)
    {
        $this->cellRowFormat = $cellRowFormat;

        return $this;
    }

    /**
     * Gets row cell format.
     *
     * @return string
     */
    public function getCellRowFormat()
    {
        return $this->cellRowFormat;
    }

    /**
     * Sets row cell content format.
     *
     * @param string $cellRowContentFormat
     *
     * @return $this
     */
    public function setCellRowContentFormat($cellRowContentFormat)
    {
        $this->cellRowContentFormat = $cellRowContentFormat;

        return $this;
    }

    /**
     * Gets row cell content format.
     *
     * @return string
     */
    public function getCellRowContentFormat()
    {
        return $this->cellRowContentFormat;
    }

    /**
     * Sets table border format.
     *
     * @param string $borderFormat
     *
     * @return $this
     */
    public function setBorderFormat($borderFormat)
    {
        $this->borderFormat = $borderFormat;

        return $this;
    }

    /**
     * Gets table border format.
     *
     * @return string
     */
    public function getBorderFormat()
    {
        return $this->borderFormat;
    }

    /**
     * Sets cell padding type.
     *
     * @param int $padType STR_PAD_*
     *
     * @return $this
     */
    public function setPadType($padType)
    {
        if (!in_array($padType, array(STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH), true)) {
            throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).');
        }

        $this->padType = $padType;

        return $this;
    }

    /**
     * Gets cell padding type.
     *
     * @return int
     */
    public function getPadType()
    {
        return $this->padType;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Formatter\OutputFormatter;

/**
 * Symfony Style Guide compliant question helper.
 *
 * @author Kevin Bond <kevinbond@gmail.com>
 */
class SymfonyQuestionHelper extends QuestionHelper
{
    /**
     * {@inheritdoc}
     *
     * To be removed in 4.0
     */
    public function ask(InputInterface $input, OutputInterface $output, Question $question)
    {
        $validator = $question->getValidator();
        $question->setValidator(function ($value) use ($validator) {
            if (null !== $validator) {
                $value = $validator($value);
            } else {
                // make required
                if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) {
                    @trigger_error('The default question validator is deprecated since Symfony 3.3 and will not be used anymore in version 4.0. Set a custom question validator if needed.', E_USER_DEPRECATED);

                    throw new LogicException('A value is required.');
                }
            }

            return $value;
        });

        return parent::ask($input, $output, $question);
    }

    /**
     * {@inheritdoc}
     */
    protected function writePrompt(OutputInterface $output, Question $question)
    {
        $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion());
        $default = $question->getDefault();

        switch (true) {
            case null === $default:
                $text = sprintf(' <info>%s</info>:', $text);

                break;

            case $question instanceof ConfirmationQuestion:
                $text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');

                break;

            case $question instanceof ChoiceQuestion && $question->isMultiselect():
                $choices = $question->getChoices();
                $default = explode(',', $default);

                foreach ($default as $key => $value) {
                    $default[$key] = $choices[trim($value)];
                }

                $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape(implode(', ', $default)));

                break;

            case $question instanceof ChoiceQuestion:
                $choices = $question->getChoices();
                $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($choices[$default]));

                break;

            default:
                $text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, OutputFormatter::escape($default));
        }

        $output->writeln($text);

        if ($question instanceof ChoiceQuestion) {
            $width = max(array_map('strlen', array_keys($question->getChoices())));

            foreach ($question->getChoices() as $key => $value) {
                $output->writeln(sprintf("  [<comment>%-${width}s</comment>] %s", $key, $value));
            }
        }

        $output->write(' > ');
    }

    /**
     * {@inheritdoc}
     */
    protected function writeError(OutputInterface $output, \Exception $error)
    {
        if ($output instanceof SymfonyStyle) {
            $output->newLine();
            $output->error($error->getMessage());

            return;
        }

        parent::writeError($output, $error);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

/**
 * HelperInterface is the interface all helpers must implement.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface HelperInterface
{
    /**
     * Sets the helper set associated with this helper.
     */
    public function setHelperSet(HelperSet $helperSet = null);

    /**
     * Gets the helper set associated with this helper.
     *
     * @return HelperSet A HelperSet instance
     */
    public function getHelperSet();

    /**
     * Returns the canonical name of this helper.
     *
     * @return string The canonical name
     */
    public function getName();
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
 */
class TableCell
{
    private $value;
    private $options = array(
        'rowspan' => 1,
        'colspan' => 1,
    );

    /**
     * @param string $value
     * @param array  $options
     */
    public function __construct($value = '', array $options = array())
    {
        if (is_numeric($value) && !is_string($value)) {
            $value = (string) $value;
        }

        $this->value = $value;

        // check option names
        if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
            throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff)));
        }

        $this->options = array_merge($this->options, $options);
    }

    /**
     * Returns the cell value.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->value;
    }

    /**
     * Gets number of colspan.
     *
     * @return int
     */
    public function getColspan()
    {
        return (int) $this->options['colspan'];
    }

    /**
     * Gets number of rowspan.
     *
     * @return int
     */
    public function getRowspan()
    {
        return (int) $this->options['rowspan'];
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

/**
 * The ProcessHelper class provides helpers to run external processes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ProcessHelper extends Helper
{
    /**
     * Runs an external process.
     *
     * @param OutputInterface      $output    An OutputInterface instance
     * @param string|array|Process $cmd       An instance of Process or an array of arguments to escape and run or a command to run
     * @param string|null          $error     An error message that must be displayed if something went wrong
     * @param callable|null        $callback  A PHP callback to run whenever there is some
     *                                        output available on STDOUT or STDERR
     * @param int                  $verbosity The threshold for verbosity
     *
     * @return Process The process that ran
     */
    public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE)
    {
        if ($output instanceof ConsoleOutputInterface) {
            $output = $output->getErrorOutput();
        }

        $formatter = $this->getHelperSet()->get('debug_formatter');

        if ($cmd instanceof Process) {
            $process = $cmd;
        } else {
            $process = new Process($cmd);
        }

        if ($verbosity <= $output->getVerbosity()) {
            $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
        }

        if ($output->isDebug()) {
            $callback = $this->wrapCallback($output, $process, $callback);
        }

        $process->run($callback);

        if ($verbosity <= $output->getVerbosity()) {
            $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
            $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
        }

        if (!$process->isSuccessful() && null !== $error) {
            $output->writeln(sprintf('<error>%s</error>', $this->escapeString($error)));
        }

        return $process;
    }

    /**
     * Runs the process.
     *
     * This is identical to run() except that an exception is thrown if the process
     * exits with a non-zero exit code.
     *
     * @param OutputInterface $output   An OutputInterface instance
     * @param string|Process  $cmd      An instance of Process or a command to run
     * @param string|null     $error    An error message that must be displayed if something went wrong
     * @param callable|null   $callback A PHP callback to run whenever there is some
     *                                  output available on STDOUT or STDERR
     *
     * @return Process The process that ran
     *
     * @throws ProcessFailedException
     *
     * @see run()
     */
    public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null)
    {
        $process = $this->run($output, $cmd, $error, $callback);

        if (!$process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        return $process;
    }

    /**
     * Wraps a Process callback to add debugging output.
     *
     * @param OutputInterface $output   An OutputInterface interface
     * @param Process         $process  The Process
     * @param callable|null   $callback A PHP callable
     *
     * @return callable
     */
    public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null)
    {
        if ($output instanceof ConsoleOutputInterface) {
            $output = $output->getErrorOutput();
        }

        $formatter = $this->getHelperSet()->get('debug_formatter');

        return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
            $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type));

            if (null !== $callback) {
                call_user_func($callback, $type, $buffer);
            }
        };
    }

    private function escapeString($str)
    {
        return str_replace('<', '\\<', $str);
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'process';
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

/**
 * Marks a row as being a separator.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TableSeparator extends TableCell
{
    public function __construct(array $options = array())
    {
        parent::__construct('', $options);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Formatter\OutputFormatter;

/**
 * The Formatter class provides helpers to format messages.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FormatterHelper extends Helper
{
    /**
     * Formats a message within a section.
     *
     * @param string $section The section name
     * @param string $message The message
     * @param string $style   The style to apply to the section
     *
     * @return string The format section
     */
    public function formatSection($section, $message, $style = 'info')
    {
        return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
    }

    /**
     * Formats a message as a block of text.
     *
     * @param string|array $messages The message to write in the block
     * @param string       $style    The style to apply to the whole block
     * @param bool         $large    Whether to return a large block
     *
     * @return string The formatter message
     */
    public function formatBlock($messages, $style, $large = false)
    {
        if (!is_array($messages)) {
            $messages = array($messages);
        }

        $len = 0;
        $lines = array();
        foreach ($messages as $message) {
            $message = OutputFormatter::escape($message);
            $lines[] = sprintf($large ? '  %s  ' : ' %s ', $message);
            $len = max($this->strlen($message) + ($large ? 4 : 2), $len);
        }

        $messages = $large ? array(str_repeat(' ', $len)) : array();
        for ($i = 0; isset($lines[$i]); ++$i) {
            $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i]));
        }
        if ($large) {
            $messages[] = str_repeat(' ', $len);
        }

        for ($i = 0; isset($messages[$i]); ++$i) {
            $messages[$i] = sprintf('<%s>%s</%s>', $style, $messages[$i], $style);
        }

        return implode("\n", $messages);
    }

    /**
     * Truncates a message to the given length.
     *
     * @param string $message
     * @param int    $length
     * @param string $suffix
     *
     * @return string
     */
    public function truncate($message, $length, $suffix = '...')
    {
        $computedLength = $length - $this->strlen($suffix);

        if ($computedLength > $this->strlen($message)) {
            return $message;
        }

        if (false === $encoding = mb_detect_encoding($message, null, true)) {
            return substr($message, 0, $length).$suffix;
        }

        return mb_substr($message, 0, $length, $encoding).$suffix;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'formatter';
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Terminal;

/**
 * The ProgressBar provides helpers to display progress output.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Chris Jones <leeked@gmail.com>
 */
final class ProgressBar
{
    private $barWidth = 28;
    private $barChar;
    private $emptyBarChar = '-';
    private $progressChar = '>';
    private $format;
    private $internalFormat;
    private $redrawFreq = 1;
    private $output;
    private $step = 0;
    private $max;
    private $startTime;
    private $stepWidth;
    private $percent = 0.0;
    private $formatLineCount;
    private $messages = array();
    private $overwrite = true;
    private $terminal;
    private $firstRun = true;

    private static $formatters;
    private static $formats;

    /**
     * @param OutputInterface $output An OutputInterface instance
     * @param int             $max    Maximum steps (0 if unknown)
     */
    public function __construct(OutputInterface $output, $max = 0)
    {
        if ($output instanceof ConsoleOutputInterface) {
            $output = $output->getErrorOutput();
        }

        $this->output = $output;
        $this->setMaxSteps($max);
        $this->terminal = new Terminal();

        if (!$this->output->isDecorated()) {
            // disable overwrite when output does not support ANSI codes.
            $this->overwrite = false;

            // set a reasonable redraw frequency so output isn't flooded
            $this->setRedrawFrequency($max / 10);
        }

        $this->startTime = time();
    }

    /**
     * Sets a placeholder formatter for a given name.
     *
     * This method also allow you to override an existing placeholder.
     *
     * @param string   $name     The placeholder name (including the delimiter char like %)
     * @param callable $callable A PHP callable
     */
    public static function setPlaceholderFormatterDefinition($name, callable $callable)
    {
        if (!self::$formatters) {
            self::$formatters = self::initPlaceholderFormatters();
        }

        self::$formatters[$name] = $callable;
    }

    /**
     * Gets the placeholder formatter for a given name.
     *
     * @param string $name The placeholder name (including the delimiter char like %)
     *
     * @return callable|null A PHP callable
     */
    public static function getPlaceholderFormatterDefinition($name)
    {
        if (!self::$formatters) {
            self::$formatters = self::initPlaceholderFormatters();
        }

        return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
    }

    /**
     * Sets a format for a given name.
     *
     * This method also allow you to override an existing format.
     *
     * @param string $name   The format name
     * @param string $format A format string
     */
    public static function setFormatDefinition($name, $format)
    {
        if (!self::$formats) {
            self::$formats = self::initFormats();
        }

        self::$formats[$name] = $format;
    }

    /**
     * Gets the format for a given name.
     *
     * @param string $name The format name
     *
     * @return string|null A format string
     */
    public static function getFormatDefinition($name)
    {
        if (!self::$formats) {
            self::$formats = self::initFormats();
        }

        return isset(self::$formats[$name]) ? self::$formats[$name] : null;
    }

    /**
     * Associates a text with a named placeholder.
     *
     * The text is displayed when the progress bar is rendered but only
     * when the corresponding placeholder is part of the custom format line
     * (by wrapping the name with %).
     *
     * @param string $message The text to associate with the placeholder
     * @param string $name    The name of the placeholder
     */
    public function setMessage($message, $name = 'message')
    {
        $this->messages[$name] = $message;
    }

    public function getMessage($name = 'message')
    {
        return $this->messages[$name];
    }

    /**
     * Gets the progress bar start time.
     *
     * @return int The progress bar start time
     */
    public function getStartTime()
    {
        return $this->startTime;
    }

    /**
     * Gets the progress bar maximal steps.
     *
     * @return int The progress bar max steps
     */
    public function getMaxSteps()
    {
        return $this->max;
    }

    /**
     * Gets the current step position.
     *
     * @return int The progress bar step
     */
    public function getProgress()
    {
        return $this->step;
    }

    /**
     * Gets the progress bar step width.
     *
     * @return int The progress bar step width
     */
    private function getStepWidth()
    {
        return $this->stepWidth;
    }

    /**
     * Gets the current progress bar percent.
     *
     * @return float The current progress bar percent
     */
    public function getProgressPercent()
    {
        return $this->percent;
    }

    /**
     * Sets the progress bar width.
     *
     * @param int $size The progress bar size
     */
    public function setBarWidth($size)
    {
        $this->barWidth = max(1, (int) $size);
    }

    /**
     * Gets the progress bar width.
     *
     * @return int The progress bar size
     */
    public function getBarWidth()
    {
        return $this->barWidth;
    }

    /**
     * Sets the bar character.
     *
     * @param string $char A character
     */
    public function setBarCharacter($char)
    {
        $this->barChar = $char;
    }

    /**
     * Gets the bar character.
     *
     * @return string A character
     */
    public function getBarCharacter()
    {
        if (null === $this->barChar) {
            return $this->max ? '=' : $this->emptyBarChar;
        }

        return $this->barChar;
    }

    /**
     * Sets the empty bar character.
     *
     * @param string $char A character
     */
    public function setEmptyBarCharacter($char)
    {
        $this->emptyBarChar = $char;
    }

    /**
     * Gets the empty bar character.
     *
     * @return string A character
     */
    public function getEmptyBarCharacter()
    {
        return $this->emptyBarChar;
    }

    /**
     * Sets the progress bar character.
     *
     * @param string $char A character
     */
    public function setProgressCharacter($char)
    {
        $this->progressChar = $char;
    }

    /**
     * Gets the progress bar character.
     *
     * @return string A character
     */
    public function getProgressCharacter()
    {
        return $this->progressChar;
    }

    /**
     * Sets the progress bar format.
     *
     * @param string $format The format
     */
    public function setFormat($format)
    {
        $this->format = null;
        $this->internalFormat = $format;
    }

    /**
     * Sets the redraw frequency.
     *
     * @param int|float $freq The frequency in steps
     */
    public function setRedrawFrequency($freq)
    {
        $this->redrawFreq = max((int) $freq, 1);
    }

    /**
     * Starts the progress output.
     *
     * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
     */
    public function start($max = null)
    {
        $this->startTime = time();
        $this->step = 0;
        $this->percent = 0.0;

        if (null !== $max) {
            $this->setMaxSteps($max);
        }

        $this->display();
    }

    /**
     * Advances the progress output X steps.
     *
     * @param int $step Number of steps to advance
     */
    public function advance($step = 1)
    {
        $this->setProgress($this->step + $step);
    }

    /**
     * Sets whether to overwrite the progressbar, false for new line.
     *
     * @param bool $overwrite
     */
    public function setOverwrite($overwrite)
    {
        $this->overwrite = (bool) $overwrite;
    }

    /**
     * Sets the current progress.
     *
     * @param int $step The current progress
     */
    public function setProgress($step)
    {
        $step = (int) $step;

        if ($this->max && $step > $this->max) {
            $this->max = $step;
        } elseif ($step < 0) {
            $step = 0;
        }

        $prevPeriod = (int) ($this->step / $this->redrawFreq);
        $currPeriod = (int) ($step / $this->redrawFreq);
        $this->step = $step;
        $this->percent = $this->max ? (float) $this->step / $this->max : 0;
        if ($prevPeriod !== $currPeriod || $this->max === $step) {
            $this->display();
        }
    }

    /**
     * Finishes the progress output.
     */
    public function finish()
    {
        if (!$this->max) {
            $this->max = $this->step;
        }

        if ($this->step === $this->max && !$this->overwrite) {
            // prevent double 100% output
            return;
        }

        $this->setProgress($this->max);
    }

    /**
     * Outputs the current progress string.
     */
    public function display()
    {
        if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
            return;
        }

        if (null === $this->format) {
            $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
        }

        $this->overwrite($this->buildLine());
    }

    /**
     * Removes the progress bar from the current line.
     *
     * This is useful if you wish to write some output
     * while a progress bar is running.
     * Call display() to show the progress bar again.
     */
    public function clear()
    {
        if (!$this->overwrite) {
            return;
        }

        if (null === $this->format) {
            $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
        }

        $this->overwrite('');
    }

    /**
     * Sets the progress bar format.
     *
     * @param string $format The format
     */
    private function setRealFormat($format)
    {
        // try to use the _nomax variant if available
        if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
            $this->format = self::getFormatDefinition($format.'_nomax');
        } elseif (null !== self::getFormatDefinition($format)) {
            $this->format = self::getFormatDefinition($format);
        } else {
            $this->format = $format;
        }

        $this->formatLineCount = substr_count($this->format, "\n");
    }

    /**
     * Sets the progress bar maximal steps.
     *
     * @param int $max The progress bar max steps
     */
    private function setMaxSteps($max)
    {
        $this->max = max(0, (int) $max);
        $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4;
    }

    /**
     * Overwrites a previous message to the output.
     *
     * @param string $message The message
     */
    private function overwrite($message)
    {
        if ($this->overwrite) {
            if (!$this->firstRun) {
                // Move the cursor to the beginning of the line
                $this->output->write("\x0D");

                // Erase the line
                $this->output->write("\x1B[2K");

                // Erase previous lines
                if ($this->formatLineCount > 0) {
                    $this->output->write(str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount));
                }
            }
        } elseif ($this->step > 0) {
            $this->output->writeln('');
        }

        $this->firstRun = false;

        $this->output->write($message);
    }

    private function determineBestFormat()
    {
        switch ($this->output->getVerbosity()) {
            // OutputInterface::VERBOSITY_QUIET: display is disabled anyway
            case OutputInterface::VERBOSITY_VERBOSE:
                return $this->max ? 'verbose' : 'verbose_nomax';
            case OutputInterface::VERBOSITY_VERY_VERBOSE:
                return $this->max ? 'very_verbose' : 'very_verbose_nomax';
            case OutputInterface::VERBOSITY_DEBUG:
                return $this->max ? 'debug' : 'debug_nomax';
            default:
                return $this->max ? 'normal' : 'normal_nomax';
        }
    }

    private static function initPlaceholderFormatters()
    {
        return array(
            'bar' => function (ProgressBar $bar, OutputInterface $output) {
                $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth());
                $display = str_repeat($bar->getBarCharacter(), $completeBars);
                if ($completeBars < $bar->getBarWidth()) {
                    $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
                    $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
                }

                return $display;
            },
            'elapsed' => function (ProgressBar $bar) {
                return Helper::formatTime(time() - $bar->getStartTime());
            },
            'remaining' => function (ProgressBar $bar) {
                if (!$bar->getMaxSteps()) {
                    throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
                }

                if (!$bar->getProgress()) {
                    $remaining = 0;
                } else {
                    $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress()));
                }

                return Helper::formatTime($remaining);
            },
            'estimated' => function (ProgressBar $bar) {
                if (!$bar->getMaxSteps()) {
                    throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.');
                }

                if (!$bar->getProgress()) {
                    $estimated = 0;
                } else {
                    $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps());
                }

                return Helper::formatTime($estimated);
            },
            'memory' => function (ProgressBar $bar) {
                return Helper::formatMemory(memory_get_usage(true));
            },
            'current' => function (ProgressBar $bar) {
                return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT);
            },
            'max' => function (ProgressBar $bar) {
                return $bar->getMaxSteps();
            },
            'percent' => function (ProgressBar $bar) {
                return floor($bar->getProgressPercent() * 100);
            },
        );
    }

    private static function initFormats()
    {
        return array(
            'normal' => ' %current%/%max% [%bar%] %percent:3s%%',
            'normal_nomax' => ' %current% [%bar%]',

            'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%',
            'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',

            'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%',
            'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%',

            'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%',
            'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
        );
    }

    /**
     * @return string
     */
    private function buildLine()
    {
        $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
        $callback = function ($matches) {
            if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
                $text = call_user_func($formatter, $this, $this->output);
            } elseif (isset($this->messages[$matches[1]])) {
                $text = $this->messages[$matches[1]];
            } else {
                return $matches[0];
            }

            if (isset($matches[2])) {
                $text = sprintf('%'.$matches[2], $text);
            }

            return $text;
        };
        $line = preg_replace_callback($regex, $callback, $this->format);

        // gets string length for each sub line with multiline format
        $linesLength = array_map(function ($subLine) {
            return Helper::strlenWithoutDecoration($this->output->getFormatter(), rtrim($subLine, "\r"));
        }, explode("\n", $line));

        $linesWidth = max($linesLength);

        $terminalWidth = $this->terminal->getWidth();
        if ($linesWidth <= $terminalWidth) {
            return $line;
        }

        $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth);

        return preg_replace_callback($regex, $callback, $this->format);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

/**
 * Helps outputting debug information when running an external program from a command.
 *
 * An external program can be a Process, an HTTP request, or anything else.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DebugFormatterHelper extends Helper
{
    private $colors = array('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default');
    private $started = array();
    private $count = -1;

    /**
     * Starts a debug formatting session.
     *
     * @param string $id      The id of the formatting session
     * @param string $message The message to display
     * @param string $prefix  The prefix to use
     *
     * @return string
     */
    public function start($id, $message, $prefix = 'RUN')
    {
        $this->started[$id] = array('border' => ++$this->count % count($this->colors));

        return sprintf("%s<bg=blue;fg=white> %s </> <fg=blue>%s</>\n", $this->getBorder($id), $prefix, $message);
    }

    /**
     * Adds progress to a formatting session.
     *
     * @param string $id          The id of the formatting session
     * @param string $buffer      The message to display
     * @param bool   $error       Whether to consider the buffer as error
     * @param string $prefix      The prefix for output
     * @param string $errorPrefix The prefix for error output
     *
     * @return string
     */
    public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR')
    {
        $message = '';

        if ($error) {
            if (isset($this->started[$id]['out'])) {
                $message .= "\n";
                unset($this->started[$id]['out']);
            }
            if (!isset($this->started[$id]['err'])) {
                $message .= sprintf('%s<bg=red;fg=white> %s </> ', $this->getBorder($id), $errorPrefix);
                $this->started[$id]['err'] = true;
            }

            $message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix), $buffer);
        } else {
            if (isset($this->started[$id]['err'])) {
                $message .= "\n";
                unset($this->started[$id]['err']);
            }
            if (!isset($this->started[$id]['out'])) {
                $message .= sprintf('%s<bg=green;fg=white> %s </> ', $this->getBorder($id), $prefix);
                $this->started[$id]['out'] = true;
            }

            $message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix), $buffer);
        }

        return $message;
    }

    /**
     * Stops a formatting session.
     *
     * @param string $id         The id of the formatting session
     * @param string $message    The message to display
     * @param bool   $successful Whether to consider the result as success
     * @param string $prefix     The prefix for the end output
     *
     * @return string
     */
    public function stop($id, $message, $successful, $prefix = 'RES')
    {
        $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';

        if ($successful) {
            return sprintf("%s%s<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
        }

        $message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);

        unset($this->started[$id]['out'], $this->started[$id]['err']);

        return $message;
    }

    /**
     * @param string $id The id of the formatting session
     *
     * @return string
     */
    private function getBorder($id)
    {
        return sprintf('<bg=%s> </>', $this->colors[$this->started[$id]['border']]);
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'debug_formatter';
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * HelperSet represents a set of helpers to be used with a command.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class HelperSet implements \IteratorAggregate
{
    /**
     * @var Helper[]
     */
    private $helpers = array();
    private $command;

    /**
     * @param Helper[] $helpers An array of helper
     */
    public function __construct(array $helpers = array())
    {
        foreach ($helpers as $alias => $helper) {
            $this->set($helper, is_int($alias) ? null : $alias);
        }
    }

    /**
     * Sets a helper.
     *
     * @param HelperInterface $helper The helper instance
     * @param string          $alias  An alias
     */
    public function set(HelperInterface $helper, $alias = null)
    {
        $this->helpers[$helper->getName()] = $helper;
        if (null !== $alias) {
            $this->helpers[$alias] = $helper;
        }

        $helper->setHelperSet($this);
    }

    /**
     * Returns true if the helper if defined.
     *
     * @param string $name The helper name
     *
     * @return bool true if the helper is defined, false otherwise
     */
    public function has($name)
    {
        return isset($this->helpers[$name]);
    }

    /**
     * Gets a helper value.
     *
     * @param string $name The helper name
     *
     * @return HelperInterface The helper instance
     *
     * @throws InvalidArgumentException if the helper is not defined
     */
    public function get($name)
    {
        if (!$this->has($name)) {
            throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
        }

        return $this->helpers[$name];
    }

    public function setCommand(Command $command = null)
    {
        $this->command = $command;
    }

    /**
     * Gets the command associated with this helper set.
     *
     * @return Command A Command instance
     */
    public function getCommand()
    {
        return $this->command;
    }

    /**
     * @return Helper[]
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->helpers);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputAwareInterface;

/**
 * An implementation of InputAwareInterface for Helpers.
 *
 * @author Wouter J <waldio.webdesign@gmail.com>
 */
abstract class InputAwareHelper extends Helper implements InputAwareInterface
{
    protected $input;

    /**
     * {@inheritdoc}
     */
    public function setInput(InputInterface $input)
    {
        $this->input = $input;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StreamableInputInterface;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Question\ChoiceQuestion;

/**
 * The QuestionHelper class provides helpers to interact with the user.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class QuestionHelper extends Helper
{
    private $inputStream;
    private static $shell;
    private static $stty;

    /**
     * Asks a question to the user.
     *
     * @return mixed The user answer
     *
     * @throws RuntimeException If there is no data to read in the input stream
     */
    public function ask(InputInterface $input, OutputInterface $output, Question $question)
    {
        if ($output instanceof ConsoleOutputInterface) {
            $output = $output->getErrorOutput();
        }

        if (!$input->isInteractive()) {
            if ($question instanceof ChoiceQuestion) {
                $choices = $question->getChoices();

                return $choices[$question->getDefault()];
            }

            return $question->getDefault();
        }

        if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) {
            $this->inputStream = $stream;
        }

        if (!$question->getValidator()) {
            return $this->doAsk($output, $question);
        }

        $interviewer = function () use ($output, $question) {
            return $this->doAsk($output, $question);
        };

        return $this->validateAttempts($interviewer, $output, $question);
    }

    /**
     * Sets the input stream to read from when interacting with the user.
     *
     * This is mainly useful for testing purpose.
     *
     * @deprecated since version 3.2, to be removed in 4.0. Use
     *             StreamableInputInterface::setStream() instead.
     *
     * @param resource $stream The input stream
     *
     * @throws InvalidArgumentException In case the stream is not a resource
     */
    public function setInputStream($stream)
    {
        @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::setStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED);

        if (!is_resource($stream)) {
            throw new InvalidArgumentException('Input stream must be a valid resource.');
        }

        $this->inputStream = $stream;
    }

    /**
     * Returns the helper's input stream.
     *
     * @deprecated since version 3.2, to be removed in 4.0. Use
     *             StreamableInputInterface::getStream() instead.
     *
     * @return resource
     */
    public function getInputStream()
    {
        if (0 === func_num_args() || func_get_arg(0)) {
            @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::getStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED);
        }

        return $this->inputStream;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'question';
    }

    /**
     * Prevents usage of stty.
     */
    public static function disableStty()
    {
        self::$stty = false;
    }

    /**
     * Asks the question to the user.
     *
     * @return bool|mixed|null|string
     *
     * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
     */
    private function doAsk(OutputInterface $output, Question $question)
    {
        $this->writePrompt($output, $question);

        $inputStream = $this->inputStream ?: STDIN;
        $autocomplete = $question->getAutocompleterValues();

        if (null === $autocomplete || !$this->hasSttyAvailable()) {
            $ret = false;
            if ($question->isHidden()) {
                try {
                    $ret = trim($this->getHiddenResponse($output, $inputStream));
                } catch (RuntimeException $e) {
                    if (!$question->isHiddenFallback()) {
                        throw $e;
                    }
                }
            }

            if (false === $ret) {
                $ret = fgets($inputStream, 4096);
                if (false === $ret) {
                    throw new RuntimeException('Aborted');
                }
                $ret = trim($ret);
            }
        } else {
            $ret = trim($this->autocomplete($output, $question, $inputStream, is_array($autocomplete) ? $autocomplete : iterator_to_array($autocomplete, false)));
        }

        $ret = strlen($ret) > 0 ? $ret : $question->getDefault();

        if ($normalizer = $question->getNormalizer()) {
            return $normalizer($ret);
        }

        return $ret;
    }

    /**
     * Outputs the question prompt.
     */
    protected function writePrompt(OutputInterface $output, Question $question)
    {
        $message = $question->getQuestion();

        if ($question instanceof ChoiceQuestion) {
            $maxWidth = max(array_map(array($this, 'strlen'), array_keys($question->getChoices())));

            $messages = (array) $question->getQuestion();
            foreach ($question->getChoices() as $key => $value) {
                $width = $maxWidth - $this->strlen($key);
                $messages[] = '  [<info>'.$key.str_repeat(' ', $width).'</info>] '.$value;
            }

            $output->writeln($messages);

            $message = $question->getPrompt();
        }

        $output->write($message);
    }

    /**
     * Outputs an error message.
     */
    protected function writeError(OutputInterface $output, \Exception $error)
    {
        if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
            $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
        } else {
            $message = '<error>'.$error->getMessage().'</error>';
        }

        $output->writeln($message);
    }

    /**
     * Autocompletes a question.
     *
     * @param OutputInterface $output
     * @param Question        $question
     * @param resource        $inputStream
     * @param array           $autocomplete
     *
     * @return string
     */
    private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete)
    {
        $ret = '';

        $i = 0;
        $ofs = -1;
        $matches = $autocomplete;
        $numMatches = count($matches);

        $sttyMode = shell_exec('stty -g');

        // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead)
        shell_exec('stty -icanon -echo');

        // Add highlighted text style
        $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));

        // Read a keypress
        while (!feof($inputStream)) {
            $c = fread($inputStream, 1);

            // Backspace Character
            if ("\177" === $c) {
                if (0 === $numMatches && 0 !== $i) {
                    --$i;
                    // Move cursor backwards
                    $output->write("\033[1D");
                }

                if (0 === $i) {
                    $ofs = -1;
                    $matches = $autocomplete;
                    $numMatches = count($matches);
                } else {
                    $numMatches = 0;
                }

                // Pop the last character off the end of our string
                $ret = substr($ret, 0, $i);
            } elseif ("\033" === $c) {
                // Did we read an escape sequence?
                $c .= fread($inputStream, 2);

                // A = Up Arrow. B = Down Arrow
                if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
                    if ('A' === $c[2] && -1 === $ofs) {
                        $ofs = 0;
                    }

                    if (0 === $numMatches) {
                        continue;
                    }

                    $ofs += ('A' === $c[2]) ? -1 : 1;
                    $ofs = ($numMatches + $ofs) % $numMatches;
                }
            } elseif (ord($c) < 32) {
                if ("\t" === $c || "\n" === $c) {
                    if ($numMatches > 0 && -1 !== $ofs) {
                        $ret = $matches[$ofs];
                        // Echo out remaining chars for current match
                        $output->write(substr($ret, $i));
                        $i = strlen($ret);
                    }

                    if ("\n" === $c) {
                        $output->write($c);
                        break;
                    }

                    $numMatches = 0;
                }

                continue;
            } else {
                $output->write($c);
                $ret .= $c;
                ++$i;

                $numMatches = 0;
                $ofs = 0;

                foreach ($autocomplete as $value) {
                    // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
                    if (0 === strpos($value, $ret) && $i !== strlen($value)) {
                        $matches[$numMatches++] = $value;
                    }
                }
            }

            // Erase characters from cursor to end of line
            $output->write("\033[K");

            if ($numMatches > 0 && -1 !== $ofs) {
                // Save cursor position
                $output->write("\0337");
                // Write highlighted text
                $output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $i)).'</hl>');
                // Restore cursor position
                $output->write("\0338");
            }
        }

        // Reset stty so it behaves normally again
        shell_exec(sprintf('stty %s', $sttyMode));

        return $ret;
    }

    /**
     * Gets a hidden response from user.
     *
     * @param OutputInterface $output      An Output instance
     * @param resource        $inputStream The handler resource
     *
     * @return string The answer
     *
     * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden
     */
    private function getHiddenResponse(OutputInterface $output, $inputStream)
    {
        if ('\\' === DIRECTORY_SEPARATOR) {
            $exe = __DIR__.'/../Resources/bin/hiddeninput.exe';

            // handle code running from a phar
            if ('phar:' === substr(__FILE__, 0, 5)) {
                $tmpExe = sys_get_temp_dir().'/hiddeninput.exe';
                copy($exe, $tmpExe);
                $exe = $tmpExe;
            }

            $value = rtrim(shell_exec($exe));
            $output->writeln('');

            if (isset($tmpExe)) {
                unlink($tmpExe);
            }

            return $value;
        }

        if ($this->hasSttyAvailable()) {
            $sttyMode = shell_exec('stty -g');

            shell_exec('stty -echo');
            $value = fgets($inputStream, 4096);
            shell_exec(sprintf('stty %s', $sttyMode));

            if (false === $value) {
                throw new RuntimeException('Aborted');
            }

            $value = trim($value);
            $output->writeln('');

            return $value;
        }

        if (false !== $shell = $this->getShell()) {
            $readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
            $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
            $value = rtrim(shell_exec($command));
            $output->writeln('');

            return $value;
        }

        throw new RuntimeException('Unable to hide the response.');
    }

    /**
     * Validates an attempt.
     *
     * @param callable        $interviewer A callable that will ask for a question and return the result
     * @param OutputInterface $output      An Output instance
     * @param Question        $question    A Question instance
     *
     * @return mixed The validated response
     *
     * @throws \Exception In case the max number of attempts has been reached and no valid response has been given
     */
    private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question)
    {
        $error = null;
        $attempts = $question->getMaxAttempts();
        while (null === $attempts || $attempts--) {
            if (null !== $error) {
                $this->writeError($output, $error);
            }

            try {
                return call_user_func($question->getValidator(), $interviewer());
            } catch (RuntimeException $e) {
                throw $e;
            } catch (\Exception $error) {
            }
        }

        throw $error;
    }

    /**
     * Returns a valid unix shell.
     *
     * @return string|bool The valid shell name, false in case no valid shell is found
     */
    private function getShell()
    {
        if (null !== self::$shell) {
            return self::$shell;
        }

        self::$shell = false;

        if (file_exists('/usr/bin/env')) {
            // handle other OSs with bash/zsh/ksh/csh if available to hide the answer
            $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
            foreach (array('bash', 'zsh', 'ksh', 'csh') as $sh) {
                if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
                    self::$shell = $sh;
                    break;
                }
            }
        }

        return self::$shell;
    }

    /**
     * Returns whether Stty is available or not.
     *
     * @return bool
     */
    private function hasSttyAvailable()
    {
        if (null !== self::$stty) {
            return self::$stty;
        }

        exec('stty 2>&1', $output, $exitcode);

        return self::$stty = 0 === $exitcode;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;

/**
 * Helper is the base class for all helper classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class Helper implements HelperInterface
{
    protected $helperSet = null;

    /**
     * {@inheritdoc}
     */
    public function setHelperSet(HelperSet $helperSet = null)
    {
        $this->helperSet = $helperSet;
    }

    /**
     * {@inheritdoc}
     */
    public function getHelperSet()
    {
        return $this->helperSet;
    }

    /**
     * Returns the length of a string, using mb_strwidth if it is available.
     *
     * @param string $string The string to check its length
     *
     * @return int The length of the string
     */
    public static function strlen($string)
    {
        if (false === $encoding = mb_detect_encoding($string, null, true)) {
            return strlen($string);
        }

        return mb_strwidth($string, $encoding);
    }

    /**
     * Returns the subset of a string, using mb_substr if it is available.
     *
     * @param string   $string String to subset
     * @param int      $from   Start offset
     * @param int|null $length Length to read
     *
     * @return string The string subset
     */
    public static function substr($string, $from, $length = null)
    {
        if (false === $encoding = mb_detect_encoding($string, null, true)) {
            return substr($string, $from, $length);
        }

        return mb_substr($string, $from, $length, $encoding);
    }

    public static function formatTime($secs)
    {
        static $timeFormats = array(
            array(0, '< 1 sec'),
            array(1, '1 sec'),
            array(2, 'secs', 1),
            array(60, '1 min'),
            array(120, 'mins', 60),
            array(3600, '1 hr'),
            array(7200, 'hrs', 3600),
            array(86400, '1 day'),
            array(172800, 'days', 86400),
        );

        foreach ($timeFormats as $index => $format) {
            if ($secs >= $format[0]) {
                if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0])
                    || $index == count($timeFormats) - 1
                ) {
                    if (2 == count($format)) {
                        return $format[1];
                    }

                    return floor($secs / $format[2]).' '.$format[1];
                }
            }
        }
    }

    public static function formatMemory($memory)
    {
        if ($memory >= 1024 * 1024 * 1024) {
            return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
        }

        if ($memory >= 1024 * 1024) {
            return sprintf('%.1f MiB', $memory / 1024 / 1024);
        }

        if ($memory >= 1024) {
            return sprintf('%d KiB', $memory / 1024);
        }

        return sprintf('%d B', $memory);
    }

    public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string)
    {
        return self::strlen(self::removeDecoration($formatter, $string));
    }

    public static function removeDecoration(OutputFormatterInterface $formatter, $string)
    {
        $isDecorated = $formatter->isDecorated();
        $formatter->setDecorated(false);
        // remove <...> formatting
        $string = $formatter->format($string);
        // remove already formatted characters
        $string = preg_replace("/\033\[[^m]*m/", '', $string);
        $formatter->setDecorated($isDecorated);

        return $string;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * Provides helpers to display a table.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Ð¡Ð°ÑˆÐ° Ð¡Ñ‚Ð°Ð¼ÐµÐ½ÐºÐ¾Ð²Ð¸Ñ› <umpirsky@gmail.com>
 * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
 * @author Max Grigorian <maxakawizard@gmail.com>
 */
class Table
{
    /**
     * Table headers.
     */
    private $headers = array();

    /**
     * Table rows.
     */
    private $rows = array();

    /**
     * Column widths cache.
     */
    private $effectiveColumnWidths = array();

    /**
     * Number of columns cache.
     *
     * @var int
     */
    private $numberOfColumns;

    /**
     * @var OutputInterface
     */
    private $output;

    /**
     * @var TableStyle
     */
    private $style;

    /**
     * @var array
     */
    private $columnStyles = array();

    /**
     * User set column widths.
     *
     * @var array
     */
    private $columnWidths = array();

    private static $styles;

    public function __construct(OutputInterface $output)
    {
        $this->output = $output;

        if (!self::$styles) {
            self::$styles = self::initStyles();
        }

        $this->setStyle('default');
    }

    /**
     * Sets a style definition.
     *
     * @param string     $name  The style name
     * @param TableStyle $style A TableStyle instance
     */
    public static function setStyleDefinition($name, TableStyle $style)
    {
        if (!self::$styles) {
            self::$styles = self::initStyles();
        }

        self::$styles[$name] = $style;
    }

    /**
     * Gets a style definition by name.
     *
     * @param string $name The style name
     *
     * @return TableStyle
     */
    public static function getStyleDefinition($name)
    {
        if (!self::$styles) {
            self::$styles = self::initStyles();
        }

        if (isset(self::$styles[$name])) {
            return self::$styles[$name];
        }

        throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
    }

    /**
     * Sets table style.
     *
     * @param TableStyle|string $name The style name or a TableStyle instance
     *
     * @return $this
     */
    public function setStyle($name)
    {
        $this->style = $this->resolveStyle($name);

        return $this;
    }

    /**
     * Gets the current table style.
     *
     * @return TableStyle
     */
    public function getStyle()
    {
        return $this->style;
    }

    /**
     * Sets table column style.
     *
     * @param int               $columnIndex Column index
     * @param TableStyle|string $name        The style name or a TableStyle instance
     *
     * @return $this
     */
    public function setColumnStyle($columnIndex, $name)
    {
        $columnIndex = (int) $columnIndex;

        $this->columnStyles[$columnIndex] = $this->resolveStyle($name);

        return $this;
    }

    /**
     * Gets the current style for a column.
     *
     * If style was not set, it returns the global table style.
     *
     * @param int $columnIndex Column index
     *
     * @return TableStyle
     */
    public function getColumnStyle($columnIndex)
    {
        if (isset($this->columnStyles[$columnIndex])) {
            return $this->columnStyles[$columnIndex];
        }

        return $this->getStyle();
    }

    /**
     * Sets the minimum width of a column.
     *
     * @param int $columnIndex Column index
     * @param int $width       Minimum column width in characters
     *
     * @return $this
     */
    public function setColumnWidth($columnIndex, $width)
    {
        $this->columnWidths[(int) $columnIndex] = (int) $width;

        return $this;
    }

    /**
     * Sets the minimum width of all columns.
     *
     * @param array $widths
     *
     * @return $this
     */
    public function setColumnWidths(array $widths)
    {
        $this->columnWidths = array();
        foreach ($widths as $index => $width) {
            $this->setColumnWidth($index, $width);
        }

        return $this;
    }

    public function setHeaders(array $headers)
    {
        $headers = array_values($headers);
        if (!empty($headers) && !is_array($headers[0])) {
            $headers = array($headers);
        }

        $this->headers = $headers;

        return $this;
    }

    public function setRows(array $rows)
    {
        $this->rows = array();

        return $this->addRows($rows);
    }

    public function addRows(array $rows)
    {
        foreach ($rows as $row) {
            $this->addRow($row);
        }

        return $this;
    }

    public function addRow($row)
    {
        if ($row instanceof TableSeparator) {
            $this->rows[] = $row;

            return $this;
        }

        if (!is_array($row)) {
            throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.');
        }

        $this->rows[] = array_values($row);

        return $this;
    }

    public function setRow($column, array $row)
    {
        $this->rows[$column] = $row;

        return $this;
    }

    /**
     * Renders table to output.
     *
     * Example:
     * <code>
     * +---------------+-----------------------+------------------+
     * | ISBN          | Title                 | Author           |
     * +---------------+-----------------------+------------------+
     * | 99921-58-10-7 | Divine Comedy         | Dante Alighieri  |
     * | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
     * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
     * +---------------+-----------------------+------------------+
     * </code>
     */
    public function render()
    {
        $this->calculateNumberOfColumns();
        $rows = $this->buildTableRows($this->rows);
        $headers = $this->buildTableRows($this->headers);

        $this->calculateColumnsWidth(array_merge($headers, $rows));

        $this->renderRowSeparator();
        if (!empty($headers)) {
            foreach ($headers as $header) {
                $this->renderRow($header, $this->style->getCellHeaderFormat());
                $this->renderRowSeparator();
            }
        }
        foreach ($rows as $row) {
            if ($row instanceof TableSeparator) {
                $this->renderRowSeparator();
            } else {
                $this->renderRow($row, $this->style->getCellRowFormat());
            }
        }
        if (!empty($rows)) {
            $this->renderRowSeparator();
        }

        $this->cleanup();
    }

    /**
     * Renders horizontal header separator.
     *
     * Example: <code>+-----+-----------+-------+</code>
     */
    private function renderRowSeparator()
    {
        if (0 === $count = $this->numberOfColumns) {
            return;
        }

        if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) {
            return;
        }

        $markup = $this->style->getCrossingChar();
        for ($column = 0; $column < $count; ++$column) {
            $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->effectiveColumnWidths[$column]).$this->style->getCrossingChar();
        }

        $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup));
    }

    /**
     * Renders vertical column separator.
     */
    private function renderColumnSeparator()
    {
        return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar());
    }

    /**
     * Renders table row.
     *
     * Example: <code>| 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |</code>
     *
     * @param array  $row
     * @param string $cellFormat
     */
    private function renderRow(array $row, $cellFormat)
    {
        if (empty($row)) {
            return;
        }

        $rowContent = $this->renderColumnSeparator();
        foreach ($this->getRowColumns($row) as $column) {
            $rowContent .= $this->renderCell($row, $column, $cellFormat);
            $rowContent .= $this->renderColumnSeparator();
        }
        $this->output->writeln($rowContent);
    }

    /**
     * Renders table cell with padding.
     *
     * @param array  $row
     * @param int    $column
     * @param string $cellFormat
     */
    private function renderCell(array $row, $column, $cellFormat)
    {
        $cell = isset($row[$column]) ? $row[$column] : '';
        $width = $this->effectiveColumnWidths[$column];
        if ($cell instanceof TableCell && $cell->getColspan() > 1) {
            // add the width of the following columns(numbers of colspan).
            foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) {
                $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn];
            }
        }

        // str_pad won't work properly with multi-byte strings, we need to fix the padding
        if (false !== $encoding = mb_detect_encoding($cell, null, true)) {
            $width += strlen($cell) - mb_strwidth($cell, $encoding);
        }

        $style = $this->getColumnStyle($column);

        if ($cell instanceof TableSeparator) {
            return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width));
        }

        $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
        $content = sprintf($style->getCellRowContentFormat(), $cell);

        return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType()));
    }

    /**
     * Calculate number of columns for this table.
     */
    private function calculateNumberOfColumns()
    {
        if (null !== $this->numberOfColumns) {
            return;
        }

        $columns = array(0);
        foreach (array_merge($this->headers, $this->rows) as $row) {
            if ($row instanceof TableSeparator) {
                continue;
            }

            $columns[] = $this->getNumberOfColumns($row);
        }

        $this->numberOfColumns = max($columns);
    }

    private function buildTableRows($rows)
    {
        $unmergedRows = array();
        for ($rowKey = 0; $rowKey < count($rows); ++$rowKey) {
            $rows = $this->fillNextRows($rows, $rowKey);

            // Remove any new line breaks and replace it with a new line
            foreach ($rows[$rowKey] as $column => $cell) {
                if (!strstr($cell, "\n")) {
                    continue;
                }
                $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
                foreach ($lines as $lineKey => $line) {
                    if ($cell instanceof TableCell) {
                        $line = new TableCell($line, array('colspan' => $cell->getColspan()));
                    }
                    if (0 === $lineKey) {
                        $rows[$rowKey][$column] = $line;
                    } else {
                        $unmergedRows[$rowKey][$lineKey][$column] = $line;
                    }
                }
            }
        }

        $tableRows = array();
        foreach ($rows as $rowKey => $row) {
            $tableRows[] = $this->fillCells($row);
            if (isset($unmergedRows[$rowKey])) {
                $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]);
            }
        }

        return $tableRows;
    }

    /**
     * fill rows that contains rowspan > 1.
     *
     * @param array $rows
     * @param int   $line
     *
     * @return array
     *
     * @throws InvalidArgumentException
     */
    private function fillNextRows(array $rows, $line)
    {
        $unmergedRows = array();
        foreach ($rows[$line] as $column => $cell) {
            if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(is_object($cell) && method_exists($cell, '__toString'))) {
                throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing __toString, %s given.', gettype($cell)));
            }
            if ($cell instanceof TableCell && $cell->getRowspan() > 1) {
                $nbLines = $cell->getRowspan() - 1;
                $lines = array($cell);
                if (strstr($cell, "\n")) {
                    $lines = explode("\n", str_replace("\n", "<fg=default;bg=default>\n</>", $cell));
                    $nbLines = count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines;

                    $rows[$line][$column] = new TableCell($lines[0], array('colspan' => $cell->getColspan()));
                    unset($lines[0]);
                }

                // create a two dimensional array (rowspan x colspan)
                $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, array()), $unmergedRows);
                foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
                    $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : '';
                    $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, array('colspan' => $cell->getColspan()));
                    if ($nbLines === $unmergedRowKey - $line) {
                        break;
                    }
                }
            }
        }

        foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) {
            // we need to know if $unmergedRow will be merged or inserted into $rows
            if (isset($rows[$unmergedRowKey]) && is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) {
                foreach ($unmergedRow as $cellKey => $cell) {
                    // insert cell into row at cellKey position
                    array_splice($rows[$unmergedRowKey], $cellKey, 0, array($cell));
                }
            } else {
                $row = $this->copyRow($rows, $unmergedRowKey - 1);
                foreach ($unmergedRow as $column => $cell) {
                    if (!empty($cell)) {
                        $row[$column] = $unmergedRow[$column];
                    }
                }
                array_splice($rows, $unmergedRowKey, 0, array($row));
            }
        }

        return $rows;
    }

    /**
     * fill cells for a row that contains colspan > 1.
     *
     * @return array
     */
    private function fillCells($row)
    {
        $newRow = array();
        foreach ($row as $column => $cell) {
            $newRow[] = $cell;
            if ($cell instanceof TableCell && $cell->getColspan() > 1) {
                foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) {
                    // insert empty value at column position
                    $newRow[] = '';
                }
            }
        }

        return $newRow ?: $row;
    }

    /**
     * @param array $rows
     * @param int   $line
     *
     * @return array
     */
    private function copyRow(array $rows, $line)
    {
        $row = $rows[$line];
        foreach ($row as $cellKey => $cellValue) {
            $row[$cellKey] = '';
            if ($cellValue instanceof TableCell) {
                $row[$cellKey] = new TableCell('', array('colspan' => $cellValue->getColspan()));
            }
        }

        return $row;
    }

    /**
     * Gets number of columns by row.
     *
     * @return int
     */
    private function getNumberOfColumns(array $row)
    {
        $columns = count($row);
        foreach ($row as $column) {
            $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0;
        }

        return $columns;
    }

    /**
     * Gets list of columns for the given row.
     *
     * @return array
     */
    private function getRowColumns(array $row)
    {
        $columns = range(0, $this->numberOfColumns - 1);
        foreach ($row as $cellKey => $cell) {
            if ($cell instanceof TableCell && $cell->getColspan() > 1) {
                // exclude grouped columns.
                $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1));
            }
        }

        return $columns;
    }

    /**
     * Calculates columns widths.
     */
    private function calculateColumnsWidth(array $rows)
    {
        for ($column = 0; $column < $this->numberOfColumns; ++$column) {
            $lengths = array();
            foreach ($rows as $row) {
                if ($row instanceof TableSeparator) {
                    continue;
                }

                foreach ($row as $i => $cell) {
                    if ($cell instanceof TableCell) {
                        $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
                        $textLength = Helper::strlen($textContent);
                        if ($textLength > 0) {
                            $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
                            foreach ($contentColumns as $position => $content) {
                                $row[$i + $position] = $content;
                            }
                        }
                    }
                }

                $lengths[] = $this->getCellWidth($row, $column);
            }

            $this->effectiveColumnWidths[$column] = max($lengths) + strlen($this->style->getCellRowContentFormat()) - 2;
        }
    }

    /**
     * Gets column width.
     *
     * @return int
     */
    private function getColumnSeparatorWidth()
    {
        return strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()));
    }

    /**
     * Gets cell width.
     *
     * @param array $row
     * @param int   $column
     *
     * @return int
     */
    private function getCellWidth(array $row, $column)
    {
        $cellWidth = 0;

        if (isset($row[$column])) {
            $cell = $row[$column];
            $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
        }

        $columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0;

        return max($cellWidth, $columnWidth);
    }

    /**
     * Called after rendering to cleanup cache data.
     */
    private function cleanup()
    {
        $this->effectiveColumnWidths = array();
        $this->numberOfColumns = null;
    }

    private static function initStyles()
    {
        $borderless = new TableStyle();
        $borderless
            ->setHorizontalBorderChar('=')
            ->setVerticalBorderChar(' ')
            ->setCrossingChar(' ')
        ;

        $compact = new TableStyle();
        $compact
            ->setHorizontalBorderChar('')
            ->setVerticalBorderChar(' ')
            ->setCrossingChar('')
            ->setCellRowContentFormat('%s')
        ;

        $styleGuide = new TableStyle();
        $styleGuide
            ->setHorizontalBorderChar('-')
            ->setVerticalBorderChar(' ')
            ->setCrossingChar(' ')
            ->setCellHeaderFormat('%s')
        ;

        return array(
            'default' => new TableStyle(),
            'borderless' => $borderless,
            'compact' => $compact,
            'symfony-style-guide' => $styleGuide,
        );
    }

    private function resolveStyle($name)
    {
        if ($name instanceof TableStyle) {
            return $name;
        }

        if (isset(self::$styles[$name])) {
            return self::$styles[$name];
        }

        throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $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\Console\Helper;

use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * @author Kevin Bond <kevinbond@gmail.com>
 */
class ProgressIndicator
{
    private $output;
    private $startTime;
    private $format;
    private $message;
    private $indicatorValues;
    private $indicatorCurrent;
    private $indicatorChangeInterval;
    private $indicatorUpdateTime;
    private $started = false;

    private static $formatters;
    private static $formats;

    /**
     * @param OutputInterface $output
     * @param string|null     $format                  Indicator format
     * @param int             $indicatorChangeInterval Change interval in milliseconds
     * @param array|null      $indicatorValues         Animated indicator characters
     */
    public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null)
    {
        $this->output = $output;

        if (null === $format) {
            $format = $this->determineBestFormat();
        }

        if (null === $indicatorValues) {
            $indicatorValues = array('-', '\\', '|', '/');
        }

        $indicatorValues = array_values($indicatorValues);

        if (2 > count($indicatorValues)) {
            throw new InvalidArgumentException('Must have at least 2 indicator value characters.');
        }

        $this->format = self::getFormatDefinition($format);
        $this->indicatorChangeInterval = $indicatorChangeInterval;
        $this->indicatorValues = $indicatorValues;
        $this->startTime = time();
    }

    /**
     * Sets the current indicator message.
     *
     * @param string|null $message
     */
    public function setMessage($message)
    {
        $this->message = $message;

        $this->display();
    }

    /**
     * Starts the indicator output.
     *
     * @param $message
     */
    public function start($message)
    {
        if ($this->started) {
            throw new LogicException('Progress indicator already started.');
        }

        $this->message = $message;
        $this->started = true;
        $this->startTime = time();
        $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval;
        $this->indicatorCurrent = 0;

        $this->display();
    }

    /**
     * Advances the indicator.
     */
    public function advance()
    {
        if (!$this->started) {
            throw new LogicException('Progress indicator has not yet been started.');
        }

        if (!$this->output->isDecorated()) {
            return;
        }

        $currentTime = $this->getCurrentTimeInMilliseconds();

        if ($currentTime < $this->indicatorUpdateTime) {
            return;
        }

        $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval;
        ++$this->indicatorCurrent;

        $this->display();
    }

    /**
     * Finish the indicator with message.
     *
     * @param $message
     */
    public function finish($message)
    {
        if (!$this->started) {
            throw new LogicException('Progress indicator has not yet been started.');
        }

        $this->message = $message;
        $this->display();
        $this->output->writeln('');
        $this->started = false;
    }

    /**
     * Gets the format for a given name.
     *
     * @param string $name The format name
     *
     * @return string|null A format string
     */
    public static function getFormatDefinition($name)
    {
        if (!self::$formats) {
            self::$formats = self::initFormats();
        }

        return isset(self::$formats[$name]) ? self::$formats[$name] : null;
    }

    /**
     * Sets a placeholder formatter for a given name.
     *
     * This method also allow you to override an existing placeholder.
     *
     * @param string   $name     The placeholder name (including the delimiter char like %)
     * @param callable $callable A PHP callable
     */
    public static function setPlaceholderFormatterDefinition($name, $callable)
    {
        if (!self::$formatters) {
            self::$formatters = self::initPlaceholderFormatters();
        }

        self::$formatters[$name] = $callable;
    }

    /**
     * Gets the placeholder formatter for a given name.
     *
     * @param string $name The placeholder name (including the delimiter char like %)
     *
     * @return callable|null A PHP callable
     */
    public static function getPlaceholderFormatterDefinition($name)
    {
        if (!self::$formatters) {
            self::$formatters = self::initPlaceholderFormatters();
        }

        return isset(self::$formatters[$name]) ? self::$formatters[$name] : null;
    }

    private function display()
    {
        if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
            return;
        }

        $self = $this;

        $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) {
            if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
                return call_user_func($formatter, $self);
            }

            return $matches[0];
        }, $this->format));
    }

    private function determineBestFormat()
    {
        switch ($this->output->getVerbosity()) {
            // OutputInterface::VERBOSITY_QUIET: display is disabled anyway
            case OutputInterface::VERBOSITY_VERBOSE:
                return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi';
            case OutputInterface::VERBOSITY_VERY_VERBOSE:
            case OutputInterface::VERBOSITY_DEBUG:
                return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi';
            default:
                return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi';
        }
    }

    /**
     * Overwrites a previous message to the output.
     *
     * @param string $message The message
     */
    private function overwrite($message)
    {
        if ($this->output->isDecorated()) {
            $this->output->write("\x0D\x1B[2K");
            $this->output->write($message);
        } else {
            $this->output->writeln($message);
        }
    }

    private function getCurrentTimeInMilliseconds()
    {
        return round(microtime(true) * 1000);
    }

    private static function initPlaceholderFormatters()
    {
        return array(
            'indicator' => function (ProgressIndicator $indicator) {
                return $indicator->indicatorValues[$indicator->indicatorCurrent % count($indicator->indicatorValues)];
            },
            'message' => function (ProgressIndicator $indicator) {
                return $indicator->message;
            },
            'elapsed' => function (ProgressIndicator $indicator) {
                return Helper::formatTime(time() - $indicator->startTime);
            },
            'memory' => function () {
                return Helper::formatMemory(memory_get_usage(true));
            },
        );
    }

    private static function initFormats()
    {
        return array(
            'normal' => ' %indicator% %message%',
            'normal_no_ansi' => ' %message%',

            'verbose' => ' %indicator% %message% (%elapsed:6s%)',
            'verbose_no_ansi' => ' %message% (%elapsed:6s%)',

            'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)',
            'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)',
        );
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Descriptor\JsonDescriptor;
use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\InvalidArgumentException;

/**
 * This class adds helper method to describe objects in various formats.
 *
 * @author Jean-FranÃ§ois Simon <contact@jfsimon.fr>
 */
class DescriptorHelper extends Helper
{
    /**
     * @var DescriptorInterface[]
     */
    private $descriptors = array();

    public function __construct()
    {
        $this
            ->register('txt', new TextDescriptor())
            ->register('xml', new XmlDescriptor())
            ->register('json', new JsonDescriptor())
            ->register('md', new MarkdownDescriptor())
        ;
    }

    /**
     * Describes an object if supported.
     *
     * Available options are:
     * * format: string, the output format name
     * * raw_text: boolean, sets output type as raw
     *
     * @param OutputInterface $output
     * @param object          $object
     * @param array           $options
     *
     * @throws InvalidArgumentException when the given format is not supported
     */
    public function describe(OutputInterface $output, $object, array $options = array())
    {
        $options = array_merge(array(
            'raw_text' => false,
            'format' => 'txt',
        ), $options);

        if (!isset($this->descriptors[$options['format']])) {
            throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format']));
        }

        $descriptor = $this->descriptors[$options['format']];
        $descriptor->describe($output, $object, $options);
    }

    /**
     * Registers a descriptor.
     *
     * @param string              $format
     * @param DescriptorInterface $descriptor
     *
     * @return $this
     */
    public function register($format, DescriptorInterface $descriptor)
    {
        $this->descriptors[$format] = $descriptor;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'descriptor';
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\DependencyInjection;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\TypedReference;

/**
 * Registers console commands.
 *
 * @author GrÃ©goire Pineau <lyrixx@lyrixx.info>
 */
class AddConsoleCommandPass implements CompilerPassInterface
{
    private $commandLoaderServiceId;
    private $commandTag;

    public function __construct($commandLoaderServiceId = 'console.command_loader', $commandTag = 'console.command')
    {
        $this->commandLoaderServiceId = $commandLoaderServiceId;
        $this->commandTag = $commandTag;
    }

    public function process(ContainerBuilder $container)
    {
        $commandServices = $container->findTaggedServiceIds($this->commandTag, true);
        $lazyCommandMap = array();
        $lazyCommandRefs = array();
        $serviceIds = array();
        $lazyServiceIds = array();

        foreach ($commandServices as $id => $tags) {
            $definition = $container->getDefinition($id);
            $class = $container->getParameterBag()->resolveValue($definition->getClass());

            $commandId = 'console.command.'.strtolower(str_replace('\\', '_', $class));

            if (isset($tags[0]['command'])) {
                $commandName = $tags[0]['command'];
            } else {
                if (!$r = $container->getReflectionClass($class)) {
                    throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
                }
                if (!$r->isSubclassOf(Command::class)) {
                    throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class));
                }
                $commandName = $class::getDefaultName();
            }

            if (null === $commandName) {
                if (isset($serviceIds[$commandId]) || $container->hasAlias($commandId)) {
                    $commandId = $commandId.'_'.$id;
                }
                if (!$definition->isPublic() || $definition->isPrivate()) {
                    $container->setAlias($commandId, $id)->setPublic(true);
                    $id = $commandId;
                }
                $serviceIds[$commandId] = $id;

                continue;
            }

            $serviceIds[$commandId] = $id;
            $lazyServiceIds[$id] = true;
            unset($tags[0]);
            $lazyCommandMap[$commandName] = $id;
            $lazyCommandRefs[$id] = new TypedReference($id, $class);
            $aliases = array();

            foreach ($tags as $tag) {
                if (isset($tag['command'])) {
                    $aliases[] = $tag['command'];
                    $lazyCommandMap[$tag['command']] = $id;
                }
            }

            $definition->addMethodCall('setName', array($commandName));

            if ($aliases) {
                $definition->addMethodCall('setAliases', array($aliases));
            }
        }

        $container
            ->register($this->commandLoaderServiceId, ContainerCommandLoader::class)
            ->setPublic(true)
            ->setArguments(array(ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap));

        $container->setParameter('console.command.ids', $serviceIds);
        $container->setParameter('console.lazy_command.ids', $lazyServiceIds);
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Exception;

/**
 * @author JÃ©rÃ´me Tamarelle <jerome@tamarelle.net>
 */
class LogicException extends \LogicException 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\Console\Exception;

/**
 * Represents an incorrect option name typed in the console.
 *
 * @author JÃ©rÃ´me Tamarelle <jerome@tamarelle.net>
 */
class InvalidOptionException extends \InvalidArgumentException 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\Console\Exception;

/**
 * ExceptionInterface.
 *
 * @author JÃ©rÃ´me Tamarelle <jerome@tamarelle.net>
 */
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\Console\Exception;

/**
 * @author JÃ©rÃ´me Tamarelle <jerome@tamarelle.net>
 */
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\Console\Exception;

/**
 * @author JÃ©rÃ´me Tamarelle <jerome@tamarelle.net>
 */
class InvalidArgumentException extends \InvalidArgumentException 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\Console\Exception;

/**
 * Represents an incorrect command name typed in the console.
 *
 * @author JÃ©rÃ´me Tamarelle <jerome@tamarelle.net>
 */
class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface
{
    private $alternatives;

    /**
     * @param string     $message      Exception message to throw
     * @param array      $alternatives List of similar defined names
     * @param int        $code         Exception code
     * @param \Exception $previous     Previous exception used for the exception chaining
     */
    public function __construct($message, array $alternatives = array(), $code = 0, \Exception $previous = null)
    {
        parent::__construct($message, $code, $previous);

        $this->alternatives = $alternatives;
    }

    /**
     * @return array A list of similar defined names
     */
    public function getAlternatives()
    {
        return $this->alternatives;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Event;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Allows to manipulate the exit code of a command after its execution.
 *
 * @author Francesco Levorato <git@flevour.net>
 */
class ConsoleTerminateEvent extends ConsoleEvent
{
    /**
     * The exit code of the command.
     *
     * @var int
     */
    private $exitCode;

    public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode)
    {
        parent::__construct($command, $input, $output);

        $this->setExitCode($exitCode);
    }

    /**
     * Sets the exit code.
     *
     * @param int $exitCode The command exit code
     */
    public function setExitCode($exitCode)
    {
        $this->exitCode = (int) $exitCode;
    }

    /**
     * Gets the exit code.
     *
     * @return int The command exit code
     */
    public function getExitCode()
    {
        return $this->exitCode;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Event;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Allows to handle throwables thrown while running a command.
 *
 * @author Wouter de Jong <wouter@wouterj.nl>
 */
final class ConsoleErrorEvent extends ConsoleEvent
{
    private $error;
    private $exitCode;

    public function __construct(InputInterface $input, OutputInterface $output, $error, Command $command = null)
    {
        parent::__construct($command, $input, $output);

        $this->setError($error);
    }

    /**
     * Returns the thrown error/exception.
     *
     * @return \Throwable
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * Replaces the thrown error/exception.
     *
     * @param \Throwable $error
     */
    public function setError($error)
    {
        if (!$error instanceof \Throwable && !$error instanceof \Exception) {
            throw new InvalidArgumentException(sprintf('The error passed to ConsoleErrorEvent must be an instance of \Throwable or \Exception, "%s" was passed instead.', is_object($error) ? get_class($error) : gettype($error)));
        }

        $this->error = $error;
    }

    /**
     * Sets the exit code.
     *
     * @param int $exitCode The command exit code
     */
    public function setExitCode($exitCode)
    {
        $this->exitCode = (int) $exitCode;

        $r = new \ReflectionProperty($this->error, 'code');
        $r->setAccessible(true);
        $r->setValue($this->error, $this->exitCode);
    }

    /**
     * Gets the exit code.
     *
     * @return int The command exit code
     */
    public function getExitCode()
    {
        return null !== $this->exitCode ? $this->exitCode : (is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 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\Console\Event;

/**
 * Allows to do things before the command is executed, like skipping the command or changing the input.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ConsoleCommandEvent extends ConsoleEvent
{
    /**
     * The return code for skipped commands, this will also be passed into the terminate event.
     */
    const RETURN_CODE_DISABLED = 113;

    /**
     * Indicates if the command should be run or skipped.
     */
    private $commandShouldRun = true;

    /**
     * Disables the command, so it won't be run.
     *
     * @return bool
     */
    public function disableCommand()
    {
        return $this->commandShouldRun = false;
    }

    /**
     * Enables the command.
     *
     * @return bool
     */
    public function enableCommand()
    {
        return $this->commandShouldRun = true;
    }

    /**
     * Returns true if the command is runnable, false otherwise.
     *
     * @return bool
     */
    public function commandShouldRun()
    {
        return $this->commandShouldRun;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\Event;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\Event;

/**
 * Allows to inspect input and output of a command.
 *
 * @author Francesco Levorato <git@flevour.net>
 */
class ConsoleEvent extends Event
{
    protected $command;

    private $input;
    private $output;

    public function __construct(Command $command = null, InputInterface $input, OutputInterface $output)
    {
        $this->command = $command;
        $this->input = $input;
        $this->output = $output;
    }

    /**
     * Gets the command that is executed.
     *
     * @return Command|null A Command instance
     */
    public function getCommand()
    {
        return $this->command;
    }

    /**
     * Gets the input instance.
     *
     * @return InputInterface An InputInterface instance
     */
    public function getInput()
    {
        return $this->input;
    }

    /**
     * Gets the output instance.
     *
     * @return OutputInterface An OutputInterface instance
     */
    public function getOutput()
    {
        return $this->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\Console\Event;

@trigger_error(sprintf('The "%s" class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ConsoleErrorEvent instead.', ConsoleExceptionEvent::class), E_USER_DEPRECATED);

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Allows to handle exception thrown in a command.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since version 3.3, to be removed in 4.0. Use ConsoleErrorEvent instead.
 */
class ConsoleExceptionEvent extends ConsoleEvent
{
    private $exception;
    private $exitCode;

    public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode)
    {
        parent::__construct($command, $input, $output);

        $this->setException($exception);
        $this->exitCode = (int) $exitCode;
    }

    /**
     * Returns the thrown exception.
     *
     * @return \Exception The thrown exception
     */
    public function getException()
    {
        return $this->exception;
    }

    /**
     * Replaces the thrown exception.
     *
     * This exception will be thrown if no response is set in the event.
     *
     * @param \Exception $exception The thrown exception
     */
    public function setException(\Exception $exception)
    {
        $this->exception = $exception;
    }

    /**
     * Gets the exit code.
     *
     * @return int The command exit code
     */
    public function getExitCode()
    {
        return $this->exitCode;
    }
}
{
    "name": "symfony/console",
    "type": "library",
    "description": "Symfony Console Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": "^5.5.9|>=7.0.8",
        "symfony/polyfill-mbstring": "~1.0",
        "symfony/debug": "~2.8|~3.0|~4.0"
    },
    "require-dev": {
        "symfony/config": "~3.3|~4.0",
        "symfony/event-dispatcher": "~2.8|~3.0|~4.0",
        "symfony/dependency-injection": "~3.4|~4.0",
        "symfony/lock": "~3.4|~4.0",
        "symfony/process": "~3.3|~4.0",
        "psr/log": "~1.0"
    },
    "suggest": {
        "symfony/event-dispatcher": "",
        "symfony/lock": "",
        "symfony/process": "",
        "psr/log": "For using the console logger"
    },
    "conflict": {
        "symfony/dependency-injection": "<3.4",
        "symfony/process": "<3.3"
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\Console\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "3.4-dev"
        }
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Console\EventListener;

use Psr\Log\LoggerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * @author James Halsall <james.t.halsall@googlemail.com>
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
class ErrorListener implements EventSubscriberInterface
{
    private $logger;

    public function __construct(LoggerInterface $logger = null)
    {
        $this->logger = $logger;
    }

    public function onConsoleError(ConsoleErrorEvent $event)
    {
        if (null === $this->logger) {
            return;
        }

        $error = $event->getError();

        if (!$inputString = $this->getInputString($event)) {
            return $this->logger->error('An error occurred while using the console. Message: "{message}"', array('error' => $error, 'message' => $error->getMessage()));
        }

        $this->logger->error('Error thrown while running command "{command}". Message: "{message}"', array('error' => $error, 'command' => $inputString, 'message' => $error->getMessage()));
    }

    public function onConsoleTerminate(ConsoleTerminateEvent $event)
    {
        if (null === $this->logger) {
            return;
        }

        $exitCode = $event->getExitCode();

        if (0 === $exitCode) {
            return;
        }

        if (!$inputString = $this->getInputString($event)) {
            return $this->logger->debug('The console exited with code "{code}"', array('code' => $exitCode));
        }

        $this->logger->debug('Command "{command}" exited with code "{code}"', array('command' => $inputString, 'code' => $exitCode));
    }

    public static function getSubscribedEvents()
    {
        return array(
            ConsoleEvents::ERROR => array('onConsoleError', -128),
            ConsoleEvents::TERMINATE => array('onConsoleTerminate', -128),
        );
    }

    private static function getInputString(ConsoleEvent $event)
    {
        $commandName = $event->getCommand() ? $event->getCommand()->getName() : null;
        $input = $event->getInput();

        if (method_exists($input, '__toString')) {
            if ($commandName) {
                return str_replace(array("'$commandName'", "\"$commandName\""), $commandName, (string) $input);
            }

            return (string) $input;
        }

        return $commandName;
    }
}
<?php

static $data = array (
  'A' => 'a',
  'B' => 'b',
  'C' => 'c',
  'D' => 'd',
  'E' => 'e',
  'F' => 'f',
  'G' => 'g',
  'H' => 'h',
  'I' => 'i',
  'J' => 'j',
  'K' => 'k',
  'L' => 'l',
  'M' => 'm',
  'N' => 'n',
  'O' => 'o',
  'P' => 'p',
  'Q' => 'q',
  'R' => 'r',
  'S' => 's',
  'T' => 't',
  'U' => 'u',
  'V' => 'v',
  'W' => 'w',
  'X' => 'x',
  'Y' => 'y',
  'Z' => 'z',
  'Ã€' => 'Ã ',
  'Ã�' => 'Ã¡',
  'Ã‚' => 'Ã¢',
  'Ãƒ' => 'Ã£',
  'Ã„' => 'Ã¤',
  'Ã…' => 'Ã¥',
  'Ã†' => 'Ã¦',
  'Ã‡' => 'Ã§',
  'Ãˆ' => 'Ã¨',
  'Ã‰' => 'Ã©',
  'ÃŠ' => 'Ãª',
  'Ã‹' => 'Ã«',
  'ÃŒ' => 'Ã¬',
  'Ã�' => 'Ã­',
  'ÃŽ' => 'Ã®',
  'Ã�' => 'Ã¯',
  'Ã�' => 'Ã°',
  'Ã‘' => 'Ã±',
  'Ã’' => 'Ã²',
  'Ã“' => 'Ã³',
  'Ã”' => 'Ã´',
  'Ã•' => 'Ãµ',
  'Ã–' => 'Ã¶',
  'Ã˜' => 'Ã¸',
  'Ã™' => 'Ã¹',
  'Ãš' => 'Ãº',
  'Ã›' => 'Ã»',
  'Ãœ' => 'Ã¼',
  'Ã�' => 'Ã½',
  'Ãž' => 'Ã¾',
  'Ä€' => 'Ä�',
  'Ä‚' => 'Äƒ',
  'Ä„' => 'Ä…',
  'Ä†' => 'Ä‡',
  'Äˆ' => 'Ä‰',
  'ÄŠ' => 'Ä‹',
  'ÄŒ' => 'Ä�',
  'ÄŽ' => 'Ä�',
  'Ä�' => 'Ä‘',
  'Ä’' => 'Ä“',
  'Ä”' => 'Ä•',
  'Ä–' => 'Ä—',
  'Ä˜' => 'Ä™',
  'Äš' => 'Ä›',
  'Äœ' => 'Ä�',
  'Äž' => 'ÄŸ',
  'Ä ' => 'Ä¡',
  'Ä¢' => 'Ä£',
  'Ä¤' => 'Ä¥',
  'Ä¦' => 'Ä§',
  'Ä¨' => 'Ä©',
  'Äª' => 'Ä«',
  'Ä¬' => 'Ä­',
  'Ä®' => 'Ä¯',
  'Ä°' => 'i',
  'Ä²' => 'Ä³',
  'Ä´' => 'Äµ',
  'Ä¶' => 'Ä·',
  'Ä¹' => 'Äº',
  'Ä»' => 'Ä¼',
  'Ä½' => 'Ä¾',
  'Ä¿' => 'Å€',
  'Å�' => 'Å‚',
  'Åƒ' => 'Å„',
  'Å…' => 'Å†',
  'Å‡' => 'Åˆ',
  'ÅŠ' => 'Å‹',
  'ÅŒ' => 'Å�',
  'ÅŽ' => 'Å�',
  'Å�' => 'Å‘',
  'Å’' => 'Å“',
  'Å”' => 'Å•',
  'Å–' => 'Å—',
  'Å˜' => 'Å™',
  'Åš' => 'Å›',
  'Åœ' => 'Å�',
  'Åž' => 'ÅŸ',
  'Å ' => 'Å¡',
  'Å¢' => 'Å£',
  'Å¤' => 'Å¥',
  'Å¦' => 'Å§',
  'Å¨' => 'Å©',
  'Åª' => 'Å«',
  'Å¬' => 'Å­',
  'Å®' => 'Å¯',
  'Å°' => 'Å±',
  'Å²' => 'Å³',
  'Å´' => 'Åµ',
  'Å¶' => 'Å·',
  'Å¸' => 'Ã¿',
  'Å¹' => 'Åº',
  'Å»' => 'Å¼',
  'Å½' => 'Å¾',
  'Æ�' => 'É“',
  'Æ‚' => 'Æƒ',
  'Æ„' => 'Æ…',
  'Æ†' => 'É”',
  'Æ‡' => 'Æˆ',
  'Æ‰' => 'É–',
  'ÆŠ' => 'É—',
  'Æ‹' => 'ÆŒ',
  'ÆŽ' => 'Ç�',
  'Æ�' => 'É™',
  'Æ�' => 'É›',
  'Æ‘' => 'Æ’',
  'Æ“' => 'É ',
  'Æ”' => 'É£',
  'Æ–' => 'É©',
  'Æ—' => 'É¨',
  'Æ˜' => 'Æ™',
  'Æœ' => 'É¯',
  'Æ�' => 'É²',
  'ÆŸ' => 'Éµ',
  'Æ ' => 'Æ¡',
  'Æ¢' => 'Æ£',
  'Æ¤' => 'Æ¥',
  'Æ¦' => 'Ê€',
  'Æ§' => 'Æ¨',
  'Æ©' => 'Êƒ',
  'Æ¬' => 'Æ­',
  'Æ®' => 'Êˆ',
  'Æ¯' => 'Æ°',
  'Æ±' => 'ÊŠ',
  'Æ²' => 'Ê‹',
  'Æ³' => 'Æ´',
  'Æµ' => 'Æ¶',
  'Æ·' => 'Ê’',
  'Æ¸' => 'Æ¹',
  'Æ¼' => 'Æ½',
  'Ç„' => 'Ç†',
  'Ç…' => 'Ç†',
  'Ç‡' => 'Ç‰',
  'Çˆ' => 'Ç‰',
  'ÇŠ' => 'ÇŒ',
  'Ç‹' => 'ÇŒ',
  'Ç�' => 'ÇŽ',
  'Ç�' => 'Ç�',
  'Ç‘' => 'Ç’',
  'Ç“' => 'Ç”',
  'Ç•' => 'Ç–',
  'Ç—' => 'Ç˜',
  'Ç™' => 'Çš',
  'Ç›' => 'Çœ',
  'Çž' => 'ÇŸ',
  'Ç ' => 'Ç¡',
  'Ç¢' => 'Ç£',
  'Ç¤' => 'Ç¥',
  'Ç¦' => 'Ç§',
  'Ç¨' => 'Ç©',
  'Çª' => 'Ç«',
  'Ç¬' => 'Ç­',
  'Ç®' => 'Ç¯',
  'Ç±' => 'Ç³',
  'Ç²' => 'Ç³',
  'Ç´' => 'Çµ',
  'Ç¶' => 'Æ•',
  'Ç·' => 'Æ¿',
  'Ç¸' => 'Ç¹',
  'Çº' => 'Ç»',
  'Ç¼' => 'Ç½',
  'Ç¾' => 'Ç¿',
  'È€' => 'È�',
  'È‚' => 'Èƒ',
  'È„' => 'È…',
  'È†' => 'È‡',
  'Èˆ' => 'È‰',
  'ÈŠ' => 'È‹',
  'ÈŒ' => 'È�',
  'ÈŽ' => 'È�',
  'È�' => 'È‘',
  'È’' => 'È“',
  'È”' => 'È•',
  'È–' => 'È—',
  'È˜' => 'È™',
  'Èš' => 'È›',
  'Èœ' => 'È�',
  'Èž' => 'ÈŸ',
  'È ' => 'Æž',
  'È¢' => 'È£',
  'È¤' => 'È¥',
  'È¦' => 'È§',
  'È¨' => 'È©',
  'Èª' => 'È«',
  'È¬' => 'È­',
  'È®' => 'È¯',
  'È°' => 'È±',
  'È²' => 'È³',
  'Èº' => 'â±¥',
  'È»' => 'È¼',
  'È½' => 'Æš',
  'È¾' => 'â±¦',
  'É�' => 'É‚',
  'Éƒ' => 'Æ€',
  'É„' => 'Ê‰',
  'É…' => 'ÊŒ',
  'É†' => 'É‡',
  'Éˆ' => 'É‰',
  'ÉŠ' => 'É‹',
  'ÉŒ' => 'É�',
  'ÉŽ' => 'É�',
  'Í°' => 'Í±',
  'Í²' => 'Í³',
  'Í¶' => 'Í·',
  'Í¿' => 'Ï³',
  'Î†' => 'Î¬',
  'Îˆ' => 'Î­',
  'Î‰' => 'Î®',
  'ÎŠ' => 'Î¯',
  'ÎŒ' => 'ÏŒ',
  'ÎŽ' => 'Ï�',
  'Î�' => 'ÏŽ',
  'Î‘' => 'Î±',
  'Î’' => 'Î²',
  'Î“' => 'Î³',
  'Î”' => 'Î´',
  'Î•' => 'Îµ',
  'Î–' => 'Î¶',
  'Î—' => 'Î·',
  'Î˜' => 'Î¸',
  'Î™' => 'Î¹',
  'Îš' => 'Îº',
  'Î›' => 'Î»',
  'Îœ' => 'Î¼',
  'Î�' => 'Î½',
  'Îž' => 'Î¾',
  'ÎŸ' => 'Î¿',
  'Î ' => 'Ï€',
  'Î¡' => 'Ï�',
  'Î£' => 'Ïƒ',
  'Î¤' => 'Ï„',
  'Î¥' => 'Ï…',
  'Î¦' => 'Ï†',
  'Î§' => 'Ï‡',
  'Î¨' => 'Ïˆ',
  'Î©' => 'Ï‰',
  'Îª' => 'ÏŠ',
  'Î«' => 'Ï‹',
  'Ï�' => 'Ï—',
  'Ï˜' => 'Ï™',
  'Ïš' => 'Ï›',
  'Ïœ' => 'Ï�',
  'Ïž' => 'ÏŸ',
  'Ï ' => 'Ï¡',
  'Ï¢' => 'Ï£',
  'Ï¤' => 'Ï¥',
  'Ï¦' => 'Ï§',
  'Ï¨' => 'Ï©',
  'Ïª' => 'Ï«',
  'Ï¬' => 'Ï­',
  'Ï®' => 'Ï¯',
  'Ï´' => 'Î¸',
  'Ï·' => 'Ï¸',
  'Ï¹' => 'Ï²',
  'Ïº' => 'Ï»',
  'Ï½' => 'Í»',
  'Ï¾' => 'Í¼',
  'Ï¿' => 'Í½',
  'Ð€' => 'Ñ�',
  'Ð�' => 'Ñ‘',
  'Ð‚' => 'Ñ’',
  'Ðƒ' => 'Ñ“',
  'Ð„' => 'Ñ”',
  'Ð…' => 'Ñ•',
  'Ð†' => 'Ñ–',
  'Ð‡' => 'Ñ—',
  'Ðˆ' => 'Ñ˜',
  'Ð‰' => 'Ñ™',
  'ÐŠ' => 'Ñš',
  'Ð‹' => 'Ñ›',
  'ÐŒ' => 'Ñœ',
  'Ð�' => 'Ñ�',
  'ÐŽ' => 'Ñž',
  'Ð�' => 'ÑŸ',
  'Ð�' => 'Ð°',
  'Ð‘' => 'Ð±',
  'Ð’' => 'Ð²',
  'Ð“' => 'Ð³',
  'Ð”' => 'Ð´',
  'Ð•' => 'Ðµ',
  'Ð–' => 'Ð¶',
  'Ð—' => 'Ð·',
  'Ð˜' => 'Ð¸',
  'Ð™' => 'Ð¹',
  'Ðš' => 'Ðº',
  'Ð›' => 'Ð»',
  'Ðœ' => 'Ð¼',
  'Ð�' => 'Ð½',
  'Ðž' => 'Ð¾',
  'ÐŸ' => 'Ð¿',
  'Ð ' => 'Ñ€',
  'Ð¡' => 'Ñ�',
  'Ð¢' => 'Ñ‚',
  'Ð£' => 'Ñƒ',
  'Ð¤' => 'Ñ„',
  'Ð¥' => 'Ñ…',
  'Ð¦' => 'Ñ†',
  'Ð§' => 'Ñ‡',
  'Ð¨' => 'Ñˆ',
  'Ð©' => 'Ñ‰',
  'Ðª' => 'ÑŠ',
  'Ð«' => 'Ñ‹',
  'Ð¬' => 'ÑŒ',
  'Ð­' => 'Ñ�',
  'Ð®' => 'ÑŽ',
  'Ð¯' => 'Ñ�',
  'Ñ ' => 'Ñ¡',
  'Ñ¢' => 'Ñ£',
  'Ñ¤' => 'Ñ¥',
  'Ñ¦' => 'Ñ§',
  'Ñ¨' => 'Ñ©',
  'Ñª' => 'Ñ«',
  'Ñ¬' => 'Ñ­',
  'Ñ®' => 'Ñ¯',
  'Ñ°' => 'Ñ±',
  'Ñ²' => 'Ñ³',
  'Ñ´' => 'Ñµ',
  'Ñ¶' => 'Ñ·',
  'Ñ¸' => 'Ñ¹',
  'Ñº' => 'Ñ»',
  'Ñ¼' => 'Ñ½',
  'Ñ¾' => 'Ñ¿',
  'Ò€' => 'Ò�',
  'ÒŠ' => 'Ò‹',
  'ÒŒ' => 'Ò�',
  'ÒŽ' => 'Ò�',
  'Ò�' => 'Ò‘',
  'Ò’' => 'Ò“',
  'Ò”' => 'Ò•',
  'Ò–' => 'Ò—',
  'Ò˜' => 'Ò™',
  'Òš' => 'Ò›',
  'Òœ' => 'Ò�',
  'Òž' => 'ÒŸ',
  'Ò ' => 'Ò¡',
  'Ò¢' => 'Ò£',
  'Ò¤' => 'Ò¥',
  'Ò¦' => 'Ò§',
  'Ò¨' => 'Ò©',
  'Òª' => 'Ò«',
  'Ò¬' => 'Ò­',
  'Ò®' => 'Ò¯',
  'Ò°' => 'Ò±',
  'Ò²' => 'Ò³',
  'Ò´' => 'Òµ',
  'Ò¶' => 'Ò·',
  'Ò¸' => 'Ò¹',
  'Òº' => 'Ò»',
  'Ò¼' => 'Ò½',
  'Ò¾' => 'Ò¿',
  'Ó€' => 'Ó�',
  'Ó�' => 'Ó‚',
  'Óƒ' => 'Ó„',
  'Ó…' => 'Ó†',
  'Ó‡' => 'Óˆ',
  'Ó‰' => 'ÓŠ',
  'Ó‹' => 'ÓŒ',
  'Ó�' => 'ÓŽ',
  'Ó�' => 'Ó‘',
  'Ó’' => 'Ó“',
  'Ó”' => 'Ó•',
  'Ó–' => 'Ó—',
  'Ó˜' => 'Ó™',
  'Óš' => 'Ó›',
  'Óœ' => 'Ó�',
  'Óž' => 'ÓŸ',
  'Ó ' => 'Ó¡',
  'Ó¢' => 'Ó£',
  'Ó¤' => 'Ó¥',
  'Ó¦' => 'Ó§',
  'Ó¨' => 'Ó©',
  'Óª' => 'Ó«',
  'Ó¬' => 'Ó­',
  'Ó®' => 'Ó¯',
  'Ó°' => 'Ó±',
  'Ó²' => 'Ó³',
  'Ó´' => 'Óµ',
  'Ó¶' => 'Ó·',
  'Ó¸' => 'Ó¹',
  'Óº' => 'Ó»',
  'Ó¼' => 'Ó½',
  'Ó¾' => 'Ó¿',
  'Ô€' => 'Ô�',
  'Ô‚' => 'Ôƒ',
  'Ô„' => 'Ô…',
  'Ô†' => 'Ô‡',
  'Ôˆ' => 'Ô‰',
  'ÔŠ' => 'Ô‹',
  'ÔŒ' => 'Ô�',
  'ÔŽ' => 'Ô�',
  'Ô�' => 'Ô‘',
  'Ô’' => 'Ô“',
  'Ô”' => 'Ô•',
  'Ô–' => 'Ô—',
  'Ô˜' => 'Ô™',
  'Ôš' => 'Ô›',
  'Ôœ' => 'Ô�',
  'Ôž' => 'ÔŸ',
  'Ô ' => 'Ô¡',
  'Ô¢' => 'Ô£',
  'Ô¤' => 'Ô¥',
  'Ô¦' => 'Ô§',
  'Ô¨' => 'Ô©',
  'Ôª' => 'Ô«',
  'Ô¬' => 'Ô­',
  'Ô®' => 'Ô¯',
  'Ô±' => 'Õ¡',
  'Ô²' => 'Õ¢',
  'Ô³' => 'Õ£',
  'Ô´' => 'Õ¤',
  'Ôµ' => 'Õ¥',
  'Ô¶' => 'Õ¦',
  'Ô·' => 'Õ§',
  'Ô¸' => 'Õ¨',
  'Ô¹' => 'Õ©',
  'Ôº' => 'Õª',
  'Ô»' => 'Õ«',
  'Ô¼' => 'Õ¬',
  'Ô½' => 'Õ­',
  'Ô¾' => 'Õ®',
  'Ô¿' => 'Õ¯',
  'Õ€' => 'Õ°',
  'Õ�' => 'Õ±',
  'Õ‚' => 'Õ²',
  'Õƒ' => 'Õ³',
  'Õ„' => 'Õ´',
  'Õ…' => 'Õµ',
  'Õ†' => 'Õ¶',
  'Õ‡' => 'Õ·',
  'Õˆ' => 'Õ¸',
  'Õ‰' => 'Õ¹',
  'ÕŠ' => 'Õº',
  'Õ‹' => 'Õ»',
  'ÕŒ' => 'Õ¼',
  'Õ�' => 'Õ½',
  'ÕŽ' => 'Õ¾',
  'Õ�' => 'Õ¿',
  'Õ�' => 'Ö€',
  'Õ‘' => 'Ö�',
  'Õ’' => 'Ö‚',
  'Õ“' => 'Öƒ',
  'Õ”' => 'Ö„',
  'Õ•' => 'Ö…',
  'Õ–' => 'Ö†',
  'á‚ ' => 'â´€',
  'á‚¡' => 'â´�',
  'á‚¢' => 'â´‚',
  'á‚£' => 'â´ƒ',
  'á‚¤' => 'â´„',
  'á‚¥' => 'â´…',
  'á‚¦' => 'â´†',
  'á‚§' => 'â´‡',
  'á‚¨' => 'â´ˆ',
  'á‚©' => 'â´‰',
  'á‚ª' => 'â´Š',
  'á‚«' => 'â´‹',
  'á‚¬' => 'â´Œ',
  'á‚­' => 'â´�',
  'á‚®' => 'â´Ž',
  'á‚¯' => 'â´�',
  'á‚°' => 'â´�',
  'á‚±' => 'â´‘',
  'á‚²' => 'â´’',
  'á‚³' => 'â´“',
  'á‚´' => 'â´”',
  'á‚µ' => 'â´•',
  'á‚¶' => 'â´–',
  'á‚·' => 'â´—',
  'á‚¸' => 'â´˜',
  'á‚¹' => 'â´™',
  'á‚º' => 'â´š',
  'á‚»' => 'â´›',
  'á‚¼' => 'â´œ',
  'á‚½' => 'â´�',
  'á‚¾' => 'â´ž',
  'á‚¿' => 'â´Ÿ',
  'áƒ€' => 'â´ ',
  'áƒ�' => 'â´¡',
  'áƒ‚' => 'â´¢',
  'áƒƒ' => 'â´£',
  'áƒ„' => 'â´¤',
  'áƒ…' => 'â´¥',
  'áƒ‡' => 'â´§',
  'áƒ�' => 'â´­',
  'á¸€' => 'á¸�',
  'á¸‚' => 'á¸ƒ',
  'á¸„' => 'á¸…',
  'á¸†' => 'á¸‡',
  'á¸ˆ' => 'á¸‰',
  'á¸Š' => 'á¸‹',
  'á¸Œ' => 'á¸�',
  'á¸Ž' => 'á¸�',
  'á¸�' => 'á¸‘',
  'á¸’' => 'á¸“',
  'á¸”' => 'á¸•',
  'á¸–' => 'á¸—',
  'á¸˜' => 'á¸™',
  'á¸š' => 'á¸›',
  'á¸œ' => 'á¸�',
  'á¸ž' => 'á¸Ÿ',
  'á¸ ' => 'á¸¡',
  'á¸¢' => 'á¸£',
  'á¸¤' => 'á¸¥',
  'á¸¦' => 'á¸§',
  'á¸¨' => 'á¸©',
  'á¸ª' => 'á¸«',
  'á¸¬' => 'á¸­',
  'á¸®' => 'á¸¯',
  'á¸°' => 'á¸±',
  'á¸²' => 'á¸³',
  'á¸´' => 'á¸µ',
  'á¸¶' => 'á¸·',
  'á¸¸' => 'á¸¹',
  'á¸º' => 'á¸»',
  'á¸¼' => 'á¸½',
  'á¸¾' => 'á¸¿',
  'á¹€' => 'á¹�',
  'á¹‚' => 'á¹ƒ',
  'á¹„' => 'á¹…',
  'á¹†' => 'á¹‡',
  'á¹ˆ' => 'á¹‰',
  'á¹Š' => 'á¹‹',
  'á¹Œ' => 'á¹�',
  'á¹Ž' => 'á¹�',
  'á¹�' => 'á¹‘',
  'á¹’' => 'á¹“',
  'á¹”' => 'á¹•',
  'á¹–' => 'á¹—',
  'á¹˜' => 'á¹™',
  'á¹š' => 'á¹›',
  'á¹œ' => 'á¹�',
  'á¹ž' => 'á¹Ÿ',
  'á¹ ' => 'á¹¡',
  'á¹¢' => 'á¹£',
  'á¹¤' => 'á¹¥',
  'á¹¦' => 'á¹§',
  'á¹¨' => 'á¹©',
  'á¹ª' => 'á¹«',
  'á¹¬' => 'á¹­',
  'á¹®' => 'á¹¯',
  'á¹°' => 'á¹±',
  'á¹²' => 'á¹³',
  'á¹´' => 'á¹µ',
  'á¹¶' => 'á¹·',
  'á¹¸' => 'á¹¹',
  'á¹º' => 'á¹»',
  'á¹¼' => 'á¹½',
  'á¹¾' => 'á¹¿',
  'áº€' => 'áº�',
  'áº‚' => 'áºƒ',
  'áº„' => 'áº…',
  'áº†' => 'áº‡',
  'áºˆ' => 'áº‰',
  'áºŠ' => 'áº‹',
  'áºŒ' => 'áº�',
  'áºŽ' => 'áº�',
  'áº�' => 'áº‘',
  'áº’' => 'áº“',
  'áº”' => 'áº•',
  'áºž' => 'ÃŸ',
  'áº ' => 'áº¡',
  'áº¢' => 'áº£',
  'áº¤' => 'áº¥',
  'áº¦' => 'áº§',
  'áº¨' => 'áº©',
  'áºª' => 'áº«',
  'áº¬' => 'áº­',
  'áº®' => 'áº¯',
  'áº°' => 'áº±',
  'áº²' => 'áº³',
  'áº´' => 'áºµ',
  'áº¶' => 'áº·',
  'áº¸' => 'áº¹',
  'áºº' => 'áº»',
  'áº¼' => 'áº½',
  'áº¾' => 'áº¿',
  'á»€' => 'á»�',
  'á»‚' => 'á»ƒ',
  'á»„' => 'á»…',
  'á»†' => 'á»‡',
  'á»ˆ' => 'á»‰',
  'á»Š' => 'á»‹',
  'á»Œ' => 'á»�',
  'á»Ž' => 'á»�',
  'á»�' => 'á»‘',
  'á»’' => 'á»“',
  'á»”' => 'á»•',
  'á»–' => 'á»—',
  'á»˜' => 'á»™',
  'á»š' => 'á»›',
  'á»œ' => 'á»�',
  'á»ž' => 'á»Ÿ',
  'á» ' => 'á»¡',
  'á»¢' => 'á»£',
  'á»¤' => 'á»¥',
  'á»¦' => 'á»§',
  'á»¨' => 'á»©',
  'á»ª' => 'á»«',
  'á»¬' => 'á»­',
  'á»®' => 'á»¯',
  'á»°' => 'á»±',
  'á»²' => 'á»³',
  'á»´' => 'á»µ',
  'á»¶' => 'á»·',
  'á»¸' => 'á»¹',
  'á»º' => 'á»»',
  'á»¼' => 'á»½',
  'á»¾' => 'á»¿',
  'á¼ˆ' => 'á¼€',
  'á¼‰' => 'á¼�',
  'á¼Š' => 'á¼‚',
  'á¼‹' => 'á¼ƒ',
  'á¼Œ' => 'á¼„',
  'á¼�' => 'á¼…',
  'á¼Ž' => 'á¼†',
  'á¼�' => 'á¼‡',
  'á¼˜' => 'á¼�',
  'á¼™' => 'á¼‘',
  'á¼š' => 'á¼’',
  'á¼›' => 'á¼“',
  'á¼œ' => 'á¼”',
  'á¼�' => 'á¼•',
  'á¼¨' => 'á¼ ',
  'á¼©' => 'á¼¡',
  'á¼ª' => 'á¼¢',
  'á¼«' => 'á¼£',
  'á¼¬' => 'á¼¤',
  'á¼­' => 'á¼¥',
  'á¼®' => 'á¼¦',
  'á¼¯' => 'á¼§',
  'á¼¸' => 'á¼°',
  'á¼¹' => 'á¼±',
  'á¼º' => 'á¼²',
  'á¼»' => 'á¼³',
  'á¼¼' => 'á¼´',
  'á¼½' => 'á¼µ',
  'á¼¾' => 'á¼¶',
  'á¼¿' => 'á¼·',
  'á½ˆ' => 'á½€',
  'á½‰' => 'á½�',
  'á½Š' => 'á½‚',
  'á½‹' => 'á½ƒ',
  'á½Œ' => 'á½„',
  'á½�' => 'á½…',
  'á½™' => 'á½‘',
  'á½›' => 'á½“',
  'á½�' => 'á½•',
  'á½Ÿ' => 'á½—',
  'á½¨' => 'á½ ',
  'á½©' => 'á½¡',
  'á½ª' => 'á½¢',
  'á½«' => 'á½£',
  'á½¬' => 'á½¤',
  'á½­' => 'á½¥',
  'á½®' => 'á½¦',
  'á½¯' => 'á½§',
  'á¾ˆ' => 'á¾€',
  'á¾‰' => 'á¾�',
  'á¾Š' => 'á¾‚',
  'á¾‹' => 'á¾ƒ',
  'á¾Œ' => 'á¾„',
  'á¾�' => 'á¾…',
  'á¾Ž' => 'á¾†',
  'á¾�' => 'á¾‡',
  'á¾˜' => 'á¾�',
  'á¾™' => 'á¾‘',
  'á¾š' => 'á¾’',
  'á¾›' => 'á¾“',
  'á¾œ' => 'á¾”',
  'á¾�' => 'á¾•',
  'á¾ž' => 'á¾–',
  'á¾Ÿ' => 'á¾—',
  'á¾¨' => 'á¾ ',
  'á¾©' => 'á¾¡',
  'á¾ª' => 'á¾¢',
  'á¾«' => 'á¾£',
  'á¾¬' => 'á¾¤',
  'á¾­' => 'á¾¥',
  'á¾®' => 'á¾¦',
  'á¾¯' => 'á¾§',
  'á¾¸' => 'á¾°',
  'á¾¹' => 'á¾±',
  'á¾º' => 'á½°',
  'á¾»' => 'á½±',
  'á¾¼' => 'á¾³',
  'á¿ˆ' => 'á½²',
  'á¿‰' => 'á½³',
  'á¿Š' => 'á½´',
  'á¿‹' => 'á½µ',
  'á¿Œ' => 'á¿ƒ',
  'á¿˜' => 'á¿�',
  'á¿™' => 'á¿‘',
  'á¿š' => 'á½¶',
  'á¿›' => 'á½·',
  'á¿¨' => 'á¿ ',
  'á¿©' => 'á¿¡',
  'á¿ª' => 'á½º',
  'á¿«' => 'á½»',
  'á¿¬' => 'á¿¥',
  'á¿¸' => 'á½¸',
  'á¿¹' => 'á½¹',
  'á¿º' => 'á½¼',
  'á¿»' => 'á½½',
  'á¿¼' => 'á¿³',
  'â„¦' => 'Ï‰',
  'â„ª' => 'k',
  'â„«' => 'Ã¥',
  'â„²' => 'â…Ž',
  'â… ' => 'â…°',
  'â…¡' => 'â…±',
  'â…¢' => 'â…²',
  'â…£' => 'â…³',
  'â…¤' => 'â…´',
  'â…¥' => 'â…µ',
  'â…¦' => 'â…¶',
  'â…§' => 'â…·',
  'â…¨' => 'â…¸',
  'â…©' => 'â…¹',
  'â…ª' => 'â…º',
  'â…«' => 'â…»',
  'â…¬' => 'â…¼',
  'â…­' => 'â…½',
  'â…®' => 'â…¾',
  'â…¯' => 'â…¿',
  'â†ƒ' => 'â†„',
  'â’¶' => 'â“�',
  'â’·' => 'â“‘',
  'â’¸' => 'â“’',
  'â’¹' => 'â““',
  'â’º' => 'â“”',
  'â’»' => 'â“•',
  'â’¼' => 'â“–',
  'â’½' => 'â“—',
  'â’¾' => 'â“˜',
  'â’¿' => 'â“™',
  'â“€' => 'â“š',
  'â“�' => 'â“›',
  'â“‚' => 'â“œ',
  'â“ƒ' => 'â“�',
  'â“„' => 'â“ž',
  'â“…' => 'â“Ÿ',
  'â“†' => 'â“ ',
  'â“‡' => 'â“¡',
  'â“ˆ' => 'â“¢',
  'â“‰' => 'â“£',
  'â“Š' => 'â“¤',
  'â“‹' => 'â“¥',
  'â“Œ' => 'â“¦',
  'â“�' => 'â“§',
  'â“Ž' => 'â“¨',
  'â“�' => 'â“©',
  'â°€' => 'â°°',
  'â°�' => 'â°±',
  'â°‚' => 'â°²',
  'â°ƒ' => 'â°³',
  'â°„' => 'â°´',
  'â°…' => 'â°µ',
  'â°†' => 'â°¶',
  'â°‡' => 'â°·',
  'â°ˆ' => 'â°¸',
  'â°‰' => 'â°¹',
  'â°Š' => 'â°º',
  'â°‹' => 'â°»',
  'â°Œ' => 'â°¼',
  'â°�' => 'â°½',
  'â°Ž' => 'â°¾',
  'â°�' => 'â°¿',
  'â°�' => 'â±€',
  'â°‘' => 'â±�',
  'â°’' => 'â±‚',
  'â°“' => 'â±ƒ',
  'â°”' => 'â±„',
  'â°•' => 'â±…',
  'â°–' => 'â±†',
  'â°—' => 'â±‡',
  'â°˜' => 'â±ˆ',
  'â°™' => 'â±‰',
  'â°š' => 'â±Š',
  'â°›' => 'â±‹',
  'â°œ' => 'â±Œ',
  'â°�' => 'â±�',
  'â°ž' => 'â±Ž',
  'â°Ÿ' => 'â±�',
  'â° ' => 'â±�',
  'â°¡' => 'â±‘',
  'â°¢' => 'â±’',
  'â°£' => 'â±“',
  'â°¤' => 'â±”',
  'â°¥' => 'â±•',
  'â°¦' => 'â±–',
  'â°§' => 'â±—',
  'â°¨' => 'â±˜',
  'â°©' => 'â±™',
  'â°ª' => 'â±š',
  'â°«' => 'â±›',
  'â°¬' => 'â±œ',
  'â°­' => 'â±�',
  'â°®' => 'â±ž',
  'â± ' => 'â±¡',
  'â±¢' => 'É«',
  'â±£' => 'áµ½',
  'â±¤' => 'É½',
  'â±§' => 'â±¨',
  'â±©' => 'â±ª',
  'â±«' => 'â±¬',
  'â±­' => 'É‘',
  'â±®' => 'É±',
  'â±¯' => 'É�',
  'â±°' => 'É’',
  'â±²' => 'â±³',
  'â±µ' => 'â±¶',
  'â±¾' => 'È¿',
  'â±¿' => 'É€',
  'â²€' => 'â²�',
  'â²‚' => 'â²ƒ',
  'â²„' => 'â²…',
  'â²†' => 'â²‡',
  'â²ˆ' => 'â²‰',
  'â²Š' => 'â²‹',
  'â²Œ' => 'â²�',
  'â²Ž' => 'â²�',
  'â²�' => 'â²‘',
  'â²’' => 'â²“',
  'â²”' => 'â²•',
  'â²–' => 'â²—',
  'â²˜' => 'â²™',
  'â²š' => 'â²›',
  'â²œ' => 'â²�',
  'â²ž' => 'â²Ÿ',
  'â² ' => 'â²¡',
  'â²¢' => 'â²£',
  'â²¤' => 'â²¥',
  'â²¦' => 'â²§',
  'â²¨' => 'â²©',
  'â²ª' => 'â²«',
  'â²¬' => 'â²­',
  'â²®' => 'â²¯',
  'â²°' => 'â²±',
  'â²²' => 'â²³',
  'â²´' => 'â²µ',
  'â²¶' => 'â²·',
  'â²¸' => 'â²¹',
  'â²º' => 'â²»',
  'â²¼' => 'â²½',
  'â²¾' => 'â²¿',
  'â³€' => 'â³�',
  'â³‚' => 'â³ƒ',
  'â³„' => 'â³…',
  'â³†' => 'â³‡',
  'â³ˆ' => 'â³‰',
  'â³Š' => 'â³‹',
  'â³Œ' => 'â³�',
  'â³Ž' => 'â³�',
  'â³�' => 'â³‘',
  'â³’' => 'â³“',
  'â³”' => 'â³•',
  'â³–' => 'â³—',
  'â³˜' => 'â³™',
  'â³š' => 'â³›',
  'â³œ' => 'â³�',
  'â³ž' => 'â³Ÿ',
  'â³ ' => 'â³¡',
  'â³¢' => 'â³£',
  'â³«' => 'â³¬',
  'â³­' => 'â³®',
  'â³²' => 'â³³',
  'ê™€' => 'ê™�',
  'ê™‚' => 'ê™ƒ',
  'ê™„' => 'ê™…',
  'ê™†' => 'ê™‡',
  'ê™ˆ' => 'ê™‰',
  'ê™Š' => 'ê™‹',
  'ê™Œ' => 'ê™�',
  'ê™Ž' => 'ê™�',
  'ê™�' => 'ê™‘',
  'ê™’' => 'ê™“',
  'ê™”' => 'ê™•',
  'ê™–' => 'ê™—',
  'ê™˜' => 'ê™™',
  'ê™š' => 'ê™›',
  'ê™œ' => 'ê™�',
  'ê™ž' => 'ê™Ÿ',
  'ê™ ' => 'ê™¡',
  'ê™¢' => 'ê™£',
  'ê™¤' => 'ê™¥',
  'ê™¦' => 'ê™§',
  'ê™¨' => 'ê™©',
  'ê™ª' => 'ê™«',
  'ê™¬' => 'ê™­',
  'êš€' => 'êš�',
  'êš‚' => 'êšƒ',
  'êš„' => 'êš…',
  'êš†' => 'êš‡',
  'êšˆ' => 'êš‰',
  'êšŠ' => 'êš‹',
  'êšŒ' => 'êš�',
  'êšŽ' => 'êš�',
  'êš�' => 'êš‘',
  'êš’' => 'êš“',
  'êš”' => 'êš•',
  'êš–' => 'êš—',
  'êš˜' => 'êš™',
  'êšš' => 'êš›',
  'êœ¢' => 'êœ£',
  'êœ¤' => 'êœ¥',
  'êœ¦' => 'êœ§',
  'êœ¨' => 'êœ©',
  'êœª' => 'êœ«',
  'êœ¬' => 'êœ­',
  'êœ®' => 'êœ¯',
  'êœ²' => 'êœ³',
  'êœ´' => 'êœµ',
  'êœ¶' => 'êœ·',
  'êœ¸' => 'êœ¹',
  'êœº' => 'êœ»',
  'êœ¼' => 'êœ½',
  'êœ¾' => 'êœ¿',
  'ê�€' => 'ê��',
  'ê�‚' => 'ê�ƒ',
  'ê�„' => 'ê�…',
  'ê�†' => 'ê�‡',
  'ê�ˆ' => 'ê�‰',
  'ê�Š' => 'ê�‹',
  'ê�Œ' => 'ê��',
  'ê�Ž' => 'ê��',
  'ê��' => 'ê�‘',
  'ê�’' => 'ê�“',
  'ê�”' => 'ê�•',
  'ê�–' => 'ê�—',
  'ê�˜' => 'ê�™',
  'ê�š' => 'ê�›',
  'ê�œ' => 'ê��',
  'ê�ž' => 'ê�Ÿ',
  'ê� ' => 'ê�¡',
  'ê�¢' => 'ê�£',
  'ê�¤' => 'ê�¥',
  'ê�¦' => 'ê�§',
  'ê�¨' => 'ê�©',
  'ê�ª' => 'ê�«',
  'ê�¬' => 'ê�­',
  'ê�®' => 'ê�¯',
  'ê�¹' => 'ê�º',
  'ê�»' => 'ê�¼',
  'ê�½' => 'áµ¹',
  'ê�¾' => 'ê�¿',
  'êž€' => 'êž�',
  'êž‚' => 'êžƒ',
  'êž„' => 'êž…',
  'êž†' => 'êž‡',
  'êž‹' => 'êžŒ',
  'êž�' => 'É¥',
  'êž�' => 'êž‘',
  'êž’' => 'êž“',
  'êž–' => 'êž—',
  'êž˜' => 'êž™',
  'êžš' => 'êž›',
  'êžœ' => 'êž�',
  'êžž' => 'êžŸ',
  'êž ' => 'êž¡',
  'êž¢' => 'êž£',
  'êž¤' => 'êž¥',
  'êž¦' => 'êž§',
  'êž¨' => 'êž©',
  'êžª' => 'É¦',
  'êž«' => 'Éœ',
  'êž¬' => 'É¡',
  'êž­' => 'É¬',
  'êž°' => 'Êž',
  'êž±' => 'Ê‡',
  'ï¼¡' => 'ï½�',
  'ï¼¢' => 'ï½‚',
  'ï¼£' => 'ï½ƒ',
  'ï¼¤' => 'ï½„',
  'ï¼¥' => 'ï½…',
  'ï¼¦' => 'ï½†',
  'ï¼§' => 'ï½‡',
  'ï¼¨' => 'ï½ˆ',
  'ï¼©' => 'ï½‰',
  'ï¼ª' => 'ï½Š',
  'ï¼«' => 'ï½‹',
  'ï¼¬' => 'ï½Œ',
  'ï¼­' => 'ï½�',
  'ï¼®' => 'ï½Ž',
  'ï¼¯' => 'ï½�',
  'ï¼°' => 'ï½�',
  'ï¼±' => 'ï½‘',
  'ï¼²' => 'ï½’',
  'ï¼³' => 'ï½“',
  'ï¼´' => 'ï½”',
  'ï¼µ' => 'ï½•',
  'ï¼¶' => 'ï½–',
  'ï¼·' => 'ï½—',
  'ï¼¸' => 'ï½˜',
  'ï¼¹' => 'ï½™',
  'ï¼º' => 'ï½š',
  'ð��€' => 'ð��¨',
  'ð���' => 'ð��©',
  'ð��‚' => 'ð��ª',
  'ð��ƒ' => 'ð��«',
  'ð��„' => 'ð��¬',
  'ð��…' => 'ð��­',
  'ð��†' => 'ð��®',
  'ð��‡' => 'ð��¯',
  'ð��ˆ' => 'ð��°',
  'ð��‰' => 'ð��±',
  'ð��Š' => 'ð��²',
  'ð��‹' => 'ð��³',
  'ð��Œ' => 'ð��´',
  'ð���' => 'ð��µ',
  'ð��Ž' => 'ð��¶',
  'ð���' => 'ð��·',
  'ð���' => 'ð��¸',
  'ð��‘' => 'ð��¹',
  'ð��’' => 'ð��º',
  'ð��“' => 'ð��»',
  'ð��”' => 'ð��¼',
  'ð��•' => 'ð��½',
  'ð��–' => 'ð��¾',
  'ð��—' => 'ð��¿',
  'ð��˜' => 'ð�‘€',
  'ð��™' => 'ð�‘�',
  'ð��š' => 'ð�‘‚',
  'ð��›' => 'ð�‘ƒ',
  'ð��œ' => 'ð�‘„',
  'ð���' => 'ð�‘…',
  'ð��ž' => 'ð�‘†',
  'ð��Ÿ' => 'ð�‘‡',
  'ð�� ' => 'ð�‘ˆ',
  'ð��¡' => 'ð�‘‰',
  'ð��¢' => 'ð�‘Š',
  'ð��£' => 'ð�‘‹',
  'ð��¤' => 'ð�‘Œ',
  'ð��¥' => 'ð�‘�',
  'ð��¦' => 'ð�‘Ž',
  'ð��§' => 'ð�‘�',
  'ð‘¢ ' => 'ð‘£€',
  'ð‘¢¡' => 'ð‘£�',
  'ð‘¢¢' => 'ð‘£‚',
  'ð‘¢£' => 'ð‘£ƒ',
  'ð‘¢¤' => 'ð‘£„',
  'ð‘¢¥' => 'ð‘£…',
  'ð‘¢¦' => 'ð‘£†',
  'ð‘¢§' => 'ð‘£‡',
  'ð‘¢¨' => 'ð‘£ˆ',
  'ð‘¢©' => 'ð‘£‰',
  'ð‘¢ª' => 'ð‘£Š',
  'ð‘¢«' => 'ð‘£‹',
  'ð‘¢¬' => 'ð‘£Œ',
  'ð‘¢­' => 'ð‘£�',
  'ð‘¢®' => 'ð‘£Ž',
  'ð‘¢¯' => 'ð‘£�',
  'ð‘¢°' => 'ð‘£�',
  'ð‘¢±' => 'ð‘£‘',
  'ð‘¢²' => 'ð‘£’',
  'ð‘¢³' => 'ð‘£“',
  'ð‘¢´' => 'ð‘£”',
  'ð‘¢µ' => 'ð‘£•',
  'ð‘¢¶' => 'ð‘£–',
  'ð‘¢·' => 'ð‘£—',
  'ð‘¢¸' => 'ð‘£˜',
  'ð‘¢¹' => 'ð‘£™',
  'ð‘¢º' => 'ð‘£š',
  'ð‘¢»' => 'ð‘£›',
  'ð‘¢¼' => 'ð‘£œ',
  'ð‘¢½' => 'ð‘£�',
  'ð‘¢¾' => 'ð‘£ž',
  'ð‘¢¿' => 'ð‘£Ÿ',
);

$result =& $data;
unset($data);

return $result;
<?php

static $data = array (
  'a' => 'A',
  'b' => 'B',
  'c' => 'C',
  'd' => 'D',
  'e' => 'E',
  'f' => 'F',
  'g' => 'G',
  'h' => 'H',
  'i' => 'I',
  'j' => 'J',
  'k' => 'K',
  'l' => 'L',
  'm' => 'M',
  'n' => 'N',
  'o' => 'O',
  'p' => 'P',
  'q' => 'Q',
  'r' => 'R',
  's' => 'S',
  't' => 'T',
  'u' => 'U',
  'v' => 'V',
  'w' => 'W',
  'x' => 'X',
  'y' => 'Y',
  'z' => 'Z',
  'Âµ' => 'Îœ',
  'Ã ' => 'Ã€',
  'Ã¡' => 'Ã�',
  'Ã¢' => 'Ã‚',
  'Ã£' => 'Ãƒ',
  'Ã¤' => 'Ã„',
  'Ã¥' => 'Ã…',
  'Ã¦' => 'Ã†',
  'Ã§' => 'Ã‡',
  'Ã¨' => 'Ãˆ',
  'Ã©' => 'Ã‰',
  'Ãª' => 'ÃŠ',
  'Ã«' => 'Ã‹',
  'Ã¬' => 'ÃŒ',
  'Ã­' => 'Ã�',
  'Ã®' => 'ÃŽ',
  'Ã¯' => 'Ã�',
  'Ã°' => 'Ã�',
  'Ã±' => 'Ã‘',
  'Ã²' => 'Ã’',
  'Ã³' => 'Ã“',
  'Ã´' => 'Ã”',
  'Ãµ' => 'Ã•',
  'Ã¶' => 'Ã–',
  'Ã¸' => 'Ã˜',
  'Ã¹' => 'Ã™',
  'Ãº' => 'Ãš',
  'Ã»' => 'Ã›',
  'Ã¼' => 'Ãœ',
  'Ã½' => 'Ã�',
  'Ã¾' => 'Ãž',
  'Ã¿' => 'Å¸',
  'Ä�' => 'Ä€',
  'Äƒ' => 'Ä‚',
  'Ä…' => 'Ä„',
  'Ä‡' => 'Ä†',
  'Ä‰' => 'Äˆ',
  'Ä‹' => 'ÄŠ',
  'Ä�' => 'ÄŒ',
  'Ä�' => 'ÄŽ',
  'Ä‘' => 'Ä�',
  'Ä“' => 'Ä’',
  'Ä•' => 'Ä”',
  'Ä—' => 'Ä–',
  'Ä™' => 'Ä˜',
  'Ä›' => 'Äš',
  'Ä�' => 'Äœ',
  'ÄŸ' => 'Äž',
  'Ä¡' => 'Ä ',
  'Ä£' => 'Ä¢',
  'Ä¥' => 'Ä¤',
  'Ä§' => 'Ä¦',
  'Ä©' => 'Ä¨',
  'Ä«' => 'Äª',
  'Ä­' => 'Ä¬',
  'Ä¯' => 'Ä®',
  'Ä±' => 'I',
  'Ä³' => 'Ä²',
  'Äµ' => 'Ä´',
  'Ä·' => 'Ä¶',
  'Äº' => 'Ä¹',
  'Ä¼' => 'Ä»',
  'Ä¾' => 'Ä½',
  'Å€' => 'Ä¿',
  'Å‚' => 'Å�',
  'Å„' => 'Åƒ',
  'Å†' => 'Å…',
  'Åˆ' => 'Å‡',
  'Å‹' => 'ÅŠ',
  'Å�' => 'ÅŒ',
  'Å�' => 'ÅŽ',
  'Å‘' => 'Å�',
  'Å“' => 'Å’',
  'Å•' => 'Å”',
  'Å—' => 'Å–',
  'Å™' => 'Å˜',
  'Å›' => 'Åš',
  'Å�' => 'Åœ',
  'ÅŸ' => 'Åž',
  'Å¡' => 'Å ',
  'Å£' => 'Å¢',
  'Å¥' => 'Å¤',
  'Å§' => 'Å¦',
  'Å©' => 'Å¨',
  'Å«' => 'Åª',
  'Å­' => 'Å¬',
  'Å¯' => 'Å®',
  'Å±' => 'Å°',
  'Å³' => 'Å²',
  'Åµ' => 'Å´',
  'Å·' => 'Å¶',
  'Åº' => 'Å¹',
  'Å¼' => 'Å»',
  'Å¾' => 'Å½',
  'Å¿' => 'S',
  'Æ€' => 'Éƒ',
  'Æƒ' => 'Æ‚',
  'Æ…' => 'Æ„',
  'Æˆ' => 'Æ‡',
  'ÆŒ' => 'Æ‹',
  'Æ’' => 'Æ‘',
  'Æ•' => 'Ç¶',
  'Æ™' => 'Æ˜',
  'Æš' => 'È½',
  'Æž' => 'È ',
  'Æ¡' => 'Æ ',
  'Æ£' => 'Æ¢',
  'Æ¥' => 'Æ¤',
  'Æ¨' => 'Æ§',
  'Æ­' => 'Æ¬',
  'Æ°' => 'Æ¯',
  'Æ´' => 'Æ³',
  'Æ¶' => 'Æµ',
  'Æ¹' => 'Æ¸',
  'Æ½' => 'Æ¼',
  'Æ¿' => 'Ç·',
  'Ç…' => 'Ç„',
  'Ç†' => 'Ç„',
  'Çˆ' => 'Ç‡',
  'Ç‰' => 'Ç‡',
  'Ç‹' => 'ÇŠ',
  'ÇŒ' => 'ÇŠ',
  'ÇŽ' => 'Ç�',
  'Ç�' => 'Ç�',
  'Ç’' => 'Ç‘',
  'Ç”' => 'Ç“',
  'Ç–' => 'Ç•',
  'Ç˜' => 'Ç—',
  'Çš' => 'Ç™',
  'Çœ' => 'Ç›',
  'Ç�' => 'ÆŽ',
  'ÇŸ' => 'Çž',
  'Ç¡' => 'Ç ',
  'Ç£' => 'Ç¢',
  'Ç¥' => 'Ç¤',
  'Ç§' => 'Ç¦',
  'Ç©' => 'Ç¨',
  'Ç«' => 'Çª',
  'Ç­' => 'Ç¬',
  'Ç¯' => 'Ç®',
  'Ç²' => 'Ç±',
  'Ç³' => 'Ç±',
  'Çµ' => 'Ç´',
  'Ç¹' => 'Ç¸',
  'Ç»' => 'Çº',
  'Ç½' => 'Ç¼',
  'Ç¿' => 'Ç¾',
  'È�' => 'È€',
  'Èƒ' => 'È‚',
  'È…' => 'È„',
  'È‡' => 'È†',
  'È‰' => 'Èˆ',
  'È‹' => 'ÈŠ',
  'È�' => 'ÈŒ',
  'È�' => 'ÈŽ',
  'È‘' => 'È�',
  'È“' => 'È’',
  'È•' => 'È”',
  'È—' => 'È–',
  'È™' => 'È˜',
  'È›' => 'Èš',
  'È�' => 'Èœ',
  'ÈŸ' => 'Èž',
  'È£' => 'È¢',
  'È¥' => 'È¤',
  'È§' => 'È¦',
  'È©' => 'È¨',
  'È«' => 'Èª',
  'È­' => 'È¬',
  'È¯' => 'È®',
  'È±' => 'È°',
  'È³' => 'È²',
  'È¼' => 'È»',
  'È¿' => 'â±¾',
  'É€' => 'â±¿',
  'É‚' => 'É�',
  'É‡' => 'É†',
  'É‰' => 'Éˆ',
  'É‹' => 'ÉŠ',
  'É�' => 'ÉŒ',
  'É�' => 'ÉŽ',
  'É�' => 'â±¯',
  'É‘' => 'â±­',
  'É’' => 'â±°',
  'É“' => 'Æ�',
  'É”' => 'Æ†',
  'É–' => 'Æ‰',
  'É—' => 'ÆŠ',
  'É™' => 'Æ�',
  'É›' => 'Æ�',
  'Éœ' => 'êž«',
  'É ' => 'Æ“',
  'É¡' => 'êž¬',
  'É£' => 'Æ”',
  'É¥' => 'êž�',
  'É¦' => 'êžª',
  'É¨' => 'Æ—',
  'É©' => 'Æ–',
  'É«' => 'â±¢',
  'É¬' => 'êž­',
  'É¯' => 'Æœ',
  'É±' => 'â±®',
  'É²' => 'Æ�',
  'Éµ' => 'ÆŸ',
  'É½' => 'â±¤',
  'Ê€' => 'Æ¦',
  'Êƒ' => 'Æ©',
  'Ê‡' => 'êž±',
  'Êˆ' => 'Æ®',
  'Ê‰' => 'É„',
  'ÊŠ' => 'Æ±',
  'Ê‹' => 'Æ²',
  'ÊŒ' => 'É…',
  'Ê’' => 'Æ·',
  'Êž' => 'êž°',
  'Í…' => 'Î™',
  'Í±' => 'Í°',
  'Í³' => 'Í²',
  'Í·' => 'Í¶',
  'Í»' => 'Ï½',
  'Í¼' => 'Ï¾',
  'Í½' => 'Ï¿',
  'Î¬' => 'Î†',
  'Î­' => 'Îˆ',
  'Î®' => 'Î‰',
  'Î¯' => 'ÎŠ',
  'Î±' => 'Î‘',
  'Î²' => 'Î’',
  'Î³' => 'Î“',
  'Î´' => 'Î”',
  'Îµ' => 'Î•',
  'Î¶' => 'Î–',
  'Î·' => 'Î—',
  'Î¸' => 'Î˜',
  'Î¹' => 'Î™',
  'Îº' => 'Îš',
  'Î»' => 'Î›',
  'Î¼' => 'Îœ',
  'Î½' => 'Î�',
  'Î¾' => 'Îž',
  'Î¿' => 'ÎŸ',
  'Ï€' => 'Î ',
  'Ï�' => 'Î¡',
  'Ï‚' => 'Î£',
  'Ïƒ' => 'Î£',
  'Ï„' => 'Î¤',
  'Ï…' => 'Î¥',
  'Ï†' => 'Î¦',
  'Ï‡' => 'Î§',
  'Ïˆ' => 'Î¨',
  'Ï‰' => 'Î©',
  'ÏŠ' => 'Îª',
  'Ï‹' => 'Î«',
  'ÏŒ' => 'ÎŒ',
  'Ï�' => 'ÎŽ',
  'ÏŽ' => 'Î�',
  'Ï�' => 'Î’',
  'Ï‘' => 'Î˜',
  'Ï•' => 'Î¦',
  'Ï–' => 'Î ',
  'Ï—' => 'Ï�',
  'Ï™' => 'Ï˜',
  'Ï›' => 'Ïš',
  'Ï�' => 'Ïœ',
  'ÏŸ' => 'Ïž',
  'Ï¡' => 'Ï ',
  'Ï£' => 'Ï¢',
  'Ï¥' => 'Ï¤',
  'Ï§' => 'Ï¦',
  'Ï©' => 'Ï¨',
  'Ï«' => 'Ïª',
  'Ï­' => 'Ï¬',
  'Ï¯' => 'Ï®',
  'Ï°' => 'Îš',
  'Ï±' => 'Î¡',
  'Ï²' => 'Ï¹',
  'Ï³' => 'Í¿',
  'Ïµ' => 'Î•',
  'Ï¸' => 'Ï·',
  'Ï»' => 'Ïº',
  'Ð°' => 'Ð�',
  'Ð±' => 'Ð‘',
  'Ð²' => 'Ð’',
  'Ð³' => 'Ð“',
  'Ð´' => 'Ð”',
  'Ðµ' => 'Ð•',
  'Ð¶' => 'Ð–',
  'Ð·' => 'Ð—',
  'Ð¸' => 'Ð˜',
  'Ð¹' => 'Ð™',
  'Ðº' => 'Ðš',
  'Ð»' => 'Ð›',
  'Ð¼' => 'Ðœ',
  'Ð½' => 'Ð�',
  'Ð¾' => 'Ðž',
  'Ð¿' => 'ÐŸ',
  'Ñ€' => 'Ð ',
  'Ñ�' => 'Ð¡',
  'Ñ‚' => 'Ð¢',
  'Ñƒ' => 'Ð£',
  'Ñ„' => 'Ð¤',
  'Ñ…' => 'Ð¥',
  'Ñ†' => 'Ð¦',
  'Ñ‡' => 'Ð§',
  'Ñˆ' => 'Ð¨',
  'Ñ‰' => 'Ð©',
  'ÑŠ' => 'Ðª',
  'Ñ‹' => 'Ð«',
  'ÑŒ' => 'Ð¬',
  'Ñ�' => 'Ð­',
  'ÑŽ' => 'Ð®',
  'Ñ�' => 'Ð¯',
  'Ñ�' => 'Ð€',
  'Ñ‘' => 'Ð�',
  'Ñ’' => 'Ð‚',
  'Ñ“' => 'Ðƒ',
  'Ñ”' => 'Ð„',
  'Ñ•' => 'Ð…',
  'Ñ–' => 'Ð†',
  'Ñ—' => 'Ð‡',
  'Ñ˜' => 'Ðˆ',
  'Ñ™' => 'Ð‰',
  'Ñš' => 'ÐŠ',
  'Ñ›' => 'Ð‹',
  'Ñœ' => 'ÐŒ',
  'Ñ�' => 'Ð�',
  'Ñž' => 'ÐŽ',
  'ÑŸ' => 'Ð�',
  'Ñ¡' => 'Ñ ',
  'Ñ£' => 'Ñ¢',
  'Ñ¥' => 'Ñ¤',
  'Ñ§' => 'Ñ¦',
  'Ñ©' => 'Ñ¨',
  'Ñ«' => 'Ñª',
  'Ñ­' => 'Ñ¬',
  'Ñ¯' => 'Ñ®',
  'Ñ±' => 'Ñ°',
  'Ñ³' => 'Ñ²',
  'Ñµ' => 'Ñ´',
  'Ñ·' => 'Ñ¶',
  'Ñ¹' => 'Ñ¸',
  'Ñ»' => 'Ñº',
  'Ñ½' => 'Ñ¼',
  'Ñ¿' => 'Ñ¾',
  'Ò�' => 'Ò€',
  'Ò‹' => 'ÒŠ',
  'Ò�' => 'ÒŒ',
  'Ò�' => 'ÒŽ',
  'Ò‘' => 'Ò�',
  'Ò“' => 'Ò’',
  'Ò•' => 'Ò”',
  'Ò—' => 'Ò–',
  'Ò™' => 'Ò˜',
  'Ò›' => 'Òš',
  'Ò�' => 'Òœ',
  'ÒŸ' => 'Òž',
  'Ò¡' => 'Ò ',
  'Ò£' => 'Ò¢',
  'Ò¥' => 'Ò¤',
  'Ò§' => 'Ò¦',
  'Ò©' => 'Ò¨',
  'Ò«' => 'Òª',
  'Ò­' => 'Ò¬',
  'Ò¯' => 'Ò®',
  'Ò±' => 'Ò°',
  'Ò³' => 'Ò²',
  'Òµ' => 'Ò´',
  'Ò·' => 'Ò¶',
  'Ò¹' => 'Ò¸',
  'Ò»' => 'Òº',
  'Ò½' => 'Ò¼',
  'Ò¿' => 'Ò¾',
  'Ó‚' => 'Ó�',
  'Ó„' => 'Óƒ',
  'Ó†' => 'Ó…',
  'Óˆ' => 'Ó‡',
  'ÓŠ' => 'Ó‰',
  'ÓŒ' => 'Ó‹',
  'ÓŽ' => 'Ó�',
  'Ó�' => 'Ó€',
  'Ó‘' => 'Ó�',
  'Ó“' => 'Ó’',
  'Ó•' => 'Ó”',
  'Ó—' => 'Ó–',
  'Ó™' => 'Ó˜',
  'Ó›' => 'Óš',
  'Ó�' => 'Óœ',
  'ÓŸ' => 'Óž',
  'Ó¡' => 'Ó ',
  'Ó£' => 'Ó¢',
  'Ó¥' => 'Ó¤',
  'Ó§' => 'Ó¦',
  'Ó©' => 'Ó¨',
  'Ó«' => 'Óª',
  'Ó­' => 'Ó¬',
  'Ó¯' => 'Ó®',
  'Ó±' => 'Ó°',
  'Ó³' => 'Ó²',
  'Óµ' => 'Ó´',
  'Ó·' => 'Ó¶',
  'Ó¹' => 'Ó¸',
  'Ó»' => 'Óº',
  'Ó½' => 'Ó¼',
  'Ó¿' => 'Ó¾',
  'Ô�' => 'Ô€',
  'Ôƒ' => 'Ô‚',
  'Ô…' => 'Ô„',
  'Ô‡' => 'Ô†',
  'Ô‰' => 'Ôˆ',
  'Ô‹' => 'ÔŠ',
  'Ô�' => 'ÔŒ',
  'Ô�' => 'ÔŽ',
  'Ô‘' => 'Ô�',
  'Ô“' => 'Ô’',
  'Ô•' => 'Ô”',
  'Ô—' => 'Ô–',
  'Ô™' => 'Ô˜',
  'Ô›' => 'Ôš',
  'Ô�' => 'Ôœ',
  'ÔŸ' => 'Ôž',
  'Ô¡' => 'Ô ',
  'Ô£' => 'Ô¢',
  'Ô¥' => 'Ô¤',
  'Ô§' => 'Ô¦',
  'Ô©' => 'Ô¨',
  'Ô«' => 'Ôª',
  'Ô­' => 'Ô¬',
  'Ô¯' => 'Ô®',
  'Õ¡' => 'Ô±',
  'Õ¢' => 'Ô²',
  'Õ£' => 'Ô³',
  'Õ¤' => 'Ô´',
  'Õ¥' => 'Ôµ',
  'Õ¦' => 'Ô¶',
  'Õ§' => 'Ô·',
  'Õ¨' => 'Ô¸',
  'Õ©' => 'Ô¹',
  'Õª' => 'Ôº',
  'Õ«' => 'Ô»',
  'Õ¬' => 'Ô¼',
  'Õ­' => 'Ô½',
  'Õ®' => 'Ô¾',
  'Õ¯' => 'Ô¿',
  'Õ°' => 'Õ€',
  'Õ±' => 'Õ�',
  'Õ²' => 'Õ‚',
  'Õ³' => 'Õƒ',
  'Õ´' => 'Õ„',
  'Õµ' => 'Õ…',
  'Õ¶' => 'Õ†',
  'Õ·' => 'Õ‡',
  'Õ¸' => 'Õˆ',
  'Õ¹' => 'Õ‰',
  'Õº' => 'ÕŠ',
  'Õ»' => 'Õ‹',
  'Õ¼' => 'ÕŒ',
  'Õ½' => 'Õ�',
  'Õ¾' => 'ÕŽ',
  'Õ¿' => 'Õ�',
  'Ö€' => 'Õ�',
  'Ö�' => 'Õ‘',
  'Ö‚' => 'Õ’',
  'Öƒ' => 'Õ“',
  'Ö„' => 'Õ”',
  'Ö…' => 'Õ•',
  'Ö†' => 'Õ–',
  'áµ¹' => 'ê�½',
  'áµ½' => 'â±£',
  'á¸�' => 'á¸€',
  'á¸ƒ' => 'á¸‚',
  'á¸…' => 'á¸„',
  'á¸‡' => 'á¸†',
  'á¸‰' => 'á¸ˆ',
  'á¸‹' => 'á¸Š',
  'á¸�' => 'á¸Œ',
  'á¸�' => 'á¸Ž',
  'á¸‘' => 'á¸�',
  'á¸“' => 'á¸’',
  'á¸•' => 'á¸”',
  'á¸—' => 'á¸–',
  'á¸™' => 'á¸˜',
  'á¸›' => 'á¸š',
  'á¸�' => 'á¸œ',
  'á¸Ÿ' => 'á¸ž',
  'á¸¡' => 'á¸ ',
  'á¸£' => 'á¸¢',
  'á¸¥' => 'á¸¤',
  'á¸§' => 'á¸¦',
  'á¸©' => 'á¸¨',
  'á¸«' => 'á¸ª',
  'á¸­' => 'á¸¬',
  'á¸¯' => 'á¸®',
  'á¸±' => 'á¸°',
  'á¸³' => 'á¸²',
  'á¸µ' => 'á¸´',
  'á¸·' => 'á¸¶',
  'á¸¹' => 'á¸¸',
  'á¸»' => 'á¸º',
  'á¸½' => 'á¸¼',
  'á¸¿' => 'á¸¾',
  'á¹�' => 'á¹€',
  'á¹ƒ' => 'á¹‚',
  'á¹…' => 'á¹„',
  'á¹‡' => 'á¹†',
  'á¹‰' => 'á¹ˆ',
  'á¹‹' => 'á¹Š',
  'á¹�' => 'á¹Œ',
  'á¹�' => 'á¹Ž',
  'á¹‘' => 'á¹�',
  'á¹“' => 'á¹’',
  'á¹•' => 'á¹”',
  'á¹—' => 'á¹–',
  'á¹™' => 'á¹˜',
  'á¹›' => 'á¹š',
  'á¹�' => 'á¹œ',
  'á¹Ÿ' => 'á¹ž',
  'á¹¡' => 'á¹ ',
  'á¹£' => 'á¹¢',
  'á¹¥' => 'á¹¤',
  'á¹§' => 'á¹¦',
  'á¹©' => 'á¹¨',
  'á¹«' => 'á¹ª',
  'á¹­' => 'á¹¬',
  'á¹¯' => 'á¹®',
  'á¹±' => 'á¹°',
  'á¹³' => 'á¹²',
  'á¹µ' => 'á¹´',
  'á¹·' => 'á¹¶',
  'á¹¹' => 'á¹¸',
  'á¹»' => 'á¹º',
  'á¹½' => 'á¹¼',
  'á¹¿' => 'á¹¾',
  'áº�' => 'áº€',
  'áºƒ' => 'áº‚',
  'áº…' => 'áº„',
  'áº‡' => 'áº†',
  'áº‰' => 'áºˆ',
  'áº‹' => 'áºŠ',
  'áº�' => 'áºŒ',
  'áº�' => 'áºŽ',
  'áº‘' => 'áº�',
  'áº“' => 'áº’',
  'áº•' => 'áº”',
  'áº›' => 'á¹ ',
  'áº¡' => 'áº ',
  'áº£' => 'áº¢',
  'áº¥' => 'áº¤',
  'áº§' => 'áº¦',
  'áº©' => 'áº¨',
  'áº«' => 'áºª',
  'áº­' => 'áº¬',
  'áº¯' => 'áº®',
  'áº±' => 'áº°',
  'áº³' => 'áº²',
  'áºµ' => 'áº´',
  'áº·' => 'áº¶',
  'áº¹' => 'áº¸',
  'áº»' => 'áºº',
  'áº½' => 'áº¼',
  'áº¿' => 'áº¾',
  'á»�' => 'á»€',
  'á»ƒ' => 'á»‚',
  'á»…' => 'á»„',
  'á»‡' => 'á»†',
  'á»‰' => 'á»ˆ',
  'á»‹' => 'á»Š',
  'á»�' => 'á»Œ',
  'á»�' => 'á»Ž',
  'á»‘' => 'á»�',
  'á»“' => 'á»’',
  'á»•' => 'á»”',
  'á»—' => 'á»–',
  'á»™' => 'á»˜',
  'á»›' => 'á»š',
  'á»�' => 'á»œ',
  'á»Ÿ' => 'á»ž',
  'á»¡' => 'á» ',
  'á»£' => 'á»¢',
  'á»¥' => 'á»¤',
  'á»§' => 'á»¦',
  'á»©' => 'á»¨',
  'á»«' => 'á»ª',
  'á»­' => 'á»¬',
  'á»¯' => 'á»®',
  'á»±' => 'á»°',
  'á»³' => 'á»²',
  'á»µ' => 'á»´',
  'á»·' => 'á»¶',
  'á»¹' => 'á»¸',
  'á»»' => 'á»º',
  'á»½' => 'á»¼',
  'á»¿' => 'á»¾',
  'á¼€' => 'á¼ˆ',
  'á¼�' => 'á¼‰',
  'á¼‚' => 'á¼Š',
  'á¼ƒ' => 'á¼‹',
  'á¼„' => 'á¼Œ',
  'á¼…' => 'á¼�',
  'á¼†' => 'á¼Ž',
  'á¼‡' => 'á¼�',
  'á¼�' => 'á¼˜',
  'á¼‘' => 'á¼™',
  'á¼’' => 'á¼š',
  'á¼“' => 'á¼›',
  'á¼”' => 'á¼œ',
  'á¼•' => 'á¼�',
  'á¼ ' => 'á¼¨',
  'á¼¡' => 'á¼©',
  'á¼¢' => 'á¼ª',
  'á¼£' => 'á¼«',
  'á¼¤' => 'á¼¬',
  'á¼¥' => 'á¼­',
  'á¼¦' => 'á¼®',
  'á¼§' => 'á¼¯',
  'á¼°' => 'á¼¸',
  'á¼±' => 'á¼¹',
  'á¼²' => 'á¼º',
  'á¼³' => 'á¼»',
  'á¼´' => 'á¼¼',
  'á¼µ' => 'á¼½',
  'á¼¶' => 'á¼¾',
  'á¼·' => 'á¼¿',
  'á½€' => 'á½ˆ',
  'á½�' => 'á½‰',
  'á½‚' => 'á½Š',
  'á½ƒ' => 'á½‹',
  'á½„' => 'á½Œ',
  'á½…' => 'á½�',
  'á½‘' => 'á½™',
  'á½“' => 'á½›',
  'á½•' => 'á½�',
  'á½—' => 'á½Ÿ',
  'á½ ' => 'á½¨',
  'á½¡' => 'á½©',
  'á½¢' => 'á½ª',
  'á½£' => 'á½«',
  'á½¤' => 'á½¬',
  'á½¥' => 'á½­',
  'á½¦' => 'á½®',
  'á½§' => 'á½¯',
  'á½°' => 'á¾º',
  'á½±' => 'á¾»',
  'á½²' => 'á¿ˆ',
  'á½³' => 'á¿‰',
  'á½´' => 'á¿Š',
  'á½µ' => 'á¿‹',
  'á½¶' => 'á¿š',
  'á½·' => 'á¿›',
  'á½¸' => 'á¿¸',
  'á½¹' => 'á¿¹',
  'á½º' => 'á¿ª',
  'á½»' => 'á¿«',
  'á½¼' => 'á¿º',
  'á½½' => 'á¿»',
  'á¾€' => 'á¾ˆ',
  'á¾�' => 'á¾‰',
  'á¾‚' => 'á¾Š',
  'á¾ƒ' => 'á¾‹',
  'á¾„' => 'á¾Œ',
  'á¾…' => 'á¾�',
  'á¾†' => 'á¾Ž',
  'á¾‡' => 'á¾�',
  'á¾�' => 'á¾˜',
  'á¾‘' => 'á¾™',
  'á¾’' => 'á¾š',
  'á¾“' => 'á¾›',
  'á¾”' => 'á¾œ',
  'á¾•' => 'á¾�',
  'á¾–' => 'á¾ž',
  'á¾—' => 'á¾Ÿ',
  'á¾ ' => 'á¾¨',
  'á¾¡' => 'á¾©',
  'á¾¢' => 'á¾ª',
  'á¾£' => 'á¾«',
  'á¾¤' => 'á¾¬',
  'á¾¥' => 'á¾­',
  'á¾¦' => 'á¾®',
  'á¾§' => 'á¾¯',
  'á¾°' => 'á¾¸',
  'á¾±' => 'á¾¹',
  'á¾³' => 'á¾¼',
  'á¾¾' => 'Î™',
  'á¿ƒ' => 'á¿Œ',
  'á¿�' => 'á¿˜',
  'á¿‘' => 'á¿™',
  'á¿ ' => 'á¿¨',
  'á¿¡' => 'á¿©',
  'á¿¥' => 'á¿¬',
  'á¿³' => 'á¿¼',
  'â…Ž' => 'â„²',
  'â…°' => 'â… ',
  'â…±' => 'â…¡',
  'â…²' => 'â…¢',
  'â…³' => 'â…£',
  'â…´' => 'â…¤',
  'â…µ' => 'â…¥',
  'â…¶' => 'â…¦',
  'â…·' => 'â…§',
  'â…¸' => 'â…¨',
  'â…¹' => 'â…©',
  'â…º' => 'â…ª',
  'â…»' => 'â…«',
  'â…¼' => 'â…¬',
  'â…½' => 'â…­',
  'â…¾' => 'â…®',
  'â…¿' => 'â…¯',
  'â†„' => 'â†ƒ',
  'â“�' => 'â’¶',
  'â“‘' => 'â’·',
  'â“’' => 'â’¸',
  'â““' => 'â’¹',
  'â“”' => 'â’º',
  'â“•' => 'â’»',
  'â“–' => 'â’¼',
  'â“—' => 'â’½',
  'â“˜' => 'â’¾',
  'â“™' => 'â’¿',
  'â“š' => 'â“€',
  'â“›' => 'â“�',
  'â“œ' => 'â“‚',
  'â“�' => 'â“ƒ',
  'â“ž' => 'â“„',
  'â“Ÿ' => 'â“…',
  'â“ ' => 'â“†',
  'â“¡' => 'â“‡',
  'â“¢' => 'â“ˆ',
  'â“£' => 'â“‰',
  'â“¤' => 'â“Š',
  'â“¥' => 'â“‹',
  'â“¦' => 'â“Œ',
  'â“§' => 'â“�',
  'â“¨' => 'â“Ž',
  'â“©' => 'â“�',
  'â°°' => 'â°€',
  'â°±' => 'â°�',
  'â°²' => 'â°‚',
  'â°³' => 'â°ƒ',
  'â°´' => 'â°„',
  'â°µ' => 'â°…',
  'â°¶' => 'â°†',
  'â°·' => 'â°‡',
  'â°¸' => 'â°ˆ',
  'â°¹' => 'â°‰',
  'â°º' => 'â°Š',
  'â°»' => 'â°‹',
  'â°¼' => 'â°Œ',
  'â°½' => 'â°�',
  'â°¾' => 'â°Ž',
  'â°¿' => 'â°�',
  'â±€' => 'â°�',
  'â±�' => 'â°‘',
  'â±‚' => 'â°’',
  'â±ƒ' => 'â°“',
  'â±„' => 'â°”',
  'â±…' => 'â°•',
  'â±†' => 'â°–',
  'â±‡' => 'â°—',
  'â±ˆ' => 'â°˜',
  'â±‰' => 'â°™',
  'â±Š' => 'â°š',
  'â±‹' => 'â°›',
  'â±Œ' => 'â°œ',
  'â±�' => 'â°�',
  'â±Ž' => 'â°ž',
  'â±�' => 'â°Ÿ',
  'â±�' => 'â° ',
  'â±‘' => 'â°¡',
  'â±’' => 'â°¢',
  'â±“' => 'â°£',
  'â±”' => 'â°¤',
  'â±•' => 'â°¥',
  'â±–' => 'â°¦',
  'â±—' => 'â°§',
  'â±˜' => 'â°¨',
  'â±™' => 'â°©',
  'â±š' => 'â°ª',
  'â±›' => 'â°«',
  'â±œ' => 'â°¬',
  'â±�' => 'â°­',
  'â±ž' => 'â°®',
  'â±¡' => 'â± ',
  'â±¥' => 'Èº',
  'â±¦' => 'È¾',
  'â±¨' => 'â±§',
  'â±ª' => 'â±©',
  'â±¬' => 'â±«',
  'â±³' => 'â±²',
  'â±¶' => 'â±µ',
  'â²�' => 'â²€',
  'â²ƒ' => 'â²‚',
  'â²…' => 'â²„',
  'â²‡' => 'â²†',
  'â²‰' => 'â²ˆ',
  'â²‹' => 'â²Š',
  'â²�' => 'â²Œ',
  'â²�' => 'â²Ž',
  'â²‘' => 'â²�',
  'â²“' => 'â²’',
  'â²•' => 'â²”',
  'â²—' => 'â²–',
  'â²™' => 'â²˜',
  'â²›' => 'â²š',
  'â²�' => 'â²œ',
  'â²Ÿ' => 'â²ž',
  'â²¡' => 'â² ',
  'â²£' => 'â²¢',
  'â²¥' => 'â²¤',
  'â²§' => 'â²¦',
  'â²©' => 'â²¨',
  'â²«' => 'â²ª',
  'â²­' => 'â²¬',
  'â²¯' => 'â²®',
  'â²±' => 'â²°',
  'â²³' => 'â²²',
  'â²µ' => 'â²´',
  'â²·' => 'â²¶',
  'â²¹' => 'â²¸',
  'â²»' => 'â²º',
  'â²½' => 'â²¼',
  'â²¿' => 'â²¾',
  'â³�' => 'â³€',
  'â³ƒ' => 'â³‚',
  'â³…' => 'â³„',
  'â³‡' => 'â³†',
  'â³‰' => 'â³ˆ',
  'â³‹' => 'â³Š',
  'â³�' => 'â³Œ',
  'â³�' => 'â³Ž',
  'â³‘' => 'â³�',
  'â³“' => 'â³’',
  'â³•' => 'â³”',
  'â³—' => 'â³–',
  'â³™' => 'â³˜',
  'â³›' => 'â³š',
  'â³�' => 'â³œ',
  'â³Ÿ' => 'â³ž',
  'â³¡' => 'â³ ',
  'â³£' => 'â³¢',
  'â³¬' => 'â³«',
  'â³®' => 'â³­',
  'â³³' => 'â³²',
  'â´€' => 'á‚ ',
  'â´�' => 'á‚¡',
  'â´‚' => 'á‚¢',
  'â´ƒ' => 'á‚£',
  'â´„' => 'á‚¤',
  'â´…' => 'á‚¥',
  'â´†' => 'á‚¦',
  'â´‡' => 'á‚§',
  'â´ˆ' => 'á‚¨',
  'â´‰' => 'á‚©',
  'â´Š' => 'á‚ª',
  'â´‹' => 'á‚«',
  'â´Œ' => 'á‚¬',
  'â´�' => 'á‚­',
  'â´Ž' => 'á‚®',
  'â´�' => 'á‚¯',
  'â´�' => 'á‚°',
  'â´‘' => 'á‚±',
  'â´’' => 'á‚²',
  'â´“' => 'á‚³',
  'â´”' => 'á‚´',
  'â´•' => 'á‚µ',
  'â´–' => 'á‚¶',
  'â´—' => 'á‚·',
  'â´˜' => 'á‚¸',
  'â´™' => 'á‚¹',
  'â´š' => 'á‚º',
  'â´›' => 'á‚»',
  'â´œ' => 'á‚¼',
  'â´�' => 'á‚½',
  'â´ž' => 'á‚¾',
  'â´Ÿ' => 'á‚¿',
  'â´ ' => 'áƒ€',
  'â´¡' => 'áƒ�',
  'â´¢' => 'áƒ‚',
  'â´£' => 'áƒƒ',
  'â´¤' => 'áƒ„',
  'â´¥' => 'áƒ…',
  'â´§' => 'áƒ‡',
  'â´­' => 'áƒ�',
  'ê™�' => 'ê™€',
  'ê™ƒ' => 'ê™‚',
  'ê™…' => 'ê™„',
  'ê™‡' => 'ê™†',
  'ê™‰' => 'ê™ˆ',
  'ê™‹' => 'ê™Š',
  'ê™�' => 'ê™Œ',
  'ê™�' => 'ê™Ž',
  'ê™‘' => 'ê™�',
  'ê™“' => 'ê™’',
  'ê™•' => 'ê™”',
  'ê™—' => 'ê™–',
  'ê™™' => 'ê™˜',
  'ê™›' => 'ê™š',
  'ê™�' => 'ê™œ',
  'ê™Ÿ' => 'ê™ž',
  'ê™¡' => 'ê™ ',
  'ê™£' => 'ê™¢',
  'ê™¥' => 'ê™¤',
  'ê™§' => 'ê™¦',
  'ê™©' => 'ê™¨',
  'ê™«' => 'ê™ª',
  'ê™­' => 'ê™¬',
  'êš�' => 'êš€',
  'êšƒ' => 'êš‚',
  'êš…' => 'êš„',
  'êš‡' => 'êš†',
  'êš‰' => 'êšˆ',
  'êš‹' => 'êšŠ',
  'êš�' => 'êšŒ',
  'êš�' => 'êšŽ',
  'êš‘' => 'êš�',
  'êš“' => 'êš’',
  'êš•' => 'êš”',
  'êš—' => 'êš–',
  'êš™' => 'êš˜',
  'êš›' => 'êšš',
  'êœ£' => 'êœ¢',
  'êœ¥' => 'êœ¤',
  'êœ§' => 'êœ¦',
  'êœ©' => 'êœ¨',
  'êœ«' => 'êœª',
  'êœ­' => 'êœ¬',
  'êœ¯' => 'êœ®',
  'êœ³' => 'êœ²',
  'êœµ' => 'êœ´',
  'êœ·' => 'êœ¶',
  'êœ¹' => 'êœ¸',
  'êœ»' => 'êœº',
  'êœ½' => 'êœ¼',
  'êœ¿' => 'êœ¾',
  'ê��' => 'ê�€',
  'ê�ƒ' => 'ê�‚',
  'ê�…' => 'ê�„',
  'ê�‡' => 'ê�†',
  'ê�‰' => 'ê�ˆ',
  'ê�‹' => 'ê�Š',
  'ê��' => 'ê�Œ',
  'ê��' => 'ê�Ž',
  'ê�‘' => 'ê��',
  'ê�“' => 'ê�’',
  'ê�•' => 'ê�”',
  'ê�—' => 'ê�–',
  'ê�™' => 'ê�˜',
  'ê�›' => 'ê�š',
  'ê��' => 'ê�œ',
  'ê�Ÿ' => 'ê�ž',
  'ê�¡' => 'ê� ',
  'ê�£' => 'ê�¢',
  'ê�¥' => 'ê�¤',
  'ê�§' => 'ê�¦',
  'ê�©' => 'ê�¨',
  'ê�«' => 'ê�ª',
  'ê�­' => 'ê�¬',
  'ê�¯' => 'ê�®',
  'ê�º' => 'ê�¹',
  'ê�¼' => 'ê�»',
  'ê�¿' => 'ê�¾',
  'êž�' => 'êž€',
  'êžƒ' => 'êž‚',
  'êž…' => 'êž„',
  'êž‡' => 'êž†',
  'êžŒ' => 'êž‹',
  'êž‘' => 'êž�',
  'êž“' => 'êž’',
  'êž—' => 'êž–',
  'êž™' => 'êž˜',
  'êž›' => 'êžš',
  'êž�' => 'êžœ',
  'êžŸ' => 'êžž',
  'êž¡' => 'êž ',
  'êž£' => 'êž¢',
  'êž¥' => 'êž¤',
  'êž§' => 'êž¦',
  'êž©' => 'êž¨',
  'ï½�' => 'ï¼¡',
  'ï½‚' => 'ï¼¢',
  'ï½ƒ' => 'ï¼£',
  'ï½„' => 'ï¼¤',
  'ï½…' => 'ï¼¥',
  'ï½†' => 'ï¼¦',
  'ï½‡' => 'ï¼§',
  'ï½ˆ' => 'ï¼¨',
  'ï½‰' => 'ï¼©',
  'ï½Š' => 'ï¼ª',
  'ï½‹' => 'ï¼«',
  'ï½Œ' => 'ï¼¬',
  'ï½�' => 'ï¼­',
  'ï½Ž' => 'ï¼®',
  'ï½�' => 'ï¼¯',
  'ï½�' => 'ï¼°',
  'ï½‘' => 'ï¼±',
  'ï½’' => 'ï¼²',
  'ï½“' => 'ï¼³',
  'ï½”' => 'ï¼´',
  'ï½•' => 'ï¼µ',
  'ï½–' => 'ï¼¶',
  'ï½—' => 'ï¼·',
  'ï½˜' => 'ï¼¸',
  'ï½™' => 'ï¼¹',
  'ï½š' => 'ï¼º',
  'ð��¨' => 'ð��€',
  'ð��©' => 'ð���',
  'ð��ª' => 'ð��‚',
  'ð��«' => 'ð��ƒ',
  'ð��¬' => 'ð��„',
  'ð��­' => 'ð��…',
  'ð��®' => 'ð��†',
  'ð��¯' => 'ð��‡',
  'ð��°' => 'ð��ˆ',
  'ð��±' => 'ð��‰',
  'ð��²' => 'ð��Š',
  'ð��³' => 'ð��‹',
  'ð��´' => 'ð��Œ',
  'ð��µ' => 'ð���',
  'ð��¶' => 'ð��Ž',
  'ð��·' => 'ð���',
  'ð��¸' => 'ð���',
  'ð��¹' => 'ð��‘',
  'ð��º' => 'ð��’',
  'ð��»' => 'ð��“',
  'ð��¼' => 'ð��”',
  'ð��½' => 'ð��•',
  'ð��¾' => 'ð��–',
  'ð��¿' => 'ð��—',
  'ð�‘€' => 'ð��˜',
  'ð�‘�' => 'ð��™',
  'ð�‘‚' => 'ð��š',
  'ð�‘ƒ' => 'ð��›',
  'ð�‘„' => 'ð��œ',
  'ð�‘…' => 'ð���',
  'ð�‘†' => 'ð��ž',
  'ð�‘‡' => 'ð��Ÿ',
  'ð�‘ˆ' => 'ð�� ',
  'ð�‘‰' => 'ð��¡',
  'ð�‘Š' => 'ð��¢',
  'ð�‘‹' => 'ð��£',
  'ð�‘Œ' => 'ð��¤',
  'ð�‘�' => 'ð��¥',
  'ð�‘Ž' => 'ð��¦',
  'ð�‘�' => 'ð��§',
  'ð‘£€' => 'ð‘¢ ',
  'ð‘£�' => 'ð‘¢¡',
  'ð‘£‚' => 'ð‘¢¢',
  'ð‘£ƒ' => 'ð‘¢£',
  'ð‘£„' => 'ð‘¢¤',
  'ð‘£…' => 'ð‘¢¥',
  'ð‘£†' => 'ð‘¢¦',
  'ð‘£‡' => 'ð‘¢§',
  'ð‘£ˆ' => 'ð‘¢¨',
  'ð‘£‰' => 'ð‘¢©',
  'ð‘£Š' => 'ð‘¢ª',
  'ð‘£‹' => 'ð‘¢«',
  'ð‘£Œ' => 'ð‘¢¬',
  'ð‘£�' => 'ð‘¢­',
  'ð‘£Ž' => 'ð‘¢®',
  'ð‘£�' => 'ð‘¢¯',
  'ð‘£�' => 'ð‘¢°',
  'ð‘£‘' => 'ð‘¢±',
  'ð‘£’' => 'ð‘¢²',
  'ð‘£“' => 'ð‘¢³',
  'ð‘£”' => 'ð‘¢´',
  'ð‘£•' => 'ð‘¢µ',
  'ð‘£–' => 'ð‘¢¶',
  'ð‘£—' => 'ð‘¢·',
  'ð‘£˜' => 'ð‘¢¸',
  'ð‘£™' => 'ð‘¢¹',
  'ð‘£š' => 'ð‘¢º',
  'ð‘£›' => 'ð‘¢»',
  'ð‘£œ' => 'ð‘¢¼',
  'ð‘£�' => 'ð‘¢½',
  'ð‘£ž' => 'ð‘¢¾',
  'ð‘£Ÿ' => 'ð‘¢¿',
);

$result =& $data;
unset($data);

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\Polyfill\Mbstring;

/**
 * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
 *
 * Implemented:
 * - mb_chr                  - Returns a specific character from its Unicode code point
 * - mb_convert_encoding     - Convert character encoding
 * - mb_convert_variables    - Convert character code in variable(s)
 * - mb_decode_mimeheader    - Decode string in MIME header field
 * - mb_encode_mimeheader    - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
 * - mb_decode_numericentity - Decode HTML numeric string reference to character
 * - mb_encode_numericentity - Encode character to HTML numeric string reference
 * - mb_convert_case         - Perform case folding on a string
 * - mb_detect_encoding      - Detect character encoding
 * - mb_get_info             - Get internal settings of mbstring
 * - mb_http_input           - Detect HTTP input character encoding
 * - mb_http_output          - Set/Get HTTP output character encoding
 * - mb_internal_encoding    - Set/Get internal character encoding
 * - mb_list_encodings       - Returns an array of all supported encodings
 * - mb_ord                  - Returns the Unicode code point of a character
 * - mb_output_handler       - Callback function converts character encoding in output buffer
 * - mb_scrub                - Replaces ill-formed byte sequences with substitute characters
 * - mb_strlen               - Get string length
 * - mb_strpos               - Find position of first occurrence of string in a string
 * - mb_strrpos              - Find position of last occurrence of a string in a string
 * - mb_strtolower           - Make a string lowercase
 * - mb_strtoupper           - Make a string uppercase
 * - mb_substitute_character - Set/Get substitution character
 * - mb_substr               - Get part of string
 * - mb_stripos              - Finds position of first occurrence of a string within another, case insensitive
 * - mb_stristr              - Finds first occurrence of a string within another, case insensitive
 * - mb_strrchr              - Finds the last occurrence of a character in a string within another
 * - mb_strrichr             - Finds the last occurrence of a character in a string within another, case insensitive
 * - mb_strripos             - Finds position of last occurrence of a string within another, case insensitive
 * - mb_strstr               - Finds first occurrence of a string within anothers
 * - mb_strwidth             - Return width of string
 * - mb_substr_count         - Count the number of substring occurrences
 *
 * Not implemented:
 * - mb_convert_kana         - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
 * - mb_ereg_*               - Regular expression with multibyte support
 * - mb_parse_str            - Parse GET/POST/COOKIE data and set global variable
 * - mb_preferred_mime_name  - Get MIME charset string
 * - mb_regex_encoding       - Returns current encoding for multibyte regex as string
 * - mb_regex_set_options    - Set/Get the default options for mbregex functions
 * - mb_send_mail            - Send encoded mail
 * - mb_split                - Split multibyte string using regular expression
 * - mb_strcut               - Get part of string
 * - mb_strimwidth           - Get truncated string with specified width
 *
 * @author Nicolas Grekas <p@tchwork.com>
 *
 * @internal
 */
final class Mbstring
{
    const MB_CASE_FOLD = PHP_INT_MAX;

    private static $encodingList = array('ASCII', 'UTF-8');
    private static $language = 'neutral';
    private static $internalEncoding = 'UTF-8';
    private static $caseFold = array(
        array('Âµ','Å¿',"\xCD\x85",'Ï‚',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"),
        array('Î¼','s','Î¹',       'Ïƒ','Î²',       'Î¸',       'Ï†',       'Ï€',       'Îº',       'Ï�',       'Îµ',       "\xE1\xB9\xA1",'Î¹'),
    );

    public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
    {
        if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
            $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
        } else {
            $fromEncoding = self::getEncoding($fromEncoding);
        }

        $toEncoding = self::getEncoding($toEncoding);

        if ('BASE64' === $fromEncoding) {
            $s = base64_decode($s);
            $fromEncoding = $toEncoding;
        }

        if ('BASE64' === $toEncoding) {
            return base64_encode($s);
        }

        if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
            if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
                $fromEncoding = 'Windows-1252';
            }
            if ('UTF-8' !== $fromEncoding) {
                $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
            }

            return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s);
        }

        if ('HTML-ENTITIES' === $fromEncoding) {
            $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8');
            $fromEncoding = 'UTF-8';
        }

        return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
    }

    public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null)
    {
        $vars = array(&$a, &$b, &$c, &$d, &$e, &$f);

        $ok = true;
        array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
            if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
                $ok = false;
            }
        });

        return $ok ? $fromEncoding : false;
    }

    public static function mb_decode_mimeheader($s)
    {
        return iconv_mime_decode($s, 2, self::$internalEncoding);
    }

    public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
    {
        trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING);
    }

    public static function mb_decode_numericentity($s, $convmap, $encoding = null)
    {
        if (null !== $s && !is_scalar($s) && !(is_object($s) && method_exists($s, '__toString'))) {
            trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
            return null;
        }

        if (!is_array($convmap) || !$convmap) {
            return false;
        }

        if (null !== $encoding && !is_scalar($encoding)) {
            trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
            return '';  // Instead of null (cf. mb_encode_numericentity).
        }

        $s = (string) $s;
        if ('' === $s) {
            return '';
        }

        $encoding = self::getEncoding($encoding);

        if ('UTF-8' === $encoding) {
            $encoding = null;
            if (!preg_match('//u', $s)) {
                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
            }
        } else {
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
        }

        $cnt = floor(count($convmap) / 4) * 4;

        for ($i = 0; $i < $cnt; $i += 4) {
            // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
            $convmap[$i] += $convmap[$i + 2];
            $convmap[$i + 1] += $convmap[$i + 2];
        }

        $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
            $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
            for ($i = 0; $i < $cnt; $i += 4) {
                if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
                    return Mbstring::mb_chr($c - $convmap[$i + 2]);
                }
            }
            return $m[0];
        }, $s);

        if (null === $encoding) {
            return $s;
        }

        return iconv('UTF-8', $encoding.'//IGNORE', $s);
    }

    public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
    {
        if (null !== $s && !is_scalar($s) && !(is_object($s) && method_exists($s, '__toString'))) {
            trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
            return null;
        }

        if (!is_array($convmap) || !$convmap) {
            return false;
        }

        if (null !== $encoding && !is_scalar($encoding)) {
            trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
            return null;  // Instead of '' (cf. mb_decode_numericentity).
        }

        if (null !== $is_hex && !is_scalar($is_hex)) {
            trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.gettype($s).' given', E_USER_WARNING);
            return null;
        }

        $s = (string) $s;
        if ('' === $s) {
            return '';
        }

        $encoding = self::getEncoding($encoding);

        if ('UTF-8' === $encoding) {
            $encoding = null;
            if (!preg_match('//u', $s)) {
                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
            }
        } else {
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
        }

        static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);

        $cnt = floor(count($convmap) / 4) * 4;
        $i = 0;
        $len = strlen($s);
        $result = '';

        while ($i < $len) {
            $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
            $uchr = substr($s, $i, $ulen);
            $i += $ulen;
            $c = self::mb_ord($uchr);

            for ($j = 0; $j < $cnt; $j += 4) {
                if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
                    $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
                    $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
                    continue 2;
                }
            }
            $result .= $uchr;
        }

        if (null === $encoding) {
            return $result;
        }

        return iconv('UTF-8', $encoding.'//IGNORE', $result);
    }

    public static function mb_convert_case($s, $mode, $encoding = null)
    {
        $s = (string) $s;
        if ('' === $s) {
            return '';
        }

        $encoding = self::getEncoding($encoding);

        if ('UTF-8' === $encoding) {
            $encoding = null;
            if (!preg_match('//u', $s)) {
                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
            }
        } else {
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
        }

        if (MB_CASE_TITLE == $mode) {
            $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s);
            $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s);
        } else {
            if (MB_CASE_UPPER == $mode) {
                static $upper = null;
                if (null === $upper) {
                    $upper = self::getData('upperCase');
                }
                $map = $upper;
            } else {
                if (self::MB_CASE_FOLD === $mode) {
                    $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s);
                }

                static $lower = null;
                if (null === $lower) {
                    $lower = self::getData('lowerCase');
                }
                $map = $lower;
            }

            static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);

            $i = 0;
            $len = strlen($s);

            while ($i < $len) {
                $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
                $uchr = substr($s, $i, $ulen);
                $i += $ulen;

                if (isset($map[$uchr])) {
                    $uchr = $map[$uchr];
                    $nlen = strlen($uchr);

                    if ($nlen == $ulen) {
                        $nlen = $i;
                        do {
                            $s[--$nlen] = $uchr[--$ulen];
                        } while ($ulen);
                    } else {
                        $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
                        $len += $nlen - $ulen;
                        $i   += $nlen - $ulen;
                    }
                }
            }
        }

        if (null === $encoding) {
            return $s;
        }

        return iconv('UTF-8', $encoding.'//IGNORE', $s);
    }

    public static function mb_internal_encoding($encoding = null)
    {
        if (null === $encoding) {
            return self::$internalEncoding;
        }

        $encoding = self::getEncoding($encoding);

        if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) {
            self::$internalEncoding = $encoding;

            return true;
        }

        return false;
    }

    public static function mb_language($lang = null)
    {
        if (null === $lang) {
            return self::$language;
        }

        switch ($lang = strtolower($lang)) {
            case 'uni':
            case 'neutral':
                self::$language = $lang;

                return true;
        }

        return false;
    }

    public static function mb_list_encodings()
    {
        return array('UTF-8');
    }

    public static function mb_encoding_aliases($encoding)
    {
        switch (strtoupper($encoding)) {
            case 'UTF8':
            case 'UTF-8':
                return array('utf8');
        }

        return false;
    }

    public static function mb_check_encoding($var = null, $encoding = null)
    {
        if (null === $encoding) {
            if (null === $var) {
                return false;
            }
            $encoding = self::$internalEncoding;
        }

        return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var);
    }

    public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
    {
        if (null === $encodingList) {
            $encodingList = self::$encodingList;
        } else {
            if (!is_array($encodingList)) {
                $encodingList = array_map('trim', explode(',', $encodingList));
            }
            $encodingList = array_map('strtoupper', $encodingList);
        }

        foreach ($encodingList as $enc) {
            switch ($enc) {
                case 'ASCII':
                    if (!preg_match('/[\x80-\xFF]/', $str)) {
                        return $enc;
                    }
                    break;

                case 'UTF8':
                case 'UTF-8':
                    if (preg_match('//u', $str)) {
                        return 'UTF-8';
                    }
                    break;

                default:
                    if (0 === strncmp($enc, 'ISO-8859-', 9)) {
                        return $enc;
                    }
            }
        }

        return false;
    }

    public static function mb_detect_order($encodingList = null)
    {
        if (null === $encodingList) {
            return self::$encodingList;
        }

        if (!is_array($encodingList)) {
            $encodingList = array_map('trim', explode(',', $encodingList));
        }
        $encodingList = array_map('strtoupper', $encodingList);

        foreach ($encodingList as $enc) {
            switch ($enc) {
                default:
                    if (strncmp($enc, 'ISO-8859-', 9)) {
                        return false;
                    }
                case 'ASCII':
                case 'UTF8':
                case 'UTF-8':
            }
        }

        self::$encodingList = $encodingList;

        return true;
    }

    public static function mb_strlen($s, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
            return strlen($s);
        }

        return @iconv_strlen($s, $encoding);
    }

    public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
            return strpos($haystack, $needle, $offset);
        }

        $needle = (string) $needle;
        if ('' === $needle) {
            trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING);

            return false;
        }

        return iconv_strpos($haystack, $needle, $offset, $encoding);
    }

    public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
            return strrpos($haystack, $needle, $offset);
        }

        if ($offset != (int) $offset) {
            $offset = 0;
        } elseif ($offset = (int) $offset) {
            if ($offset < 0) {
                $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
                $offset = 0;
            } else {
                $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
            }
        }

        $pos = iconv_strrpos($haystack, $needle, $encoding);

        return false !== $pos ? $offset + $pos : false;
    }

    public static function mb_strtolower($s, $encoding = null)
    {
        return self::mb_convert_case($s, MB_CASE_LOWER, $encoding);
    }

    public static function mb_strtoupper($s, $encoding = null)
    {
        return self::mb_convert_case($s, MB_CASE_UPPER, $encoding);
    }

    public static function mb_substitute_character($c = null)
    {
        if (0 === strcasecmp($c, 'none')) {
            return true;
        }

        return null !== $c ? false : 'none';
    }

    public static function mb_substr($s, $start, $length = null, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
            return substr($s, $start, null === $length ? 2147483647 : $length);
        }

        if ($start < 0) {
            $start = iconv_strlen($s, $encoding) + $start;
            if ($start < 0) {
                $start = 0;
            }
        }

        if (null === $length) {
            $length = 2147483647;
        } elseif ($length < 0) {
            $length = iconv_strlen($s, $encoding) + $length - $start;
            if ($length < 0) {
                return '';
            }
        }

        return (string) iconv_substr($s, $start, $length, $encoding);
    }

    public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
    {
        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);

        return self::mb_strpos($haystack, $needle, $offset, $encoding);
    }

    public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
    {
        $pos = self::mb_stripos($haystack, $needle, 0, $encoding);

        return self::getSubpart($pos, $part, $haystack, $encoding);
    }

    public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
            return strrchr($haystack, $needle, $part);
        }
        $needle = self::mb_substr($needle, 0, 1, $encoding);
        $pos = iconv_strrpos($haystack, $needle, $encoding);

        return self::getSubpart($pos, $part, $haystack, $encoding);
    }

    public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
    {
        $needle = self::mb_substr($needle, 0, 1, $encoding);
        $pos = self::mb_strripos($haystack, $needle, $encoding);

        return self::getSubpart($pos, $part, $haystack, $encoding);
    }

    public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
    {
        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);

        return self::mb_strrpos($haystack, $needle, $offset, $encoding);
    }

    public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
    {
        $pos = strpos($haystack, $needle);
        if (false === $pos) {
            return false;
        }
        if ($part) {
            return substr($haystack, 0, $pos);
        }

        return substr($haystack, $pos);
    }

    public static function mb_get_info($type = 'all')
    {
        $info = array(
            'internal_encoding' => self::$internalEncoding,
            'http_output' => 'pass',
            'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
            'func_overload' => 0,
            'func_overload_list' => 'no overload',
            'mail_charset' => 'UTF-8',
            'mail_header_encoding' => 'BASE64',
            'mail_body_encoding' => 'BASE64',
            'illegal_chars' => 0,
            'encoding_translation' => 'Off',
            'language' => self::$language,
            'detect_order' => self::$encodingList,
            'substitute_character' => 'none',
            'strict_detection' => 'Off',
        );

        if ('all' === $type) {
            return $info;
        }
        if (isset($info[$type])) {
            return $info[$type];
        }

        return false;
    }

    public static function mb_http_input($type = '')
    {
        return false;
    }

    public static function mb_http_output($encoding = null)
    {
        return null !== $encoding ? 'pass' === $encoding : 'pass';
    }

    public static function mb_strwidth($s, $encoding = null)
    {
        $encoding = self::getEncoding($encoding);

        if ('UTF-8' !== $encoding) {
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
        }

        $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);

        return ($wide << 1) + iconv_strlen($s, 'UTF-8');
    }

    public static function mb_substr_count($haystack, $needle, $encoding = null)
    {
        return substr_count($haystack, $needle);
    }

    public static function mb_output_handler($contents, $status)
    {
        return $contents;
    }

    public static function mb_chr($code, $encoding = null)
    {
        if (0x80 > $code %= 0x200000) {
            $s = chr($code);
        } elseif (0x800 > $code) {
            $s = chr(0xC0 | $code >> 6).chr(0x80 | $code & 0x3F);
        } elseif (0x10000 > $code) {
            $s = chr(0xE0 | $code >> 12).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F);
        } else {
            $s = chr(0xF0 | $code >> 18).chr(0x80 | $code >> 12 & 0x3F).chr(0x80 | $code >> 6 & 0x3F).chr(0x80 | $code & 0x3F);
        }

        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
            $s = mb_convert_encoding($s, $encoding, 'UTF-8');
        }

        return $s;
    }

    public static function mb_ord($s, $encoding = null)
    {
        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
            $s = mb_convert_encoding($s, 'UTF-8', $encoding);
        }

        $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
        if (0xF0 <= $code) {
            return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
        }
        if (0xE0 <= $code) {
            return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
        }
        if (0xC0 <= $code) {
            return (($code - 0xC0) << 6) + $s[2] - 0x80;
        }

        return $code;
    }

    private static function getSubpart($pos, $part, $haystack, $encoding)
    {
        if (false === $pos) {
            return false;
        }
        if ($part) {
            return self::mb_substr($haystack, 0, $pos, $encoding);
        }

        return self::mb_substr($haystack, $pos, null, $encoding);
    }

    private static function html_encoding_callback(array $m)
    {
        $i = 1;
        $entities = '';
        $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8'));

        while (isset($m[$i])) {
            if (0x80 > $m[$i]) {
                $entities .= chr($m[$i++]);
                continue;
            }
            if (0xF0 <= $m[$i]) {
                $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
            } elseif (0xE0 <= $m[$i]) {
                $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
            } else {
                $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
            }

            $entities .= '&#'.$c.';';
        }

        return $entities;
    }

    private static function title_case_lower(array $s)
    {
        return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8');
    }

    private static function title_case_upper(array $s)
    {
        return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8');
    }

    private static function getData($file)
    {
        if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
            return require $file;
        }

        return false;
    }

    private static function getEncoding($encoding)
    {
        if (null === $encoding) {
            return self::$internalEncoding;
        }

        $encoding = strtoupper($encoding);

        if ('8BIT' === $encoding || 'BINARY' === $encoding) {
            return 'CP850';
        }
        if ('UTF8' === $encoding) {
            return 'UTF-8';
        }

        return $encoding;
    }
}
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Mbstring as p;

if (!function_exists('mb_strlen')) {
    define('MB_CASE_UPPER', 0);
    define('MB_CASE_LOWER', 1);
    define('MB_CASE_TITLE', 2);

    function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); }
    function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); }
    function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); }
    function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); }
    function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); }
    function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); }
    function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); }
    function mb_language($lang = null) { return p\Mbstring::mb_language($lang); }
    function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); }
    function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); }
    function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); }
    function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); }
    function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); }
    function mb_parse_str($s, &$result = array()) { parse_str($s, $result); }
    function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); }
    function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); }
    function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); }
    function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); }
    function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); }
    function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); }
    function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); }
    function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); }
    function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); }
    function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); }
    function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); }
    function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); }
    function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); }
    function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); }
    function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); }
    function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); }
    function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); }
    function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); }
    function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); }
    function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); }
}
if (!function_exists('mb_chr')) {
    function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); }
    function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); }
    function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); }
}
{
    "name": "symfony/polyfill-mbstring",
    "type": "library",
    "description": "Symfony polyfill for the Mbstring extension",
    "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Nicolas Grekas",
            "email": "p@tchwork.com"
        },
        {
            "name": "Symfony Community",
            "homepage": "https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=5.3.3"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" },
        "files": [ "bootstrap.php" ]
    },
    "suggest": {
        "ext-mbstring": "For best performance"
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "1.7-dev"
        }
    }
}
<?php
use com\linways\base\queue\TaskQueue;
require_once 'web.php';
putenv('AMS_CONFIG=test');

$RABBITMQ_HOST = 'localhost';
$RABBITMQ_USER = 'guest';
$RABBITMQ_PASSWORD = 'guest';

$executeHooks = function($data){
    echo "EXECUTE HOOKS working";
    
    // require_once($data->data->CONF);
    // require_once($data->data->DB_PROFESIONAL_CONFIG);
    // // require_once __DIR__ . '/vendor/linways/ams-addons/bootstrap/attachedHooks.php';
    // global $hooks;
    // $hooks->do_action('test_action', $data->data->param1);
  };
  
  $handleMessage = function($msg){
    echo " [x] Received ", $msg->body, "\n";
    $data = json_decode($msg->body);
    switch ($data->command) {
      case 'EXECUTE QUERY':
        global $executeHooks;
        $executeHooks($data);
        break;
      default:
        echo "Invalid command";
    }
    echo " [x] Done", "\n"; 
    TaskQueue::sendACK($msg);
  };
  

$taskQueue = new TaskQueue();

$taskQueue->basicConsume($handleMessage);<?php
function testHook(){
    echo "This is from a test hook";
}

$hooks->add_action('test_action', 'testHook');
<?php
require_once 'web.php';
use com\linways\base\hooks\LinwaysHooks;

define('HOOKS_LOADER', __DIR__ . '/testHook.php');

$hooks = LinwaysHooks::getInstance();

$hooks->do_action('test_action');
<?php
require_once "../vendor/autoload.php";
// $dbPath = '/var/www/html/ams-autonomous-core/test/db_config/db_ams_autonomous_conf.php';
// putenv("DB_AMS_AUTONOMOUS_CONFIG=$dbPath");

<?php
use com\linways\base\dto\Email;
use com\linways\base\dto\EmailTo;
use com\linways\base\service\EmailService;
use com\linways\base\dto\EmailFrom;

require_once 'web.php';


$emailService = EmailService::getInstance();

try {
    $SMTPHost = "smtp.gmail.com";
    $SMTPPort = 465;
    $mailID = "linwaysdev@gmail.com";
    $mailpassword =  "linwaysdev@2017";
    
    $email = new Email();
    
    $emailFrom = new EmailFrom();
    $emailFrom->email = "linwaysdev@gmail.com";
    
    $emailTo = new EmailTo();
    $emailTo->name = "Arun";
    $emailTo->email = "aruntg@linways.com";
    
    $email->from = $emailFrom;
    $email->to = $emailTo;
    
    $email->body = "Txxsdsfest Arun";
    $email->subject = "Treat";
    
    var_dump($emailService->sendEmail($email));
} catch (Exception $e) {
    
    var_dump($e);
}<?php
require_once 'web.php';

use com\linways\base\queue\TaskQueue;
putenv('AMS_CONFIG=test');

$RABBITMQ_HOST = 'localhost';
$RABBITMQ_USER = 'guest';
$RABBITMQ_PASSWORD = 'guest';


class TaskQueueTest extends TaskQueue{

    function __construct($conf){
        $conf = [getenv('AMS_CONFIG')];
        parent::__construct($conf);
    }
}

$conf = getenv('AMS_CONFIG');
$taskQueue = new TaskQueueTest($conf);

$taskQueue->enqueue("EXECUTE QUERY", ["param1" => "rohith"]);{
  "name": "linways/linways-base",
  "description": "Linways base",
  "require": {
    "php": ">=5.4.0",
    "php-amqplib/php-amqplib": "^2.7",
    "voku/php-hooks": "^0.2.11"

  },
  "minimum-stability": "stable",
  "autoload": {
    "psr-4": {
      "com\\linways\\base\\": "src/com/linways/base"
    }
  },

  "require-dev": {
    "phpunit/phpunit": "^5.7"
  }
}
<?php
namespace com\linways\base\dto;

use com\linways\base\dto\BaseDTO;

class Email extends BaseDTO
{

    /**
     * Email From : Object of EmailFrom
     * 
     * @var EmailFrom
     */
    public $from;

    /**
     * Email To : Object of EmailTo
     * 
     * @var EmailTo
     */
    public $to;
    /**
     * Email To : Object of EmailTo
     *
     * @var EmailTo
     */
    public $ccList;
    /**
     * Email To : Object of EmailTo
     *
     * @var array [EmailTo]
     */
    public $toList;
    // /**
    // * Email To Addresses : List of EmailTo Objects
    // * @var array of EmailTo
    // */
    // public $to = [];
    /**
     * Email Subject
     * 
     * @var string
     */
    public $subject;

    /**
     *
     * @var string
     */
    public $body;

    /**
     * Word wrap default is 50
     * 
     * @var int
     */
    public $wordWrap = 50;

    /**
     * Set message type to HTML/Plain.Default is HTML.
     * 
     * @var int
     */
    public $isHtml = TRUE;

    /**
     * Supported mailer type to send mail: MAIL,SEND_MAIL,SMTP
     * 
     * @var string
     */
    const MAIL = "mail";

    const SEND_MAIL = "sendmail";

    const SMTP = "smtp";

    public $mailerType = self::SMTP;
    
    /**
     * Constructor for email
     * @param EmailFrom $from
     * @param array $toList ( [EmailTo])
     * @param string $subject
     * @param string $body
     */
    public function __construct($from = null,$toList = null,$subject = null,$body = null)
    {
        $this->from = $from;
        $this->toList = $toList;
        $this->body = $body;
        $this->subject = $subject;
    }
}<?php
namespace com\linways\base\dto;

use com\linways\base\dto\BaseDTO;

class EmailFrom extends BaseDTO
{

    public $email;

    public $name;

    public function __construct($email = null, $name  = null)
    {
        $this->email = $email;
        $this->name = $name;
    }
}<?php
namespace com\linways\base\dto;

use com\linways\base\dto\BaseDTO;

class EmailCC extends BaseDTO
{

    public $email;

    public $name;

    public function __construct($email = null, $name = NULL)
    {
        $this->email = $email;
        $this->name = $name;
    }
}<?php
namespace com\linways\base\dto;

class PageSupport
{

    const DEFAULT_PAGE_SIZE = 5000;

    /**
     * Starting page no
     * 
     * @var int
     */
    public $startIndex = 0;

    /**
     * count of no of records
     * 
     * @var int
     */
    public $endIndex = self::DEFAULT_PAGE_SIZE;

    public $pageSize = self::DEFAULT_PAGE_SIZE;
    
}<?php
namespace com\linways\base\dto;

class BaseDTO
{

    public $createdBy;

    public $createdDate;

    public $updatedDate;

    public $updatedBy;

    public function __construct($resultObject = NULL)
    {
        if ($resultObject != NULL) {
            $this->createdBy = $resultObject->createdBy;
            $this->createdDate = $resultObject->createdDate;
            $this->updatedDate = $resultObject->updatedDate;
            $this->updatedBy = $resultObject->updatedBy;
        }
    }
}<?php
namespace com\linways\base\dto;
use com\linways\base\dto\BaseDTO;

class EmailTo extends BaseDTO
{
    public $email;
    public $name;
    
    public function __construct($email = null,$name = NULL)
    {
        $this->email = $email;
        $this->name = $name;
    }
}<?php
namespace com\linways\base\dto;

class AjaxPaginator
{
    
    // Maximum data will be displayed on page
    public $maxDataPerPage;
    
    // max page number link in a page
    public $maxTotalLinkPage;

    /**
     *
     * @var integer
     */
    public $totalRecords = 0;

    /**
     * Render paging components
     * @param number $start
     * @return string
     */
    public function render($start = 0)
    {
        $totalData = $this->totalRecords;
        $totalPage = ceil($totalData / $this->maxDataPerPage);
        
        if ($totalPage > 1) {
            $nav = "<ul class='pagination mtm mbm'>";
            // Specify page index.
            $iPage = ($start / $this->maxDataPerPage) + 1;
            
            // Specify start and stop index of page will be displayed as link
            if (($iPage + $this->maxTotalLinkPage - 1) > $totalPage) {
                $startPage = $totalPage - $this->maxTotalLinkPage;
                $endPage = $totalPage;
            } else {
                $startPage = $iPage - 1;
                $endPage = $startPage + $this->maxTotalLinkPage;
            }
            if ($startPage <= 0)
                $startPage = 1;
            
            if ($totalPage > $this->maxTotalLinkPage) {
                if ($startPage == 1)
                    $nav .= '<li class="disabled"><a href="#"><< First</a></li>';
                else
                    $nav .= "<li><a href=\"#\" onClick=\"jumpPage(0); return false;\"><< First</a></li> ";
            }
            
            for ($i = $startPage; $i <= $endPage; $i ++) {
                $startVal = ($i - 1) * $this->maxDataPerPage;
                if ($startVal == $start)
                    $nav .= '<li class="active"><a href="#">' . $i . '</a></li>';
                else
                    $nav .= "<li><a href=\"#\" onClick=\"jumpPage($startVal); return false;\">$i </a></li>";
            }
            
            if ($totalPage > $this->maxTotalLinkPage) {
                $startVal = ($totalPage - 1) * $this->maxDataPerPage;
                if ($startPage == ($totalPage - $this->maxTotalLinkPage))
                    $nav .= '<li class="disabled"><a href="#">Last >></a></li>';
                else
                    $nav .= "<li><a href=\"#\" onClick=\"jumpPage($startVal); return false;\">Last >></a></li>";
            }
            $nav .= '</ul>';
            return $nav;
        }
    }
}<?php
namespace com\linways\base\dto;

class MySqlResult
{

    public $sqlResult;

    public $id;
}<?php
namespace com\linways\base\test;

use com\linways\base\exception\CoreException;
use PHPUnit\Framework\TestCase;
/**
 * Base class for all unittest cases.
 * @author Rohith Bhaskaran
 */
class BaseTestCase extends TestCase
{

  /**
   * @return [type] Mysqli database connection
   */
  private function getConnection(){
    // Reading db config for runnning tests from environment
    $dbconfigFilePath = getenv('TEST_DB_CONFIG');
    if (empty($dbconfigFilePath)) {
        throw new \Exception("Database config for testing is not found. Please set env `TEST_DB_CONFIG`");
    }
    require $dbconfigFilePath;
    if (empty ( $DB_HOST ) || empty ( $DB_USER ) || empty ( $DB_PASSWD ) || empty ( $DB_NAME )) {
        throw new CoreException ( CoreException::DB_CONFIG_NOT_SET, 'DB config for test not set properly. Please set `$DB_HOST`, `$DB_USER`, `$DB_PASSWD` and `$DB_NAME`' );
    }
    $connection = mysqli_connect($DB_HOST, $DB_USER, $DB_PASSWD, $DB_NAME);
    return $connection;
  }

  /**
   * Helper function to assertDatabaseHas and assertDatabaseHasNot
   * @param  string $tableName Name of the table
   * @param  array $map        A assoc array with atribute name as
   *                           key and attribute value as value.
   * @return object            {result, query} Object with result and prepared query
   */
  public function assertDatabaseHelper($tableName, $map)
  {
      $returnObject = new \stdClass();
      if (! isset($tableName) || ! isset($map)) {
          throw new CoreException('Database name and field map is required');
      }
      $connection = $this->getConnection();
      $query = "SELECT * from $tableName where 1=1 ";
      foreach ($map as $key => $value) {
          $query .= 'and '.$key . "='" . $value . "'";
      }
      $result = mysqli_query($connection, $query);
      mysqli_close($connection);
      $returnObject->result = $result;
      $returnObject->query = $query;
      return $returnObject;
  }

    /**
     * Assert a given database table contain a row with attributes specified in $map
     * @param  string $tableName Name of the table
     * @param  array $map        A assoc array with atribute name as
     *                           key and attribute value as value.
     */
    public function assertDatabaseHas($tableName, $map)
    {
        $result = $this->assertDatabaseHelper($tableName, $map);
        $message = "\033[31m Expected data not found on table `$tableName`. \n Query:$result->query \n\033[0m";
        $this->assertTrue(mysqli_num_rows($result->result) > 0, $message);
    }


    /**
     * Assert a given database table does not contain a row with attributes specified in $map
     * @param  string $tableName Name of the table
     * @param  array $map        A assoc array with atribute name as
     *                           key and attribute value as value.
     */
    public function assertDatabaseHasNot($tableName, $map)
    {
        $result = $this->assertDatabaseHelper($tableName, $map);
        $message = "\033[31m Expected data not found on table `$tableName`. \nQuery: $result->query \n\033[0m";
        $this->assertEquals(0, mysqli_num_rows($result->result), $message);
    }



    /**
     * Trucate a given database table
     * @param  string $tableName  Name of the table
     */
    public function clearDBTable($tableName)
    {
        if (! isset($tableName)) {
            throw new CoreException('table name  is required');
        }
        $connection = $connection = $this->getConnection();
        mysqli_query($connection, "SET FOREIGN_KEY_CHECKS = 0");
        mysqli_query($connection, "TRUNCATE table $tableName");
        mysqli_query($connection, "SET FOREIGN_KEY_CHECKS = 1");
    }
}
<?php
namespace com\linways\base\messages;

class CommonMessages
{

    const NO_ACTION_DEFINED = "No action defined!";

    const FETCH_ERROR = "An error occured while fetching the data.Please contact systemadminsitrator!";

    const SAVE_FAILED_ERROR = "An error occured while saving the details.Please try again or contact system administrator!";

    const DELETE_SUCCESS = "Record deleted successfully!.";

    const DELETE_ERROR = "An error occured while deleting the record.Please try again or contact system administrator!.";
}<?php
namespace com\linways\base\response;
/**
 * @author arun
 *
 */
class BaseResponse
{
    /**
     * used for pagination
     * @var integer
     */
    public $totalRecords;
    
}<?php

namespace com\linways\base\cache;
/**
 * LinCache - Server memory cache manager
 * 
 * @author arun
 *
 */
class LinCache {
	private $cache = null;
	const DEFAULT_SERVER_IP = "127.0.0.1";
	const DEFAULT_PORT = 11211;
	
	/**
	 * If server ip and port is  not set then setting default config 
	 * @param string $serverIP
	 * @param int $port
	 */
	public function __construct($serverIP = NULL, $port = NULL) {
		$this->init ( $serverIP, $port );
	}
	/**
	 * Inititalize the mem server
	 * @param string $serverIP
	 * @param int $port
	 */
	public function init($serverIP = NULL, $port = NULL) {
		$serverIP = empty ( $serverIP ) ? self::DEFAULT_SERVER_IP : $serverIP;
		$port = empty ( $port ) ? self::DEFAULT_PORT : $port;
		
		$this->cache = new \Memcached ();
		$this->cache->addServer ( $serverIP, $port );
	}
	
	/**
	 * Get value from cache
	 * @param string $key
	 * @return object
	 */
	public function getValue($key) {
		$value = null;
		$result = $this->cache->get ( $key );
		
		$value = ! $result ? null : $result;
		
		return $value;
	}
	/**
	 * Set value in cache
	 * @param string $key
	 * @param object $value
	 */
	public function setValue($key, $value) {
		$this->cache->set ( $key, $value );
	}
}<?php
namespace com\linways\base\connection;

use com\linways\base\exception\CoreException;
use com\linways\base\util\MySqlUtil;

/**
 * MySql Connector class for managing mysql connection
 * <p>This class is responsible for supplying DB connection properties.Which has to be set externally
 * 
 * </p>@author arun
 *        
 */
class MySqlConnector 
{
    /**
     * To store the instance of base service. To use multiple connection
     * @var object  (Object of BaseService of project)
     */
    public $instanceOf;
    /**
     * DataBase Host eg:localhost
     * @var string
     */
    public $DB_HOST;
    /**
     * DataBase username 
     * @var string
     */
    public $DB_USER;
    /**
     * DataBase password
     * @var string
     */
    public $DB_PASSWD;
    /**
     * DataBase name
     * @var string
     */
    public $DB_NAME;
    private static $_instance = null;

    public  $connection = NULL;
    // /Condition 2 - Locked down the constructor
    private function __construct()
    {}
    // Prevent any oustide instantiation of this class
    
    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {}
    // Prevent any copy of this object
    
    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (! is_object(self::$_instance))
        {
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();
        }
        
        return self::$_instance;
    }
 
    /**
     * Get DB Connection
     *
     * @param
     *            $dbConfigPath
     * @throws CoreException
     * @return NULL|\mysqli
     */
    public function getConnection()
    {
        
        // Create connection
        // If connection exist reuse the connection else create new connection
        if ($this->connection == NULL) {
            // check DB configurations
            
//             $dbconfigFilePath = getenv($dbConfigPath);
            
//             // check filed configured correctly
//             if (empty($dbconfigFilePath)) {
//                 throw new CoreException(CoreException::DB_CONFIG_FILE_PATH_NOT_DEFINED_IN_SERVER, "Database configuration '$dbConfigPath' not configured in server conf!.");
//             }
            
//             // include db configuration file to load connection properties
//             require_once $dbconfigFilePath;
            
            if (empty($this->DB_HOST) || empty($this->DB_USER) || empty($this->DB_PASSWD) || empty($this->DB_NAME)) {
                throw new CoreException(CoreException::DB_CONFIG_NOT_SET, "DB configuration connection variables not set properly");
            }
            
            $this->connection = new \mysqli($this->DB_HOST, $this->DB_USER, $this->DB_PASSWD, $this->DB_NAME);
        }
        
        // Check connection
        if ($this->connection->connect_error) {
            $this->connection = NULL;
            throw new CoreException(CoreException::CONNECTION_FAILED, $this->connection->connect_error);
        }
        return $this->connection;
    }

    /**
     * close db connection
     *
     * @param unknown $connection            
     */
    public function closeConnection()
    {
        // close connection if exist
        if ($this->connection) {
            MySqlUtil::closeConnection($this->connection);
        }
        
        $this->connection = NULL;
    }

    public function __destruct()
    {
        // close connection if exist
        $this->closeConnection();
    }
}<?php
namespace com\linways\base\connection;
/**
 * Interface for establishing mysqlConnection
 * <p> This interface must implmenet in subproject/projects to getconnection,which is used in MySqlQuery.</p>
 * @author arun
 *
 */
interface IMySqlConnector
{
    
    public function establishConnection();
}<?php
namespace com\linways\base\connection;

use com\linways\base\dto\MySqlResult;
use com\linways\base\exception\SQLException;
use com\linways\base\mapper\Result;
use com\linways\base\util\MapperUtil;
use com\linways\base\util\ObjectUtil;

class MySqlQuery implements IMySqlConnector

{

    /**
     *
     * @var \mysqli connection
     */
    public $connection;

    /**
     * Method for executing the mysql query
     *
     * @param string $sql
     *            : sql to execute
     * @param boolean $isReturnKey
     *            : If you
     *            need id in case of add operation specify
     *            true to get the last inserted id
     */
    protected function executeQuery($sql, $isReturnKey = FALSE)
    {
        $sqlresult = NULL;
        // Establish the connection by invoking establishConnection which is defined in IMySqlConnector .
        //
        $this->establishConnection();
        
        $result = mysqli_query($this->connection, $sql);
        
        try {
            $sqlresult = $this->processResult($result, $isReturnKey);
        } catch (\Exception $e) {
            throw $e;
        }
        
        return $sqlresult;
    }

    /**
     * Method for executing the mysql query and return result as object list
     *
     * @param string $sql
     *            : sql to execute
     * @param boolean $isReturnKey
     *            : If you
     *            need id in case of add operation specify
     *            true to get the last inserted id
     * @return Object List
     */
    protected function executeQueryForList($sql, $mapper = NULL)
    {
        $sqlresult = NULL;
        $objectList = [];
        // Establish the connection by invoking establishConnection which is defined in IMySqlConnector .
        //
        $this->establishConnection();
        
        $result = mysqli_query($this->connection, $sql);
        
        try {
            $sqlresult = $this->processResult($result);
            
            if (empty($mapper)) {
                $objectList = ObjectUtil::fetchObjectList($sqlresult);
            }
            else {
                $objectList = MapperUtil::fetchObjectList($sqlresult,$mapper);
            }
        } catch (\Exception $e) {
            throw $e;
        }
        
        return $objectList;
    }

    private function getConnection()
    {
        $this->establishConnection();
        return $this->connection;
    }

    /**
     * Method for executing the mysql query and return result as object
     *
     * @param string $sql
     *            : sql to execute
     * @param boolean $isReturnKey
     *            : If you
     *            need id in case of add operation specify
     *            true to get the last inserted id
     * @return Object - if $isReturnKey is TRUE then return id else queryresult object
     */
    protected function executeQueryForObject($sql, $isReturnKey = FALSE, $mapper = null)
    {
        $sqlresult = NULL;
        $object = NULL;
        // Establish the connection by invoking establishConnection which is defined in IMySqlConnector .
        //
        // $this->establishConnection();
        
        $result = mysqli_query($this->getConnection(), $sql);
        
        try {
            $sqlresult = $this->processResult($result, $isReturnKey);
            
            if ($isReturnKey) {
                $object = $sqlresult->id;
            } else {
                
                if ($mapper == null) {
                    $object = ObjectUtil::fetchObject($sqlresult);
                } else {
                    
                    $object = MapperUtil::fetchObject($sqlresult, $mapper);
                }
            }
        } catch (\Exception $e) {
            throw $e;
        }
        
        return $object;
    }

    /**
     * Process the mysql result
     *
     * @param unknown $mysqlResult            
     * @param unknown $isReturnKey            
     * @throws unknown
     * @return \com\linways\base\dto\MySqlResult
     */
    private function processResult($mysqlResult, $isReturnKey = FALSE)
    {
        $sqlresult = NULL;
        
        if (! $mysqlResult) {
            
            $sqlException = $this->parseSqlException();
            throw $sqlException;
        }
        
        $sqlresult = new MySqlResult();
        $sqlresult->sqlResult = $mysqlResult;
        $sqlresult->id = $isReturnKey ? mysqli_insert_id($this->connection) : NULL;
        
        return $sqlresult;
    }

    /**
     * Parse and create sql exception
     *
     * @return \com\linways\base\exception\SQLException
     */
    private function parseSqlException()
    {
        $errorCode = NULL;
        $errorMessage = mysqli_error($this->connection);
        switch (mysqli_errno($this->connection)) {
            case 1062:
                $errorCode = SQLException::DUPLICATE_ENTRY;
                break;
            
            default:
                // TO DO Add more errors
                $errorCode = mysqli_errno($this->connection);
                break;
        }
        return new SQLException($errorCode, $errorMessage);
    }

    /**
     * mysql real escape string
     *
     * @param string $string            
     */
    public function realEscapeString($string)
    {
        return mysqli_real_escape_string($this->getConnection(), trim($string));
    }
    /**
     * mysql real escape array of objects
     *
     * @param array $objArray
     */
    public  function realEscapeArray($objArray)
    {
    	if(!empty($objArray))
    	{
    		foreach ($objArray as $key => $object) {
    			$objArray[$key] = $this->realEscapeObject($object);
    		}
    
    	}
    	return $objArray;
    }
    /**
     * mysql real escape object
     *
     * @param object $object
     */
    public  function realEscapeObject($object)
    {
    	if(!empty($object))
    	{
    		foreach ($object as $key => $value) {
    			if(is_array($value))
    			{
    
    				$object->{$key} = $this->realEscapeArray($value);
    
    			}
    			else if(is_object($value)) {
    				$object->{$key} = $this->realEscapeObject($value);
    			}
    			else
    			{
    				$object->{$key} = $this->realEscapeString($value);
    			}
    			 
    		}
    
    
    	}
    	return $object;
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see \com\linways\base\connection\IMySqlConnector::establishConnection()
     */
    public function establishConnection()
    {
        // TODO: Auto-generated method stub
    }
}<?php
namespace com\linways\base\util;

/**
 *
 * @author arun
 *        
 */
class StringUtil
{

    /**
     * replace_tags replaces tags in the source string with data from the tags map.
     * The tags are identified by being wrapped in '{{' and '}}' i.e. '{{tag}}'.
     * If a tag value is not present in the tags map, it is replaced with an empty
     * string
     * 
     * @param string $string
     *            A string containing 1 or more tags wrapped in '{{}}'.Eg.Hi student {{studentName}}.Hi teacher {{teacherName}}
     * @param array $tags
     *            A map of key-value pairs used to replace tags.Eg. array('studentName'=>'Rohith','teacherName'=>'Bastin')
     * @param
     *            force_lower if true, converts matching tags in string via strtolower()
     *            before checking the tags map.
     * @return string The resulting string with all matching tags replaced.
     */
    public static function replace_tags($string, $tags, $force_lower = false)
    {
        return preg_replace_callback('/\\{\\{([^{}]+)\}\\}/', function ($matches) use ($force_lower, $tags) {
            $key = $force_lower ? strtolower($matches[1]) : $matches[1];
            return array_key_exists($key, $tags) ? $tags[$key] : '';
        }, $string);
    }
}<?php
namespace com\linways\base\util;

class CalendarUtil
{

    public static function getAllMonth()
    {
        $monthList = [];
        $months = array(
            '01' => array(
                'name' => 'January',
                'code' => 'Jan'
            ),
            '02' => array(
                'name' => 'February',
                'code' => 'Feb'
            ),
            '03' => array(
                'name' => 'March',
                'code' => 'Mar'
            ),
            '04' => array(
                'name' => 'April',
                'code' => 'Apr'
            ),
            '05' => array(
                'name' => 'May',
                'code' => 'May'
            ),
            '06' => array(
                'name' => 'June',
                'code' => 'Jun'
            ),
            '07' => array(
                'name' => 'July',
                'code' => 'Jul'
            ),
            '08' => array(
                'name' => 'August',
                'code' => 'Aug'
            ),
            '09' => array(
                'name' => 'September',
                'code' => 'Sep'
            ),
            '10' => array(
                'name' => 'October',
                'code' => 'Oct'
            ),
            '11' => array(
                'name' => 'November',
                'code' => 'Nov'
            ),
            '12' => array(
                'name' => 'December',
                'code' => 'Dec'
            )
        );
        foreach ($months as $num => $month) {
            $monthList[$num] = (object) $month;
        }
        
        return $monthList;
    }
}<?php
namespace com\linways\base\util;

class CommonUtil
{

    /**
     * method for addOrdinalNumberSuffix
     * 
     * @param string $num            
     * @return string
     */
    public static function addOrdinalNumberSuffix($num)
    {
        if (! in_array(($num % 100), array(
            11,
            12,
            13
        ))) {
            switch ($num % 10) {
                // Handle 1st, 2nd, 3rd
                case 1:
                    return $num . 'st';
                case 2:
                    return $num . 'nd';
                case 3:
                    return $num . 'rd';
            }
        }
        return $num . 'th';
    }
}<?php
namespace com\linways\base\util;

use com\linways\base\exception\CoreException;

class RequestUtil
{

    /**
     * Method for validating sort request
     *
     * @param string $sortBy            
     * @param string $sortOrder            
     * @throws CoreException
     * @return boolean true if valid sort order request
     */
    public static function validateSortOrder($sortBy, $sortOrder)
    {
        if ($sortBy == NULL || $sortBy == "") {
            throw new CoreException(CoreException::SORTBY_NOT_DEFINED, "Sort By is not defined!");
        }
        
        if ($sortOrder == NULL || $sortOrder == "") {
            throw new CoreException(CoreException::SORT_ORDER_NOT_DEFINED, "Sort order is not defined!");
        }
        
        return true;
    }
    /**
     * method for getting $_GET request parameters value
     * @param string $paramName
     * @return string
     */
    public static function get($paramName)
    {
        return $_GET[$paramName];
    }
    
    /**
     * method for getting $_POST request parameters value
     * @param string $paramName
     * @return string
     */
    public static function post($paramName)
    {
        return $_POST[$paramName];
    }
}<?php
namespace com\linways\base\util;

use com\linways\base\mapper\Result;
use com\linways\base\exception\MapperException;
use com\linways\base\mapper\ResultMap;

class MapperUtil
{

    /**
     * Method for creating object from result
     *
     * @param MYSQLResult $result            
     * @param ResultMap $mapper            
     * @return $objectList[]
     */
    public static function fetchObject($result, $mapper)
    {
        $mappedObject = NULL;
        
        if (MySqlUtil::hasRows($result->sqlResult)) {
            
            $inObjectsArray = [];
            while ($fromObject = MySqlUtil::fetchObject($result->sqlResult)) {
                
                $mappedObject = self::mapObject($mapper, $fromObject, $inObjectsArray);
                $inObjectsArray[$fromObject->{$mapper->primeryKeyColumn}] = $mappedObject;
            }
        }
        // MySqlUtil::freeResult($result->sqlResult);
        return $mappedObject;
    }

    /**
     * Method for creating object List from result
     *
     * @param MYSQLResult $result            
     * @param ResultMap $mapper            
     * @return $objectList[]
     */
    public static function fetchObjectList($result, $mapper)
    {
        $mappedObjectList = [];
        $mappedObject = null;
        
        if (MySqlUtil::hasRows($result->sqlResult)) {
            
            while ($fromObject = MySqlUtil::fetchObject($result->sqlResult)) {
                
                $mappedObject = self::mapObject($mapper, $fromObject, $mappedObjectList);
                $mappedObjectList[$fromObject->{$mapper->primeryKeyColumn}] = $mappedObject;
            }
        }
        
        // MySqlUtil::freeResult($result->sqlResult);
        return array_values($mappedObjectList);
    }

    /**
     * Validate Mapper class
     *
     * @param ResultMap $mapper            
     */
    public static function validateMapper($mapper)
    {
        // If mapper is null throw exception else go ahead with mapping
        if (empty($mapper)) {
            throw new MapperException(MapperException::MAPPER_NOT_DEFINED, "Mapper class not defined!.");
        }
        
        if (empty($mapper->primeryKeyColumn)) {
            throw new MapperException(MapperException::MAPPER_PRIMARY_KEY_COLUMN_NOT_DEFINED, "Primar Key Column property not set for the mapper '" . $mapper->id . "'");
        }
        if (empty($mapper->primeryKey)) {
            throw new MapperException(MapperException::MAPPER_PRIMARY_KEY_NOT_DEFINED, "Primar Key  property not set for the mapper '" . $mapper->id . "'");
        }
    }

    /**
     * Find Object in an objectArray
     *
     * @param array $objectsArray            
     * @param string $key            
     * @param int $id            
     * @return unknown|NULL
     */
    public static function findObject($objectsArray, $key, $id)
    {
        foreach ($objectsArray as $value) {
            
            if ($value->{$key} == $id) {
                return $value;
            }
        }
        
        return null;
    }

    /**
     * Map object from sql result to dto or model classes
     *
     * @param ResultMap $mapper            
     * @param \stdClass $fromObject            
     * @param array $children            
     * @return NULL|unknown|\com\linways\base\util\unknown|\com\linways\base\util\NULL
     */
    public static function mapObject($mapper, $fromObject, $children)
    {
        $id = null;
        $toObject = null;
        $subObject = null;
        $alreadyMapped = false;
        // Validate Mapper class
        self::validateMapper($mapper);
        // Find the id of the fromobject to check if it is already mapped or not
        $id = $fromObject->{$mapper->primeryKeyColumn};
        
        // If id is null then no mapping found
        if ($id == null) {
            return null;
        }
        // Find already mapped object else create new object from mapper class
        $toObject = self::findObject($children, $mapper->primeryKey, $fromObject->{$mapper->primeryKeyColumn});
        if ($toObject == null) {
            $toObject = new $mapper->class();
        }
        
        // Map all mapped result property from mysql result object to dto/model classes
        foreach ($mapper->results as $result) {
            // If result type is null then no relation exist.so direclty map the value
            // else check for result type to find about the relationship
            if ($result->resultType == null) {
                $toObject->{$result->property} = $fromObject->{$result->column};
            } else  // One to one relation ship
                if ($result->resultType == Result::OBJECT && empty($toObject->{$result->property})) {
                    $subObject = self::mapOnetoOne($result->resultMap, $fromObject);
                    $toObject->{$result->property} = $subObject;
                } else  // One to many releation ship
                    if ($result->resultType == Result::OBJECT_ARRAY) {
                        $subObject = null;
                        $alreadyMapped = false;
                        if (empty($toObject->{$result->property})) {
                            $toObject->{$result->property} = [];
                            // map the object
                            $subObject = self::mapObject($result->resultMap, $fromObject, $toObject->{$result->property});
                        } else {
                            
                            $subObject = self::findObject($toObject->{$result->property}, $result->resultMap->primeryKey, $fromObject->{$result->resultMap->primeryKeyColumn});
                            
                            // Check if object already mapped or not
                            // If mapped getthe instance and do the mapping else
                            // map the object and add it to the toobject list
                            if (empty($subObject)) {
                                $alreadyMapped = false;
                            } else {
                                $alreadyMapped = true;
                            }
                            // map the object
                            $subObject = self::mapObject($result->resultMap, $fromObject, $toObject->{$result->property});
                        }
                        if (! empty($subObject) && ! $alreadyMapped) {
                            $toObject->{$result->property}[] = $subObject;
                        }
                    }
        }
        
        return $toObject;
    }

    /**
     * Method fro mapping one to one relation ship
     *
     * @param ResultMap $mapper            
     * @param \stdClass $fromobject            
     * @return object
     */
    public static function mapOnetoOne($mapper, $fromObject)
    {
        $id = null;
        $toObject = null;
        $subObject = null;
        $alreadyMapped = false;
        // Validate Mapper class
        self::validateMapper($mapper);
        // Find the id of the fromobject
        $id = $fromObject->{$mapper->primeryKeyColumn};
        
        // If id is null then no mapping found
        if ($id == null) {
            return null;
        }
        $toObject = new $mapper->class();
        foreach ($mapper->results as $result) {
            
            if ($result->resultType == null) {
                $toObject->{$result->property} = $fromObject->{$result->column};
            } else 
                if ($result->resultType == Result::OBJECT) {
                    $toObject->{$result->property} = self::mapOnetoOne($result->resultMap, $fromObject);
                } else  // One to many releation ship
                    if ($result->resultType == Result::OBJECT_ARRAY) {
                        $subObject = null;
                        $alreadyMapped = false;
                        if (empty($toObject->{$result->property})) {
                            $toObject->{$result->property} = [];
                            // map the object
                            $subObject = self::mapObject($result->resultMap, $fromObject, $toObject->{$result->property});
                        } else {
                            
                            $subObject = self::findObject($toObject->{$result->property}, $result->resultMap->primeryKey, $fromObject->{$result->resultMap->primeryKeyColumn});
                            // Check if object already mapped or not
                            // If mapped getthe instance and do the mapping else
                            // map the object and add it to the toobject list
                            if (empty($subObject)) {
                                $alreadyMapped = false;
                            } else {
                                $alreadyMapped = true;
                            }
                            // map the object
                            $subObject = self::mapObject($result->resultMap, $fromObject, $toObject->{$result->property});
                        }
                        
                        if (! empty($subObject) && ! $alreadyMapped) {
                            $toObject->{$result->property}[] = $subObject;
                        }
                    }
        }
        return $toObject;
    }
}<?php
namespace com\linways\base\util;

use com\linways\base\mapper\Result;
use com\linways\base\mapper\ResultMap;

class ObjectUtil
{

    /**
     * Method for creating object list from result
     *
     * @param MYSQLResult $result            
     * @return $objectList[]
     */
    public static function fetchObjectList($result)
    {
        $objectList = [];
        
        if (MySqlUtil::hasRows($result->sqlResult)) {
            while ($obj = MySqlUtil::fetchObject($result->sqlResult)) {
                $objectList[] = $obj;
            }
        }
        // MySqlUtil::freeResult($result->sqlResult);
        return $objectList;
    }

    /**
     * Method for creating object from result
     *
     * @param MYSQLResult $result            
     * @param ResultMap $mapper            
     * @return $objectList[]
     */
    public static function fetchObject($result, $mapper = null)
    {
        $object = NULL;
        
        if (MySqlUtil::hasRows($result->sqlResult)) {
            
                $object = MySqlUtil::fetchObject($result->sqlResult);
        }
        // MySqlUtil::freeResult($result->sqlResult);
        return $object;
    }

}<?php
namespace com\linways\base\util;

class PhoneUtil
{

    /**
     *
     * @param unknown $phonNo            
     * @return unknown|string
     */
    public static function checkPhoneNo($phonNo)
    {
        if (preg_match("/^91[0-9]{10}$/", $phonNo)) {
            return $phonNo;
        } else {
            if (preg_match("/^[0-9]{10}$/", $phonNo)) {
                $phonNo = "91" . $phonNo;
                return $phonNo;
            } else {
                $phonNo = "";
            }
        }
    }

    /**
     *
     * @param unknown $num            
     * @return string
     */
    public static function addOrdinalNumberSuffix($num)
    {
        if (! in_array(($num % 100), array(
            11,
            12,
            13
        ))) {
            switch ($num % 10) {
                // Handle 1st, 2nd, 3rd
                case 1:
                    return $num . 'st';
                case 2:
                    return $num . 'nd';
                case 3:
                    return $num . 'rd';
            }
        }
        return $num . 'th';
    }
}<?php
namespace com\linways\base\util;

class MySqlUtil
{

    /**
     * check sql result has records/rows
     *
     * @param mysqlresult $sqlResult            
     * @return boolean true if yes else false
     */
    public static function hasRows($sqlResult)
    {
        return self::getNumOfRows($sqlResult) > 0 ? true : false;
    }

    /**
     * Close mysql connection
     *
     * @param connection $connection            
     * @return unknown
     */
    public static function closeConnection($connection)
    {
        return mysqli_close($connection);
    }

    /**
     * get num of rows in mysql result
     *
     * @param mysqlresult $sqlResult            
     * @return num of rows
     */
    public static function getNumOfRows($sqlResult)
    {
        return mysqli_num_rows($sqlResult);
    }

    /**
     * get num of fields in mysql result
     *
     * @param mysqlresult $sqlResult            
     * @return num of fields
     */
    public static function getNumOfFields($sqlResult)
    {
        return mysqli_num_fields($sqlResult);
    }

    /**
     * fetch rows in mysql result
     *
     * @param mysqlresult $sqlResult            
     * @return row
     */
    public static function fetchRow($sqlResult)
    {
        return mysqli_fetch_row($sqlResult);
    }

    /**
     * fetch array in mysql result
     *
     * @param mysqlresult $sqlResult            
     * @return array
     */
    public static function fetchArray($sqlResult)
    {
        return mysqli_fetch_array($sqlResult);
    }

    /**
     * fetch object in mysql result
     *
     * @param mysqlresult $sqlResult            
     * @return object
     */
    public static function fetchObject($sqlResult, $class_name = NULL, $params = NULL)
    {
        return mysqli_fetch_object($sqlResult);
    }

    /**
     * set data seek in mysql result
     *
     * @param mysqlresult $sqlResult            
     * @return dataseek
     */
    public static function dataSeek($sqlResult, $pos)
    {
        return mysqli_data_seek($sqlResult, $pos);
    }

    /**
     * free mysql result
     *
     * @param mysqlresult $sqlResult            
     * @return boolean
     */
    public static function freeResult($sqlResult)
    {
        return mysqli_free_result($sqlResult);
    }

    /**
     * get insert id of current connection
     *
     * @param connection $connection            
     * @return id
     */
    public static function getInsertId($connection)
    {
        return mysqli_insert_id($connection);
    }

    /**
     * mysql real escape string
     *
     * @param string $string            
     */
    public static function realEscapeString($string)
    {
        return mysql_real_escape_string($string);
    }
}<?php
namespace com\linways\base\mapper;

class Result
{

    const OBJECT = "OBJECT";
    const OBJECT_ARRAY = "OBJECT_ARRAY";

    /**
     * Class property name
     * @var string
     */
    public $property;

    /**
     * SQL column name
     * @var string
     */
    public $column;

    /**
     *Class mapper
     * @var ResultMap
     */
    public $resultMap;


    /**
     * Type of class property.
     * eg:OBJECT,OBJECT_ARRAY
     * If not defined or null,sql column mapped to property 
     * else look for resultMap of specified result type.
     * To DO: primary dataType(int,float,string..etc) conversion if mentioned
     * @var string
     */
    public $resultType;


    public function __construct($property = null, $column = null,$resultType = null, $resultMap = null)
    {
        $this->property = $property;
        $this->column = $column;
        $this->resultMap = $resultMap;
        $this->resultType = $resultType;
    }
}<?php
namespace com\linways\base\mapper;

/**
 * 
 * @author arun
 *
 */
class ResultMap
{

    /**
     * Id of resultmap
     * @var string
     */
    public $id;

    /**
     * Class name of mapper class.Specify the namespace also.
     * eg:com\linways\core\ams\professional\dto\Subject
     * @var string
     */
    public $class;

    /**
     * Class properties mapping details array
     * @var array
     */
    public $results;
    /**
     * PrimayKey property of a class
     * @var unknown
     */
    public $primeryKey;
    /**
     * Sql primaykeycolumn name 
     * @var unknown
     */
    public $primeryKeyColumn;

    public function __construct($id = null, $class = null,$primaryKey = null,$primeryKeyColumn=NULL, $results = null)
    {
        $this->id = $id;
        $this->class = $class;
        $this->results = $results;
        $this->primeryKey = $primaryKey;
        $this->primeryKeyColumn = $primeryKeyColumn;
    }
    
 //Mapper Example   
//     class SubjectServiceMapper implements IMapper
//     {
         
//         // /Condition 1 - Presence of a static member variable
//         private static $_instance = null;
    
//         // /Condition 2 - Locked down the constructor
//         private function __construct()
//         {
//         }
//         // Prevent any oustide instantiation of this class
    
//         // /Condition 3 - Prevent any object or instance of that class to be cloned
//         private function __clone()
//         {}
//         // Prevent any copy of this object
    
//         // /Condition 4 - Have a single globally accessible static method
//         public static function getInstance()
//         {
//             if (! is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
//                 self::$_instance = new self();
    
//                 return self::$_instance;
//         }
//         /**
//          * Mapper array
//          * @var array
//          */
//         private $mapper;
//         /**
//          * {@inheritDoc}
//          * @see \com\linways\core\ams\professional\IMapper::getMapper()
//          */
//         public function getMapper() {
    
//             if(empty( $this->mapper))
//             {
//                 $this->mapper =  [];
//                 $this->mapper ['subjectMapper'] = $this->getSubjectMapper();
                 
//             }
//             return  $this->mapper;
    
//         }
    
//         /**
//          * GetSubject Mapper
//          * @return \com\linways\base\mapper\ResultMap
//          */
//         private function getSubjectMapper()
//         {
//             $mapper = null;
//             $mapper = new ResultMap("subjectMapper",'com\linways\core\ams\professional\dto\Subject','id','subjectID');
//             $mapper->results = [];
//             $mapper->results [] = new Result('id','subjectID');
//             $mapper->results [] = new Result('name','subjectName');
//             $mapper->results [] = new Result('syllabusName','syllabusName');
//             $mapper->results [] = new Result('semester','semester',Result::OBJECT_ARRAY,$this->getSemesterMapper());
    
//             return $mapper;
//         }
    
//         /**
//          * GetSubject semeseter mapper
//          * @return \com\linways\base\mapper\ResultMap
//          */
//         private function getSemesterMapper()
//         {
//             $mapper = null;
//             $mapper = new ResultMap("semesterMapper",'com\linways\core\ams\professional\dto\Semester','id','semID');
//             $mapper->results = [];
//             $mapper->results [] = new Result('id','semID');
//                 //      $mapper->results [] = new Result('subjects','subjects',Result::OBJECT_ARRAY,$this->getSubjectMapper());
    
//             return $mapper;
             
//         }
    
}<?php
namespace com\linways\base\mapper;

interface IMapper
{
    /**
     * Get defined mappers of mapper class
     * @return array  
     */
    public function getMapper();
}<?php
namespace com\linways\base\soap;

/**
 * transport class for sending/receiving data via HTTP and HTTPS
 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @author   Scott Nichol <snichol@users.sourceforge.net>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access public
 */
class soap_transport_http extends nusoap_base {
    
    var $url = '';
    var $uri = '';
    var $digest_uri = '';
    var $scheme = '';
    var $host = '';
    var $port = '';
    var $path = '';
    var $request_method = 'POST';
    var $protocol_version = '1.0';
    var $encoding = '';
    var $outgoing_headers = array();
    var $incoming_headers = array();
    var $incoming_cookies = array();
    var $outgoing_payload = '';
    var $incoming_payload = '';
    var $response_status_line;	// HTTP response status line
    var $useSOAPAction = true;
    var $persistentConnection = false;
    var $ch = false;	// cURL handle
    var $ch_options = array();	// cURL custom options
    var $use_curl = false;		// force cURL use
    var $proxy = null;			// proxy information (associative array)
    var $username = '';
    var $password = '';
    var $authtype = '';
    var $digestRequest = array();
    var $certRequest = array();	// keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
    // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
    // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
    // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
    // passphrase: SSL key password/passphrase
    // certpassword: SSL certificate password
    // verifypeer: default is 1
    // verifyhost: default is 1
    
    /**
     * constructor
     *
     * @param string $url The URL to which to connect
     * @param array $curl_options User-specified cURL options
     * @param boolean $use_curl Whether to try to force cURL use
     * @access public
     */
    function soap_transport_http($url, $curl_options = NULL, $use_curl = false){
        parent::nusoap_base();
        $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
        $this->appendDebug($this->varDump($curl_options));
        $this->setURL($url);
        if (is_array($curl_options)) {
            $this->ch_options = $curl_options;
        }
        $this->use_curl = $use_curl;
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
        $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
    }
    
    /**
     * sets a cURL option
     *
     * @param	mixed $option The cURL option (always integer?)
     * @param	mixed $value The cURL option value
     * @access   private
     */
    function setCurlOption($option, $value) {
        $this->debug("setCurlOption option=$option, value=");
        $this->appendDebug($this->varDump($value));
        curl_setopt($this->ch, $option, $value);
    }
    
    /**
     * sets an HTTP header
     *
     * @param string $name The name of the header
     * @param string $value The value of the header
     * @access private
     */
    function setHeader($name, $value) {
        $this->outgoing_headers[$name] = $value;
        $this->debug("set header $name: $value");
    }
    
    /**
     * unsets an HTTP header
     *
     * @param string $name The name of the header
     * @access private
     */
    function unsetHeader($name) {
        if (isset($this->outgoing_headers[$name])) {
            $this->debug("unset header $name");
            unset($this->outgoing_headers[$name]);
        }
    }
    
    /**
     * sets the URL to which to connect
     *
     * @param string $url The URL to which to connect
     * @access private
     */
    function setURL($url) {
        $this->url = $url;
        
        $u = parse_url($url);
        foreach($u as $k => $v){
            $this->debug("parsed URL $k = $v");
            $this->$k = $v;
        }
        
        // add any GET params to path
        if(isset($u['query']) && $u['query'] != ''){
            $this->path .= '?' . $u['query'];
        }
        
        // set default port
        if(!isset($u['port'])){
            if($u['scheme'] == 'https'){
                $this->port = 443;
            } else {
                $this->port = 80;
            }
        }
        
        $this->uri = $this->path;
        $this->digest_uri = $this->uri;
        
        // build headers
        if (!isset($u['port'])) {
            $this->setHeader('Host', $this->host);
        } else {
            $this->setHeader('Host', $this->host.':'.$this->port);
        }
        
        if (isset($u['user']) && $u['user'] != '') {
            $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
        }
    }
    
    /**
     * gets the I/O method to use
     *
     * @return	string	I/O method to use (socket|curl|unknown)
     * @access	private
     */
    function io_method() {
        if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
            return 'curl';
            if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
                return 'socket';
                return 'unknown';
    }
    
    /**
     * establish an HTTP connection
     *
     * @param    integer $timeout set connection timeout in seconds
     * @param	integer $response_timeout set response timeout in seconds
     * @return	boolean true if connected, false if not
     * @access   private
     */
    function connect($connection_timeout=0,$response_timeout=30){
        // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
        // "regular" socket.
        // TODO: disabled for now because OpenSSL must be *compiled* in (not just
        //       loaded), and until PHP5 stream_get_wrappers is not available.
        //	  	if ($this->scheme == 'https') {
            //		  	if (version_compare(phpversion(), '4.3.0') >= 0) {
            //		  		if (extension_loaded('openssl')) {
            //		  			$this->scheme = 'ssl';
            //		  			$this->debug('Using SSL over OpenSSL');
            //		  		}
            //		  	}
            //		}
            $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
            if ($this->io_method() == 'socket') {
                if (!is_array($this->proxy)) {
                    $host = $this->host;
                    $port = $this->port;
                } else {
                    $host = $this->proxy['host'];
                    $port = $this->proxy['port'];
                }
                
                // use persistent connection
                if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
                    if (!feof($this->fp)) {
                        $this->debug('Re-use persistent connection');
                        return true;
                    }
                    fclose($this->fp);
                    $this->debug('Closed persistent connection at EOF');
                }
                
                // munge host if using OpenSSL
                if ($this->scheme == 'ssl') {
                    $host = 'ssl://' . $host;
                }
                $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
                
                // open socket
                if($connection_timeout > 0){
                    $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
                } else {
                    $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
                }
                
                // test pointer
                if(!$this->fp) {
                    $msg = 'Couldn\'t open socket connection to server ' . $this->url;
                    if ($this->errno) {
                        $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
                    } else {
                        $msg .= ' prior to connect().  This is often a problem looking up the host name.';
                    }
                    $this->debug($msg);
                    $this->setError($msg);
                    return false;
                }
                
                // set response timeout
                $this->debug('set response timeout to ' . $response_timeout);
                socket_set_timeout( $this->fp, $response_timeout);
                
                $this->debug('socket connected');
                return true;
            } else if ($this->io_method() == 'curl') {
                if (!extension_loaded('curl')) {
                    //			$this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
                    $this->setError('The PHP cURL Extension is required for HTTPS or NLTM.  You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
                    return false;
                }
                // Avoid warnings when PHP does not have these options
                if (defined('CURLOPT_CONNECTIONTIMEOUT'))
                    $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
                    else
                        $CURLOPT_CONNECTIONTIMEOUT = 78;
                        if (defined('CURLOPT_HTTPAUTH'))
                            $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
                            else
                                $CURLOPT_HTTPAUTH = 107;
                                if (defined('CURLOPT_PROXYAUTH'))
                                    $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
                                    else
                                        $CURLOPT_PROXYAUTH = 111;
                                        if (defined('CURLAUTH_BASIC'))
                                            $CURLAUTH_BASIC = CURLAUTH_BASIC;
                                            else
                                                $CURLAUTH_BASIC = 1;
                                                if (defined('CURLAUTH_DIGEST'))
                                                    $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
                                                    else
                                                        $CURLAUTH_DIGEST = 2;
                                                        if (defined('CURLAUTH_NTLM'))
                                                            $CURLAUTH_NTLM = CURLAUTH_NTLM;
                                                            else
                                                                $CURLAUTH_NTLM = 8;
                                                                
                                                                $this->debug('connect using cURL');
                                                                // init CURL
                                                                $this->ch = curl_init();
                                                                // set url
                                                                $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
                                                                // add path
                                                                $hostURL .= $this->path;
                                                                $this->setCurlOption(CURLOPT_URL, $hostURL);
                                                                // follow location headers (re-directs)
                                                                if (ini_get('safe_mode') || ini_get('open_basedir')) {
                                                                    $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
                                                                    $this->debug('safe_mode = ');
                                                                    $this->appendDebug($this->varDump(ini_get('safe_mode')));
                                                                    $this->debug('open_basedir = ');
                                                                    $this->appendDebug($this->varDump(ini_get('open_basedir')));
                                                                } else {
                                                                    $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
                                                                }
                                                                // ask for headers in the response output
                                                                $this->setCurlOption(CURLOPT_HEADER, 1);
                                                                // ask for the response output as the return value
                                                                $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
                                                                // encode
                                                                // We manage this ourselves through headers and encoding
                                                                //		if(function_exists('gzuncompress')){
                                                                //			$this->setCurlOption(CURLOPT_ENCODING, 'deflate');
                                                                //		}
                                                                // persistent connection
                                                                if ($this->persistentConnection) {
                                                                    // I believe the following comment is now bogus, having applied to
                                                                    // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
                                                                    // The way we send data, we cannot use persistent connections, since
                                                                    // there will be some "junk" at the end of our request.
                                                                    //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
                                                                    $this->persistentConnection = false;
                                                                    $this->setHeader('Connection', 'close');
                                                                }
                                                                // set timeouts
                                                                if ($connection_timeout != 0) {
                                                                    $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
                                                                }
                                                                if ($response_timeout != 0) {
                                                                    $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
                                                                }
                                                                
                                                                if ($this->scheme == 'https') {
                                                                    $this->debug('set cURL SSL verify options');
                                                                    // recent versions of cURL turn on peer/host checking by default,
                                                                    // while PHP binaries are not compiled with a default location for the
                                                                    // CA cert bundle, so disable peer/host checking.
                                                                    //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
                                                                    $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
                                                                    $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
                                                                    
                                                                    // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
                                                                    if ($this->authtype == 'certificate') {
                                                                        $this->debug('set cURL certificate options');
                                                                        if (isset($this->certRequest['cainfofile'])) {
                                                                            $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
                                                                        }
                                                                        if (isset($this->certRequest['verifypeer'])) {
                                                                            $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
                                                                        } else {
                                                                            $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
                                                                        }
                                                                        if (isset($this->certRequest['verifyhost'])) {
                                                                            $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
                                                                        } else {
                                                                            $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
                                                                        }
                                                                        if (isset($this->certRequest['sslcertfile'])) {
                                                                            $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
                                                                        }
                                                                        if (isset($this->certRequest['sslkeyfile'])) {
                                                                            $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
                                                                        }
                                                                        if (isset($this->certRequest['passphrase'])) {
                                                                            $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
                                                                        }
                                                                        if (isset($this->certRequest['certpassword'])) {
                                                                            $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
                                                                        }
                                                                    }
                                                                }
                                                                if ($this->authtype && ($this->authtype != 'certificate')) {
                                                                    if ($this->username) {
                                                                        $this->debug('set cURL username/password');
                                                                        $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
                                                                    }
                                                                    if ($this->authtype == 'basic') {
                                                                        $this->debug('set cURL for Basic authentication');
                                                                        $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
                                                                    }
                                                                    if ($this->authtype == 'digest') {
                                                                        $this->debug('set cURL for digest authentication');
                                                                        $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
                                                                    }
                                                                    if ($this->authtype == 'ntlm') {
                                                                        $this->debug('set cURL for NTLM authentication');
                                                                        $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
                                                                    }
                                                                }
                                                                if (is_array($this->proxy)) {
                                                                    $this->debug('set cURL proxy options');
                                                                    if ($this->proxy['port'] != '') {
                                                                        $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
                                                                    } else {
                                                                        $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
                                                                    }
                                                                    if ($this->proxy['username'] || $this->proxy['password']) {
                                                                        $this->debug('set cURL proxy authentication options');
                                                                        $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
                                                                        if ($this->proxy['authtype'] == 'basic') {
                                                                            $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
                                                                        }
                                                                        if ($this->proxy['authtype'] == 'ntlm') {
                                                                            $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
                                                                        }
                                                                    }
                                                                }
                                                                $this->debug('cURL connection set up');
                                                                return true;
            } else {
                $this->setError('Unknown scheme ' . $this->scheme);
                $this->debug('Unknown scheme ' . $this->scheme);
                return false;
            }
}

/**
 * sends the SOAP request and gets the SOAP response via HTTP[S]
 *
 * @param    string $data message data
 * @param    integer $timeout set connection timeout in seconds
 * @param	integer $response_timeout set response timeout in seconds
 * @param	array $cookies cookies to send
 * @return	string data
 * @access   public
 */
function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
    
    $this->debug('entered send() with data of length: '.strlen($data));
    
    $this->tryagain = true;
    $tries = 0;
    while ($this->tryagain) {
        $this->tryagain = false;
        if ($tries++ < 2) {
            // make connnection
            if (!$this->connect($timeout, $response_timeout)){
                return false;
            }
            
            // send request
            if (!$this->sendRequest($data, $cookies)){
                return false;
            }
            
            // get response
            $respdata = $this->getResponse();
        } else {
            $this->setError("Too many tries to get an OK response ($this->response_status_line)");
        }
    }
    $this->debug('end of send()');
    return $respdata;
}


/**
 * sends the SOAP request and gets the SOAP response via HTTPS using CURL
 *
 * @param    string $data message data
 * @param    integer $timeout set connection timeout in seconds
 * @param	integer $response_timeout set response timeout in seconds
 * @param	array $cookies cookies to send
 * @return	string data
 * @access   public
 * @deprecated
 */
function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
    return $this->send($data, $timeout, $response_timeout, $cookies);
}

/**
 * if authenticating, set user credentials here
 *
 * @param    string $username
 * @param    string $password
 * @param	string $authtype (basic|digest|certificate|ntlm)
 * @param	array $digestRequest (keys must be nonce, nc, realm, qop)
 * @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
 * @access   public
 */
function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
    $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
    $this->appendDebug($this->varDump($digestRequest));
    $this->debug("certRequest=");
    $this->appendDebug($this->varDump($certRequest));
    // cf. RFC 2617
    if ($authtype == 'basic') {
        $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
    } elseif ($authtype == 'digest') {
        if (isset($digestRequest['nonce'])) {
            $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
            
            // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
            
            // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
            $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
            
            // H(A1) = MD5(A1)
            $HA1 = md5($A1);
            
            // A2 = Method ":" digest-uri-value
            $A2 = $this->request_method . ':' . $this->digest_uri;
            
            // H(A2)
            $HA2 =  md5($A2);
            
            // KD(secret, data) = H(concat(secret, ":", data))
            // if qop == auth:
            // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
            //                              ":" nc-value
            //                              ":" unq(cnonce-value)
            //                              ":" unq(qop-value)
            //                              ":" H(A2)
            //                            ) <">
            // if qop is missing,
            // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
            
            $unhashedDigest = '';
            $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
            $cnonce = $nonce;
            if ($digestRequest['qop'] != '') {
                $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
            } else {
                $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
            }
            
            $hashedDigest = md5($unhashedDigest);
            
            $opaque = '';
            if (isset($digestRequest['opaque'])) {
                $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
            }
            
            $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
        }
    } elseif ($authtype == 'certificate') {
        $this->certRequest = $certRequest;
        $this->debug('Authorization header not set for certificate');
    } elseif ($authtype == 'ntlm') {
        // do nothing
        $this->debug('Authorization header not set for ntlm');
    }
    $this->username = $username;
    $this->password = $password;
    $this->authtype = $authtype;
    $this->digestRequest = $digestRequest;
}

/**
 * set the soapaction value
 *
 * @param    string $soapaction
 * @access   public
 */
function setSOAPAction($soapaction) {
    $this->setHeader('SOAPAction', '"' . $soapaction . '"');
}

/**
 * use http encoding
 *
 * @param    string $enc encoding style. supported values: gzip, deflate, or both
 * @access   public
 */
function setEncoding($enc='gzip, deflate') {
    if (function_exists('gzdeflate')) {
        $this->protocol_version = '1.1';
        $this->setHeader('Accept-Encoding', $enc);
        if (!isset($this->outgoing_headers['Connection'])) {
            $this->setHeader('Connection', 'close');
            $this->persistentConnection = false;
        }
        // deprecated as of PHP 5.3.0
        //set_magic_quotes_runtime(0);
        $this->encoding = $enc;
    }
}

/**
 * set proxy info here
 *
 * @param    string $proxyhost use an empty string to remove proxy
 * @param    string $proxyport
 * @param	string $proxyusername
 * @param	string $proxypassword
 * @param	string $proxyauthtype (basic|ntlm)
 * @access   public
 */
function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
    if ($proxyhost) {
        $this->proxy = array(
            'host' => $proxyhost,
            'port' => $proxyport,
            'username' => $proxyusername,
            'password' => $proxypassword,
            'authtype' => $proxyauthtype
        );
        if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
            $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
        }
    } else {
        $this->debug('remove proxy');
        $proxy = null;
        unsetHeader('Proxy-Authorization');
    }
}


/**
 * Test if the given string starts with a header that is to be skipped.
 * Skippable headers result from chunked transfer and proxy requests.
 *
 * @param	string $data The string to check.
 * @returns	boolean	Whether a skippable header was found.
 * @access	private
 */
function isSkippableCurlHeader(&$data) {
    $skipHeaders = array(	'HTTP/1.1 100',
        'HTTP/1.0 301',
        'HTTP/1.1 301',
        'HTTP/1.0 302',
        'HTTP/1.1 302',
        'HTTP/1.0 401',
        'HTTP/1.1 401',
        'HTTP/1.0 200 Connection established');
    foreach ($skipHeaders as $hd) {
        $prefix = substr($data, 0, strlen($hd));
        if ($prefix == $hd) return true;
    }
    
    return false;
}

/**
 * decode a string that is encoded w/ "chunked' transfer encoding
 * as defined in RFC2068 19.4.6
 *
 * @param    string $buffer
 * @param    string $lb
 * @returns	string
 * @access   public
 * @deprecated
 */
function decodeChunked($buffer, $lb){
    // length := 0
    $length = 0;
    $new = '';
    
    // read chunk-size, chunk-extension (if any) and CRLF
    // get the position of the linebreak
    $chunkend = strpos($buffer, $lb);
    if ($chunkend == FALSE) {
        $this->debug('no linebreak found in decodeChunked');
        return $new;
    }
    $temp = substr($buffer,0,$chunkend);
    $chunk_size = hexdec( trim($temp) );
    $chunkstart = $chunkend + strlen($lb);
    // while (chunk-size > 0) {
    while ($chunk_size > 0) {
        $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
        $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
        
        // Just in case we got a broken connection
        if ($chunkend == FALSE) {
            $chunk = substr($buffer,$chunkstart);
            // append chunk-data to entity-body
            $new .= $chunk;
            $length += strlen($chunk);
            break;
        }
        
        // read chunk-data and CRLF
        $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
        // append chunk-data to entity-body
        $new .= $chunk;
        // length := length + chunk-size
        $length += strlen($chunk);
        // read chunk-size and CRLF
        $chunkstart = $chunkend + strlen($lb);
        
        $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
        if ($chunkend == FALSE) {
            break; //Just in case we got a broken connection
        }
        $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
        $chunk_size = hexdec( trim($temp) );
        $chunkstart = $chunkend;
    }
    return $new;
}

/**
 * Writes the payload, including HTTP headers, to $this->outgoing_payload.
 *
 * @param	string $data HTTP body
 * @param	string $cookie_str data for HTTP Cookie header
 * @return	void
 * @access	private
 */
function buildPayload($data, $cookie_str = '') {
    // Note: for cURL connections, $this->outgoing_payload is ignored,
    // as is the Content-Length header, but these are still created as
    // debugging guides.
    
    // add content-length header
    if ($this->request_method != 'GET') {
        $this->setHeader('Content-Length', strlen($data));
    }
    
    // start building outgoing payload:
    if ($this->proxy) {
        $uri = $this->url;
    } else {
        $uri = $this->uri;
    }
    $req = "$this->request_method $uri HTTP/$this->protocol_version";
    $this->debug("HTTP request: $req");
    $this->outgoing_payload = "$req\r\n";
    
    // loop thru headers, serializing
    foreach($this->outgoing_headers as $k => $v){
        $hdr = $k.': '.$v;
        $this->debug("HTTP header: $hdr");
        $this->outgoing_payload .= "$hdr\r\n";
    }
    
    // add any cookies
    if ($cookie_str != '') {
        $hdr = 'Cookie: '.$cookie_str;
        $this->debug("HTTP header: $hdr");
        $this->outgoing_payload .= "$hdr\r\n";
    }
    
    // header/body separator
    $this->outgoing_payload .= "\r\n";
    
    // add data
    $this->outgoing_payload .= $data;
}

/**
 * sends the SOAP request via HTTP[S]
 *
 * @param    string $data message data
 * @param	array $cookies cookies to send
 * @return	boolean	true if OK, false if problem
 * @access   private
 */
function sendRequest($data, $cookies = NULL) {
    // build cookie string
    $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
    
    // build payload
    $this->buildPayload($data, $cookie_str);
    
    if ($this->io_method() == 'socket') {
        // send payload
        if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
            $this->setError('couldn\'t write message data to socket');
            $this->debug('couldn\'t write message data to socket');
            return false;
        }
        $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
        return true;
    } else if ($this->io_method() == 'curl') {
        // set payload
        // cURL does say this should only be the verb, and in fact it
        // turns out that the URI and HTTP version are appended to this, which
        // some servers refuse to work with (so we no longer use this method!)
        //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
        $curl_headers = array();
        foreach($this->outgoing_headers as $k => $v){
            if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
                $this->debug("Skip cURL header $k: $v");
            } else {
                $curl_headers[] = "$k: $v";
            }
        }
        if ($cookie_str != '') {
            $curl_headers[] = 'Cookie: ' . $cookie_str;
        }
        $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
        $this->debug('set cURL HTTP headers');
        if ($this->request_method == "POST") {
            $this->setCurlOption(CURLOPT_POST, 1);
            $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
            $this->debug('set cURL POST data');
        } else {
        }
        // insert custom user-set cURL options
        foreach ($this->ch_options as $key => $val) {
            $this->setCurlOption($key, $val);
        }
        
        $this->debug('set cURL payload');
        return true;
    }
}

/**
 * gets the SOAP response via HTTP[S]
 *
 * @return	string the response (also sets member variables like incoming_payload)
 * @access   private
 */
function getResponse(){
    $this->incoming_payload = '';
    
    if ($this->io_method() == 'socket') {
        // loop until headers have been retrieved
        $data = '';
        while (!isset($lb)){
            
            // We might EOF during header read.
            if(feof($this->fp)) {
                $this->incoming_payload = $data;
                $this->debug('found no headers before EOF after length ' . strlen($data));
                $this->debug("received before EOF:\n" . $data);
                $this->setError('server failed to send headers');
                return false;
            }
            
            $tmp = fgets($this->fp, 256);
            $tmplen = strlen($tmp);
            $this->debug("read line of $tmplen bytes: " . trim($tmp));
            
            if ($tmplen == 0) {
                $this->incoming_payload = $data;
                $this->debug('socket read of headers timed out after length ' . strlen($data));
                $this->debug("read before timeout: " . $data);
                $this->setError('socket read of headers timed out');
                return false;
            }
            
            $data .= $tmp;
            $pos = strpos($data,"\r\n\r\n");
            if($pos > 1){
                $lb = "\r\n";
            } else {
                $pos = strpos($data,"\n\n");
                if($pos > 1){
                    $lb = "\n";
                }
            }
            // remove 100 headers
            if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) {
                unset($lb);
                $data = '';
            }//
        }
        // store header data
        $this->incoming_payload .= $data;
        $this->debug('found end of headers after length ' . strlen($data));
        // process headers
        $header_data = trim(substr($data,0,$pos));
        $header_array = explode($lb,$header_data);
        $this->incoming_headers = array();
        $this->incoming_cookies = array();
        foreach($header_array as $header_line){
            $arr = explode(':',$header_line, 2);
            if(count($arr) > 1){
                $header_name = strtolower(trim($arr[0]));
                $this->incoming_headers[$header_name] = trim($arr[1]);
                if ($header_name == 'set-cookie') {
                    // TODO: allow multiple cookies from parseCookie
                    $cookie = $this->parseCookie(trim($arr[1]));
                    if ($cookie) {
                        $this->incoming_cookies[] = $cookie;
                        $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
                    } else {
                        $this->debug('did not find cookie in ' . trim($arr[1]));
                    }
                }
            } else if (isset($header_name)) {
                // append continuation line to previous header
                $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
            }
        }
        
        // loop until msg has been received
        if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
            $content_length =  2147483647;	// ignore any content-length header
            $chunked = true;
            $this->debug("want to read chunked content");
        } elseif (isset($this->incoming_headers['content-length'])) {
            $content_length = $this->incoming_headers['content-length'];
            $chunked = false;
            $this->debug("want to read content of length $content_length");
        } else {
            $content_length =  2147483647;
            $chunked = false;
            $this->debug("want to read content to EOF");
        }
        $data = '';
        do {
            if ($chunked) {
                $tmp = fgets($this->fp, 256);
                $tmplen = strlen($tmp);
                $this->debug("read chunk line of $tmplen bytes");
                if ($tmplen == 0) {
                    $this->incoming_payload = $data;
                    $this->debug('socket read of chunk length timed out after length ' . strlen($data));
                    $this->debug("read before timeout:\n" . $data);
                    $this->setError('socket read of chunk length timed out');
                    return false;
                }
                $content_length = hexdec(trim($tmp));
                $this->debug("chunk length $content_length");
            }
            $strlen = 0;
            while (($strlen < $content_length) && (!feof($this->fp))) {
                $readlen = min(8192, $content_length - $strlen);
                $tmp = fread($this->fp, $readlen);
                $tmplen = strlen($tmp);
                $this->debug("read buffer of $tmplen bytes");
                if (($tmplen == 0) && (!feof($this->fp))) {
                    $this->incoming_payload = $data;
                    $this->debug('socket read of body timed out after length ' . strlen($data));
                    $this->debug("read before timeout:\n" . $data);
                    $this->setError('socket read of body timed out');
                    return false;
                }
                $strlen += $tmplen;
                $data .= $tmp;
            }
            if ($chunked && ($content_length > 0)) {
                $tmp = fgets($this->fp, 256);
                $tmplen = strlen($tmp);
                $this->debug("read chunk terminator of $tmplen bytes");
                if ($tmplen == 0) {
                    $this->incoming_payload = $data;
                    $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
                    $this->debug("read before timeout:\n" . $data);
                    $this->setError('socket read of chunk terminator timed out');
                    return false;
                }
            }
        } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
        if (feof($this->fp)) {
            $this->debug('read to EOF');
        }
        $this->debug('read body of length ' . strlen($data));
        $this->incoming_payload .= $data;
        $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
        
        // close filepointer
        if(
            (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
            (! $this->persistentConnection) || feof($this->fp)){
                fclose($this->fp);
                $this->fp = false;
                $this->debug('closed socket');
        }
        
        // connection was closed unexpectedly
        if($this->incoming_payload == ''){
            $this->setError('no response from server');
            return false;
        }
        
        // decode transfer-encoding
        //		if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
        //			if(!$data = $this->decodeChunked($data, $lb)){
        //				$this->setError('Decoding of chunked data failed');
        //				return false;
        //			}
        //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
        // set decoded payload
        //			$this->incoming_payload = $header_data.$lb.$lb.$data;
        //		}
        
} else if ($this->io_method() == 'curl') {
    // send and receive
    $this->debug('send and receive with cURL');
    $this->incoming_payload = curl_exec($this->ch);
    $data = $this->incoming_payload;
    
    $cErr = curl_error($this->ch);
    if ($cErr != '') {
        $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
        // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
        foreach(curl_getinfo($this->ch) as $k => $v){
            $err .= "$k: $v<br>";
        }
        $this->debug($err);
        $this->setError($err);
        curl_close($this->ch);
        return false;
    } else {
        //echo '<pre>';
        //var_dump(curl_getinfo($this->ch));
        //echo '</pre>';
    }
    // close curl
    $this->debug('No cURL error, closing cURL');
    curl_close($this->ch);
    
    // try removing skippable headers
    $savedata = $data;
    while ($this->isSkippableCurlHeader($data)) {
        $this->debug("Found HTTP header to skip");
        if ($pos = strpos($data,"\r\n\r\n")) {
            $data = ltrim(substr($data,$pos));
        } elseif($pos = strpos($data,"\n\n") ) {
            $data = ltrim(substr($data,$pos));
        }
    }
    
    if ($data == '') {
        // have nothing left; just remove 100 header(s)
        $data = $savedata;
        while (preg_match('/^HTTP\/1.1 100/',$data)) {
            if ($pos = strpos($data,"\r\n\r\n")) {
                $data = ltrim(substr($data,$pos));
            } elseif($pos = strpos($data,"\n\n") ) {
                $data = ltrim(substr($data,$pos));
            }
        }
    }
    
    // separate content from HTTP headers
    if ($pos = strpos($data,"\r\n\r\n")) {
        $lb = "\r\n";
    } elseif( $pos = strpos($data,"\n\n")) {
        $lb = "\n";
    } else {
        $this->debug('no proper separation of headers and document');
        $this->setError('no proper separation of headers and document');
        return false;
    }
    $header_data = trim(substr($data,0,$pos));
    $header_array = explode($lb,$header_data);
    $data = ltrim(substr($data,$pos));
    $this->debug('found proper separation of headers and document');
    $this->debug('cleaned data, stringlen: '.strlen($data));
    // clean headers
    foreach ($header_array as $header_line) {
        $arr = explode(':',$header_line,2);
        if(count($arr) > 1){
            $header_name = strtolower(trim($arr[0]));
            $this->incoming_headers[$header_name] = trim($arr[1]);
            if ($header_name == 'set-cookie') {
                // TODO: allow multiple cookies from parseCookie
                $cookie = $this->parseCookie(trim($arr[1]));
                if ($cookie) {
                    $this->incoming_cookies[] = $cookie;
                    $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
                } else {
                    $this->debug('did not find cookie in ' . trim($arr[1]));
                }
            }
        } else if (isset($header_name)) {
            // append continuation line to previous header
            $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
        }
    }
}

$this->response_status_line = $header_array[0];
$arr = explode(' ', $this->response_status_line, 3);
$http_version = $arr[0];
$http_status = intval($arr[1]);
$http_reason = count($arr) > 2 ? $arr[2] : '';

// see if we need to resend the request with http digest authentication
if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
    $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
    $this->setURL($this->incoming_headers['location']);
    $this->tryagain = true;
    return false;
}

// see if we need to resend the request with http digest authentication
if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
    $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
    if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
        $this->debug('Server wants digest authentication');
        // remove "Digest " from our elements
        $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
        
        // parse elements into array
        $digestElements = explode(',', $digestString);
        foreach ($digestElements as $val) {
            $tempElement = explode('=', trim($val), 2);
            $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
        }
        
        // should have (at least) qop, realm, nonce
        if (isset($digestRequest['nonce'])) {
            $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
            $this->tryagain = true;
            return false;
        }
    }
    $this->debug('HTTP authentication failed');
    $this->setError('HTTP authentication failed');
    return false;
}

if (
    ($http_status >= 300 && $http_status <= 307) ||
    ($http_status >= 400 && $http_status <= 417) ||
    ($http_status >= 501 && $http_status <= 505)
    ) {
        $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
        return false;
    }
    
    // decode content-encoding
    if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
        if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
            // if decoding works, use it. else assume data wasn't gzencoded
            if(function_exists('gzinflate')){
                //$timer->setMarker('starting decoding of gzip/deflated content');
                // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
                // this means there are no Zlib headers, although there should be
                $this->debug('The gzinflate function exists');
                $datalen = strlen($data);
                if ($this->incoming_headers['content-encoding'] == 'deflate') {
                    if ($degzdata = @gzinflate($data)) {
                        $data = $degzdata;
                        $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
                        if (strlen($data) < $datalen) {
                            // test for the case that the payload has been compressed twice
                            $this->debug('The inflated payload is smaller than the gzipped one; try again');
                            if ($degzdata = @gzinflate($data)) {
                                $data = $degzdata;
                                $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
                            }
                        }
                    } else {
                        $this->debug('Error using gzinflate to inflate the payload');
                        $this->setError('Error using gzinflate to inflate the payload');
                    }
                } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
                    if ($degzdata = @gzinflate(substr($data, 10))) {	// do our best
                        $data = $degzdata;
                        $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
                        if (strlen($data) < $datalen) {
                            // test for the case that the payload has been compressed twice
                            $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
                            if ($degzdata = @gzinflate(substr($data, 10))) {
                                $data = $degzdata;
                                $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
                            }
                        }
                    } else {
                        $this->debug('Error using gzinflate to un-gzip the payload');
                        $this->setError('Error using gzinflate to un-gzip the payload');
                    }
                }
                //$timer->setMarker('finished decoding of gzip/deflated content');
                //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
                // set decoded payload
                $this->incoming_payload = $header_data.$lb.$lb.$data;
            } else {
                $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
                $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
            }
        } else {
            $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
            $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
        }
    } else {
        $this->debug('No Content-Encoding header');
    }
    
    if(strlen($data) == 0){
        $this->debug('no data after headers!');
        $this->setError('no data present after HTTP headers');
        return false;
    }
    
    return $data;
}

/**
 * sets the content-type for the SOAP message to be sent
 *
 * @param	string $type the content type, MIME style
 * @param	mixed $charset character set used for encoding (or false)
 * @access	public
 */
function setContentType($type, $charset = false) {
    $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
}

/**
 * specifies that an HTTP persistent connection should be used
 *
 * @return	boolean whether the request was honored by this method.
 * @access	public
 */
function usePersistentConnection(){
    if (isset($this->outgoing_headers['Accept-Encoding'])) {
        return false;
    }
    $this->protocol_version = '1.1';
    $this->persistentConnection = true;
    $this->setHeader('Connection', 'Keep-Alive');
    return true;
}

/**
 * parse an incoming Cookie into it's parts
 *
 * @param	string $cookie_str content of cookie
 * @return	array with data of that cookie
 * @access	private
 */
/*
 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
 */
function parseCookie($cookie_str) {
    $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
    $data = preg_split('/;/', $cookie_str);
    $value_str = $data[0];
    
    $cookie_param = 'domain=';
    $start = strpos($cookie_str, $cookie_param);
    if ($start > 0) {
        $domain = substr($cookie_str, $start + strlen($cookie_param));
        $domain = substr($domain, 0, strpos($domain, ';'));
    } else {
        $domain = '';
    }
    
    $cookie_param = 'expires=';
    $start = strpos($cookie_str, $cookie_param);
    if ($start > 0) {
        $expires = substr($cookie_str, $start + strlen($cookie_param));
        $expires = substr($expires, 0, strpos($expires, ';'));
    } else {
        $expires = '';
    }
    
    $cookie_param = 'path=';
    $start = strpos($cookie_str, $cookie_param);
    if ( $start > 0 ) {
        $path = substr($cookie_str, $start + strlen($cookie_param));
        $path = substr($path, 0, strpos($path, ';'));
    } else {
        $path = '/';
    }
    
    $cookie_param = ';secure;';
    if (strpos($cookie_str, $cookie_param) !== FALSE) {
        $secure = true;
    } else {
        $secure = false;
    }
    
    $sep_pos = strpos($value_str, '=');
    
    if ($sep_pos) {
        $name = substr($value_str, 0, $sep_pos);
        $value = substr($value_str, $sep_pos + 1);
        $cookie= array(	'name' => $name,
            'value' => $value,
            'domain' => $domain,
            'path' => $path,
            'expires' => $expires,
            'secure' => $secure
        );
        return $cookie;
    }
    return false;
}

/**
 * sort out cookies for the current request
 *
 * @param	array $cookies array with all cookies
 * @param	boolean $secure is the send-content secure or not?
 * @return	string for Cookie-HTTP-Header
 * @access	private
 */
function getCookiesForRequest($cookies, $secure=false) {
    $cookie_str = '';
    if ((! is_null($cookies)) && (is_array($cookies))) {
        foreach ($cookies as $cookie) {
            if (! is_array($cookie)) {
                continue;
            }
            $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
            if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
                if (strtotime($cookie['expires']) <= time()) {
                    $this->debug('cookie has expired');
                    continue;
                }
            }
            if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
                $domain = preg_quote($cookie['domain']);
                if (! preg_match("'.*$domain$'i", $this->host)) {
                    $this->debug('cookie has different domain');
                    continue;
                }
            }
            if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
                $path = preg_quote($cookie['path']);
                if (! preg_match("'^$path.*'i", $this->path)) {
                    $this->debug('cookie is for a different path');
                    continue;
                }
            }
            if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
                $this->debug('cookie is secure, transport is not');
                continue;
            }
            $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
            $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
        }
    }
    return $cookie_str;
}
}
<?php
namespace com\linways\base\soap;


/**
 *
 * [nu]soapclient higher level class for easy usage.
 *
 * usage:
 *
 * // instantiate client with server info
 * $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
 *
 * // call method, get results
 * echo $soapclient->call( string methodname [ ,array parameters] );
 *
 * // bye bye client
 * unset($soapclient);
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @author   Scott Nichol <snichol@users.sourceforge.net>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access   public
 */
class nusoap_client extends nusoap_base  {
    
    var $username = '';				// Username for HTTP authentication
    var $password = '';				// Password for HTTP authentication
    var $authtype = '';				// Type of HTTP authentication
    var $certRequest = array();		// Certificate for HTTP SSL authentication
    var $requestHeaders = false;	// SOAP headers in request (text)
    var $responseHeaders = '';		// SOAP headers from response (incomplete namespace resolution) (text)
    var $responseHeader = NULL;		// SOAP Header from response (parsed)
    var $document = '';				// SOAP body response portion (incomplete namespace resolution) (text)
    var $endpoint;
    var $forceEndpoint = '';		// overrides WSDL endpoint
    var $proxyhost = '';
    var $proxyport = '';
    var $proxyusername = '';
    var $proxypassword = '';
    var $portName = '';				// port name to use in WSDL
    var $xml_encoding = '';			// character set encoding of incoming (response) messages
    var $http_encoding = false;
    var $timeout = 0;				// HTTP connection timeout
    var $response_timeout = 30;		// HTTP response timeout
    var $endpointType = '';			// soap|wsdl, empty for WSDL initialization error
    var $persistentConnection = false;
    var $defaultRpcParams = false;	// This is no longer used
    var $request = '';				// HTTP request
    var $response = '';				// HTTP response
    var $responseData = '';			// SOAP payload of response
    var $cookies = array();			// Cookies from response or for request
    var $decode_utf8 = true;		// toggles whether the parser decodes element content w/ utf8_decode()
    var $operations = array();		// WSDL operations, empty for WSDL initialization error
    var $curl_options = array();	// User-specified cURL options
    var $bindingType = '';			// WSDL operation binding type
    var $use_curl = false;			// whether to always try to use cURL
    
    /*
     * fault related variables
     */
    /**
     * @var      fault
     * @access   public
     */
    var $fault;
    /**
     * @var      faultcode
     * @access   public
     */
    var $faultcode;
    /**
     * @var      faultstring
     * @access   public
     */
    var $faultstring;
    /**
     * @var      faultdetail
     * @access   public
     */
    var $faultdetail;
    
    /**
     * constructor
     *
     * @param    mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
     * @param    mixed $wsdl optional, set to 'wsdl' or true if using WSDL
     * @param    string $proxyhost optional
     * @param    string $proxyport optional
     * @param	string $proxyusername optional
     * @param	string $proxypassword optional
     * @param	integer $timeout set the connection timeout
     * @param	integer $response_timeout set the response timeout
     * @param	string $portName optional portName in WSDL document
     * @access   public
     */
    function nusoap_client($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = ''){
        parent::nusoap_base();
        $this->endpoint = $endpoint;
        $this->proxyhost = $proxyhost;
        $this->proxyport = $proxyport;
        $this->proxyusername = $proxyusername;
        $this->proxypassword = $proxypassword;
        $this->timeout = $timeout;
        $this->response_timeout = $response_timeout;
        $this->portName = $portName;
        
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
        $this->appendDebug('endpoint=' . $this->varDump($endpoint));
        
        // make values
        if($wsdl){
            if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
                $this->wsdl = $endpoint;
                $this->endpoint = $this->wsdl->wsdl;
                $this->wsdlFile = $this->endpoint;
                $this->debug('existing wsdl instance created from ' . $this->endpoint);
                $this->checkWSDL();
            } else {
                $this->wsdlFile = $this->endpoint;
                $this->wsdl = null;
                $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
            }
            $this->endpointType = 'wsdl';
        } else {
            $this->debug("instantiate SOAP with endpoint at $endpoint");
            $this->endpointType = 'soap';
        }
    }
    
    /**
     * calls method, returns PHP native type
     *
     * @param    string $operation SOAP server URL or path
     * @param    mixed $params An array, associative or simple, of the parameters
     *			              for the method call, or a string that is the XML
     *			              for the call.  For rpc style, this call will
     *			              wrap the XML in a tag named after the method, as
     *			              well as the SOAP Envelope and Body.  For document
     *			              style, this will only wrap with the Envelope and Body.
     *			              IMPORTANT: when using an array with document style,
     *			              in which case there
     *                         is really one parameter, the root of the fragment
     *                         used in the call, which encloses what programmers
     *                         normally think of parameters.  A parameter array
     *                         *must* include the wrapper.
     * @param	string $namespace optional method namespace (WSDL can override)
     * @param	string $soapAction optional SOAPAction value (WSDL can override)
     * @param	mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
     * @param	boolean $rpcParams optional (no longer used)
     * @param	string	$style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
     * @param	string	$use optional (encoded|literal) the use when serializing parameters (WSDL can override)
     * @return	mixed	response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
     * @access   public
     */
    function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
        $this->operation = $operation;
        $this->fault = false;
        $this->setError('');
        $this->request = '';
        $this->response = '';
        $this->responseData = '';
        $this->faultstring = '';
        $this->faultcode = '';
        $this->opData = array();
        
        $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
        $this->appendDebug('params=' . $this->varDump($params));
        $this->appendDebug('headers=' . $this->varDump($headers));
        if ($headers) {
            $this->requestHeaders = $headers;
        }
        if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
            $this->loadWSDL();
            if ($this->getError())
                return false;
        }
        // serialize parameters
        if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
            // use WSDL for operation
            $this->opData = $opData;
            $this->debug("found operation");
            $this->appendDebug('opData=' . $this->varDump($opData));
            if (isset($opData['soapAction'])) {
                $soapAction = $opData['soapAction'];
            }
            if (! $this->forceEndpoint) {
                $this->endpoint = $opData['endpoint'];
            } else {
                $this->endpoint = $this->forceEndpoint;
            }
            $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] :	$namespace;
            $style = $opData['style'];
            $use = $opData['input']['use'];
            // add ns to ns array
            if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
                $nsPrefix = 'ns' . rand(1000, 9999);
                $this->wsdl->namespaces[$nsPrefix] = $namespace;
            }
            $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
            // serialize payload
            if (is_string($params)) {
                $this->debug("serializing param string for WSDL operation $operation");
                $payload = $params;
            } elseif (is_array($params)) {
                $this->debug("serializing param array for WSDL operation $operation");
                $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params,$this->bindingType);
            } else {
                $this->debug('params must be array or string');
                $this->setError('params must be array or string');
                return false;
            }
            $usedNamespaces = $this->wsdl->usedNamespaces;
            if (isset($opData['input']['encodingStyle'])) {
                $encodingStyle = $opData['input']['encodingStyle'];
            } else {
                $encodingStyle = '';
            }
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            if ($errstr = $this->wsdl->getError()) {
                $this->debug('got wsdl error: '.$errstr);
                $this->setError('wsdl error: '.$errstr);
                return false;
            }
        } elseif($this->endpointType == 'wsdl') {
            // operation not in WSDL
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            $this->setError('operation '.$operation.' not present in WSDL.');
            $this->debug("operation '$operation' not present in WSDL.");
            return false;
        } else {
            // no WSDL
            //$this->namespaces['ns1'] = $namespace;
            $nsPrefix = 'ns' . rand(1000, 9999);
            // serialize
            $payload = '';
            if (is_string($params)) {
                $this->debug("serializing param string for operation $operation");
                $payload = $params;
            } elseif (is_array($params)) {
                $this->debug("serializing param array for operation $operation");
                foreach($params as $k => $v){
                    $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
                }
            } else {
                $this->debug('params must be array or string');
                $this->setError('params must be array or string');
                return false;
            }
            $usedNamespaces = array();
            if ($use == 'encoded') {
                $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
            } else {
                $encodingStyle = '';
            }
        }
        // wrap RPC calls with method element
        if ($style == 'rpc') {
            if ($use == 'literal') {
                $this->debug("wrapping RPC request with literal method element");
                if ($namespace) {
                    // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
                    $payload .
                    "</$nsPrefix:$operation>";
                } else {
                    $payload = "<$operation>" . $payload . "</$operation>";
                }
            } else {
                $this->debug("wrapping RPC request with encoded method element");
                if ($namespace) {
                    $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
                    $payload .
                    "</$nsPrefix:$operation>";
                } else {
                    $payload = "<$operation>" .
                    $payload .
                    "</$operation>";
                }
            }
        }
        // serialize envelope
        $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
        $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
        $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
        // send
        $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
        if($errstr = $this->getError()){
            $this->debug('Error: '.$errstr);
            return false;
        } else {
            $this->return = $return;
            $this->debug('sent message successfully and got a(n) '.gettype($return));
            $this->appendDebug('return=' . $this->varDump($return));
            
            // fault?
            if(is_array($return) && isset($return['faultcode'])){
                $this->debug('got fault');
                $this->setError($return['faultcode'].': '.$return['faultstring']);
                $this->fault = true;
                foreach($return as $k => $v){
                    $this->$k = $v;
                    $this->debug("$k = $v<br>");
                }
                return $return;
            } elseif ($style == 'document') {
                // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
                // we are only going to return the first part here...sorry about that
                return $return;
            } else {
                // array of return values
                if(is_array($return)){
                    // multiple 'out' parameters, which we return wrapped up
                    // in the array
                    if(sizeof($return) > 1){
                        return $return;
                    }
                    // single 'out' parameter (normally the return value)
                    $return = array_shift($return);
                    $this->debug('return shifted value: ');
                    $this->appendDebug($this->varDump($return));
                    return $return;
                    // nothing returned (ie, echoVoid)
                } else {
                    return "";
                }
            }
        }
    }
    
    /**
     * check WSDL passed as an instance or pulled from an endpoint
     *
     * @access   private
     */
    function checkWSDL() {
        $this->appendDebug($this->wsdl->getDebug());
        $this->wsdl->clearDebug();
        $this->debug('checkWSDL');
        // catch errors
        if ($errstr = $this->wsdl->getError()) {
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            $this->debug('got wsdl error: '.$errstr);
            $this->setError('wsdl error: '.$errstr);
        } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) {
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            $this->bindingType = 'soap';
            $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
        } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) {
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            $this->bindingType = 'soap12';
            $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
            $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
        } else {
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            $this->debug('getOperations returned false');
            $this->setError('no operations defined in the WSDL document!');
        }
    }
    
    /**
     * instantiate wsdl object and parse wsdl file
     *
     * @access	public
     */
    function loadWSDL() {
        $this->debug('instantiating wsdl class with doc: '.$this->wsdlFile);
        $this->wsdl = new wsdl('',$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout,$this->curl_options,$this->use_curl);
        $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
        $this->wsdl->fetchWSDL($this->wsdlFile);
        $this->checkWSDL();
    }
    
    /**
     * get available data pertaining to an operation
     *
     * @param    string $operation operation name
     * @return	array array of data pertaining to the operation
     * @access   public
     */
    function getOperationData($operation){
        if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
            $this->loadWSDL();
            if ($this->getError())
                return false;
        }
        if(isset($this->operations[$operation])){
            return $this->operations[$operation];
        }
        $this->debug("No data for operation: $operation");
    }
    
    /**
     * send the SOAP message
     *
     * Note: if the operation has multiple return values
     * the return value of this method will be an array
     * of those values.
     *
     * @param    string $msg a SOAPx4 soapmsg object
     * @param    string $soapaction SOAPAction value
     * @param    integer $timeout set connection timeout in seconds
     * @param	integer $response_timeout set response timeout in seconds
     * @return	mixed native PHP types.
     * @access   private
     */
    function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
        $this->checkCookies();
        // detect transport
        switch(true){
            // http(s)
            case preg_match('/^http/',$this->endpoint):
                $this->debug('transporting via HTTP');
                if($this->persistentConnection == true && is_object($this->persistentConnection)){
                    $http =& $this->persistentConnection;
                } else {
                    $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
                    if ($this->persistentConnection) {
                        $http->usePersistentConnection();
                    }
                }
                $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
                $http->setSOAPAction($soapaction);
                if($this->proxyhost && $this->proxyport){
                    $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
                }
                if($this->authtype != '') {
                    $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
                }
                if($this->http_encoding != ''){
                    $http->setEncoding($this->http_encoding);
                }
                $this->debug('sending message, length='.strlen($msg));
                if(preg_match('/^http:/',$this->endpoint)){
                    //if(strpos($this->endpoint,'http:')){
                    $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
                } elseif(preg_match('/^https/',$this->endpoint)){
                    //} elseif(strpos($this->endpoint,'https:')){
                    //if(phpversion() == '4.3.0-dev'){
                    //$response = $http->send($msg,$timeout,$response_timeout);
                        //$this->request = $http->outgoing_payload;
                        //$this->response = $http->incoming_payload;
                        //} else
                        $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies);
                } else {
                    $this->setError('no http/s in endpoint url');
                }
                $this->request = $http->outgoing_payload;
                $this->response = $http->incoming_payload;
                $this->appendDebug($http->getDebug());
                $this->UpdateCookies($http->incoming_cookies);
                
                // save transport object if using persistent connections
                if ($this->persistentConnection) {
                    $http->clearDebug();
                    if (!is_object($this->persistentConnection)) {
                        $this->persistentConnection = $http;
                    }
                }
                
                if($err = $http->getError()){
                    $this->setError('HTTP Error: '.$err);
                    return false;
                } elseif($this->getError()){
                    return false;
                } else {
                    $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']);
                    return $this->parseResponse($http->incoming_headers, $this->responseData);
                }
                break;
            default:
                $this->setError('no transport found, or selected transport is not yet supported!');
                return false;
                break;
        }
    }
    
    /**
     * processes SOAP message returned from server
     *
     * @param	array	$headers	The HTTP headers
     * @param	string	$data		unprocessed response data from server
     * @return	mixed	value of the message, decoded into a PHP type
     * @access   private
     */
    function parseResponse($headers, $data) {
        $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
        $this->appendDebug($this->varDump($headers));
        if (!isset($headers['content-type'])) {
            $this->setError('Response not of type text/xml (no content-type header)');
            return false;
        }
        if (!strstr($headers['content-type'], 'text/xml')) {
            $this->setError('Response not of type text/xml: ' . $headers['content-type']);
            return false;
        }
        if (strpos($headers['content-type'], '=')) {
            $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
            $this->debug('Got response encoding: ' . $enc);
            if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
                $this->xml_encoding = strtoupper($enc);
            } else {
                $this->xml_encoding = 'US-ASCII';
            }
        } else {
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
            $this->xml_encoding = 'ISO-8859-1';
        }
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
        $parser = new nusoap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
        // add parser debug data to our debug
        $this->appendDebug($parser->getDebug());
        // if parse errors
        if($errstr = $parser->getError()){
            $this->setError( $errstr);
            // destroy the parser object
            unset($parser);
            return false;
        } else {
            // get SOAP headers
            $this->responseHeaders = $parser->getHeaders();
            // get SOAP headers
            $this->responseHeader = $parser->get_soapheader();
            // get decoded message
            $return = $parser->get_soapbody();
            // add document for doclit support
            $this->document = $parser->document;
            // destroy the parser object
            unset($parser);
            // return decode message
            return $return;
        }
    }
    
    /**
     * sets user-specified cURL options
     *
     * @param	mixed $option The cURL option (always integer?)
     * @param	mixed $value The cURL option value
     * @access   public
     */
    function setCurlOption($option, $value) {
        $this->debug("setCurlOption option=$option, value=");
        $this->appendDebug($this->varDump($value));
        $this->curl_options[$option] = $value;
    }
    
    /**
     * sets the SOAP endpoint, which can override WSDL
     *
     * @param	string $endpoint The endpoint URL to use, or empty string or false to prevent override
     * @access   public
     */
    function setEndpoint($endpoint) {
        $this->debug("setEndpoint(\"$endpoint\")");
        $this->forceEndpoint = $endpoint;
    }
    
    /**
     * set the SOAP headers
     *
     * @param	mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
     * @access   public
     */
    function setHeaders($headers){
        $this->debug("setHeaders headers=");
        $this->appendDebug($this->varDump($headers));
        $this->requestHeaders = $headers;
    }
    
    /**
     * get the SOAP response headers (namespace resolution incomplete)
     *
     * @return	string
     * @access   public
     */
    function getHeaders(){
        return $this->responseHeaders;
    }
    
    /**
     * get the SOAP response Header (parsed)
     *
     * @return	mixed
     * @access   public
     */
    function getHeader(){
        return $this->responseHeader;
    }
    
    /**
     * set proxy info here
     *
     * @param    string $proxyhost
     * @param    string $proxyport
     * @param	string $proxyusername
     * @param	string $proxypassword
     * @access   public
     */
    function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
        $this->proxyhost = $proxyhost;
        $this->proxyport = $proxyport;
        $this->proxyusername = $proxyusername;
        $this->proxypassword = $proxypassword;
    }
    
    /**
     * if authenticating, set user credentials here
     *
     * @param    string $username
     * @param    string $password
     * @param	string $authtype (basic|digest|certificate|ntlm)
     * @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
     * @access   public
     */
    function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
        $this->appendDebug($this->varDump($certRequest));
        $this->username = $username;
        $this->password = $password;
        $this->authtype = $authtype;
        $this->certRequest = $certRequest;
    }
    
    /**
     * use HTTP encoding
     *
     * @param    string $enc HTTP encoding
     * @access   public
     */
    function setHTTPEncoding($enc='gzip, deflate'){
        $this->debug("setHTTPEncoding(\"$enc\")");
        $this->http_encoding = $enc;
    }
    
    /**
     * Set whether to try to use cURL connections if possible
     *
     * @param	boolean $use Whether to try to use cURL
     * @access   public
     */
    function setUseCURL($use) {
        $this->debug("setUseCURL($use)");
        $this->use_curl = $use;
    }
    
    /**
     * use HTTP persistent connections if possible
     *
     * @access   public
     */
    function useHTTPPersistentConnection(){
        $this->debug("useHTTPPersistentConnection");
        $this->persistentConnection = true;
    }
    
    /**
     * gets the default RPC parameter setting.
     * If true, default is that call params are like RPC even for document style.
     * Each call() can override this value.
     *
     * This is no longer used.
     *
     * @return boolean
     * @access public
     * @deprecated
     */
    function getDefaultRpcParams() {
        return $this->defaultRpcParams;
    }
    
    /**
     * sets the default RPC parameter setting.
     * If true, default is that call params are like RPC even for document style
     * Each call() can override this value.
     *
     * This is no longer used.
     *
     * @param    boolean $rpcParams
     * @access public
     * @deprecated
     */
    function setDefaultRpcParams($rpcParams) {
        $this->defaultRpcParams = $rpcParams;
    }
    
    /**
     * dynamically creates an instance of a proxy class,
     * allowing user to directly call methods from wsdl
     *
     * @return   object soap_proxy object
     * @access   public
     */
    function getProxy() {
        $r = rand();
        $evalStr = $this->_getProxyClassCode($r);
        //$this->debug("proxy class: $evalStr");
        if ($this->getError()) {
            $this->debug("Error from _getProxyClassCode, so return NULL");
            return null;
        }
        // eval the class
        eval($evalStr);
        // instantiate proxy object
        eval("\$proxy = new nusoap_proxy_$r('');");
        // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
        $proxy->endpointType = 'wsdl';
        $proxy->wsdlFile = $this->wsdlFile;
        $proxy->wsdl = $this->wsdl;
        $proxy->operations = $this->operations;
        $proxy->defaultRpcParams = $this->defaultRpcParams;
        // transfer other state
        $proxy->soap_defencoding = $this->soap_defencoding;
        $proxy->username = $this->username;
        $proxy->password = $this->password;
        $proxy->authtype = $this->authtype;
        $proxy->certRequest = $this->certRequest;
        $proxy->requestHeaders = $this->requestHeaders;
        $proxy->endpoint = $this->endpoint;
        $proxy->forceEndpoint = $this->forceEndpoint;
        $proxy->proxyhost = $this->proxyhost;
        $proxy->proxyport = $this->proxyport;
        $proxy->proxyusername = $this->proxyusername;
        $proxy->proxypassword = $this->proxypassword;
        $proxy->http_encoding = $this->http_encoding;
        $proxy->timeout = $this->timeout;
        $proxy->response_timeout = $this->response_timeout;
        $proxy->persistentConnection = &$this->persistentConnection;
        $proxy->decode_utf8 = $this->decode_utf8;
        $proxy->curl_options = $this->curl_options;
        $proxy->bindingType = $this->bindingType;
        $proxy->use_curl = $this->use_curl;
        return $proxy;
    }
    
    /**
     * dynamically creates proxy class code
     *
     * @return   string PHP/NuSOAP code for the proxy class
     * @access   private
     */
    function _getProxyClassCode($r) {
        $this->debug("in getProxy endpointType=$this->endpointType");
        $this->appendDebug("wsdl=" . $this->varDump($this->wsdl));
        if ($this->endpointType != 'wsdl') {
            $evalStr = 'A proxy can only be created for a WSDL client';
            $this->setError($evalStr);
            $evalStr = "echo \"$evalStr\";";
            return $evalStr;
        }
        if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
            $this->loadWSDL();
            if ($this->getError()) {
                return "echo \"" . $this->getError() . "\";";
            }
        }
        $evalStr = '';
        foreach ($this->operations as $operation => $opData) {
            if ($operation != '') {
                // create param string and param comment string
                if (sizeof($opData['input']['parts']) > 0) {
                    $paramStr = '';
                    $paramArrayStr = '';
                    $paramCommentStr = '';
                    foreach ($opData['input']['parts'] as $name => $type) {
                        $paramStr .= "\$$name, ";
                        $paramArrayStr .= "'$name' => \$$name, ";
                        $paramCommentStr .= "$type \$$name, ";
                    }
                    $paramStr = substr($paramStr, 0, strlen($paramStr)-2);
                    $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2);
                    $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2);
                } else {
                    $paramStr = '';
                    $paramArrayStr = '';
                    $paramCommentStr = 'void';
                }
                $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
                $evalStr .= "// $paramCommentStr
	function " . str_replace('.', '__', $operation) . "($paramStr) {
		\$params = array($paramArrayStr);
		return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
	}
	";
                unset($paramStr);
                unset($paramCommentStr);
            }
        }
        $evalStr = 'class nusoap_proxy_'.$r.' extends nusoap_client {
	'.$evalStr.'
}';
        return $evalStr;
    }
    
    /**
     * dynamically creates proxy class code
     *
     * @return   string PHP/NuSOAP code for the proxy class
     * @access   public
     */
    function getProxyClassCode() {
        $r = rand();
        return $this->_getProxyClassCode($r);
    }
    
    /**
     * gets the HTTP body for the current request.
     *
     * @param string $soapmsg The SOAP payload
     * @return string The HTTP body, which includes the SOAP payload
     * @access private
     */
    function getHTTPBody($soapmsg) {
        return $soapmsg;
    }
    
    /**
     * gets the HTTP content type for the current request.
     *
     * Note: getHTTPBody must be called before this.
     *
     * @return string the HTTP content type for the current request.
     * @access private
     */
    function getHTTPContentType() {
        return 'text/xml';
    }
    
    /**
     * gets the HTTP content type charset for the current request.
     * returns false for non-text content types.
     *
     * Note: getHTTPBody must be called before this.
     *
     * @return string the HTTP content type charset for the current request.
     * @access private
     */
    function getHTTPContentTypeCharset() {
        return $this->soap_defencoding;
    }
    
    /*
     * whether or not parser should decode utf8 element content
     *
     * @return   always returns true
     * @access   public
     */
    function decodeUTF8($bool){
        $this->decode_utf8 = $bool;
        return true;
    }
    
    /**
     * adds a new Cookie into $this->cookies array
     *
     * @param	string $name Cookie Name
     * @param	string $value Cookie Value
     * @return	boolean if cookie-set was successful returns true, else false
     * @access	public
     */
    function setCookie($name, $value) {
        if (strlen($name) == 0) {
            return false;
        }
        $this->cookies[] = array('name' => $name, 'value' => $value);
        return true;
    }
    
    /**
     * gets all Cookies
     *
     * @return   array with all internal cookies
     * @access   public
     */
    function getCookies() {
        return $this->cookies;
    }
    
    /**
     * checks all Cookies and delete those which are expired
     *
     * @return   boolean always return true
     * @access   private
     */
    function checkCookies() {
        if (sizeof($this->cookies) == 0) {
            return true;
        }
        $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
        $curr_cookies = $this->cookies;
        $this->cookies = array();
        foreach ($curr_cookies as $cookie) {
            if (! is_array($cookie)) {
                $this->debug('Remove cookie that is not an array');
                continue;
            }
            if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
                if (strtotime($cookie['expires']) > time()) {
                    $this->cookies[] = $cookie;
                } else {
                    $this->debug('Remove expired cookie ' . $cookie['name']);
                }
            } else {
                $this->cookies[] = $cookie;
            }
        }
        $this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array');
        return true;
    }
    
    /**
     * updates the current cookies with a new set
     *
     * @param	array $cookies new cookies with which to update current ones
     * @return	boolean always return true
     * @access	private
     */
    function UpdateCookies($cookies) {
        if (sizeof($this->cookies) == 0) {
            // no existing cookies: take whatever is new
            if (sizeof($cookies) > 0) {
                $this->debug('Setting new cookie(s)');
                $this->cookies = $cookies;
            }
            return true;
        }
        if (sizeof($cookies) == 0) {
            // no new cookies: keep what we've got
            return true;
        }
        // merge
        foreach ($cookies as $newCookie) {
            if (!is_array($newCookie)) {
                continue;
            }
            if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
                continue;
            }
            $newName = $newCookie['name'];
            
            $found = false;
            for ($i = 0; $i < count($this->cookies); $i++) {
                $cookie = $this->cookies[$i];
                if (!is_array($cookie)) {
                    continue;
                }
                if (!isset($cookie['name'])) {
                    continue;
                }
                if ($newName != $cookie['name']) {
                    continue;
                }
                $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
                $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
                if ($newDomain != $domain) {
                    continue;
                }
                $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
                $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
                if ($newPath != $path) {
                    continue;
                }
                $this->cookies[$i] = $newCookie;
                $found = true;
                $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
                break;
            }
            if (! $found) {
                $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
                $this->cookies[] = $newCookie;
            }
        }
        return true;
    }
}

<?php
namespace com\linways\base\soap;
/**
* parses an XML Schema, allows access to it's data, other utility methods.
* imperfect, no validation... yet, but quite functional.
*
* @author   Dietrich Ayala <dietrich@ganx4.com>
* @author   Scott Nichol <snichol@users.sourceforge.net>
* @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
* @access   public
*/
class nusoap_xmlschema extends nusoap_base  {
	
	// files
	var $schema = '';
	var $xml = '';
	// namespaces
	var $enclosingNamespaces;
	// schema info
	var $schemaInfo = array();
	var $schemaTargetNamespace = '';
	// types, elements, attributes defined by the schema
	var $attributes = array();
	var $complexTypes = array();
	var $complexTypeStack = array();
	var $currentComplexType = null;
	var $elements = array();
	var $elementStack = array();
	var $currentElement = null;
	var $simpleTypes = array();
	var $simpleTypeStack = array();
	var $currentSimpleType = null;
	// imports
	var $imports = array();
	// parser vars
	var $parser;
	var $position = 0;
	var $depth = 0;
	var $depth_array = array();
	var $message = array();
	var $defaultNamespace = array();
    
	/**
	* constructor
	*
	* @param    string $schema schema document URI
	* @param    string $xml xml document URI
	* @param	string $namespaces namespaces defined in enclosing XML
	* @access   public
	*/
	function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){
		parent::nusoap_base();
		$this->debug('nusoap_xmlschema class instantiated, inside constructor');
		// files
		$this->schema = $schema;
		$this->xml = $xml;

		// namespaces
		$this->enclosingNamespaces = $namespaces;
		$this->namespaces = array_merge($this->namespaces, $namespaces);

		// parse schema file
		if($schema != ''){
			$this->debug('initial schema file: '.$schema);
			$this->parseFile($schema, 'schema');
		}

		// parse xml file
		if($xml != ''){
			$this->debug('initial xml file: '.$xml);
			$this->parseFile($xml, 'xml');
		}

	}

    /**
    * parse an XML file
    *
    * @param string $xml path/URL to XML file
    * @param string $type (schema | xml)
	* @return boolean
    * @access public
    */
	function parseFile($xml,$type){
		// parse xml file
		if($xml != ""){
			$xmlStr = @join("",@file($xml));
			if($xmlStr == ""){
				$msg = 'Error reading XML from '.$xml;
				$this->setError($msg);
				$this->debug($msg);
			return false;
			} else {
				$this->debug("parsing $xml");
				$this->parseString($xmlStr,$type);
				$this->debug("done parsing $xml");
			return true;
			}
		}
		return false;
	}

	/**
	* parse an XML string
	*
	* @param    string $xml path or URL
    * @param	string $type (schema|xml)
	* @access   private
	*/
	function parseString($xml,$type){
		// parse xml string
		if($xml != ""){

	    	// Create an XML parser.
	    	$this->parser = xml_parser_create();
	    	// Set the options for parsing the XML data.
	    	xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);

	    	// Set the object for the parser.
	    	xml_set_object($this->parser, $this);

	    	// Set the element handlers for the parser.
			if($type == "schema"){
		    	xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
		    	xml_set_character_data_handler($this->parser,'schemaCharacterData');
			} elseif($type == "xml"){
				xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
		    	xml_set_character_data_handler($this->parser,'xmlCharacterData');
			}

		    // Parse the XML file.
		    if(!xml_parse($this->parser,$xml,true)){
			// Display an error message.
				$errstr = sprintf('XML error parsing XML schema on line %d: %s',
				xml_get_current_line_number($this->parser),
				xml_error_string(xml_get_error_code($this->parser))
				);
				$this->debug($errstr);
				$this->debug("XML payload:\n" . $xml);
				$this->setError($errstr);
	    	}
            
			xml_parser_free($this->parser);
		} else{
			$this->debug('no xml passed to parseString()!!');
			$this->setError('no xml passed to parseString()!!');
		}
	}

	/**
	 * gets a type name for an unnamed type
	 *
	 * @param	string	Element name
	 * @return	string	A type name for an unnamed type
	 * @access	private
	 */
	function CreateTypeName($ename) {
		$scope = '';
		for ($i = 0; $i < count($this->complexTypeStack); $i++) {
			$scope .= $this->complexTypeStack[$i] . '_';
		}
		return $scope . $ename . '_ContainedType';
	}
	
	/**
	* start-element handler
	*
	* @param    string $parser XML parser object
	* @param    string $name element name
	* @param    string $attrs associative array of attributes
	* @access   private
	*/
	function schemaStartElement($parser, $name, $attrs) {
		
		// position in the total number of elements, starting from 0
		$pos = $this->position++;
		$depth = $this->depth++;
		// set self as current value for this depth
		$this->depth_array[$depth] = $pos;
		$this->message[$pos] = array('cdata' => ''); 
		if ($depth > 0) {
			$this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
		} else {
			$this->defaultNamespace[$pos] = false;
		}

		// get element prefix
		if($prefix = $this->getPrefix($name)){
			// get unqualified name
			$name = $this->getLocalPart($name);
		} else {
        	$prefix = '';
        }
		
        // loop thru attributes, expanding, and registering namespace declarations
        if(count($attrs) > 0){
        	foreach($attrs as $k => $v){
                // if ns declarations, add to class level array of valid namespaces
				if(preg_match('/^xmlns/',$k)){
                	//$this->xdebug("$k: $v");
                	//$this->xdebug('ns_prefix: '.$this->getPrefix($k));
                	if($ns_prefix = substr(strrchr($k,':'),1)){
                		//$this->xdebug("Add namespace[$ns_prefix] = $v");
						$this->namespaces[$ns_prefix] = $v;
					} else {
						$this->defaultNamespace[$pos] = $v;
						if (! $this->getPrefixFromNamespace($v)) {
							$this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
						}
					}
					if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
						$this->XMLSchemaVersion = $v;
						$this->namespaces['xsi'] = $v.'-instance';
					}
				}
        	}
        	foreach($attrs as $k => $v){
                // expand each attribute
                $k = strpos($k,':') ? $this->expandQname($k) : $k;
                $v = strpos($v,':') ? $this->expandQname($v) : $v;
        		$eAttrs[$k] = $v;
        	}
        	$attrs = $eAttrs;
        } else {
        	$attrs = array();
        }
		// find status, register data
		switch($name){
			case 'all':			// (optional) compositor content for a complexType
			case 'choice':
			case 'group':
			case 'sequence':
				//$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
				$this->complexTypes[$this->currentComplexType]['compositor'] = $name;
				//if($name == 'all' || $name == 'sequence'){
				//	$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
				//}
			break;
			case 'attribute':	// complexType attribute
            	//$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
            	$this->xdebug("parsing attribute:");
            	$this->appendDebug($this->varDump($attrs));
				if (!isset($attrs['form'])) {
					// TODO: handle globals
					$attrs['form'] = $this->schemaInfo['attributeFormDefault'];
				}
            	if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
					$v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
					if (!strpos($v, ':')) {
						// no namespace in arrayType attribute value...
						if ($this->defaultNamespace[$pos]) {
							// ...so use the default
							$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
						}
					}
            	}
                if(isset($attrs['name'])){
					$this->attributes[$attrs['name']] = $attrs;
					$aname = $attrs['name'];
				} elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
					if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
	                	$aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
	                } else {
	                	$aname = '';
	                }
				} elseif(isset($attrs['ref'])){
					$aname = $attrs['ref'];
                    $this->attributes[$attrs['ref']] = $attrs;
				}
                
				if($this->currentComplexType){	// This should *always* be
					$this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
				}
				// arrayType attribute
				if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
					$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
                	$prefix = $this->getPrefix($aname);
					if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
						$v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
					} else {
						$v = '';
					}
                    if(strpos($v,'[,]')){
                        $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
                    }
                    $v = substr($v,0,strpos($v,'[')); // clip the []
                    if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
                        $v = $this->XMLSchemaVersion.':'.$v;
                    }
                    $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
				}
			break;
			case 'complexContent':	// (optional) content for a complexType
				$this->xdebug("do nothing for element $name");
			break;
			case 'complexType':
				array_push($this->complexTypeStack, $this->currentComplexType);
				if(isset($attrs['name'])){
					// TODO: what is the scope of named complexTypes that appear
					//       nested within other c complexTypes?
					$this->xdebug('processing named complexType '.$attrs['name']);
					//$this->currentElement = false;
					$this->currentComplexType = $attrs['name'];
					$this->complexTypes[$this->currentComplexType] = $attrs;
					$this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
					// This is for constructs like
					//           <complexType name="ListOfString" base="soap:Array">
					//                <sequence>
					//                    <element name="string" type="xsd:string"
					//                        minOccurs="0" maxOccurs="unbounded" />
					//                </sequence>
					//            </complexType>
					if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
						$this->xdebug('complexType is unusual array');
						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
					} else {
						$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
					}
				} else {
					$name = $this->CreateTypeName($this->currentElement);
					$this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
					$this->currentComplexType = $name;
					//$this->currentElement = false;
					$this->complexTypes[$this->currentComplexType] = $attrs;
					$this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
					// This is for constructs like
					//           <complexType name="ListOfString" base="soap:Array">
					//                <sequence>
					//                    <element name="string" type="xsd:string"
					//                        minOccurs="0" maxOccurs="unbounded" />
					//                </sequence>
					//            </complexType>
					if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
						$this->xdebug('complexType is unusual array');
						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
					} else {
						$this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
					}
				}
				$this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
			break;
			case 'element':
				array_push($this->elementStack, $this->currentElement);
				if (!isset($attrs['form'])) {
					if ($this->currentComplexType) {
						$attrs['form'] = $this->schemaInfo['elementFormDefault'];
					} else {
						// global
						$attrs['form'] = 'qualified';
					}
				}
				if(isset($attrs['type'])){
					$this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
					if (! $this->getPrefix($attrs['type'])) {
						if ($this->defaultNamespace[$pos]) {
							$attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
							$this->xdebug('used default namespace to make type ' . $attrs['type']);
						}
					}
					// This is for constructs like
					//           <complexType name="ListOfString" base="soap:Array">
					//                <sequence>
					//                    <element name="string" type="xsd:string"
					//                        minOccurs="0" maxOccurs="unbounded" />
					//                </sequence>
					//            </complexType>
					if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
						$this->xdebug('arrayType for unusual array is ' . $attrs['type']);
						$this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
					}
					$this->currentElement = $attrs['name'];
					$ename = $attrs['name'];
				} elseif(isset($attrs['ref'])){
					$this->xdebug("processing element as ref to ".$attrs['ref']);
					$this->currentElement = "ref to ".$attrs['ref'];
					$ename = $this->getLocalPart($attrs['ref']);
				} else {
					$type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
					$this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
					$this->currentElement = $attrs['name'];
					$attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
					$ename = $attrs['name'];
				}
				if (isset($ename) && $this->currentComplexType) {
					$this->xdebug("add element $ename to complexType $this->currentComplexType");
					$this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
				} elseif (!isset($attrs['ref'])) {
					$this->xdebug("add element $ename to elements array");
					$this->elements[ $attrs['name'] ] = $attrs;
					$this->elements[ $attrs['name'] ]['typeClass'] = 'element';
				}
			break;
			case 'enumeration':	//	restriction value list member
				$this->xdebug('enumeration ' . $attrs['value']);
				if ($this->currentSimpleType) {
					$this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
				} elseif ($this->currentComplexType) {
					$this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
				}
			break;
			case 'extension':	// simpleContent or complexContent type extension
				$this->xdebug('extension ' . $attrs['base']);
				if ($this->currentComplexType) {
					$ns = $this->getPrefix($attrs['base']);
					if ($ns == '') {
						$this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
					} else {
						$this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
					}
				} else {
					$this->xdebug('no current complexType to set extensionBase');
				}
			break;
			case 'import':
			    if (isset($attrs['schemaLocation'])) {
					$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
                    $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
				} else {
					$this->xdebug('import namespace ' . $attrs['namespace']);
                    $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
					if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
						$this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
					}
				}
			break;
			case 'include':
			    if (isset($attrs['schemaLocation'])) {
					$this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
                    $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
				} else {
					$this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
				}
			break;
			case 'list':	// simpleType value list
				$this->xdebug("do nothing for element $name");
			break;
			case 'restriction':	// simpleType, simpleContent or complexContent value restriction
				$this->xdebug('restriction ' . $attrs['base']);
				if($this->currentSimpleType){
					$this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
				} elseif($this->currentComplexType){
					$this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
					if(strstr($attrs['base'],':') == ':Array'){
						$this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
					}
				}
			break;
			case 'schema':
				$this->schemaInfo = $attrs;
				$this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
				if (isset($attrs['targetNamespace'])) {
					$this->schemaTargetNamespace = $attrs['targetNamespace'];
				}
				if (!isset($attrs['elementFormDefault'])) {
					$this->schemaInfo['elementFormDefault'] = 'unqualified';
				}
				if (!isset($attrs['attributeFormDefault'])) {
					$this->schemaInfo['attributeFormDefault'] = 'unqualified';
				}
			break;
			case 'simpleContent':	// (optional) content for a complexType
				if ($this->currentComplexType) {	// This should *always* be
					$this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
				} else {
					$this->xdebug("do nothing for element $name because there is no current complexType");
				}
			break;
			case 'simpleType':
				array_push($this->simpleTypeStack, $this->currentSimpleType);
				if(isset($attrs['name'])){
					$this->xdebug("processing simpleType for name " . $attrs['name']);
					$this->currentSimpleType = $attrs['name'];
					$this->simpleTypes[ $attrs['name'] ] = $attrs;
					$this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
					$this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
				} else {
					$name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
					$this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
					$this->currentSimpleType = $name;
					//$this->currentElement = false;
					$this->simpleTypes[$this->currentSimpleType] = $attrs;
					$this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
				}
			break;
			case 'union':	// simpleType type list
				$this->xdebug("do nothing for element $name");
			break;
			default:
				$this->xdebug("do not have any logic to process element $name");
		}
	}

	/**
	* end-element handler
	*
	* @param    string $parser XML parser object
	* @param    string $name element name
	* @access   private
	*/
	function schemaEndElement($parser, $name) {
		// bring depth down a notch
		$this->depth--;
		// position of current element is equal to the last value left in depth_array for my depth
		if(isset($this->depth_array[$this->depth])){
        	$pos = $this->depth_array[$this->depth];
        }
		// get element prefix
		if ($prefix = $this->getPrefix($name)){
			// get unqualified name
			$name = $this->getLocalPart($name);
		} else {
        	$prefix = '';
        }
		// move on...
		if($name == 'complexType'){
			$this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
			$this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
			$this->currentComplexType = array_pop($this->complexTypeStack);
			//$this->currentElement = false;
		}
		if($name == 'element'){
			$this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
			$this->currentElement = array_pop($this->elementStack);
		}
		if($name == 'simpleType'){
			$this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
			$this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
			$this->currentSimpleType = array_pop($this->simpleTypeStack);
		}
	}

	/**
	* element content handler
	*
	* @param    string $parser XML parser object
	* @param    string $data element content
	* @access   private
	*/
	function schemaCharacterData($parser, $data){
		$pos = $this->depth_array[$this->depth - 1];
		$this->message[$pos]['cdata'] .= $data;
	}

	/**
	* serialize the schema
	*
	* @access   public
	*/
	function serializeSchema(){

		$schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
		$xml = '';
		// imports
		if (sizeof($this->imports) > 0) {
			foreach($this->imports as $ns => $list) {
				foreach ($list as $ii) {
					if ($ii['location'] != '') {
						$xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
					} else {
						$xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
					}
				}
			} 
		} 
		// complex types
		foreach($this->complexTypes as $typeName => $attrs){
			$contentStr = '';
			// serialize child elements
			if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
				foreach($attrs['elements'] as $element => $eParts){
					if(isset($eParts['ref'])){
						$contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
					} else {
						$contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
						foreach ($eParts as $aName => $aValue) {
							// handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
							if ($aName != 'name' && $aName != 'type') {
								$contentStr .= " $aName=\"$aValue\"";
							}
						}
						$contentStr .= "/>\n";
					}
				}
				// compositor wraps elements
				if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
					$contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
				}
			}
			// attributes
			if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
				foreach($attrs['attrs'] as $attr => $aParts){
					$contentStr .= "    <$schemaPrefix:attribute";
					foreach ($aParts as $a => $v) {
						if ($a == 'ref' || $a == 'type') {
							$contentStr .= " $a=\"".$this->contractQName($v).'"';
						} elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
							$this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
							$contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
						} else {
							$contentStr .= " $a=\"$v\"";
						}
					}
					$contentStr .= "/>\n";
				}
			}
			// if restriction
			if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
				$contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
				// complex or simple content
				if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
					$contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
				}
			}
			// finalize complex type
			if($contentStr != ''){
				$contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
			} else {
				$contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
			}
			$xml .= $contentStr;
		}
		// simple types
		if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
			foreach($this->simpleTypes as $typeName => $eParts){
				$xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
				if (isset($eParts['enumeration'])) {
					foreach ($eParts['enumeration'] as $e) {
						$xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
					}
				}
				$xml .= "  </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
			}
		}
		// elements
		if(isset($this->elements) && count($this->elements) > 0){
			foreach($this->elements as $element => $eParts){
				$xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
			}
		}
		// attributes
		if(isset($this->attributes) && count($this->attributes) > 0){
			foreach($this->attributes as $attr => $aParts){
				$xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
			}
		}
		// finish 'er up
		$attr = '';
		foreach ($this->schemaInfo as $k => $v) {
			if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
				$attr .= " $k=\"$v\"";
			}
		}
		$el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
		foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
			$el .= " xmlns:$nsp=\"$ns\"";
		}
		$xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
		return $xml;
	}

	/**
	* adds debug data to the clas level debug string
	*
	* @param    string $string debug data
	* @access   private
	*/
	function xdebug($string){
		$this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
	}

    /**
    * get the PHP type of a user defined type in the schema
    * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
    * returns false if no type exists, or not w/ the given namespace
    * else returns a string that is either a native php type, or 'struct'
    *
    * @param string $type name of defined type
    * @param string $ns namespace of type
    * @return mixed
    * @access public
    * @deprecated
    */
	function getPHPType($type,$ns){
		if(isset($this->typemap[$ns][$type])){
			//print "found type '$type' and ns $ns in typemap<br>";
			return $this->typemap[$ns][$type];
		} elseif(isset($this->complexTypes[$type])){
			//print "getting type '$type' and ns $ns from complexTypes array<br>";
			return $this->complexTypes[$type]['phpType'];
		}
		return false;
	}

	/**
    * returns an associative array of information about a given type
    * returns false if no type exists by the given name
    *
	*	For a complexType typeDef = array(
	*	'restrictionBase' => '',
	*	'phpType' => '',
	*	'compositor' => '(sequence|all)',
	*	'elements' => array(), // refs to elements array
	*	'attrs' => array() // refs to attributes array
	*	... and so on (see addComplexType)
	*	)
	*
	*   For simpleType or element, the array has different keys.
    *
    * @param string $type
    * @return mixed
    * @access public
    * @see addComplexType
    * @see addSimpleType
    * @see addElement
    */
	function getTypeDef($type){
		//$this->debug("in getTypeDef for type $type");
		if (substr($type, -1) == '^') {
			$is_element = 1;
			$type = substr($type, 0, -1);
		} else {
			$is_element = 0;
		}

		if((! $is_element) && isset($this->complexTypes[$type])){
			$this->xdebug("in getTypeDef, found complexType $type");
			return $this->complexTypes[$type];
		} elseif((! $is_element) && isset($this->simpleTypes[$type])){
			$this->xdebug("in getTypeDef, found simpleType $type");
			if (!isset($this->simpleTypes[$type]['phpType'])) {
				// get info for type to tack onto the simple type
				// TODO: can this ever really apply (i.e. what is a simpleType really?)
				$uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
				$ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
				$etype = $this->getTypeDef($uqType);
				if ($etype) {
					$this->xdebug("in getTypeDef, found type for simpleType $type:");
					$this->xdebug($this->varDump($etype));
					if (isset($etype['phpType'])) {
						$this->simpleTypes[$type]['phpType'] = $etype['phpType'];
					}
					if (isset($etype['elements'])) {
						$this->simpleTypes[$type]['elements'] = $etype['elements'];
					}
				}
			}
			return $this->simpleTypes[$type];
		} elseif(isset($this->elements[$type])){
			$this->xdebug("in getTypeDef, found element $type");
			if (!isset($this->elements[$type]['phpType'])) {
				// get info for type to tack onto the element
				$uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
				$ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
				$etype = $this->getTypeDef($uqType);
				if ($etype) {
					$this->xdebug("in getTypeDef, found type for element $type:");
					$this->xdebug($this->varDump($etype));
					if (isset($etype['phpType'])) {
						$this->elements[$type]['phpType'] = $etype['phpType'];
					}
					if (isset($etype['elements'])) {
						$this->elements[$type]['elements'] = $etype['elements'];
					}
					if (isset($etype['extensionBase'])) {
						$this->elements[$type]['extensionBase'] = $etype['extensionBase'];
					}
				} elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
					$this->xdebug("in getTypeDef, element $type is an XSD type");
					$this->elements[$type]['phpType'] = 'scalar';
				}
			}
			return $this->elements[$type];
		} elseif(isset($this->attributes[$type])){
			$this->xdebug("in getTypeDef, found attribute $type");
			return $this->attributes[$type];
		} elseif (preg_match('/_ContainedType$/', $type)) {
			$this->xdebug("in getTypeDef, have an untyped element $type");
			$typeDef['typeClass'] = 'simpleType';
			$typeDef['phpType'] = 'scalar';
			$typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
			return $typeDef;
		}
		$this->xdebug("in getTypeDef, did not find $type");
		return false;
	}

	/**
    * returns a sample serialization of a given type, or false if no type by the given name
    *
    * @param string $type name of type
    * @return mixed
    * @access public
    * @deprecated
    */
    function serializeTypeDef($type){
    	//print "in sTD() for type $type<br>";
	if($typeDef = $this->getTypeDef($type)){
		$str .= '<'.$type;
	    if(is_array($typeDef['attrs'])){
		foreach($typeDef['attrs'] as $attName => $data){
		    $str .= " $attName=\"{type = ".$data['type']."}\"";
		}
	    }
	    $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
	    if(count($typeDef['elements']) > 0){
		$str .= ">";
		foreach($typeDef['elements'] as $element => $eData){
		    $str .= $this->serializeTypeDef($element);
		}
		$str .= "</$type>";
	    } elseif($typeDef['typeClass'] == 'element') {
		$str .= "></$type>";
	    } else {
		$str .= "/>";
	    }
			return $str;
	}
    	return false;
    }

    /**
    * returns HTML form elements that allow a user
    * to enter values for creating an instance of the given type.
    *
    * @param string $name name for type instance
    * @param string $type name of type
    * @return string
    * @access public
    * @deprecated
	*/
	function typeToForm($name,$type){
		// get typedef
		if($typeDef = $this->getTypeDef($type)){
			// if struct
			if($typeDef['phpType'] == 'struct'){
				$buffer .= '<table>';
				foreach($typeDef['elements'] as $child => $childDef){
					$buffer .= "
					<tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
					<td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
				}
				$buffer .= '</table>';
			// if array
			} elseif($typeDef['phpType'] == 'array'){
				$buffer .= '<table>';
				for($i=0;$i < 3; $i++){
					$buffer .= "
					<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
					<td><input type='text' name='parameters[".$name."][]'></td></tr>";
				}
				$buffer .= '</table>';
			// if scalar
			} else {
				$buffer .= "<input type='text' name='parameters[$name]'>";
			}
		} else {
			$buffer .= "<input type='text' name='parameters[$name]'>";
		}
		return $buffer;
	}
	
	/**
	* adds a complex type to the schema
	* 
	* example: array
	* 
	* addType(
	* 	'ArrayOfstring',
	* 	'complexType',
	* 	'array',
	* 	'',
	* 	'SOAP-ENC:Array',
	* 	array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
	* 	'xsd:string'
	* );
	* 
	* example: PHP associative array ( SOAP Struct )
	* 
	* addType(
	* 	'SOAPStruct',
	* 	'complexType',
	* 	'struct',
	* 	'all',
	* 	array('myVar'=> array('name'=>'myVar','type'=>'string')
	* );
	* 
	* @param name
	* @param typeClass (complexType|simpleType|attribute)
	* @param phpType: currently supported are array and struct (php assoc array)
	* @param compositor (all|sequence|choice)
	* @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
	* @param elements = array ( name = array(name=>'',type=>'') )
	* @param attrs = array(
	* 	array(
	*		'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
	*		"http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
	* 	)
	* )
	* @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
	* @access public
	* @see getTypeDef
	*/
	function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
		$this->complexTypes[$name] = array(
	    'name'		=> $name,
	    'typeClass'	=> $typeClass,
	    'phpType'	=> $phpType,
		'compositor'=> $compositor,
	    'restrictionBase' => $restrictionBase,
		'elements'	=> $elements,
	    'attrs'		=> $attrs,
	    'arrayType'	=> $arrayType
		);
		
		$this->xdebug("addComplexType $name:");
		$this->appendDebug($this->varDump($this->complexTypes[$name]));
	}
	
	/**
	* adds a simple type to the schema
	*
	* @param string $name
	* @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
	* @param string $typeClass (should always be simpleType)
	* @param string $phpType (should always be scalar)
	* @param array $enumeration array of values
	* @access public
	* @see nusoap_xmlschema
	* @see getTypeDef
	*/
	function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
		$this->simpleTypes[$name] = array(
	    'name'			=> $name,
	    'typeClass'		=> $typeClass,
	    'phpType'		=> $phpType,
	    'type'			=> $restrictionBase,
	    'enumeration'	=> $enumeration
		);
		
		$this->xdebug("addSimpleType $name:");
		$this->appendDebug($this->varDump($this->simpleTypes[$name]));
	}

	/**
	* adds an element to the schema
	*
	* @param array $attrs attributes that must include name and type
	* @see nusoap_xmlschema
	* @access public
	*/
	function addElement($attrs) {
		if (! $this->getPrefix($attrs['type'])) {
			$attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
		}
		$this->elements[ $attrs['name'] ] = $attrs;
		$this->elements[ $attrs['name'] ]['typeClass'] = 'element';
		
		$this->xdebug("addElement " . $attrs['name']);
		$this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
	}
}
?>
<?php
namespace com\linways\base\soap;

/**
 * Backward compatibility
 */
class soap_server extends nusoap_server {
}<?php
namespace com\linways\base\soap;
/*
 $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 
 NuSOAP - Web Services Toolkit for PHP
 
 Copyright (c) 2002 NuSphere Corporation
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 The NuSOAP project home is:
 http://sourceforge.net/projects/nusoap/
 
 The primary support for NuSOAP is the Help forum on the project home page.
 
 If you have any questions or comments, please email:
 
 Dietrich Ayala
 dietrich@ganx4.com
 http://dietrich.ganx4.com/nusoap
 
 NuSphere Corporation
 http://www.nusphere.com
 
 */

/*
 *	Some of the standards implmented in whole or part by NuSOAP:
 *
 *	SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
 *	WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
 *	SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
 *	XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
 *	Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
 *	XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
 *	RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
 *	RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
 *	RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
 */

/* load classes

// necessary classes
require_once('class.soapclient.php');
require_once('class.soap_val.php');
require_once('class.soap_parser.php');
require_once('class.soap_fault.php');

// transport classes
require_once('class.soap_transport_http.php');

// optional add-on classes
require_once('class.xmlschema.php');
require_once('class.wsdl.php');

// server class
require_once('class.soap_server.php');*/

// class variable emulation
// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
$GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;

/**
 *
 * nusoap_base
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @author   Scott Nichol <snichol@users.sourceforge.net>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access   public
 */
class nusoap_base {
    /**
     * Identification for HTTP headers.
     *
     * @var string
     * @access private
     */
    var $title = 'NuSOAP';
    /**
     * Version for HTTP headers.
     *
     * @var string
     * @access private
     */
    var $version = '0.9.5';
    /**
     * CVS revision for HTTP headers.
     *
     * @var string
     * @access private
     */
    var $revision = '$Revision: 1.123 $';
    /**
     * Current error string (manipulated by getError/setError)
     *
     * @var string
     * @access private
     */
    var $error_str = '';
    /**
     * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
     *
     * @var string
     * @access private
     */
    var $debug_str = '';
    /**
     * toggles automatic encoding of special characters as entities
     * (should always be true, I think)
     *
     * @var boolean
     * @access private
     */
    var $charencoding = true;
    /**
     * the debug level for this instance
     *
     * @var	integer
     * @access private
     */
    var $debugLevel;
    
    /**
     * set schema version
     *
     * @var      string
     * @access   public
     */
    var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
    
    /**
     * charset encoding for outgoing messages
     *
     * @var      string
     * @access   public
     */
    var $soap_defencoding = 'ISO-8859-1';
    //var $soap_defencoding = 'UTF-8';
    
    /**
     * namespaces in an array of prefix => uri
     *
     * this is "seeded" by a set of constants, but it may be altered by code
     *
     * @var      array
     * @access   public
     */
    var $namespaces = array(
        'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
        'xsd' => 'http://www.w3.org/2001/XMLSchema',
        'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
        'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
    );
    
    /**
     * namespaces used in the current context, e.g. during serialization
     *
     * @var      array
     * @access   private
     */
    var $usedNamespaces = array();
    
    /**
     * XML Schema types in an array of uri => (array of xml type => php type)
     * is this legacy yet?
     * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
     * @var      array
     * @access   public
     */
    var $typemap = array(
        'http://www.w3.org/2001/XMLSchema' => array(
            'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
            'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
            'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
            // abstract "any" types
            'anyType'=>'string','anySimpleType'=>'string',
            // derived datatypes
            'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
            'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
            'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
            'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
        'http://www.w3.org/2000/10/XMLSchema' => array(
            'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
            'float'=>'double','dateTime'=>'string',
            'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
        'http://www.w3.org/1999/XMLSchema' => array(
            'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
            'float'=>'double','dateTime'=>'string',
            'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
        'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
        'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
        'http://xml.apache.org/xml-soap' => array('Map')
    );
    
    /**
     * XML entities to convert
     *
     * @var      array
     * @access   public
     * @deprecated
     * @see	expandEntities
     */
    var $xmlEntities = array('quot' => '"','amp' => '&',
        'lt' => '<','gt' => '>','apos' => "'");
    
    /**
     * constructor
     *
     * @access	public
     */
    function nusoap_base() {
        $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
    }
    
    /**
     * gets the global debug level, which applies to future instances
     *
     * @return	integer	Debug level 0-9, where 0 turns off
     * @access	public
     */
    function getGlobalDebugLevel() {
        return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
    }
    
    /**
     * sets the global debug level, which applies to future instances
     *
     * @param	int	$level	Debug level 0-9, where 0 turns off
     * @access	public
     */
    function setGlobalDebugLevel($level) {
        $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
    }
    
    /**
     * gets the debug level for this instance
     *
     * @return	int	Debug level 0-9, where 0 turns off
     * @access	public
     */
    function getDebugLevel() {
        return $this->debugLevel;
    }
    
    /**
     * sets the debug level for this instance
     *
     * @param	int	$level	Debug level 0-9, where 0 turns off
     * @access	public
     */
    function setDebugLevel($level) {
        $this->debugLevel = $level;
    }
    
    /**
     * adds debug data to the instance debug string with formatting
     *
     * @param    string $string debug data
     * @access   private
     */
    function debug($string){
        if ($this->debugLevel > 0) {
            $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
        }
    }
    
    /**
     * adds debug data to the instance debug string without formatting
     *
     * @param    string $string debug data
     * @access   public
     */
    function appendDebug($string){
        if ($this->debugLevel > 0) {
            // it would be nice to use a memory stream here to use
            // memory more efficiently
            $this->debug_str .= $string;
        }
    }
    
    /**
     * clears the current debug data for this instance
     *
     * @access   public
     */
    function clearDebug() {
        // it would be nice to use a memory stream here to use
        // memory more efficiently
        $this->debug_str = '';
    }
    
    /**
     * gets the current debug data for this instance
     *
     * @return   debug data
     * @access   public
     */
    function &getDebug() {
        // it would be nice to use a memory stream here to use
        // memory more efficiently
        return $this->debug_str;
    }
    
    /**
     * gets the current debug data for this instance as an XML comment
     * this may change the contents of the debug data
     *
     * @return   debug data as an XML comment
     * @access   public
     */
    function &getDebugAsXMLComment() {
        // it would be nice to use a memory stream here to use
        // memory more efficiently
        while (strpos($this->debug_str, '--')) {
            $this->debug_str = str_replace('--', '- -', $this->debug_str);
        }
        $ret = "<!--\n" . $this->debug_str . "\n-->";
        return $ret;
    }
    
    /**
     * expands entities, e.g. changes '<' to '&lt;'.
     *
     * @param	string	$val	The string in which to expand entities.
     * @access	private
     */
    function expandEntities($val) {
        if ($this->charencoding) {
            $val = str_replace('&', '&amp;', $val);
            $val = str_replace("'", '&apos;', $val);
            $val = str_replace('"', '&quot;', $val);
            $val = str_replace('<', '&lt;', $val);
            $val = str_replace('>', '&gt;', $val);
        }
        return $val;
    }
    
    /**
     * returns error string if present
     *
     * @return   mixed error string or false
     * @access   public
     */
    function getError(){
        if($this->error_str != ''){
            return $this->error_str;
        }
        return false;
    }
    
    /**
     * sets error string
     *
     * @return   boolean $string error string
     * @access   private
     */
    function setError($str){
        $this->error_str = $str;
    }
    
    /**
     * detect if array is a simple array or a struct (associative array)
     *
     * @param	mixed	$val	The PHP array
     * @return	string	(arraySimple|arrayStruct)
     * @access	private
     */
    function isArraySimpleOrStruct($val) {
        $keyList = array_keys($val);
        foreach ($keyList as $keyListValue) {
            if (!is_int($keyListValue)) {
                return 'arrayStruct';
            }
        }
        return 'arraySimple';
    }
    
    /**
     * serializes PHP values in accordance w/ section 5. Type information is
     * not serialized if $use == 'literal'.
     *
     * @param	mixed	$val	The value to serialize
     * @param	string	$name	The name (local part) of the XML element
     * @param	string	$type	The XML schema type (local part) for the element
     * @param	string	$name_ns	The namespace for the name of the XML element
     * @param	string	$type_ns	The namespace for the type of the element
     * @param	array	$attributes	The attributes to serialize as name=>value pairs
     * @param	string	$use	The WSDL "use" (encoded|literal)
     * @param	boolean	$soapval	Whether this is called from soapval.
     * @return	string	The serialized element, possibly with child elements
     * @access	public
     */
    function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
        $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
        $this->appendDebug('value=' . $this->varDump($val));
        $this->appendDebug('attributes=' . $this->varDump($attributes));
        
        if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
            $this->debug("serialize_val: serialize soapval");
            $xml = $val->serialize($use);
            $this->appendDebug($val->getDebug());
            $val->clearDebug();
            $this->debug("serialize_val of soapval returning $xml");
            return $xml;
        }
        // force valid name if necessary
        if (is_numeric($name)) {
            $name = '__numeric_' . $name;
        } elseif (! $name) {
            $name = 'noname';
        }
        // if name has ns, add ns prefix to name
        $xmlns = '';
        if($name_ns){
            $prefix = 'nu'.rand(1000,9999);
            $name = $prefix.':'.$name;
            $xmlns .= " xmlns:$prefix=\"$name_ns\"";
        }
        // if type is prefixed, create type prefix
        if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
            // need to fix this. shouldn't default to xsd if no ns specified
            // w/o checking against typemap
            $type_prefix = 'xsd';
        } elseif($type_ns){
            $type_prefix = 'ns'.rand(1000,9999);
            $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
        }
        // serialize attributes if present
        $atts = '';
        if($attributes){
            foreach($attributes as $k => $v){
                $atts .= " $k=\"".$this->expandEntities($v).'"';
            }
        }
        // serialize null value
        if (is_null($val)) {
            $this->debug("serialize_val: serialize null");
            if ($use == 'literal') {
                // TODO: depends on minOccurs
                $xml = "<$name$xmlns$atts/>";
                $this->debug("serialize_val returning $xml");
                return $xml;
            } else {
                if (isset($type) && isset($type_prefix)) {
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
                } else {
                    $type_str = '';
                }
                $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
                $this->debug("serialize_val returning $xml");
                return $xml;
            }
        }
        // serialize if an xsd built-in primitive type
        if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
            $this->debug("serialize_val: serialize xsd built-in primitive type");
            if (is_bool($val)) {
                if ($type == 'boolean') {
                    $val = $val ? 'true' : 'false';
                } elseif (! $val) {
                    $val = 0;
                }
            } else if (is_string($val)) {
                $val = $this->expandEntities($val);
            }
            if ($use == 'literal') {
                $xml = "<$name$xmlns$atts>$val</$name>";
                $this->debug("serialize_val returning $xml");
                return $xml;
            } else {
                $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
                $this->debug("serialize_val returning $xml");
                return $xml;
            }
        }
        // detect type and serialize
        $xml = '';
        switch(true) {
            case (is_bool($val) || $type == 'boolean'):
                $this->debug("serialize_val: serialize boolean");
                if ($type == 'boolean') {
                    $val = $val ? 'true' : 'false';
                } elseif (! $val) {
                    $val = 0;
                }
                if ($use == 'literal') {
                    $xml .= "<$name$xmlns$atts>$val</$name>";
                } else {
                    $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
                }
                break;
            case (is_int($val) || is_long($val) || $type == 'int'):
                $this->debug("serialize_val: serialize int");
                if ($use == 'literal') {
                    $xml .= "<$name$xmlns$atts>$val</$name>";
                } else {
                    $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
                }
                break;
            case (is_float($val)|| is_double($val) || $type == 'float'):
                $this->debug("serialize_val: serialize float");
                if ($use == 'literal') {
                    $xml .= "<$name$xmlns$atts>$val</$name>";
                } else {
                    $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
                }
                break;
            case (is_string($val) || $type == 'string'):
                $this->debug("serialize_val: serialize string");
                $val = $this->expandEntities($val);
                if ($use == 'literal') {
                    $xml .= "<$name$xmlns$atts>$val</$name>";
                } else {
                    $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
                }
                break;
            case is_object($val):
                $this->debug("serialize_val: serialize object");
                if (get_class($val) == 'soapval') {
                    $this->debug("serialize_val: serialize soapval object");
                    $pXml = $val->serialize($use);
                    $this->appendDebug($val->getDebug());
                    $val->clearDebug();
                } else {
                    if (! $name) {
                        $name = get_class($val);
                        $this->debug("In serialize_val, used class name $name as element name");
                    } else {
                        $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
                    }
                    foreach(get_object_vars($val) as $k => $v){
                        $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
                    }
                }
                if(isset($type) && isset($type_prefix)){
                    $type_str = " xsi:type=\"$type_prefix:$type\"";
                } else {
                    $type_str = '';
                }
                if ($use == 'literal') {
                    $xml .= "<$name$xmlns$atts>$pXml</$name>";
                } else {
                    $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
                }
                break;
                break;
            case (is_array($val) || $type):
                // detect if struct or array
                $valueType = $this->isArraySimpleOrStruct($val);
                if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){
                    $this->debug("serialize_val: serialize array");
                    $i = 0;
                    if(is_array($val) && count($val)> 0){
                        foreach($val as $v){
                            if(is_object($v) && get_class($v) ==  'soapval'){
                                $tt_ns = $v->type_ns;
                                $tt = $v->type;
                            } elseif (is_array($v)) {
                                $tt = $this->isArraySimpleOrStruct($v);
                            } else {
                                $tt = gettype($v);
                            }
                            $array_types[$tt] = 1;
                            // TODO: for literal, the name should be $name
                            $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
                            ++$i;
                        }
                        if(count($array_types) > 1){
                            $array_typename = 'xsd:anyType';
                        } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
                            if ($tt == 'integer') {
                                $tt = 'int';
                            }
                            $array_typename = 'xsd:'.$tt;
                        } elseif(isset($tt) && $tt == 'arraySimple'){
                            $array_typename = 'SOAP-ENC:Array';
                        } elseif(isset($tt) && $tt == 'arrayStruct'){
                            $array_typename = 'unnamed_struct_use_soapval';
                        } else {
                            // if type is prefixed, create type prefix
                            if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
                                $array_typename = 'xsd:' . $tt;
                            } elseif ($tt_ns) {
                                $tt_prefix = 'ns' . rand(1000, 9999);
                                $array_typename = "$tt_prefix:$tt";
                                $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
                            } else {
                                $array_typename = $tt;
                            }
                        }
                        $array_type = $i;
                        if ($use == 'literal') {
                            $type_str = '';
                        } else if (isset($type) && isset($type_prefix)) {
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
                        } else {
                            $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
                        }
                        // empty array
                    } else {
                        if ($use == 'literal') {
                            $type_str = '';
                        } else if (isset($type) && isset($type_prefix)) {
                            $type_str = " xsi:type=\"$type_prefix:$type\"";
                        } else {
                            $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
                        }
                    }
                    // TODO: for array in literal, there is no wrapper here
                    $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
                } else {
                    // got a struct
                    $this->debug("serialize_val: serialize struct");
                    if(isset($type) && isset($type_prefix)){
                        $type_str = " xsi:type=\"$type_prefix:$type\"";
                    } else {
                        $type_str = '';
                    }
                    if ($use == 'literal') {
                        $xml .= "<$name$xmlns$atts>";
                    } else {
                        $xml .= "<$name$xmlns$type_str$atts>";
                    }
                    foreach($val as $k => $v){
                        // Apache Map
                        if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
                            $xml .= '<item>';
                            $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
                            $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
                            $xml .= '</item>';
                        } else {
                            $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
                        }
                    }
                    $xml .= "</$name>";
                }
                break;
            default:
                $this->debug("serialize_val: serialize unknown");
                $xml .= 'not detected, got '.gettype($val).' for '.$val;
                break;
        }
        $this->debug("serialize_val returning $xml");
        return $xml;
    }
    
    /**
     * serializes a message
     *
     * @param string $body the XML of the SOAP body
     * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
     * @param array $namespaces optional the namespaces used in generating the body and headers
     * @param string $style optional (rpc|document)
     * @param string $use optional (encoded|literal)
     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
     * @return string the message
     * @access public
     */
    function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
        // TODO: add an option to automatically run utf8_encode on $body and $headers
        // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
        // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
        
        $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
        $this->debug("headers:");
        $this->appendDebug($this->varDump($headers));
        $this->debug("namespaces:");
        $this->appendDebug($this->varDump($namespaces));
        
        // serialize namespaces
        $ns_string = '';
        foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
            $ns_string .= " xmlns:$k=\"$v\"";
        }
        if($encodingStyle) {
            $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
        }
        
        // serialize headers
        if($headers){
            if (is_array($headers)) {
                $xml = '';
                foreach ($headers as $k => $v) {
                    if (is_object($v) && get_class($v) == 'soapval') {
                        $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
                    } else {
                        $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
                    }
                }
                $headers = $xml;
                $this->debug("In serializeEnvelope, serialized array of headers to $headers");
            }
            $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
        }
        // serialize envelope
        return
        '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
        '<SOAP-ENV:Envelope'.$ns_string.">".
        $headers.
        "<SOAP-ENV:Body>".
        $body.
        "</SOAP-ENV:Body>".
        "</SOAP-ENV:Envelope>";
    }
    
    /**
     * formats a string to be inserted into an HTML stream
     *
     * @param string $str The string to format
     * @return string The formatted string
     * @access public
     * @deprecated
     */
    function formatDump($str){
        $str = htmlspecialchars($str);
        return nl2br($str);
    }
    
    /**
     * contracts (changes namespace to prefix) a qualified name
     *
     * @param    string $qname qname
     * @return	string contracted qname
     * @access   private
     */
    function contractQname($qname){
        // get element namespace
        //$this->xdebug("Contract $qname");
        if (strrpos($qname, ':')) {
            // get unqualified name
            $name = substr($qname, strrpos($qname, ':') + 1);
            // get ns
            $ns = substr($qname, 0, strrpos($qname, ':'));
            $p = $this->getPrefixFromNamespace($ns);
            if ($p) {
                return $p . ':' . $name;
            }
            return $qname;
        } else {
            return $qname;
        }
    }
    
    /**
     * expands (changes prefix to namespace) a qualified name
     *
     * @param    string $qname qname
     * @return	string expanded qname
     * @access   private
     */
    function expandQname($qname){
        // get element prefix
        if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){
            // get unqualified name
            $name = substr(strstr($qname,':'),1);
            // get ns prefix
            $prefix = substr($qname,0,strpos($qname,':'));
            if(isset($this->namespaces[$prefix])){
                return $this->namespaces[$prefix].':'.$name;
            } else {
                return $qname;
            }
        } else {
            return $qname;
        }
    }
    
    /**
     * returns the local part of a prefixed string
     * returns the original string, if not prefixed
     *
     * @param string $str The prefixed string
     * @return string The local part
     * @access public
     */
    function getLocalPart($str){
        if($sstr = strrchr($str,':')){
            // get unqualified name
            return substr( $sstr, 1 );
        } else {
            return $str;
        }
    }
    
    /**
     * returns the prefix part of a prefixed string
     * returns false, if not prefixed
     *
     * @param string $str The prefixed string
     * @return mixed The prefix or false if there is no prefix
     * @access public
     */
    function getPrefix($str){
        if($pos = strrpos($str,':')){
            // get prefix
            return substr($str,0,$pos);
        }
        return false;
    }
    
    /**
     * pass it a prefix, it returns a namespace
     *
     * @param string $prefix The prefix
     * @return mixed The namespace, false if no namespace has the specified prefix
     * @access public
     */
    function getNamespaceFromPrefix($prefix){
        if (isset($this->namespaces[$prefix])) {
            return $this->namespaces[$prefix];
        }
        //$this->setError("No namespace registered for prefix '$prefix'");
        return false;
    }
    
    /**
     * returns the prefix for a given namespace (or prefix)
     * or false if no prefixes registered for the given namespace
     *
     * @param string $ns The namespace
     * @return mixed The prefix, false if the namespace has no prefixes
     * @access public
     */
    function getPrefixFromNamespace($ns) {
        foreach ($this->namespaces as $p => $n) {
            if ($ns == $n || $ns == $p) {
                $this->usedNamespaces[$p] = $n;
                return $p;
            }
        }
        return false;
    }
    
    /**
     * returns the time in ODBC canonical form with microseconds
     *
     * @return string The time in ODBC canonical form with microseconds
     * @access public
     */
    function getmicrotime() {
        if (function_exists('gettimeofday')) {
            $tod = gettimeofday();
            $sec = $tod['sec'];
            $usec = $tod['usec'];
        } else {
            $sec = time();
            $usec = 0;
        }
        return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
    }
    
    /**
     * Returns a string with the output of var_dump
     *
     * @param mixed $data The variable to var_dump
     * @return string The output of var_dump
     * @access public
     */
    function varDump($data) {
        ob_start();
        var_dump($data);
        $ret_val = ob_get_contents();
        ob_end_clean();
        return $ret_val;
    }
    
    /**
     * represents the object as a string
     *
     * @return	string
     * @access   public
     */
    function __toString() {
        return $this->varDump($this);
    }
}

// XML Schema Datatype Helper Functions

//xsd:dateTime helpers

/**
 * convert unix timestamp to ISO 8601 compliant date string
 *
 * @param    int $timestamp Unix time stamp
 * @param	boolean $utc Whether the time stamp is UTC or local
 * @return	mixed ISO 8601 date string or false
 * @access   public
 */
function timestamp_to_iso8601($timestamp,$utc=true){
    $datestr = date('Y-m-d\TH:i:sO',$timestamp);
    $pos = strrpos($datestr, "+");
    if ($pos === FALSE) {
        $pos = strrpos($datestr, "-");
    }
    if ($pos !== FALSE) {
        if (strlen($datestr) == $pos + 5) {
            $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
        }
    }
    if($utc){
        $pattern = '/'.
            '([0-9]{4})-'.	// centuries & years CCYY-
            '([0-9]{2})-'.	// months MM-
            '([0-9]{2})'.	// days DD
            'T'.			// separator T
            '([0-9]{2}):'.	// hours hh:
            '([0-9]{2}):'.	// minutes mm:
            '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
            '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
            '/';
        
        if(preg_match($pattern,$datestr,$regs)){
            return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
        }
        return false;
    } else {
        return $datestr;
    }
}

/**
 * convert ISO 8601 compliant date string to unix timestamp
 *
 * @param    string $datestr ISO 8601 compliant date string
 * @return	mixed Unix timestamp (int) or false
 * @access   public
 */
function iso8601_to_timestamp($datestr){
    $pattern = '/'.
        '([0-9]{4})-'.	// centuries & years CCYY-
        '([0-9]{2})-'.	// months MM-
        '([0-9]{2})'.	// days DD
        'T'.			// separator T
        '([0-9]{2}):'.	// hours hh:
        '([0-9]{2}):'.	// minutes mm:
        '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
        '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
        '/';
    if(preg_match($pattern,$datestr,$regs)){
        // not utc
        if($regs[8] != 'Z'){
            $op = substr($regs[8],0,1);
            $h = substr($regs[8],1,2);
            $m = substr($regs[8],strlen($regs[8])-2,2);
            if($op == '-'){
                $regs[4] = $regs[4] + $h;
                $regs[5] = $regs[5] + $m;
            } elseif($op == '+'){
                $regs[4] = $regs[4] - $h;
                $regs[5] = $regs[5] - $m;
            }
        }
        return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
        //		return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
    } else {
        return false;
    }
}

/**
 * sleeps some number of microseconds
 *
 * @param    string $usec the number of microseconds to sleep
 * @access   public
 * @deprecated
 */
function usleepWindows($usec)
{
    $start = gettimeofday();
    
    do
    {
        $stop = gettimeofday();
        $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
        + $stop['usec'] - $start['usec'];
    }
    while ($timePassed < $usec);
}


?>
<?php
namespace com\linways\base\soap;
/**
 * Backward compatibility
 */
class soap_parser extends nusoap_parser {
}<?php
namespace com\linways\base\soap;


/**
 * Backward compatibility
 */
class soap_fault extends nusoap_fault {
}

<?php
namespace com\linways\base\soap;

/**
 * For creating serializable abstractions of native PHP types.  This class
 * allows element name/namespace, XSD type, and XML attributes to be
 * associated with a value.  This is extremely useful when WSDL is not
 * used, but is also useful when WSDL is used with polymorphic types, including
 * xsd:anyType and user-defined types.
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access   public
 */
class soapval extends nusoap_base {
    /**
     * The XML element name
     *
     * @var string
     * @access private
     */
    var $name;
    /**
     * The XML type name (string or false)
     *
     * @var mixed
     * @access private
     */
    var $type;
    /**
     * The PHP value
     *
     * @var mixed
     * @access private
     */
    var $value;
    /**
     * The XML element namespace (string or false)
     *
     * @var mixed
     * @access private
     */
    var $element_ns;
    /**
     * The XML type namespace (string or false)
     *
     * @var mixed
     * @access private
     */
    var $type_ns;
    /**
     * The XML element attributes (array or false)
     *
     * @var mixed
     * @access private
     */
    var $attributes;
    
    /**
     * constructor
     *
     * @param    string $name optional name
     * @param    mixed $type optional type name
     * @param	mixed $value optional value
     * @param	mixed $element_ns optional namespace of value
     * @param	mixed $type_ns optional namespace of type
     * @param	mixed $attributes associative array of attributes to add to element serialization
     * @access   public
     */
    function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
        parent::nusoap_base();
        $this->name = $name;
        $this->type = $type;
        $this->value = $value;
        $this->element_ns = $element_ns;
        $this->type_ns = $type_ns;
        $this->attributes = $attributes;
    }
    
    /**
     * return serialized value
     *
     * @param	string $use The WSDL use value (encoded|literal)
     * @return	string XML data
     * @access   public
     */
    function serialize($use='encoded') {
        return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
    }
    
    /**
     * decodes a soapval object into a PHP native type
     *
     * @return	mixed
     * @access   public
     */
    function decode(){
        return $this->value;
    }
}

<?php
namespace com\linways\base\soap;


/**
 * parses a WSDL file, allows access to it's data, other utility methods.
 * also builds WSDL structures programmatically.
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @author   Scott Nichol <snichol@users.sourceforge.net>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access public
 */
class wsdl extends nusoap_base {
    // URL or filename of the root of this WSDL
    var $wsdl;
    // define internal arrays of bindings, ports, operations, messages, etc.
    var $schemas = array();
    var $currentSchema;
    var $message = array();
    var $complexTypes = array();
    var $messages = array();
    var $currentMessage;
    var $currentOperation;
    var $portTypes = array();
    var $currentPortType;
    var $bindings = array();
    var $currentBinding;
    var $ports = array();
    var $currentPort;
    var $opData = array();
    var $status = '';
    var $documentation = false;
    var $endpoint = '';
    // array of wsdl docs to import
    var $import = array();
    // parser vars
    var $parser;
    var $position = 0;
    var $depth = 0;
    var $depth_array = array();
    // for getting wsdl
    var $proxyhost = '';
    var $proxyport = '';
    var $proxyusername = '';
    var $proxypassword = '';
    var $timeout = 0;
    var $response_timeout = 30;
    var $curl_options = array();	// User-specified cURL options
    var $use_curl = false;			// whether to always try to use cURL
    // for HTTP authentication
    var $username = '';				// Username for HTTP authentication
    var $password = '';				// Password for HTTP authentication
    var $authtype = '';				// Type of HTTP authentication
    var $certRequest = array();		// Certificate for HTTP SSL authentication
    
    /**
     * constructor
     *
     * @param string $wsdl WSDL document URL
     * @param string $proxyhost
     * @param string $proxyport
     * @param string $proxyusername
     * @param string $proxypassword
     * @param integer $timeout set the connection timeout
     * @param integer $response_timeout set the response timeout
     * @param array $curl_options user-specified cURL options
     * @param boolean $use_curl try to use cURL
     * @access public
     */
    function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){
        parent::nusoap_base();
        $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
        $this->proxyhost = $proxyhost;
        $this->proxyport = $proxyport;
        $this->proxyusername = $proxyusername;
        $this->proxypassword = $proxypassword;
        $this->timeout = $timeout;
        $this->response_timeout = $response_timeout;
        if (is_array($curl_options))
            $this->curl_options = $curl_options;
            $this->use_curl = $use_curl;
            $this->fetchWSDL($wsdl);
    }
    
    /**
     * fetches the WSDL document and parses it
     *
     * @access public
     */
    function fetchWSDL($wsdl) {
        $this->debug("parse and process WSDL path=$wsdl");
        $this->wsdl = $wsdl;
        // parse wsdl file
        if ($this->wsdl != "") {
            $this->parseWSDL($this->wsdl);
        }
        // imports
        // TODO: handle imports more properly, grabbing them in-line and nesting them
        $imported_urls = array();
        $imported = 1;
        while ($imported > 0) {
            $imported = 0;
            // Schema imports
            foreach ($this->schemas as $ns => $list) {
                foreach ($list as $xs) {
                    $wsdlparts = parse_url($this->wsdl);	// this is bogusly simple!
                    foreach ($xs->imports as $ns2 => $list2) {
                        for ($ii = 0; $ii < count($list2); $ii++) {
                            if (! $list2[$ii]['loaded']) {
                                $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
                                $url = $list2[$ii]['location'];
                                if ($url != '') {
                                    $urlparts = parse_url($url);
                                    if (!isset($urlparts['host'])) {
                                        $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
                                        substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
                                    }
                                    if (! in_array($url, $imported_urls)) {
                                        $this->parseWSDL($url);
                                        $imported++;
                                        $imported_urls[] = $url;
                                    }
                                } else {
                                    $this->debug("Unexpected scenario: empty URL for unloaded import");
                                }
                            }
                        }
                    }
                }
            }
            // WSDL imports
            $wsdlparts = parse_url($this->wsdl);	// this is bogusly simple!
            foreach ($this->import as $ns => $list) {
                for ($ii = 0; $ii < count($list); $ii++) {
                    if (! $list[$ii]['loaded']) {
                        $this->import[$ns][$ii]['loaded'] = true;
                        $url = $list[$ii]['location'];
                        if ($url != '') {
                            $urlparts = parse_url($url);
                            if (!isset($urlparts['host'])) {
                                $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
                                substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
                            }
                            if (! in_array($url, $imported_urls)) {
                                $this->parseWSDL($url);
                                $imported++;
                                $imported_urls[] = $url;
                            }
                        } else {
                            $this->debug("Unexpected scenario: empty URL for unloaded import");
                        }
                    }
                }
            }
        }
        // add new data to operation data
        foreach($this->bindings as $binding => $bindingData) {
            if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
                foreach($bindingData['operations'] as $operation => $data) {
                    $this->debug('post-parse data gathering for ' . $operation);
                    $this->bindings[$binding]['operations'][$operation]['input'] =
                    isset($this->bindings[$binding]['operations'][$operation]['input']) ?
                    array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
                    $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
                    $this->bindings[$binding]['operations'][$operation]['output'] =
                    isset($this->bindings[$binding]['operations'][$operation]['output']) ?
                    array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
                    $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
                    if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
                        $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
                    }
                    if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
                        $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
                    }
                    // Set operation style if necessary, but do not override one already provided
                    if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
                        $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
                    }
                    $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
                    $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
                    $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
                }
            }
        }
    }
    
    /**
     * parses the wsdl document
     *
     * @param string $wsdl path or URL
     * @access private
     */
    function parseWSDL($wsdl = '') {
        $this->debug("parse WSDL at path=$wsdl");
        
        if ($wsdl == '') {
            $this->debug('no wsdl passed to parseWSDL()!!');
            $this->setError('no wsdl passed to parseWSDL()!!');
            return false;
        }
        
        // parse $wsdl for url format
        $wsdl_props = parse_url($wsdl);
        
        if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
            $this->debug('getting WSDL http(s) URL ' . $wsdl);
            // get wsdl
            $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
            $tr->request_method = 'GET';
            $tr->useSOAPAction = false;
            if($this->proxyhost && $this->proxyport){
                $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
            }
            if ($this->authtype != '') {
                $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
            }
            $tr->setEncoding('gzip, deflate');
            $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
            //$this->debug("WSDL request\n" . $tr->outgoing_payload);
            //$this->debug("WSDL response\n" . $tr->incoming_payload);
            $this->appendDebug($tr->getDebug());
            // catch errors
            if($err = $tr->getError() ){
                $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err;
                $this->debug($errstr);
                $this->setError($errstr);
                unset($tr);
                return false;
            }
            unset($tr);
            $this->debug("got WSDL URL");
        } else {
            // $wsdl is not http(s), so treat it as a file URL or plain file path
            if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
                $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
            } else {
                $path = $wsdl;
            }
            $this->debug('getting WSDL file ' . $path);
            if ($fp = @fopen($path, 'r')) {
                $wsdl_string = '';
                while ($data = fread($fp, 32768)) {
                    $wsdl_string .= $data;
                }
                fclose($fp);
            } else {
                $errstr = "Bad path to WSDL file $path";
                $this->debug($errstr);
                $this->setError($errstr);
                return false;
            }
        }
        $this->debug('Parse WSDL');
        // end new code added
        // Create an XML parser.
        $this->parser = xml_parser_create();
        // Set the options for parsing the XML data.
        // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
        // Set the object for the parser.
        xml_set_object($this->parser, $this);
        // Set the element handlers for the parser.
        xml_set_element_handler($this->parser, 'start_element', 'end_element');
        xml_set_character_data_handler($this->parser, 'character_data');
        // Parse the XML file.
        if (!xml_parse($this->parser, $wsdl_string, true)) {
            // Display an error message.
            $errstr = sprintf(
                'XML error parsing WSDL from %s on line %d: %s',
                $wsdl,
                xml_get_current_line_number($this->parser),
                xml_error_string(xml_get_error_code($this->parser))
                );
            $this->debug($errstr);
            $this->debug("XML payload:\n" . $wsdl_string);
            $this->setError($errstr);
            return false;
        }
        // free the parser
        xml_parser_free($this->parser);
        $this->debug('Parsing WSDL done');
        // catch wsdl parse errors
        if($this->getError()){
            return false;
        }
        return true;
    }
    
    /**
     * start-element handler
     *
     * @param string $parser XML parser object
     * @param string $name element name
     * @param string $attrs associative array of attributes
     * @access private
     */
    function start_element($parser, $name, $attrs)
    {
        if ($this->status == 'schema') {
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
            $this->appendDebug($this->currentSchema->getDebug());
            $this->currentSchema->clearDebug();
        } elseif (preg_match('/schema$/', $name)) {
            $this->debug('Parsing WSDL schema');
            // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
            $this->status = 'schema';
            $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
            $this->currentSchema->schemaStartElement($parser, $name, $attrs);
            $this->appendDebug($this->currentSchema->getDebug());
            $this->currentSchema->clearDebug();
        } else {
            // position in the total number of elements, starting from 0
            $pos = $this->position++;
            $depth = $this->depth++;
            // set self as current value for this depth
            $this->depth_array[$depth] = $pos;
            $this->message[$pos] = array('cdata' => '');
            // process attributes
            if (count($attrs) > 0) {
                // register namespace declarations
                foreach($attrs as $k => $v) {
                    if (preg_match('/^xmlns/',$k)) {
                        if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
                            $this->namespaces[$ns_prefix] = $v;
                        } else {
                            $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
                        }
                        if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
                            $this->XMLSchemaVersion = $v;
                            $this->namespaces['xsi'] = $v . '-instance';
                        }
                    }
                }
                // expand each attribute prefix to its namespace
                foreach($attrs as $k => $v) {
                    $k = strpos($k, ':') ? $this->expandQname($k) : $k;
                    if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
                        $v = strpos($v, ':') ? $this->expandQname($v) : $v;
                    }
                    $eAttrs[$k] = $v;
                }
                $attrs = $eAttrs;
            } else {
                $attrs = array();
            }
            // get element prefix, namespace and name
            if (preg_match('/:/', $name)) {
                // get ns prefix
                $prefix = substr($name, 0, strpos($name, ':'));
                // get ns
                $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
                // get unqualified name
                $name = substr(strstr($name, ':'), 1);
            }
            // process attributes, expanding any prefixes to namespaces
            // find status, register data
            switch ($this->status) {
                case 'message':
                    if ($name == 'part') {
                        if (isset($attrs['type'])) {
                            $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
                        }
                        if (isset($attrs['element'])) {
                            $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
                            $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
                        }
                    }
                    break;
                case 'portType':
                    switch ($name) {
                        case 'operation':
                            $this->currentPortOperation = $attrs['name'];
                            $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
                            if (isset($attrs['parameterOrder'])) {
                                $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
                            }
                            break;
                        case 'documentation':
                            $this->documentation = true;
                            break;
                            // merge input/output data
                        default:
                            $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
                            $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
                            break;
                    }
                    break;
                case 'binding':
                    switch ($name) {
                        case 'binding':
                            // get ns prefix
                            if (isset($attrs['style'])) {
                                $this->bindings[$this->currentBinding]['prefix'] = $prefix;
                            }
                            $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
                            break;
                        case 'header':
                            $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
                            break;
                        case 'operation':
                            if (isset($attrs['soapAction'])) {
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
                            }
                            if (isset($attrs['style'])) {
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
                            }
                            if (isset($attrs['name'])) {
                                $this->currentOperation = $attrs['name'];
                                $this->debug("current binding operation: $this->currentOperation");
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
                            }
                            break;
                        case 'input':
                            $this->opStatus = 'input';
                            break;
                        case 'output':
                            $this->opStatus = 'output';
                            break;
                        case 'body':
                            if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
                            } else {
                                $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
                            }
                            break;
                    }
                    break;
                case 'service':
                    switch ($name) {
                        case 'port':
                            $this->currentPort = $attrs['name'];
                            $this->debug('current port: ' . $this->currentPort);
                            $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
                            
                            break;
                        case 'address':
                            $this->ports[$this->currentPort]['location'] = $attrs['location'];
                            $this->ports[$this->currentPort]['bindingType'] = $namespace;
                            $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
                            $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
                            break;
                    }
                    break;
            }
            // set status
            switch ($name) {
                case 'import':
                    if (isset($attrs['location'])) {
                        $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
                        $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
                    } else {
                        $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
                        if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
                            $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
                        }
                        $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
                    }
                    break;
                    //wait for schema
                    //case 'types':
                    //	$this->status = 'schema';
                        //	break;
                    case 'message':
                        $this->status = 'message';
                        $this->messages[$attrs['name']] = array();
                        $this->currentMessage = $attrs['name'];
                        break;
                    case 'portType':
                        $this->status = 'portType';
                        $this->portTypes[$attrs['name']] = array();
                        $this->currentPortType = $attrs['name'];
                        break;
                    case "binding":
                        if (isset($attrs['name'])) {
                            // get binding name
                            if (strpos($attrs['name'], ':')) {
                                $this->currentBinding = $this->getLocalPart($attrs['name']);
                            } else {
                                $this->currentBinding = $attrs['name'];
                            }
                            $this->status = 'binding';
                            $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
                            $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
                        }
                        break;
                    case 'service':
                        $this->serviceName = $attrs['name'];
                        $this->status = 'service';
                        $this->debug('current service: ' . $this->serviceName);
                        break;
                    case 'definitions':
                        foreach ($attrs as $name => $value) {
                            $this->wsdl_info[$name] = $value;
                        }
                        break;
            }
        }
    }
    
    /**
     * end-element handler
     *
     * @param string $parser XML parser object
     * @param string $name element name
     * @access private
     */
    function end_element($parser, $name){
        // unset schema status
        if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) {
            $this->status = "";
            $this->appendDebug($this->currentSchema->getDebug());
            $this->currentSchema->clearDebug();
            $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
            $this->debug('Parsing WSDL schema done');
        }
        if ($this->status == 'schema') {
            $this->currentSchema->schemaEndElement($parser, $name);
        } else {
            // bring depth down a notch
            $this->depth--;
        }
        // end documentation
        if ($this->documentation) {
            //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
            //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
            $this->documentation = false;
        }
    }
    
    /**
     * element content handler
     *
     * @param string $parser XML parser object
     * @param string $data element content
     * @access private
     */
    function character_data($parser, $data)
    {
        $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
        if (isset($this->message[$pos]['cdata'])) {
            $this->message[$pos]['cdata'] .= $data;
        }
        if ($this->documentation) {
            $this->documentation .= $data;
        }
    }
    
    /**
     * if authenticating, set user credentials here
     *
     * @param    string $username
     * @param    string $password
     * @param	string $authtype (basic|digest|certificate|ntlm)
     * @param	array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
     * @access   public
     */
    function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
        $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
        $this->appendDebug($this->varDump($certRequest));
        $this->username = $username;
        $this->password = $password;
        $this->authtype = $authtype;
        $this->certRequest = $certRequest;
    }
    
    function getBindingData($binding)
    {
        if (is_array($this->bindings[$binding])) {
            return $this->bindings[$binding];
        }
    }
    
    /**
     * returns an assoc array of operation names => operation data
     *
     * @param string $portName WSDL port name
     * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
     * @return array
     * @access public
     */
    function getOperations($portName = '', $bindingType = 'soap') {
        $ops = array();
        if ($bindingType == 'soap') {
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
        } elseif ($bindingType == 'soap12') {
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
        } else {
            $this->debug("getOperations bindingType $bindingType may not be supported");
        }
        $this->debug("getOperations for port '$portName' bindingType $bindingType");
        // loop thru ports
        foreach($this->ports as $port => $portData) {
            $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
            if ($portName == '' || $port == $portName) {
                // binding type of port matches parameter
                if ($portData['bindingType'] == $bindingType) {
                    $this->debug("getOperations found port $port bindingType $bindingType");
                    //$this->debug("port data: " . $this->varDump($portData));
                    //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
                    // merge bindings
                    if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
                        $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
                    }
                }
            }
        }
        if (count($ops) == 0) {
            $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
        }
        return $ops;
    }
    
    /**
     * returns an associative array of data necessary for calling an operation
     *
     * @param string $operation name of operation
     * @param string $bindingType type of binding eg: soap, soap12
     * @return array
     * @access public
     */
    function getOperationData($operation, $bindingType = 'soap')
    {
        if ($bindingType == 'soap') {
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
        } elseif ($bindingType == 'soap12') {
            $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
        }
        // loop thru ports
        foreach($this->ports as $port => $portData) {
            // binding type of port matches parameter
            if ($portData['bindingType'] == $bindingType) {
                // get binding
                //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
                foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
                    // note that we could/should also check the namespace here
                    if ($operation == $bOperation) {
                        $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
                        return $opData;
                    }
                }
                }
            }
        }
        
        /**
         * returns an associative array of data necessary for calling an operation
         *
         * @param string $soapAction soapAction for operation
         * @param string $bindingType type of binding eg: soap, soap12
         * @return array
         * @access public
         */
        function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
            if ($bindingType == 'soap') {
                $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
            } elseif ($bindingType == 'soap12') {
                $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
            }
            // loop thru ports
            foreach($this->ports as $port => $portData) {
                // binding type of port matches parameter
                if ($portData['bindingType'] == $bindingType) {
                    // loop through operations for the binding
                    foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
                        if ($opData['soapAction'] == $soapAction) {
                            return $opData;
                        }
                    }
                }
            }
        }
        
        /**
         * returns an array of information about a given type
         * returns false if no type exists by the given name
         *
         *	 typeDef = array(
         *	 'elements' => array(), // refs to elements array
         *	'restrictionBase' => '',
         *	'phpType' => '',
         *	'order' => '(sequence|all)',
         *	'attrs' => array() // refs to attributes array
         *	)
         *
         * @param string $type the type
         * @param string $ns namespace (not prefix) of the type
         * @return mixed
         * @access public
         * @see nusoap_xmlschema
         */
        function getTypeDef($type, $ns) {
            $this->debug("in getTypeDef: type=$type, ns=$ns");
            if ((! $ns) && isset($this->namespaces['tns'])) {
                $ns = $this->namespaces['tns'];
                $this->debug("in getTypeDef: type namespace forced to $ns");
            }
            if (!isset($this->schemas[$ns])) {
                foreach ($this->schemas as $ns0 => $schema0) {
                    if (strcasecmp($ns, $ns0) == 0) {
                        $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
                        $ns = $ns0;
                        break;
                    }
                }
            }
            if (isset($this->schemas[$ns])) {
                $this->debug("in getTypeDef: have schema for namespace $ns");
                for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
                    $xs = &$this->schemas[$ns][$i];
                    $t = $xs->getTypeDef($type);
                    $this->appendDebug($xs->getDebug());
                    $xs->clearDebug();
                    if ($t) {
                        $this->debug("in getTypeDef: found type $type");
                        if (!isset($t['phpType'])) {
                            // get info for type to tack onto the element
                            $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
                            $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
                            $etype = $this->getTypeDef($uqType, $ns);
                            if ($etype) {
                                $this->debug("found type for [element] $type:");
                                $this->debug($this->varDump($etype));
                                if (isset($etype['phpType'])) {
                                    $t['phpType'] = $etype['phpType'];
                                }
                                if (isset($etype['elements'])) {
                                    $t['elements'] = $etype['elements'];
                                }
                                if (isset($etype['attrs'])) {
                                    $t['attrs'] = $etype['attrs'];
                                }
                            } else {
                                $this->debug("did not find type for [element] $type");
                            }
                        }
                        return $t;
                    }
                }
                $this->debug("in getTypeDef: did not find type $type");
            } else {
                $this->debug("in getTypeDef: do not have schema for namespace $ns");
            }
            return false;
        }
        
        /**
         * prints html description of services
         *
         * @access private
         */
        function webDescription(){
            global $HTTP_SERVER_VARS;
            
            if (isset($_SERVER)) {
                $PHP_SELF = $_SERVER['PHP_SELF'];
            } elseif (isset($HTTP_SERVER_VARS)) {
                $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
            } else {
                $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
            }
            
            $b = '
		<html><head><title>NuSOAP: '.$this->serviceName.'</title>
		<style type="text/css">
		    body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
		    p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
		    pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
		    ul      { margin-top: 10px; margin-left: 20px; }
		    li      { list-style-type: none; margin-top: 10px; color: #000000; }
		    .content{
			margin-left: 0px; padding-bottom: 2em; }
		    .nav {
			padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
			margin-top: 10px; margin-left: 0px; color: #000000;
			background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
		    .title {
			font-family: arial; font-size: 26px; color: #ffffff;
			background-color: #999999; width: 100%;
			margin-left: 0px; margin-right: 0px;
			padding-top: 10px; padding-bottom: 10px;}
		    .hidden {
			position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
			font-family: arial; overflow: hidden; width: 600;
			padding: 20px; font-size: 10px; background-color: #999999;
			layer-background-color:#FFFFFF; }
		    a,a:active  { color: charcoal; font-weight: bold; }
		    a:visited   { color: #666666; font-weight: bold; }
		    a:hover     { color: cc3300; font-weight: bold; }
		</style>
		<script language="JavaScript" type="text/javascript">
		<!--
		// POP-UP CAPTIONS...
		function lib_bwcheck(){ //Browsercheck (needed)
		    this.ver=navigator.appVersion
		    this.agent=navigator.userAgent
		    this.dom=document.getElementById?1:0
		    this.opera5=this.agent.indexOf("Opera 5")>-1
		    this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
		    this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
		    this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
		    this.ie=this.ie4||this.ie5||this.ie6
		    this.mac=this.agent.indexOf("Mac")>-1
		    this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
		    this.ns4=(document.layers && !this.dom)?1:0;
		    this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
		    return this
		}
		var bw = new lib_bwcheck()
		//Makes crossbrowser object.
		function makeObj(obj){
		    this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
		    if(!this.evnt) return false
		    this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
		    this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
		    this.writeIt=b_writeIt;
		    return this
		}
		// A unit of measure that will be added when setting the position of a layer.
		//var px = bw.ns4||window.opera?"":"px";
		function b_writeIt(text){
		    if (bw.ns4){this.wref.write(text);this.wref.close()}
		    else this.wref.innerHTML = text
		}
		//Shows the messages
		var oDesc;
		function popup(divid){
		    if(oDesc = new makeObj(divid)){
			oDesc.css.visibility = "visible"
		    }
		}
		function popout(){ // Hides message
		    if(oDesc) oDesc.css.visibility = "hidden"
		}
		//-->
		</script>
		</head>
		<body>
		<div class=content>
			<br><br>
			<div class=title>'.$this->serviceName.'</div>
			<div class=nav>
				<p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
				Click on an operation name to view it&apos;s details.</p>
				<ul>';
            foreach($this->getOperations() as $op => $data){
                $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
                // create hidden div
                $b .= "<div id='$op' class='hidden'>
				    <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
                foreach($data as $donnie => $marie){ // loop through opdata
                    if($donnie == 'input' || $donnie == 'output'){ // show input/output data
                        $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
                        foreach($marie as $captain => $tenille){ // loop through data
                            if($captain == 'parts'){ // loop thru parts
                                $b .= "&nbsp;&nbsp;$captain:<br>";
                                //if(is_array($tenille)){
                                foreach($tenille as $joanie => $chachi){
                                    $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
                                }
                                //}
                            } else {
                                $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
                            }
                        }
                    } else {
                        $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
                    }
                }
                $b .= '</div>';
            }
            $b .= '
				<ul>
			</div>
		</div></body></html>';
            return $b;
        }
        
        /**
         * serialize the parsed wsdl
         *
         * @param mixed $debug whether to put debug=1 in endpoint URL
         * @return string serialization of WSDL
         * @access public
         */
        function serialize($debug = 0)
        {
            $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
            $xml .= "\n<definitions";
            foreach($this->namespaces as $k => $v) {
                $xml .= " xmlns:$k=\"$v\"";
            }
            // 10.9.02 - add poulter fix for wsdl and tns declarations
            if (isset($this->namespaces['wsdl'])) {
                $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
            }
            if (isset($this->namespaces['tns'])) {
                $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
            }
            $xml .= '>';
            // imports
            if (sizeof($this->import) > 0) {
                foreach($this->import as $ns => $list) {
                    foreach ($list as $ii) {
                        if ($ii['location'] != '') {
                            $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
                        } else {
                            $xml .= '<import namespace="' . $ns . '" />';
                        }
                    }
                }
            }
            // types
            if (count($this->schemas)>=1) {
                $xml .= "\n<types>\n";
                foreach ($this->schemas as $ns => $list) {
                    foreach ($list as $xs) {
                        $xml .= $xs->serializeSchema();
                    }
                }
                $xml .= '</types>';
            }
            // messages
            if (count($this->messages) >= 1) {
                foreach($this->messages as $msgName => $msgParts) {
                    $xml .= "\n<message name=\"" . $msgName . '">';
                    if(is_array($msgParts)){
                        foreach($msgParts as $partName => $partType) {
                            // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
                            if (strpos($partType, ':')) {
                                $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
                            } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
                                // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
                                $typePrefix = 'xsd';
                            } else {
                                foreach($this->typemap as $ns => $types) {
                                    if (isset($types[$partType])) {
                                        $typePrefix = $this->getPrefixFromNamespace($ns);
                                    }
                                }
                                if (!isset($typePrefix)) {
                                    die("$partType has no namespace!");
                                }
                            }
                            $ns = $this->getNamespaceFromPrefix($typePrefix);
                            $localPart = $this->getLocalPart($partType);
                            $typeDef = $this->getTypeDef($localPart, $ns);
                            if ($typeDef['typeClass'] == 'element') {
                                $elementortype = 'element';
                                if (substr($localPart, -1) == '^') {
                                    $localPart = substr($localPart, 0, -1);
                                }
                            } else {
                                $elementortype = 'type';
                            }
                            $xml .= "\n" . '  <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
                        }
                    }
                    $xml .= '</message>';
                }
            }
            // bindings & porttypes
            if (count($this->bindings) >= 1) {
                $binding_xml = '';
                $portType_xml = '';
                foreach($this->bindings as $bindingName => $attrs) {
                    $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
                    $binding_xml .= "\n" . '  <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
                    $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
                    foreach($attrs['operations'] as $opName => $opParts) {
                        $binding_xml .= "\n" . '  <operation name="' . $opName . '">';
                        $binding_xml .= "\n" . '    <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
                        if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
                            $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
                        } else {
                            $enc_style = '';
                        }
                        $binding_xml .= "\n" . '    <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
                        if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
                            $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
                        } else {
                            $enc_style = '';
                        }
                        $binding_xml .= "\n" . '    <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
                        $binding_xml .= "\n" . '  </operation>';
                        $portType_xml .= "\n" . '  <operation name="' . $opParts['name'] . '"';
                        if (isset($opParts['parameterOrder'])) {
                            $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
                        }
                        $portType_xml .= '>';
                        if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
                            $portType_xml .= "\n" . '    <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
                        }
                        $portType_xml .= "\n" . '    <input message="tns:' . $opParts['input']['message'] . '"/>';
                        $portType_xml .= "\n" . '    <output message="tns:' . $opParts['output']['message'] . '"/>';
                        $portType_xml .= "\n" . '  </operation>';
                    }
                    $portType_xml .= "\n" . '</portType>';
                    $binding_xml .= "\n" . '</binding>';
                }
                $xml .= $portType_xml . $binding_xml;
            }
            // services
            $xml .= "\n<service name=\"" . $this->serviceName . '">';
            if (count($this->ports) >= 1) {
                foreach($this->ports as $pName => $attrs) {
                    $xml .= "\n" . '  <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
                    $xml .= "\n" . '    <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
                    $xml .= "\n" . '  </port>';
                }
            }
            $xml .= "\n" . '</service>';
            return $xml . "\n</definitions>";
        }
        
        /**
         * determine whether a set of parameters are unwrapped
         * when they are expect to be wrapped, Microsoft-style.
         *
         * @param string $type the type (element name) of the wrapper
         * @param array $parameters the parameter values for the SOAP call
         * @return boolean whether they parameters are unwrapped (and should be wrapped)
         * @access private
         */
        function parametersMatchWrapped($type, &$parameters) {
            $this->debug("in parametersMatchWrapped type=$type, parameters=");
            $this->appendDebug($this->varDump($parameters));
            
            // split type into namespace:unqualified-type
            if (strpos($type, ':')) {
                $uqType = substr($type, strrpos($type, ':') + 1);
                $ns = substr($type, 0, strrpos($type, ':'));
                $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
                if ($this->getNamespaceFromPrefix($ns)) {
                    $ns = $this->getNamespaceFromPrefix($ns);
                    $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
                }
            } else {
                // TODO: should the type be compared to types in XSD, and the namespace
                // set to XSD if the type matches?
                $this->debug("in parametersMatchWrapped: No namespace for type $type");
                $ns = '';
                $uqType = $type;
            }
            
            // get the type information
            if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
                $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
                return false;
            }
            $this->debug("in parametersMatchWrapped: found typeDef=");
            $this->appendDebug($this->varDump($typeDef));
            if (substr($uqType, -1) == '^') {
                $uqType = substr($uqType, 0, -1);
            }
            $phpType = $typeDef['phpType'];
            $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
            $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
            
            // we expect a complexType or element of complexType
            if ($phpType != 'struct') {
                $this->debug("in parametersMatchWrapped: not a struct");
                return false;
            }
            
            // see whether the parameter names match the elements
            if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
                $elements = 0;
                $matches = 0;
                foreach ($typeDef['elements'] as $name => $attrs) {
                    if (isset($parameters[$name])) {
                        $this->debug("in parametersMatchWrapped: have parameter named $name");
                        $matches++;
                    } else {
                        $this->debug("in parametersMatchWrapped: do not have parameter named $name");
                    }
                    $elements++;
                }
                
                $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
                if ($matches == 0) {
                    return false;
                }
                return true;
            }
            
            // since there are no elements for the type, if the user passed no
            // parameters, the parameters match wrapped.
            $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
            return count($parameters) == 0;
        }
        
        /**
         * serialize PHP values according to a WSDL message definition
         * contrary to the method name, this is not limited to RPC
         *
         * TODO
         * - multi-ref serialization
         * - validate PHP values against type definitions, return errors if invalid
         *
         * @param string $operation operation name
         * @param string $direction (input|output)
         * @param mixed $parameters parameter value(s)
         * @param string $bindingType (soap|soap12)
         * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
         * @access public
         */
        function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') {
            $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
            $this->appendDebug('parameters=' . $this->varDump($parameters));
            
            if ($direction != 'input' && $direction != 'output') {
                $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
                $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
                return false;
            }
            if (!$opData = $this->getOperationData($operation, $bindingType)) {
                $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
                $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
                return false;
            }
            $this->debug('in serializeRPCParameters: opData:');
            $this->appendDebug($this->varDump($opData));
            
            // Get encoding style for output and set to current
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
            if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
                $encodingStyle = $opData['output']['encodingStyle'];
                $enc_style = $encodingStyle;
            }
            
            // set input params
            $xml = '';
            if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
                $parts = &$opData[$direction]['parts'];
                $part_count = sizeof($parts);
                $style = $opData['style'];
                $use = $opData[$direction]['use'];
                $this->debug("have $part_count part(s) to serialize using $style/$use");
                if (is_array($parameters)) {
                    $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
                    $parameter_count = count($parameters);
                    $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
                    // check for Microsoft-style wrapped parameters
                    if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
                        $this->debug('check whether the caller has wrapped the parameters');
                        if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
                            // TODO: consider checking here for double-wrapping, when
                            // service function wraps, then NuSOAP wraps again
                            $this->debug("change simple array to associative with 'parameters' element");
                            $parameters['parameters'] = $parameters[0];
                            unset($parameters[0]);
                        }
                        if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
                            $this->debug('check whether caller\'s parameters match the wrapped ones');
                            if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
                                $this->debug('wrap the parameters for the caller');
                                $parameters = array('parameters' => $parameters);
                                $parameter_count = 1;
                            }
                        }
                    }
                    foreach ($parts as $name => $type) {
                        $this->debug("serializing part $name of type $type");
                        // Track encoding style
                        if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
                            $encodingStyle = $opData[$direction]['encodingStyle'];
                            $enc_style = $encodingStyle;
                        } else {
                            $enc_style = false;
                        }
                        // NOTE: add error handling here
                        // if serializeType returns false, then catch global error and fault
                        if ($parametersArrayType == 'arraySimple') {
                            $p = array_shift($parameters);
                            $this->debug('calling serializeType w/indexed param');
                            $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
                        } elseif (isset($parameters[$name])) {
                            $this->debug('calling serializeType w/named param');
                            $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
                        } else {
                            // TODO: only send nillable
                            $this->debug('calling serializeType w/null param');
                            $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
                        }
                    }
                } else {
                    $this->debug('no parameters passed.');
                }
            }
            $this->debug("serializeRPCParameters returning: $xml");
            return $xml;
        }
        
        /**
         * serialize a PHP value according to a WSDL message definition
         *
         * TODO
         * - multi-ref serialization
         * - validate PHP values against type definitions, return errors if invalid
         *
         * @param string $operation operation name
         * @param string $direction (input|output)
         * @param mixed $parameters parameter value(s)
         * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
         * @access public
         * @deprecated
         */
        function serializeParameters($operation, $direction, $parameters)
        {
            $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
            $this->appendDebug('parameters=' . $this->varDump($parameters));
            
            if ($direction != 'input' && $direction != 'output') {
                $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
                $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
                return false;
            }
            if (!$opData = $this->getOperationData($operation)) {
                $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
                $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
                return false;
            }
            $this->debug('opData:');
            $this->appendDebug($this->varDump($opData));
            
            // Get encoding style for output and set to current
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
            if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
                $encodingStyle = $opData['output']['encodingStyle'];
                $enc_style = $encodingStyle;
            }
            
            // set input params
            $xml = '';
            if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
                
                $use = $opData[$direction]['use'];
                $this->debug("use=$use");
                $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
                if (is_array($parameters)) {
                    $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
                    $this->debug('have ' . $parametersArrayType . ' parameters');
                    foreach($opData[$direction]['parts'] as $name => $type) {
                        $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
                        // Track encoding style
                        if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
                            $encodingStyle = $opData[$direction]['encodingStyle'];
                            $enc_style = $encodingStyle;
                        } else {
                            $enc_style = false;
                        }
                        // NOTE: add error handling here
                        // if serializeType returns false, then catch global error and fault
                        if ($parametersArrayType == 'arraySimple') {
                            $p = array_shift($parameters);
                            $this->debug('calling serializeType w/indexed param');
                            $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
                        } elseif (isset($parameters[$name])) {
                            $this->debug('calling serializeType w/named param');
                            $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
                        } else {
                            // TODO: only send nillable
                            $this->debug('calling serializeType w/null param');
                            $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
                        }
                    }
                } else {
                    $this->debug('no parameters passed.');
                }
            }
            $this->debug("serializeParameters returning: $xml");
            return $xml;
        }
        
        /**
         * serializes a PHP value according a given type definition
         *
         * @param string $name name of value (part or element)
         * @param string $type XML schema type of value (type or element)
         * @param mixed $value a native PHP value (parameter value)
         * @param string $use use for part (encoded|literal)
         * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
         * @param boolean $unqualified a kludge for what should be XML namespace form handling
         * @return string value serialized as an XML string
         * @access private
         */
        function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
        {
            $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
            $this->appendDebug("value=" . $this->varDump($value));
            if($use == 'encoded' && $encodingStyle) {
                $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
            }
            
            // if a soapval has been supplied, let its type override the WSDL
            if (is_object($value) && get_class($value) == 'soapval') {
                if ($value->type_ns) {
                    $type = $value->type_ns . ':' . $value->type;
                    $forceType = true;
                    $this->debug("in serializeType: soapval overrides type to $type");
                } elseif ($value->type) {
                    $type = $value->type;
                    $forceType = true;
                    $this->debug("in serializeType: soapval overrides type to $type");
                } else {
                    $forceType = false;
                    $this->debug("in serializeType: soapval does not override type");
                }
                $attrs = $value->attributes;
                $value = $value->value;
                $this->debug("in serializeType: soapval overrides value to $value");
                if ($attrs) {
                    if (!is_array($value)) {
                        $value['!'] = $value;
                    }
                    foreach ($attrs as $n => $v) {
                        $value['!' . $n] = $v;
                    }
                    $this->debug("in serializeType: soapval provides attributes");
                }
            } else {
                $forceType = false;
            }
            
            $xml = '';
            if (strpos($type, ':')) {
                $uqType = substr($type, strrpos($type, ':') + 1);
                $ns = substr($type, 0, strrpos($type, ':'));
                $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
                if ($this->getNamespaceFromPrefix($ns)) {
                    $ns = $this->getNamespaceFromPrefix($ns);
                    $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
                }
                
                if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
                    $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
                    if ($unqualified && $use == 'literal') {
                        $elementNS = " xmlns=\"\"";
                    } else {
                        $elementNS = '';
                    }
                    if (is_null($value)) {
                        if ($use == 'literal') {
                            // TODO: depends on minOccurs
                            $xml = "<$name$elementNS/>";
                        } else {
                            // TODO: depends on nillable, which should be checked before calling this method
                            $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
                        }
                        $this->debug("in serializeType: returning: $xml");
                        return $xml;
                    }
                    if ($uqType == 'Array') {
                        // JBoss/Axis does this sometimes
                        return $this->serialize_val($value, $name, false, false, false, false, $use);
                    }
                    if ($uqType == 'boolean') {
                        if ((is_string($value) && $value == 'false') || (! $value)) {
                            $value = 'false';
                        } else {
                            $value = 'true';
                        }
                    }
                    if ($uqType == 'string' && gettype($value) == 'string') {
                        $value = $this->expandEntities($value);
                    }
                    if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
                        $value = sprintf("%.0lf", $value);
                    }
                    // it's a scalar
                    // TODO: what about null/nil values?
                    // check type isn't a custom type extending xmlschema namespace
                    if (!$this->getTypeDef($uqType, $ns)) {
                        if ($use == 'literal') {
                            if ($forceType) {
                                $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
                            } else {
                                $xml = "<$name$elementNS>$value</$name>";
                            }
                        } else {
                            $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
                        }
                        $this->debug("in serializeType: returning: $xml");
                        return $xml;
                    }
                    $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
                } else if ($ns == 'http://xml.apache.org/xml-soap') {
                    $this->debug('in serializeType: appears to be Apache SOAP type');
                    if ($uqType == 'Map') {
                        $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
                        if (! $tt_prefix) {
                            $this->debug('in serializeType: Add namespace for Apache SOAP type');
                            $tt_prefix = 'ns' . rand(1000, 9999);
                            $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
                            // force this to be added to usedNamespaces
                            $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
                        }
                        $contents = '';
                        foreach($value as $k => $v) {
                            $this->debug("serializing map element: key $k, value $v");
                            $contents .= '<item>';
                            $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
                            $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
                            $contents .= '</item>';
                        }
                        if ($use == 'literal') {
                            if ($forceType) {
                                $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
                            } else {
                                $xml = "<$name>$contents</$name>";
                            }
                        } else {
                            $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
                        }
                        $this->debug("in serializeType: returning: $xml");
                        return $xml;
                    }
                    $this->debug('in serializeType: Apache SOAP type, but only support Map');
                }
            } else {
                // TODO: should the type be compared to types in XSD, and the namespace
                // set to XSD if the type matches?
                $this->debug("in serializeType: No namespace for type $type");
                $ns = '';
                $uqType = $type;
            }
            if(!$typeDef = $this->getTypeDef($uqType, $ns)){
                $this->setError("$type ($uqType) is not a supported type.");
                $this->debug("in serializeType: $type ($uqType) is not a supported type.");
                return false;
            } else {
                $this->debug("in serializeType: found typeDef");
                $this->appendDebug('typeDef=' . $this->varDump($typeDef));
                if (substr($uqType, -1) == '^') {
                    $uqType = substr($uqType, 0, -1);
                }
            }
            if (!isset($typeDef['phpType'])) {
                $this->setError("$type ($uqType) has no phpType.");
                $this->debug("in serializeType: $type ($uqType) has no phpType.");
                return false;
            }
            $phpType = $typeDef['phpType'];
            $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
            // if php type == struct, map value to the <all> element names
            if ($phpType == 'struct') {
                if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
                    $elementName = $uqType;
                    if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
                        $elementNS = " xmlns=\"$ns\"";
                    } else {
                        $elementNS = " xmlns=\"\"";
                    }
                } else {
                    $elementName = $name;
                    if ($unqualified) {
                        $elementNS = " xmlns=\"\"";
                    } else {
                        $elementNS = '';
                    }
                }
                if (is_null($value)) {
                    if ($use == 'literal') {
                        // TODO: depends on minOccurs and nillable
                        $xml = "<$elementName$elementNS/>";
                    } else {
                        $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
                    }
                    $this->debug("in serializeType: returning: $xml");
                    return $xml;
                }
                if (is_object($value)) {
                    $value = get_object_vars($value);
                }
                if (is_array($value)) {
                    $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
                    if ($use == 'literal') {
                        if ($forceType) {
                            $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
                        } else {
                            $xml = "<$elementName$elementNS$elementAttrs>";
                        }
                    } else {
                        $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
                    }
                    
                    if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
                        if (isset($value['!'])) {
                            $xml .= $value['!'];
                            $this->debug("in serializeType: serialized simpleContent for type $type");
                        } else {
                            $this->debug("in serializeType: no simpleContent to serialize for type $type");
                        }
                    } else {
                        // complexContent
                        $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
                    }
                    $xml .= "</$elementName>";
                } else {
                    $this->debug("in serializeType: phpType is struct, but value is not an array");
                    $this->setError("phpType is struct, but value is not an array: see debug output for details");
                    $xml = '';
                }
            } elseif ($phpType == 'array') {
                if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
                    $elementNS = " xmlns=\"$ns\"";
                } else {
                    if ($unqualified) {
                        $elementNS = " xmlns=\"\"";
                    } else {
                        $elementNS = '';
                    }
                }
                if (is_null($value)) {
                    if ($use == 'literal') {
                        // TODO: depends on minOccurs
                        $xml = "<$name$elementNS/>";
                    } else {
                        $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
                        $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
                        ":Array\" " .
                        $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
                        ':arrayType="' .
                        $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
                        ':' .
                        $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
                    }
                    $this->debug("in serializeType: returning: $xml");
                    return $xml;
                }
                if (isset($typeDef['multidimensional'])) {
                    $nv = array();
                    foreach($value as $v) {
                        $cols = ',' . sizeof($v);
                        $nv = array_merge($nv, $v);
                    }
                    $value = $nv;
                } else {
                    $cols = '';
                }
                if (is_array($value) && sizeof($value) >= 1) {
                    $rows = sizeof($value);
                    $contents = '';
                    foreach($value as $k => $v) {
                        $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
                        //if (strpos($typeDef['arrayType'], ':') ) {
                        if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
                            $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
                        } else {
                            $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
                        }
                    }
                } else {
                    $rows = 0;
                    $contents = null;
                }
                // TODO: for now, an empty value will be serialized as a zero element
                // array.  Revisit this when coding the handling of null/nil values.
                if ($use == 'literal') {
                    $xml = "<$name$elementNS>"
                    .$contents
                    ."</$name>";
                } else {
                    $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
                        $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
                        .':arrayType="'
                            .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
                            .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
                            .$contents
                            ."</$name>";
                }
            } elseif ($phpType == 'scalar') {
                if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
                    $elementNS = " xmlns=\"$ns\"";
                } else {
                    if ($unqualified) {
                        $elementNS = " xmlns=\"\"";
                    } else {
                        $elementNS = '';
                    }
                }
                if ($use == 'literal') {
                    if ($forceType) {
                        $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
                    } else {
                        $xml = "<$name$elementNS>$value</$name>";
                    }
                } else {
                    $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
                }
            }
            $this->debug("in serializeType: returning: $xml");
            return $xml;
        }
        
        /**
         * serializes the attributes for a complexType
         *
         * @param array $typeDef our internal representation of an XML schema type (or element)
         * @param mixed $value a native PHP value (parameter value)
         * @param string $ns the namespace of the type
         * @param string $uqType the local part of the type
         * @return string value serialized as an XML string
         * @access private
         */
        function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
            $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
            $xml = '';
            if (isset($typeDef['extensionBase'])) {
                $nsx = $this->getPrefix($typeDef['extensionBase']);
                $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
                if ($this->getNamespaceFromPrefix($nsx)) {
                    $nsx = $this->getNamespaceFromPrefix($nsx);
                }
                if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
                    $this->debug("serialize attributes for extension base $nsx:$uqTypex");
                    $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
                } else {
                    $this->debug("extension base $nsx:$uqTypex is not a supported type");
                }
            }
            if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
                $this->debug("serialize attributes for XML Schema type $ns:$uqType");
                if (is_array($value)) {
                    $xvalue = $value;
                } elseif (is_object($value)) {
                    $xvalue = get_object_vars($value);
                } else {
                    $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
                    $xvalue = array();
                }
                foreach ($typeDef['attrs'] as $aName => $attrs) {
                    if (isset($xvalue['!' . $aName])) {
                        $xname = '!' . $aName;
                        $this->debug("value provided for attribute $aName with key $xname");
                    } elseif (isset($xvalue[$aName])) {
                        $xname = $aName;
                        $this->debug("value provided for attribute $aName with key $xname");
                    } elseif (isset($attrs['default'])) {
                        $xname = '!' . $aName;
                        $xvalue[$xname] = $attrs['default'];
                        $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
                    } else {
                        $xname = '';
                        $this->debug("no value provided for attribute $aName");
                    }
                    if ($xname) {
                        $xml .=  " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
                    }
                }
            } else {
                $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
            }
            return $xml;
        }
        
        /**
         * serializes the elements for a complexType
         *
         * @param array $typeDef our internal representation of an XML schema type (or element)
         * @param mixed $value a native PHP value (parameter value)
         * @param string $ns the namespace of the type
         * @param string $uqType the local part of the type
         * @param string $use use for part (encoded|literal)
         * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
         * @return string value serialized as an XML string
         * @access private
         */
        function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
            $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
            $xml = '';
            if (isset($typeDef['extensionBase'])) {
                $nsx = $this->getPrefix($typeDef['extensionBase']);
                $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
                if ($this->getNamespaceFromPrefix($nsx)) {
                    $nsx = $this->getNamespaceFromPrefix($nsx);
                }
                if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
                    $this->debug("serialize elements for extension base $nsx:$uqTypex");
                    $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
                } else {
                    $this->debug("extension base $nsx:$uqTypex is not a supported type");
                }
            }
            if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
                $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
                if (is_array($value)) {
                    $xvalue = $value;
                } elseif (is_object($value)) {
                    $xvalue = get_object_vars($value);
                } else {
                    $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
                    $xvalue = array();
                }
                // toggle whether all elements are present - ideally should validate against schema
                if (count($typeDef['elements']) != count($xvalue)){
                    $optionals = true;
                }
                foreach ($typeDef['elements'] as $eName => $attrs) {
                    if (!isset($xvalue[$eName])) {
                        if (isset($attrs['default'])) {
                            $xvalue[$eName] = $attrs['default'];
                            $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
                        }
                    }
                    // if user took advantage of a minOccurs=0, then only serialize named parameters
                    if (isset($optionals)
                        && (!isset($xvalue[$eName]))
                        && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
                        ){
                            if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
                                $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
                            }
                            // do nothing
                            $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
                    } else {
                        // get value
                        if (isset($xvalue[$eName])) {
                            $v = $xvalue[$eName];
                        } else {
                            $v = null;
                        }
                        if (isset($attrs['form'])) {
                            $unqualified = ($attrs['form'] == 'unqualified');
                        } else {
                            $unqualified = false;
                        }
                        if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
                            $vv = $v;
                            foreach ($vv as $k => $v) {
                                if (isset($attrs['type']) || isset($attrs['ref'])) {
                                    // serialize schema-defined type
                                    $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
                                } else {
                                    // serialize generic type (can this ever really happen?)
                                    $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
                                    $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
                                }
                            }
                        } else {
                            if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') {
                                // do nothing
                            } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') {
                                // TODO: serialize a nil correctly, but for now serialize schema-defined type
                                $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
                            } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
                                // serialize schema-defined type
                                $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
                            } else {
                                // serialize generic type (can this ever really happen?)
                                $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
                                $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
                            }
                        }
                    }
                }
            } else {
                $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
            }
            return $xml;
        }
        
        /**
         * adds an XML Schema complex type to the WSDL types
         *
         * @param string	$name
         * @param string $typeClass (complexType|simpleType|attribute)
         * @param string $phpType currently supported are array and struct (php assoc array)
         * @param string $compositor (all|sequence|choice)
         * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
         * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
         * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
         * @param string $arrayType as namespace:name (xsd:string)
         * @see nusoap_xmlschema
         * @access public
         */
        function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
            if (count($elements) > 0) {
                $eElements = array();
                foreach($elements as $n => $e){
                    // expand each element
                    $ee = array();
                    foreach ($e as $k => $v) {
                        $k = strpos($k,':') ? $this->expandQname($k) : $k;
                        $v = strpos($v,':') ? $this->expandQname($v) : $v;
                        $ee[$k] = $v;
                    }
                    $eElements[$n] = $ee;
                }
                $elements = $eElements;
            }
            
            if (count($attrs) > 0) {
                foreach($attrs as $n => $a){
                    // expand each attribute
                    foreach ($a as $k => $v) {
                        $k = strpos($k,':') ? $this->expandQname($k) : $k;
                        $v = strpos($v,':') ? $this->expandQname($v) : $v;
                        $aa[$k] = $v;
                    }
                    $eAttrs[$n] = $aa;
                }
                $attrs = $eAttrs;
            }
            
            $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
            $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
            
            $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
            $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
        }
        
        /**
         * adds an XML Schema simple type to the WSDL types
         *
         * @param string $name
         * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
         * @param string $typeClass (should always be simpleType)
         * @param string $phpType (should always be scalar)
         * @param array $enumeration array of values
         * @see nusoap_xmlschema
         * @access public
         */
        function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
            $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
            
            $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
            $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
        }
        
        /**
         * adds an element to the WSDL types
         *
         * @param array $attrs attributes that must include name and type
         * @see nusoap_xmlschema
         * @access public
         */
        function addElement($attrs) {
            $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
            $this->schemas[$typens][0]->addElement($attrs);
        }
        
        /**
         * register an operation with the server
         *
         * @param string $name operation (method) name
         * @param array $in assoc array of input values: key = param name, value = param type
         * @param array $out assoc array of output values: key = param name, value = param type
         * @param string $namespace optional The namespace for the operation
         * @param string $soapaction optional The soapaction for the operation
         * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
         * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
         * @param string $documentation optional The description to include in the WSDL
         * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
         * @access public
         */
        function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
            if ($use == 'encoded' && $encodingStyle == '') {
                $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
            }
            
            if ($style == 'document') {
                $elements = array();
                foreach ($in as $n => $t) {
                    $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
                }
                $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
                $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
                $in = array('parameters' => 'tns:' . $name . '^');
                
                $elements = array();
                foreach ($out as $n => $t) {
                    $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
                }
                $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
                $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
                $out = array('parameters' => 'tns:' . $name . 'Response' . '^');
            }
            
            // get binding
            $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
            array(
                'name' => $name,
                'binding' => $this->serviceName . 'Binding',
                'endpoint' => $this->endpoint,
                'soapAction' => $soapaction,
                'style' => $style,
                'input' => array(
                    'use' => $use,
                    'namespace' => $namespace,
                    'encodingStyle' => $encodingStyle,
                    'message' => $name . 'Request',
                    'parts' => $in),
                'output' => array(
                    'use' => $use,
                    'namespace' => $namespace,
                    'encodingStyle' => $encodingStyle,
                    'message' => $name . 'Response',
                    'parts' => $out),
                'namespace' => $namespace,
                'transport' => 'http://schemas.xmlsoap.org/soap/http',
                'documentation' => $documentation);
            // add portTypes
            // add messages
            if($in)
            {
                foreach($in as $pName => $pType)
                {
                    if(strpos($pType,':')) {
                        $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
                    }
                    $this->messages[$name.'Request'][$pName] = $pType;
                }
            } else {
                $this->messages[$name.'Request']= '0';
            }
            if($out)
            {
                foreach($out as $pName => $pType)
                {
                    if(strpos($pType,':')) {
                        $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
                    }
                    $this->messages[$name.'Response'][$pName] = $pType;
                }
            } else {
                $this->messages[$name.'Response']= '0';
            }
            return true;
        }
    }

<?php
namespace com\linways\base\soap;

/**
 * Contains information for a SOAP fault.
 * Mainly used for returning faults from deployed functions
 * in a server instance.
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access public
 */
class nusoap_fault extends nusoap_base {
    /**
     * The fault code (client|server)
     * @var string
     * @access private
     */
    var $faultcode;
    /**
     * The fault actor
     * @var string
     * @access private
     */
    var $faultactor;
    /**
     * The fault string, a description of the fault
     * @var string
     * @access private
     */
    var $faultstring;
    /**
     * The fault detail, typically a string or array of string
     * @var mixed
     * @access private
     */
    var $faultdetail;
    
    /**
     * constructor
     *
     * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
     * @param string $faultactor only used when msg routed between multiple actors
     * @param string $faultstring human readable error message
     * @param mixed $faultdetail detail, typically a string or array of string
     */
    function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
        parent::nusoap_base();
        $this->faultcode = $faultcode;
        $this->faultactor = $faultactor;
        $this->faultstring = $faultstring;
        $this->faultdetail = $faultdetail;
    }
    
    /**
     * serialize a fault
     *
     * @return	string	The serialization of the fault instance.
     * @access   public
     */
    function serialize(){
        $ns_string = '';
        foreach($this->namespaces as $k => $v){
            $ns_string .= "\n  xmlns:$k=\"$v\"";
        }
        $return_msg =
        '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
        '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
        '<SOAP-ENV:Body>'.
        '<SOAP-ENV:Fault>'.
        $this->serialize_val($this->faultcode, 'faultcode').
        $this->serialize_val($this->faultactor, 'faultactor').
        $this->serialize_val($this->faultstring, 'faultstring').
        $this->serialize_val($this->faultdetail, 'detail').
        '</SOAP-ENV:Fault>'.
        '</SOAP-ENV:Body>'.
        '</SOAP-ENV:Envelope>';
        return $return_msg;
    }
}

<?php
namespace com\linways\base\soap;


/**
 *
 * nusoap_server allows the user to create a SOAP server
 * that is capable of receiving messages and returning responses
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @author   Scott Nichol <snichol@users.sourceforge.net>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access   public
 */
class nusoap_server extends nusoap_base {
    /**
     * HTTP headers of request
     * @var array
     * @access private
     */
    var $headers = array();
    /**
     * HTTP request
     * @var string
     * @access private
     */
    var $request = '';
    /**
     * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
     * @var string
     * @access public
     */
    var $requestHeaders = '';
    /**
     * SOAP Headers from request (parsed)
     * @var mixed
     * @access public
     */
    var $requestHeader = NULL;
    /**
     * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
     * @var string
     * @access public
     */
    var $document = '';
    /**
     * SOAP payload for request (text)
     * @var string
     * @access public
     */
    var $requestSOAP = '';
    /**
     * requested method namespace URI
     * @var string
     * @access private
     */
    var $methodURI = '';
    /**
     * name of method requested
     * @var string
     * @access private
     */
    var $methodname = '';
    /**
     * method parameters from request
     * @var array
     * @access private
     */
    var $methodparams = array();
    /**
     * SOAP Action from request
     * @var string
     * @access private
     */
    var $SOAPAction = '';
    /**
     * character set encoding of incoming (request) messages
     * @var string
     * @access public
     */
    var $xml_encoding = '';
    /**
     * toggles whether the parser decodes element content w/ utf8_decode()
     * @var boolean
     * @access public
     */
    var $decode_utf8 = true;
    
    /**
     * HTTP headers of response
     * @var array
     * @access public
     */
    var $outgoing_headers = array();
    /**
     * HTTP response
     * @var string
     * @access private
     */
    var $response = '';
    /**
     * SOAP headers for response (text or array of soapval or associative array)
     * @var mixed
     * @access public
     */
    var $responseHeaders = '';
    /**
     * SOAP payload for response (text)
     * @var string
     * @access private
     */
    var $responseSOAP = '';
    /**
     * method return value to place in response
     * @var mixed
     * @access private
     */
    var $methodreturn = false;
    /**
     * whether $methodreturn is a string of literal XML
     * @var boolean
     * @access public
     */
    var $methodreturnisliteralxml = false;
    /**
     * SOAP fault for response (or false)
     * @var mixed
     * @access private
     */
    var $fault = false;
    /**
     * text indication of result (for debugging)
     * @var string
     * @access private
     */
    var $result = 'successful';
    
    /**
     * assoc array of operations => opData; operations are added by the register()
     * method or by parsing an external WSDL definition
     * @var array
     * @access private
     */
    var $operations = array();
    /**
     * wsdl instance (if one)
     * @var mixed
     * @access private
     */
    var $wsdl = false;
    /**
     * URL for WSDL (if one)
     * @var mixed
     * @access private
     */
    var $externalWSDLURL = false;
    /**
     * whether to append debug to response as XML comment
     * @var boolean
     * @access public
     */
    var $debug_flag = false;
    
    
    /**
     * constructor
     * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
     *
     * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
     * @access   public
     */
    function nusoap_server($wsdl=false){
        parent::nusoap_base();
        // turn on debugging?
        global $debug;
        global $HTTP_SERVER_VARS;
        
        if (isset($_SERVER)) {
            $this->debug("_SERVER is defined:");
            $this->appendDebug($this->varDump($_SERVER));
        } elseif (isset($HTTP_SERVER_VARS)) {
            $this->debug("HTTP_SERVER_VARS is defined:");
            $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
        } else {
            $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
        }
        
        if (isset($debug)) {
            $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
            $this->debug_flag = $debug;
        } elseif (isset($_SERVER['QUERY_STRING'])) {
            $qs = explode('&', $_SERVER['QUERY_STRING']);
            foreach ($qs as $v) {
                if (substr($v, 0, 6) == 'debug=') {
                    $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
                    $this->debug_flag = substr($v, 6);
                }
            }
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
            $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
            foreach ($qs as $v) {
                if (substr($v, 0, 6) == 'debug=') {
                    $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
                    $this->debug_flag = substr($v, 6);
                }
            }
        }
        
        // wsdl
        if($wsdl){
            $this->debug("In nusoap_server, WSDL is specified");
            if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
                $this->wsdl = $wsdl;
                $this->externalWSDLURL = $this->wsdl->wsdl;
                $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
            } else {
                $this->debug('Create wsdl from ' . $wsdl);
                $this->wsdl = new wsdl($wsdl);
                $this->externalWSDLURL = $wsdl;
            }
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            if($err = $this->wsdl->getError()){
                die('WSDL ERROR: '.$err);
            }
        }
    }
    
    /**
     * processes request and returns response
     *
     * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
     * @access   public
     */
    function service($data){
        global $HTTP_SERVER_VARS;
        
        if (isset($_SERVER['REQUEST_METHOD'])) {
            $rm = $_SERVER['REQUEST_METHOD'];
        } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
            $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
        } else {
            $rm = '';
        }
        
        if (isset($_SERVER['QUERY_STRING'])) {
            $qs = $_SERVER['QUERY_STRING'];
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
            $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
        } else {
            $qs = '';
        }
        $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
        
        if ($rm == 'POST') {
            $this->debug("In service, invoke the request");
            $this->parse_request($data);
            if (! $this->fault) {
                $this->invoke_method();
            }
            if (! $this->fault) {
                $this->serialize_return();
            }
            $this->send_response();
        } elseif (preg_match('/wsdl/', $qs) ){
            $this->debug("In service, this is a request for WSDL");
            if ($this->externalWSDLURL){
                if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
                    $this->debug("In service, re-direct for WSDL");
                    header('Location: '.$this->externalWSDLURL);
                } else { // assume file
                    $this->debug("In service, use file passthru for WSDL");
                    header("Content-Type: text/xml\r\n");
                    $pos = strpos($this->externalWSDLURL, "file://");
                    if ($pos === false) {
                        $filename = $this->externalWSDLURL;
                    } else {
                        $filename = substr($this->externalWSDLURL, $pos + 7);
                    }
                    $fp = fopen($this->externalWSDLURL, 'r');
                    fpassthru($fp);
                }
            } elseif ($this->wsdl) {
                $this->debug("In service, serialize WSDL");
                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
                print $this->wsdl->serialize($this->debug_flag);
                if ($this->debug_flag) {
                    $this->debug('wsdl:');
                    $this->appendDebug($this->varDump($this->wsdl));
                    print $this->getDebugAsXMLComment();
                }
            } else {
                $this->debug("In service, there is no WSDL");
                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
                print "This service does not provide WSDL";
            }
        } elseif ($this->wsdl) {
            $this->debug("In service, return Web description");
            print $this->wsdl->webDescription();
        } else {
            $this->debug("In service, no Web description");
            header("Content-Type: text/html; charset=ISO-8859-1\r\n");
            print "This service does not provide a Web description";
        }
    }
    
    /**
     * parses HTTP request headers.
     *
     * The following fields are set by this function (when successful)
     *
     * headers
     * request
     * xml_encoding
     * SOAPAction
     *
     * @access   private
     */
    function parse_http_headers() {
        global $HTTP_SERVER_VARS;
        
        $this->request = '';
        $this->SOAPAction = '';
        if(function_exists('getallheaders')){
            $this->debug("In parse_http_headers, use getallheaders");
            $headers = getallheaders();
            foreach($headers as $k=>$v){
                $k = strtolower($k);
                $this->headers[$k] = $v;
                $this->request .= "$k: $v\r\n";
                $this->debug("$k: $v");
            }
            // get SOAPAction header
            if(isset($this->headers['soapaction'])){
                $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
            }
            // get the character encoding of the incoming request
            if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
                $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
                if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
                    $this->xml_encoding = strtoupper($enc);
                } else {
                    $this->xml_encoding = 'US-ASCII';
                }
            } else {
                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
                $this->xml_encoding = 'ISO-8859-1';
            }
        } elseif(isset($_SERVER) && is_array($_SERVER)){
            $this->debug("In parse_http_headers, use _SERVER");
            foreach ($_SERVER as $k => $v) {
                if (substr($k, 0, 5) == 'HTTP_') {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
                } else {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
                }
                if ($k == 'soapaction') {
                    // get SOAPAction header
                    $k = 'SOAPAction';
                    $v = str_replace('"', '', $v);
                    $v = str_replace('\\', '', $v);
                    $this->SOAPAction = $v;
                } else if ($k == 'content-type') {
                    // get the character encoding of the incoming request
                    if (strpos($v, '=')) {
                        $enc = substr(strstr($v, '='), 1);
                        $enc = str_replace('"', '', $enc);
                        $enc = str_replace('\\', '', $enc);
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
                            $this->xml_encoding = strtoupper($enc);
                        } else {
                            $this->xml_encoding = 'US-ASCII';
                        }
                    } else {
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
                        $this->xml_encoding = 'ISO-8859-1';
                    }
                }
                $this->headers[$k] = $v;
                $this->request .= "$k: $v\r\n";
                $this->debug("$k: $v");
            }
        } elseif (is_array($HTTP_SERVER_VARS)) {
            $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
            foreach ($HTTP_SERVER_VARS as $k => $v) {
                if (substr($k, 0, 5) == 'HTTP_') {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); 	                                         $k = strtolower(substr($k, 5));
                } else {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); 	                                         $k = strtolower($k);
                }
                if ($k == 'soapaction') {
                    // get SOAPAction header
                    $k = 'SOAPAction';
                    $v = str_replace('"', '', $v);
                    $v = str_replace('\\', '', $v);
                    $this->SOAPAction = $v;
                } else if ($k == 'content-type') {
                    // get the character encoding of the incoming request
                    if (strpos($v, '=')) {
                        $enc = substr(strstr($v, '='), 1);
                        $enc = str_replace('"', '', $enc);
                        $enc = str_replace('\\', '', $enc);
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
                            $this->xml_encoding = strtoupper($enc);
                        } else {
                            $this->xml_encoding = 'US-ASCII';
                        }
                    } else {
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
                        $this->xml_encoding = 'ISO-8859-1';
                    }
                }
                $this->headers[$k] = $v;
                $this->request .= "$k: $v\r\n";
                $this->debug("$k: $v");
            }
        } else {
            $this->debug("In parse_http_headers, HTTP headers not accessible");
            $this->setError("HTTP headers not accessible");
        }
    }
    
    /**
     * parses a request
     *
     * The following fields are set by this function (when successful)
     *
     * headers
     * request
     * xml_encoding
     * SOAPAction
     * request
     * requestSOAP
     * methodURI
     * methodname
     * methodparams
     * requestHeaders
     * document
     *
     * This sets the fault field on error
     *
     * @param    string $data XML string
     * @access   private
     */
    function parse_request($data='') {
        $this->debug('entering parse_request()');
        $this->parse_http_headers();
        $this->debug('got character encoding: '.$this->xml_encoding);
        // uncompress if necessary
        if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
            $this->debug('got content encoding: ' . $this->headers['content-encoding']);
            if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
                // if decoding works, use it. else assume data wasn't gzencoded
                if (function_exists('gzuncompress')) {
                    if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
                        $data = $degzdata;
                    } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
                        $data = $degzdata;
                    } else {
                        $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
                        return;
                    }
                } else {
                    $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
                    return;
                }
            }
        }
        $this->request .= "\r\n".$data;
        $data = $this->parseRequest($this->headers, $data);
        $this->requestSOAP = $data;
        $this->debug('leaving parse_request');
    }
    
    /**
     * invokes a PHP function for the requested SOAP method
     *
     * The following fields are set by this function (when successful)
     *
     * methodreturn
     *
     * Note that the PHP function that is called may also set the following
     * fields to affect the response sent to the client
     *
     * responseHeaders
     * outgoing_headers
     *
     * This sets the fault field on error
     *
     * @access   private
     */
    function invoke_method() {
        $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
        
        //
        // if you are debugging in this area of the code, your service uses a class to implement methods,
        // you use SOAP RPC, and the client is .NET, please be aware of the following...
        // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
        // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
        // the XML request and reading the XML response.  you need to add the RequestElementName and
        // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
        // generates for the method.  these parameters are used to specify the correct XML element names
        // for .NET to use, i.e. the names with the '.' in them.
        //
        $orig_methodname = $this->methodname;
        if ($this->wsdl) {
            if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
                $this->appendDebug('opData=' . $this->varDump($this->opData));
            } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
                $this->appendDebug('opData=' . $this->varDump($this->opData));
                $this->methodname = $this->opData['name'];
            } else {
                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
                $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
                return;
            }
        } else {
            $this->debug('in invoke_method, no WSDL to validate method');
        }
        
        // if a . is present in $this->methodname, we see if there is a class in scope,
        // which could be referred to. We will also distinguish between two deliminators,
        // to allow methods to be called a the class or an instance
        if (strpos($this->methodname, '..') > 0) {
            $delim = '..';
        } else if (strpos($this->methodname, '.') > 0) {
            $delim = '.';
        } else {
            $delim = '';
        }
        $this->debug("in invoke_method, delim=$delim");
        
        $class = '';
        $method = '';
        if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
            $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
            if (class_exists($try_class)) {
                // get the class and method name
                $class = $try_class;
                $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
                $this->debug("in invoke_method, class=$class method=$method delim=$delim");
            } else {
                $this->debug("in invoke_method, class=$try_class not found");
            }
        } else {
            $try_class = '';
            $this->debug("in invoke_method, no class to try");
        }
        
        // does method exist?
        if ($class == '') {
            if (!function_exists($this->methodname)) {
                $this->debug("in invoke_method, function '$this->methodname' not found!");
                $this->result = 'fault: method not found';
                $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
                return;
            }
        } else {
            $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
            if (!in_array($method_to_compare, get_class_methods($class))) {
                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
                $this->result = 'fault: method not found';
                $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
                return;
            }
        }
        
        // evaluate message, getting back parameters
        // verify that request parameters match the method's signature
        if(! $this->verify_method($this->methodname,$this->methodparams)){
            // debug
            $this->debug('ERROR: request not verified against method signature');
            $this->result = 'fault: request failed validation against method signature';
            // return fault
            $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
            return;
        }
        
        // if there are parameters to pass
        $this->debug('in invoke_method, params:');
        $this->appendDebug($this->varDump($this->methodparams));
        $this->debug("in invoke_method, calling '$this->methodname'");
        if (!function_exists('call_user_func_array')) {
            if ($class == '') {
                $this->debug('in invoke_method, calling function using eval()');
                $funcCall = "\$this->methodreturn = $this->methodname(";
            } else {
                if ($delim == '..') {
                    $this->debug('in invoke_method, calling class method using eval()');
                    $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
                } else {
                    $this->debug('in invoke_method, calling instance method using eval()');
                    // generate unique instance name
                    $instname = "\$inst_".time();
                    $funcCall = $instname." = new ".$class."(); ";
                    $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
                }
            }
            if ($this->methodparams) {
                foreach ($this->methodparams as $param) {
                    if (is_array($param) || is_object($param)) {
                        $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
                        return;
                    }
                    $funcCall .= "\"$param\",";
                }
                $funcCall = substr($funcCall, 0, -1);
            }
            $funcCall .= ');';
            $this->debug('in invoke_method, function call: '.$funcCall);
            @eval($funcCall);
        } else {
            if ($class == '') {
                $this->debug('in invoke_method, calling function using call_user_func_array()');
                $call_arg = "$this->methodname";	// straight assignment changes $this->methodname to lower case after call_user_func_array()
            } elseif ($delim == '..') {
                $this->debug('in invoke_method, calling class method using call_user_func_array()');
                $call_arg = array ($class, $method);
            } else {
                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
                $instance = new $class ();
                $call_arg = array(&$instance, $method);
            }
            if (is_array($this->methodparams)) {
                $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
            } else {
                $this->methodreturn = call_user_func_array($call_arg, array());
            }
        }
        $this->debug('in invoke_method, methodreturn:');
        $this->appendDebug($this->varDump($this->methodreturn));
        $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
    }
    
    /**
     * serializes the return value from a PHP function into a full SOAP Envelope
     *
     * The following fields are set by this function (when successful)
     *
     * responseSOAP
     *
     * This sets the fault field on error
     *
     * @access   private
     */
    function serialize_return() {
        $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
        // if fault
        if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
            $this->debug('got a fault object from method');
            $this->fault = $this->methodreturn;
            return;
        } elseif ($this->methodreturnisliteralxml) {
            $return_val = $this->methodreturn;
            // returned value(s)
        } else {
            $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
            $this->debug('serializing return value');
            if($this->wsdl){
                if (sizeof($this->opData['output']['parts']) > 1) {
                    $this->debug('more than one output part, so use the method return unchanged');
                    $opParams = $this->methodreturn;
                } elseif (sizeof($this->opData['output']['parts']) == 1) {
                    $this->debug('exactly one output part, so wrap the method return in a simple array');
                    // TODO: verify that it is not already wrapped!
                    //foreach ($this->opData['output']['parts'] as $name => $type) {
                    //	$this->debug('wrap in element named ' . $name);
                        //}
                        $opParams = array($this->methodreturn);
                }
                $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
                $this->appendDebug($this->wsdl->getDebug());
                $this->wsdl->clearDebug();
                if($errstr = $this->wsdl->getError()){
                    $this->debug('got wsdl error: '.$errstr);
                    $this->fault('SOAP-ENV:Server', 'unable to serialize result');
                    return;
                }
            } else {
                if (isset($this->methodreturn)) {
                    $return_val = $this->serialize_val($this->methodreturn, 'return');
                } else {
                    $return_val = '';
                    $this->debug('in absence of WSDL, assume void return for backward compatibility');
                }
            }
        }
        $this->debug('return value:');
        $this->appendDebug($this->varDump($return_val));
        
        $this->debug('serializing response');
        if ($this->wsdl) {
            $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
            if ($this->opData['style'] == 'rpc') {
                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
                if ($this->opData['output']['use'] == 'literal') {
                    // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
                    if ($this->methodURI) {
                        $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
                    } else {
                        $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
                    }
                } else {
                    if ($this->methodURI) {
                        $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
                    } else {
                        $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
                    }
                }
            } else {
                $this->debug('style is not rpc for serialization: assume document');
                $payload = $return_val;
            }
        } else {
            $this->debug('do not have WSDL for serialization: assume rpc/encoded');
            $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
        }
        $this->result = 'successful';
        if($this->wsdl){
            //if($this->debug_flag){
            $this->appendDebug($this->wsdl->getDebug());
            //	}
            if (isset($this->opData['output']['encodingStyle'])) {
                $encodingStyle = $this->opData['output']['encodingStyle'];
            } else {
                $encodingStyle = '';
            }
            // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
            $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
        } else {
            $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
        }
        $this->debug("Leaving serialize_return");
    }
    
    /**
     * sends an HTTP response
     *
     * The following fields are set by this function (when successful)
     *
     * outgoing_headers
     * response
     *
     * @access   private
     */
    function send_response() {
        $this->debug('Enter send_response');
        if ($this->fault) {
            $payload = $this->fault->serialize();
            $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
            $this->outgoing_headers[] = "Status: 500 Internal Server Error";
        } else {
            $payload = $this->responseSOAP;
            // Some combinations of PHP+Web server allow the Status
            // to come through as a header.  Since OK is the default
            // just do nothing.
            // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
            // $this->outgoing_headers[] = "Status: 200 OK";
        }
        // add debug data if in debug mode
        if(isset($this->debug_flag) && $this->debug_flag){
            $payload .= $this->getDebugAsXMLComment();
        }
        $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
        $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
        // Let the Web server decide about this
        //$this->outgoing_headers[] = "Connection: Close\r\n";
        $payload = $this->getHTTPBody($payload);
        $type = $this->getHTTPContentType();
        $charset = $this->getHTTPContentTypeCharset();
        $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
        //begin code to compress payload - by John
        // NOTE: there is no way to know whether the Web server will also compress
        // this data.
        if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
            if (strstr($this->headers['accept-encoding'], 'gzip')) {
                if (function_exists('gzencode')) {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content being gzipped -->";
                    }
                    $this->outgoing_headers[] = "Content-Encoding: gzip";
                    $payload = gzencode($payload);
                } else {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content will not be gzipped: no gzencode -->";
                    }
                }
            } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
                // instead of gzcompress output,
                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
                if (function_exists('gzdeflate')) {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content being deflated -->";
                    }
                    $this->outgoing_headers[] = "Content-Encoding: deflate";
                    $payload = gzdeflate($payload);
                } else {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content will not be deflated: no gzcompress -->";
                    }
                }
            }
        }
        //end code
        $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
        reset($this->outgoing_headers);
        foreach($this->outgoing_headers as $hdr){
            header($hdr, false);
        }
        print $payload;
        $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
    }
    
    /**
     * takes the value that was created by parsing the request
     * and compares to the method's signature, if available.
     *
     * @param	string	$operation	The operation to be invoked
     * @param	array	$request	The array of parameter values
     * @return	boolean	Whether the operation was found
     * @access   private
     */
    function verify_method($operation,$request){
        if(isset($this->wsdl) && is_object($this->wsdl)){
            if($this->wsdl->getOperationData($operation)){
                return true;
            }
        } elseif(isset($this->operations[$operation])){
            return true;
        }
        return false;
    }
    
    /**
     * processes SOAP message received from client
     *
     * @param	array	$headers	The HTTP headers
     * @param	string	$data		unprocessed request data from client
     * @return	mixed	value of the message, decoded into a PHP type
     * @access   private
     */
    function parseRequest($headers, $data) {
        $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
        $this->appendDebug($this->varDump($headers));
        if (!isset($headers['content-type'])) {
            $this->setError('Request not of type text/xml (no content-type header)');
            return false;
        }
        if (!strstr($headers['content-type'], 'text/xml')) {
            $this->setError('Request not of type text/xml');
            return false;
        }
        if (strpos($headers['content-type'], '=')) {
            $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
            $this->debug('Got response encoding: ' . $enc);
            if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
                $this->xml_encoding = strtoupper($enc);
            } else {
                $this->xml_encoding = 'US-ASCII';
            }
        } else {
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
            $this->xml_encoding = 'ISO-8859-1';
        }
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
        // parse response, get soap parser obj
        $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
        // parser debug
        $this->debug("parser debug: \n".$parser->getDebug());
        // if fault occurred during message parsing
        if($err = $parser->getError()){
            $this->result = 'fault: error in msg parsing: '.$err;
            $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
            // else successfully parsed request into soapval object
        } else {
            // get/set methodname
            $this->methodURI = $parser->root_struct_namespace;
            $this->methodname = $parser->root_struct_name;
            $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
            $this->debug('calling parser->get_soapbody()');
            $this->methodparams = $parser->get_soapbody();
            // get SOAP headers
            $this->requestHeaders = $parser->getHeaders();
            // get SOAP Header
            $this->requestHeader = $parser->get_soapheader();
            // add document for doclit support
            $this->document = $parser->document;
        }
    }
    
    /**
     * gets the HTTP body for the current response.
     *
     * @param string $soapmsg The SOAP payload
     * @return string The HTTP body, which includes the SOAP payload
     * @access private
     */
    function getHTTPBody($soapmsg) {
        return $soapmsg;
    }
    
    /**
     * gets the HTTP content type for the current response.
     *
     * Note: getHTTPBody must be called before this.
     *
     * @return string the HTTP content type for the current response.
     * @access private
     */
    function getHTTPContentType() {
        return 'text/xml';
    }
    
    /**
     * gets the HTTP content type charset for the current response.
     * returns false for non-text content types.
     *
     * Note: getHTTPBody must be called before this.
     *
     * @return string the HTTP content type charset for the current response.
     * @access private
     */
    function getHTTPContentTypeCharset() {
        return $this->soap_defencoding;
    }
    
    /**
     * add a method to the dispatch map (this has been replaced by the register method)
     *
     * @param    string $methodname
     * @param    string $in array of input values
     * @param    string $out array of output values
     * @access   public
     * @deprecated
     */
    function add_to_map($methodname,$in,$out){
        $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
    }
    
    /**
     * register a service function with the server
     *
     * @param    string $name the name of the PHP function, class.method or class..method
     * @param    array $in assoc array of input values: key = param name, value = param type
     * @param    array $out assoc array of output values: key = param name, value = param type
     * @param	mixed $namespace the element namespace for the method or false
     * @param	mixed $soapaction the soapaction for the method or false
     * @param	mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
     * @param	mixed $use optional (encoded|literal) or false
     * @param	string $documentation optional Description to include in WSDL
     * @param	string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
     * @access   public
     */
    function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
        global $HTTP_SERVER_VARS;
        
        if($this->externalWSDLURL){
            die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
        }
        if (! $name) {
            die('You must specify a name when you register an operation');
        }
        if (!is_array($in)) {
            die('You must provide an array for operation inputs');
        }
        if (!is_array($out)) {
            die('You must provide an array for operation outputs');
        }
        if(false == $namespace) {
        }
        if(false == $soapaction) {
            if (isset($_SERVER)) {
                $SERVER_NAME = $_SERVER['SERVER_NAME'];
                $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
                $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
            } elseif (isset($HTTP_SERVER_VARS)) {
                $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
                $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
                $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
            } else {
                $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
            }
            if ($HTTPS == '1' || $HTTPS == 'on') {
                $SCHEME = 'https';
            } else {
                $SCHEME = 'http';
            }
            $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
        }
        if(false == $style) {
            $style = "rpc";
        }
        if(false == $use) {
            $use = "encoded";
        }
        if ($use == 'encoded' && $encodingStyle == '') {
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
        }
        
        $this->operations[$name] = array(
            'name' => $name,
            'in' => $in,
            'out' => $out,
            'namespace' => $namespace,
            'soapaction' => $soapaction,
            'style' => $style);
        if($this->wsdl){
            $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
        }
        return true;
    }
    
    /**
     * Specify a fault to be returned to the client.
     * This also acts as a flag to the server that a fault has occured.
     *
     * @param	string $faultcode
     * @param	string $faultstring
     * @param	string $faultactor
     * @param	string $faultdetail
     * @access   public
     */
    function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
        if ($faultdetail == '' && $this->debug_flag) {
            $faultdetail = $this->getDebug();
        }
        $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
        $this->fault->soap_defencoding = $this->soap_defencoding;
    }
    
    /**
     * Sets up wsdl object.
     * Acts as a flag to enable internal WSDL generation
     *
     * @param string $serviceName, name of the service
     * @param mixed $namespace optional 'tns' service namespace or false
     * @param mixed $endpoint optional URL of service endpoint or false
     * @param string $style optional (rpc|document) WSDL style (also specified by operation)
     * @param string $transport optional SOAP transport
     * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
     */
    function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
    {
        global $HTTP_SERVER_VARS;
        
        if (isset($_SERVER)) {
            $SERVER_NAME = $_SERVER['SERVER_NAME'];
            $SERVER_PORT = $_SERVER['SERVER_PORT'];
            $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
            $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
        } elseif (isset($HTTP_SERVER_VARS)) {
            $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
            $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
            $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
            $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
        } else {
            $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
        }
        // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
        $colon = strpos($SERVER_NAME,":");
        if ($colon) {
            $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
        }
        if ($SERVER_PORT == 80) {
            $SERVER_PORT = '';
        } else {
            $SERVER_PORT = ':' . $SERVER_PORT;
        }
        if(false == $namespace) {
            $namespace = "http://$SERVER_NAME/soap/$serviceName";
        }
        
        if(false == $endpoint) {
            if ($HTTPS == '1' || $HTTPS == 'on') {
                $SCHEME = 'https';
            } else {
                $SCHEME = 'http';
            }
            $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
        }
        
        if(false == $schemaTargetNamespace) {
            $schemaTargetNamespace = $namespace;
        }
        
        $this->wsdl = new wsdl;
        $this->wsdl->serviceName = $serviceName;
        $this->wsdl->endpoint = $endpoint;
        $this->wsdl->namespaces['tns'] = $namespace;
        $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
        $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
        if ($schemaTargetNamespace != $namespace) {
            $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
        }
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
        if ($style == 'document') {
            $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
        }
        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
        $this->wsdl->bindings[$serviceName.'Binding'] = array(
            'name'=>$serviceName.'Binding',
            'style'=>$style,
            'transport'=>$transport,
            'portType'=>$serviceName.'PortType');
        $this->wsdl->ports[$serviceName.'Port'] = array(
            'binding'=>$serviceName.'Binding',
            'location'=>$endpoint,
            'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
    }
}

<?php
namespace com\linways\base\soap;
/**
 *
 * nusoap_parser class parses SOAP XML messages into native PHP values
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @author   Scott Nichol <snichol@users.sourceforge.net>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access   public
 */
class nusoap_parser extends nusoap_base {
    
    var $xml = '';
    var $xml_encoding = '';
    var $method = '';
    var $root_struct = '';
    var $root_struct_name = '';
    var $root_struct_namespace = '';
    var $root_header = '';
    var $document = '';			// incoming SOAP body (text)
    // determines where in the message we are (envelope,header,body,method)
    var $status = '';
    var $position = 0;
    var $depth = 0;
    var $default_namespace = '';
    var $namespaces = array();
    var $message = array();
    var $parent = '';
    var $fault = false;
    var $fault_code = '';
    var $fault_str = '';
    var $fault_detail = '';
    var $depth_array = array();
    var $debug_flag = true;
    var $soapresponse = NULL;	// parsed SOAP Body
    var $soapheader = NULL;		// parsed SOAP Header
    var $responseHeaders = '';	// incoming SOAP headers (text)
    var $body_position = 0;
    // for multiref parsing:
    // array of id => pos
    var $ids = array();
    // array of id => hrefs => pos
    var $multirefs = array();
    // toggle for auto-decoding element content
    var $decode_utf8 = true;
    
    /**
     * constructor that actually does the parsing
     *
     * @param    string $xml SOAP message
     * @param    string $encoding character encoding scheme of message
     * @param    string $method method for which XML is parsed (unused?)
     * @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
     * @access   public
     */
    function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
        parent::nusoap_base();
        $this->xml = $xml;
        $this->xml_encoding = $encoding;
        $this->method = $method;
        $this->decode_utf8 = $decode_utf8;
        
        // Check whether content has been read.
        if(!empty($xml)){
            // Check XML encoding
            $pos_xml = strpos($xml, '<?xml');
            if ($pos_xml !== FALSE) {
                $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
                if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
                    $xml_encoding = $res[1];
                    if (strtoupper($xml_encoding) != $encoding) {
                        $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
                        $this->debug($err);
                        if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
                            $this->setError($err);
                            return;
                        }
                        // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
                    } else {
                        $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
                    }
                } else {
                    $this->debug('No encoding specified in XML declaration');
                }
            } else {
                $this->debug('No XML declaration');
            }
            $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding);
            // Create an XML parser - why not xml_parser_create_ns?
            $this->parser = xml_parser_create($this->xml_encoding);
            // Set the options for parsing the XML data.
            //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
            xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
            xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
            // Set the object for the parser.
            xml_set_object($this->parser, $this);
            // Set the element handlers for the parser.
            xml_set_element_handler($this->parser, 'start_element','end_element');
            xml_set_character_data_handler($this->parser,'character_data');
            
            // Parse the XML file.
            if(!xml_parse($this->parser,$xml,true)){
                // Display an error message.
                $err = sprintf('XML error parsing SOAP payload on line %d: %s',
                    xml_get_current_line_number($this->parser),
                    xml_error_string(xml_get_error_code($this->parser)));
                $this->debug($err);
                $this->debug("XML payload:\n" . $xml);
                $this->setError($err);
            } else {
                $this->debug('in nusoap_parser ctor, message:');
                $this->appendDebug($this->varDump($this->message));
                $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
                // get final value
                $this->soapresponse = $this->message[$this->root_struct]['result'];
                // get header value
                if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
                    $this->soapheader = $this->message[$this->root_header]['result'];
                }
                // resolve hrefs/ids
                if(sizeof($this->multirefs) > 0){
                    foreach($this->multirefs as $id => $hrefs){
                        $this->debug('resolving multirefs for id: '.$id);
                        $idVal = $this->buildVal($this->ids[$id]);
                        if (is_array($idVal) && isset($idVal['!id'])) {
                            unset($idVal['!id']);
                        }
                        foreach($hrefs as $refPos => $ref){
                            $this->debug('resolving href at pos '.$refPos);
                            $this->multirefs[$id][$refPos] = $idVal;
                        }
                    }
                }
            }
            xml_parser_free($this->parser);
        } else {
            $this->debug('xml was empty, didn\'t parse!');
            $this->setError('xml was empty, didn\'t parse!');
        }
    }
    
    /**
     * start-element handler
     *
     * @param    resource $parser XML parser object
     * @param    string $name element name
     * @param    array $attrs associative array of attributes
     * @access   private
     */
    function start_element($parser, $name, $attrs) {
        // position in a total number of elements, starting from 0
        // update class level pos
        $pos = $this->position++;
        // and set mine
        $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
        // depth = how many levels removed from root?
        // set mine as current global depth and increment global depth value
        $this->message[$pos]['depth'] = $this->depth++;
        
        // else add self as child to whoever the current parent is
        if($pos != 0){
            $this->message[$this->parent]['children'] .= '|'.$pos;
        }
        // set my parent
        $this->message[$pos]['parent'] = $this->parent;
        // set self as current parent
        $this->parent = $pos;
        // set self as current value for this depth
        $this->depth_array[$this->depth] = $pos;
        // get element prefix
        if(strpos($name,':')){
            // get ns prefix
            $prefix = substr($name,0,strpos($name,':'));
            // get unqualified name
            $name = substr(strstr($name,':'),1);
        }
        // set status
        if ($name == 'Envelope' && $this->status == '') {
            $this->status = 'envelope';
        } elseif ($name == 'Header' && $this->status == 'envelope') {
            $this->root_header = $pos;
            $this->status = 'header';
        } elseif ($name == 'Body' && $this->status == 'envelope'){
            $this->status = 'body';
            $this->body_position = $pos;
            // set method
        } elseif($this->status == 'body' && $pos == ($this->body_position+1)) {
            $this->status = 'method';
            $this->root_struct_name = $name;
            $this->root_struct = $pos;
            $this->message[$pos]['type'] = 'struct';
            $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
        }
        // set my status
        $this->message[$pos]['status'] = $this->status;
        // set name
        $this->message[$pos]['name'] = htmlspecialchars($name);
        // set attrs
        $this->message[$pos]['attrs'] = $attrs;
        
        // loop through atts, logging ns and type declarations
        $attstr = '';
        foreach($attrs as $key => $value){
            $key_prefix = $this->getPrefix($key);
            $key_localpart = $this->getLocalPart($key);
            // if ns declarations, add to class level array of valid namespaces
            if($key_prefix == 'xmlns'){
                if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/',$value)){
                    $this->XMLSchemaVersion = $value;
                    $this->namespaces['xsd'] = $this->XMLSchemaVersion;
                    $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
                }
                $this->namespaces[$key_localpart] = $value;
                // set method namespace
                if($name == $this->root_struct_name){
                    $this->methodNamespace = $value;
                }
                // if it's a type declaration, set type
            } elseif($key_localpart == 'type'){
                if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') {
                    // do nothing: already processed arrayType
                } else {
                    $value_prefix = $this->getPrefix($value);
                    $value_localpart = $this->getLocalPart($value);
                    $this->message[$pos]['type'] = $value_localpart;
                    $this->message[$pos]['typePrefix'] = $value_prefix;
                    if(isset($this->namespaces[$value_prefix])){
                        $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
                    } else if(isset($attrs['xmlns:'.$value_prefix])) {
                        $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
                    }
                    // should do something here with the namespace of specified type?
                }
            } elseif($key_localpart == 'arrayType'){
                $this->message[$pos]['type'] = 'array';
                /* do arrayType ereg here
                 [1]    arrayTypeValue    ::=    atype asize
                 [2]    atype    ::=    QName rank*
                 [3]    rank    ::=    '[' (',')* ']'
                 [4]    asize    ::=    '[' length~ ']'
                 [5]    length    ::=    nextDimension* Digit+
                 [6]    nextDimension    ::=    Digit+ ','
                 */
                $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
                if(preg_match($expr,$value,$regs)){
                    $this->message[$pos]['typePrefix'] = $regs[1];
                    $this->message[$pos]['arrayTypePrefix'] = $regs[1];
                    if (isset($this->namespaces[$regs[1]])) {
                        $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
                    } else if (isset($attrs['xmlns:'.$regs[1]])) {
                        $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
                    }
                    $this->message[$pos]['arrayType'] = $regs[2];
                    $this->message[$pos]['arraySize'] = $regs[3];
                    $this->message[$pos]['arrayCols'] = $regs[4];
                }
                // specifies nil value (or not)
            } elseif ($key_localpart == 'nil'){
                $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
                // some other attribute
            } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
                $this->message[$pos]['xattrs']['!' . $key] = $value;
            }
            
            if ($key == 'xmlns') {
                $this->default_namespace = $value;
            }
            // log id
            if($key == 'id'){
                $this->ids[$value] = $pos;
            }
            // root
            if($key_localpart == 'root' && $value == 1){
                $this->status = 'method';
                $this->root_struct_name = $name;
                $this->root_struct = $pos;
                $this->debug("found root struct $this->root_struct_name, pos $pos");
            }
            // for doclit
            $attstr .= " $key=\"$value\"";
        }
        // get namespace - must be done after namespace atts are processed
        if(isset($prefix)){
            $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
            $this->default_namespace = $this->namespaces[$prefix];
        } else {
            $this->message[$pos]['namespace'] = $this->default_namespace;
        }
        if($this->status == 'header'){
            if ($this->root_header != $pos) {
                $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
            }
        } elseif($this->root_struct_name != ''){
            $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
        }
    }
    
    /**
     * end-element handler
     *
     * @param    resource $parser XML parser object
     * @param    string $name element name
     * @access   private
     */
    function end_element($parser, $name) {
        // position of current element is equal to the last value left in depth_array for my depth
        $pos = $this->depth_array[$this->depth--];
        
        // get element prefix
        if(strpos($name,':')){
            // get ns prefix
            $prefix = substr($name,0,strpos($name,':'));
            // get unqualified name
            $name = substr(strstr($name,':'),1);
        }
        
        // build to native type
        if(isset($this->body_position) && $pos > $this->body_position){
            // deal w/ multirefs
            if(isset($this->message[$pos]['attrs']['href'])){
                // get id
                $id = substr($this->message[$pos]['attrs']['href'],1);
                // add placeholder to href array
                $this->multirefs[$id][$pos] = 'placeholder';
                // add set a reference to it as the result value
                $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
                // build complexType values
            } elseif($this->message[$pos]['children'] != ''){
                // if result has already been generated (struct/array)
                if(!isset($this->message[$pos]['result'])){
                    $this->message[$pos]['result'] = $this->buildVal($pos);
                }
                // build complexType values of attributes and possibly simpleContent
            } elseif (isset($this->message[$pos]['xattrs'])) {
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
                    $this->message[$pos]['xattrs']['!'] = null;
                } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
                    if (isset($this->message[$pos]['type'])) {
                        $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
                    } else {
                        $parent = $this->message[$pos]['parent'];
                        if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
                            $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
                        } else {
                            $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
                        }
                    }
                }
                $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
                // set value of simpleType (or nil complexType)
            } else {
                //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
                if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
                    $this->message[$pos]['xattrs']['!'] = null;
                } elseif (isset($this->message[$pos]['type'])) {
                    $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
                } else {
                    $parent = $this->message[$pos]['parent'];
                    if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
                        $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
                    } else {
                        $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
                    }
                }
                
                /* add value to parent's result, if parent is struct/array
                 $parent = $this->message[$pos]['parent'];
                 if($this->message[$parent]['type'] != 'map'){
                 if(strtolower($this->message[$parent]['type']) == 'array'){
                 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
                 } else {
                 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
                 }
                 }
                 */
            }
        }
        
        // for doclit
        if($this->status == 'header'){
            if ($this->root_header != $pos) {
                $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
            }
        } elseif($pos >= $this->root_struct){
            $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
        }
        // switch status
        if ($pos == $this->root_struct){
            $this->status = 'body';
            $this->root_struct_namespace = $this->message[$pos]['namespace'];
        } elseif ($pos == $this->root_header) {
            $this->status = 'envelope';
        } elseif ($name == 'Body' && $this->status == 'body') {
            $this->status = 'envelope';
        } elseif ($name == 'Header' && $this->status == 'header') { // will never happen
            $this->status = 'envelope';
        } elseif ($name == 'Envelope' && $this->status == 'envelope') {
            $this->status = '';
        }
        // set parent back to my parent
        $this->parent = $this->message[$pos]['parent'];
    }
    
    /**
     * element content handler
     *
     * @param    resource $parser XML parser object
     * @param    string $data element content
     * @access   private
     */
    function character_data($parser, $data){
        $pos = $this->depth_array[$this->depth];
        if ($this->xml_encoding=='UTF-8'){
            // TODO: add an option to disable this for folks who want
            // raw UTF-8 that, e.g., might not map to iso-8859-1
            // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
            if($this->decode_utf8){
                $data = utf8_decode($data);
            }
        }
        $this->message[$pos]['cdata'] .= $data;
        // for doclit
        if($this->status == 'header'){
            $this->responseHeaders .= $data;
        } else {
            $this->document .= $data;
        }
    }
    
    /**
     * get the parsed message (SOAP Body)
     *
     * @return	mixed
     * @access   public
     * @deprecated	use get_soapbody instead
     */
    function get_response(){
        return $this->soapresponse;
    }
    
    /**
     * get the parsed SOAP Body (NULL if there was none)
     *
     * @return	mixed
     * @access   public
     */
    function get_soapbody(){
        return $this->soapresponse;
    }
    
    /**
     * get the parsed SOAP Header (NULL if there was none)
     *
     * @return	mixed
     * @access   public
     */
    function get_soapheader(){
        return $this->soapheader;
    }
    
    /**
     * get the unparsed SOAP Header
     *
     * @return	string XML or empty if no Header
     * @access   public
     */
    function getHeaders(){
        return $this->responseHeaders;
    }
    
    /**
     * decodes simple types into PHP variables
     *
     * @param    string $value value to decode
     * @param    string $type XML type to decode
     * @param    string $typens XML type namespace to decode
     * @return	mixed PHP value
     * @access   private
     */
    function decodeSimple($value, $type, $typens) {
        // TODO: use the namespace!
        if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
            return (string) $value;
        }
        if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
            return (int) $value;
        }
        if ($type == 'float' || $type == 'double' || $type == 'decimal') {
            return (double) $value;
        }
        if ($type == 'boolean') {
            if (strtolower($value) == 'false' || strtolower($value) == 'f') {
                return false;
            }
            return (boolean) $value;
        }
        if ($type == 'base64' || $type == 'base64Binary') {
            $this->debug('Decode base64 value');
            return base64_decode($value);
        }
        // obscure numeric types
        if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
            || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
            || $type == 'unsignedInt'
            || $type == 'unsignedShort' || $type == 'unsignedByte') {
                return (int) $value;
            }
            // bogus: parser treats array with no elements as a simple type
            if ($type == 'array') {
                return array();
            }
            // everything else
            return (string) $value;
    }
    
    /**
     * builds response structures for compound values (arrays/structs)
     * and scalars
     *
     * @param    integer $pos position in node tree
     * @return	mixed	PHP value
     * @access   private
     */
    function buildVal($pos){
        if(!isset($this->message[$pos]['type'])){
            $this->message[$pos]['type'] = '';
        }
        $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
        // if there are children...
        if($this->message[$pos]['children'] != ''){
            $this->debug('in buildVal, there are children');
            $children = explode('|',$this->message[$pos]['children']);
            array_shift($children); // knock off empty
            // md array
            if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
                $r=0; // rowcount
                $c=0; // colcount
                foreach($children as $child_pos){
                    $this->debug("in buildVal, got an MD array element: $r, $c");
                    $params[$r][] = $this->message[$child_pos]['result'];
                    $c++;
                    if($c == $this->message[$pos]['arrayCols']){
                        $c = 0;
                        $r++;
                    }
                }
                // array
            } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
                $this->debug('in buildVal, adding array '.$this->message[$pos]['name']);
                foreach($children as $child_pos){
                    $params[] = &$this->message[$child_pos]['result'];
                }
                // apache Map type: java hashtable
            } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
                $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']);
                foreach($children as $child_pos){
                    $kv = explode("|",$this->message[$child_pos]['children']);
                    $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
                }
                // generic compound type
                //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
            } else {
                // Apache Vector type: treat as an array
                $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']);
                if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
                    $notstruct = 1;
                } else {
                    $notstruct = 0;
                }
                //
                foreach($children as $child_pos){
                    if($notstruct){
                        $params[] = &$this->message[$child_pos]['result'];
                    } else {
                        if (isset($params[$this->message[$child_pos]['name']])) {
                            // de-serialize repeated element name into an array
                            if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
                                $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
                            }
                            $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
                        } else {
                            $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
                        }
                    }
                }
            }
            if (isset($this->message[$pos]['xattrs'])) {
                $this->debug('in buildVal, handling attributes');
                foreach ($this->message[$pos]['xattrs'] as $n => $v) {
                    $params[$n] = $v;
                }
            }
            // handle simpleContent
            if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
                $this->debug('in buildVal, handling simpleContent');
                if (isset($this->message[$pos]['type'])) {
                    $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
                } else {
                    $parent = $this->message[$pos]['parent'];
                    if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
                        $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
                    } else {
                        $params['!'] = $this->message[$pos]['cdata'];
                    }
                }
            }
            $ret = is_array($params) ? $params : array();
            $this->debug('in buildVal, return:');
            $this->appendDebug($this->varDump($ret));
            return $ret;
            } else {
                $this->debug('in buildVal, no children, building scalar');
                $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
                if (isset($this->message[$pos]['type'])) {
                    $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
                    $this->debug("in buildVal, return: $ret");
                    return $ret;
                }
                $parent = $this->message[$pos]['parent'];
                if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
                    $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
                    $this->debug("in buildVal, return: $ret");
                    return $ret;
                }
                $ret = $this->message[$pos]['cdata'];
                $this->debug("in buildVal, return: $ret");
                return $ret;
            }
        }
    }

<?php
namespace com\linways\base\soap;
/**
 * Backward compatibility
 */
class XMLSchema extends nusoap_xmlschema {
}

<?php
namespace com\linways\base\soap;

class soapclient extends nusoap_client {
}

<?php
namespace com\linways\base\hooks;

use voku\helper\Hooks;


/**
 * Extended From PHP Hooks(voku/php-hooks).
 * @author gadheyan
 */
class LinwaysHooks extends Hooks
{
    protected static $_instance = null;

    protected function __construct()
    {
        parent::__construct();
        $this->loadAllHooks();
    }

    // To prevent any object or instance of that class to be cloned
    protected function __clone()
    {}

    /**
     * Loads all attached hooks and returns the singleton instance
     * @return Hooks
     */
    public static function getInstance()
    {
        static $instance;
        if (null === $instance) {
            $instance = new self();
        }
        return $instance;
    }

    /**
     * Loads all attached hooks.
     * In each project the constant `HOOKS_LOADER` should be defined and it should contain
     * path to a file which includes all required hooks.
     */
    private function loadAllHooks(){
        if(defined('HOOKS_LOADER')){
          $hooks = $this;
          require_once HOOKS_LOADER;
        }
    }
}
<?php
namespace com\linways\base\mcrypt;

class CryptAES
{
    protected $cipher     = MCRYPT_RIJNDAEL_128;
    protected $mode       = MCRYPT_MODE_CBC;
    protected $pad_method = NULL;
    public function set_cipher($cipher)
    {
        $this->cipher = $cipher;
    }
    
    public function set_mode($mode)
    {
        $this->mode = $mode;
    }
    
    public function set_iv($iv)
    {
        $this->iv = $iv;
    }
    
    public function set_key($secret_key)
    {
        $this->secret_key = $secret_key;
    }
    
    public function require_pkcs5()
    {
        $this->pad_method = 'pkcs5';
    }
    
    protected function pad_or_unpad($str, $ext)
    {
        if (is_null($this->pad_method))
        {
            return $str;
        }
        else
        {
            $func_name = __CLASS__ . '::' . $this->pad_method . '_' . $ext . 'pad';
            if (is_callable($func_name))
            {
                $size = mcrypt_get_block_size($this->cipher, $this->mode);
                return call_user_func($func_name, $str, $size);
            }
        }
        
        return $str;
    }
    
    protected function pad($str)
    {
        return $this->pad_or_unpad($str, '');
    }
    
    protected function unpad($str)
    {
        return $this->pad_or_unpad($str, 'un');
    }
    
    
    public function encrypt($str)
    {
        $str = $this->pad($str);
        $td = mcrypt_module_open($this->cipher, '', $this->mode, '');
        
        if (empty($this->iv))
        {
            $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        }
        else
        {
            $iv = $this->iv;
        }
        
        mcrypt_generic_init($td, $this->secret_key, $iv);
        $cyper_text = mcrypt_generic($td, $str);
        $rt = base64_encode($cyper_text);
        // $rt = bin2hex($cyper_text);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        
        return $rt;
    }
    
    public function decrypt($str){
        $td = mcrypt_module_open($this->cipher, '', $this->mode, '');
        
        if (empty($this->iv))
        {
            $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        }
        else
        {
            $iv = $this->iv;
        }
        
        mcrypt_generic_init($td, $this->secret_key, $iv);
        $decrypted_text = mdecrypt_generic($td, base64_decode($str));
        // $decrypted_text = mdecrypt_generic($td, self::hex2bin($str));
        $rt = $decrypted_text;
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        
        return $this->unpad($rt);
    }
    
    public static function hex2bin($hexdata) {
        $bindata = '';
        $length = strlen($hexdata);
        for ($i=0; $i < $length; $i += 2)
        {
            $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
        }
        return $bindata;
    }
    
    public static function pkcs5_pad($text, $blocksize)
    {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }
    
    public static function pkcs5_unpad($text)
    {
        $pad = ord($text{strlen($text) - 1});
        if ($pad > strlen($text)) return false;
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
        return substr($text, 0, -1 * $pad);
    }
}

/*$aes = new CryptAES();
 $aes->require_pkcs5();
 
 $aes->set_cipher(MCRYPT_RIJNDAEL_128);
 $aes->set_mode(MCRYPT_MODE_CBC);
 $aes->set_iv('2112842308FFQCOY');
 echo $set_iv;
 $aes->set_key('8660558953GPQKOD');
 
 $txn_id =  rand(5, 15);
 $date = date('Y-m-d');
 
 //$payload=
 $payload="techprocess";
 $checksumkey =  SHA1($payload);
 
 echo $checksumkey.'<br/>';
 
 $payloadWithSha1 = $payload.'|hash='.$checksumkey;
 
 
 
 echo $a = $aes->encrypt($payloadWithSha1);*/

?>

<?php
namespace com\linways\base\queue;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use com\linways\base\exception\TaskQueueException;
/**
 * For queuing tasks and running services asyncronously.
 *
 * @author Rohith Bhaskaran
 */
class TaskQueue {
    const TASK_QUEUE = "TASK_QUEUE";

    private $host;
    private $port;
    private $user;
    private $password;

    private $connection;
    private $channel;

    private $conf = [];

    /**
     * establishConnection establish connection to RabbitMQ server
     * and creates/select channel for task queue.
     *
     * @return void/Exception
     */
    public function establishConnection()
    {
        try{
            $this->connection = new AMQPStreamConnection($this->host, $this->port, $this->user, $this->password);
            $this->channel = $this->connection->channel();
            $this->channel->queue_declare(self::TASK_QUEUE , false, true, false, false);
        }catch(\Exception $e){
            throw new TaskQueueException ( TaskQueueException::RABBITMQ_CONNECTION_FAILED,  $e->getMessage() );
        }
    }

    /**
     * Establishes the connection with the server and set the configuration variale
     *
     * @param [Array] $conf     Array of conf file paths, This will be included before running a task
     *                          example : $conf = [getenv('AMS_CONFIG'), getenv('AMS_CONFIG_2')] or $conf = []
     */
    public function __construct($conf = []){
        global $RABBITMQ_HOST, $RABBITMQ_PORT, $RABBITMQ_USER, $RABBITMQ_PASSWORD;
        if (!isset ( $RABBITMQ_HOST ) || !isset ( $RABBITMQ_USER ) || !isset ( $RABBITMQ_PASSWORD )) {
            throw new TaskQueueException ( TaskQueueException::RABBITMQ_CREDENTIALS_REQUIRED, "Credentials for RabbitMQ server is required" );
        }
        $this->conf = $conf;
        $this->host = $RABBITMQ_HOST;
        $this->port = isset($RABBITMQ_PORT)? $RABBITMQ_PORT: 5672;
        $this->user = $RABBITMQ_USER;
        $this->password = $RABBITMQ_PASSWORD;
        $this->establishConnection();
    }

    /**
     * enqueue - To push a command to the task queue
     *
     * @param [String] $command     The command name defined  in the task runner. Eg; EXECUTE_HOOKS
     * @param array $params         Parameters to be passed to the task runner for the command to execute
     * @param array $conf           (optional)Array of conf file paths, This will be included before running a task
     *                              example : $conf = [getenv('AMS_CONFIG'), getenv('AMS_CONFIG_2')] or $conf = []
     *                              Only set this param if you require to queue multiple tasks with different tenant conf.
     *                              Otherwise set conf in the constructor.
     * @return void
     */
    public function enqueue($command, $params=[], $conf=[]){
        if(empty($command)){
            throw new TaskQueueException ( TaskQueueException::COMMAND_REQUIRED, "Command is required for all tasks." );
        }
        $data = [
            "command" => $command,
            "params" => $params,
            "conf" => $conf? $conf: $this->conf
        ];
        $data = json_encode($data);
        $msg = new AMQPMessage($data,array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
        $this->channel->basic_publish($msg, '', self::TASK_QUEUE);
    }

    /**
     * basicConsume  Waits indefinitely while listening to the task queue
     *
     * @param [function] $msgHandler  Function for handling the messages.
     *                                This function will be called when a message  (PhpAmqpLib\Message\AMQPMessage)
     *                                is received with the message as parameter
     *
     * @return void
     */
    public function basicConsume($msgHandler){
        if(empty($msgHandler)){
            throw new TaskQueueException ( TaskQueueException::MESSAGE_HANDLER_REQUIRED, "Message handler is required" );
        }
        $this->channel->basic_consume(self::TASK_QUEUE, '', false, false, false, false, $msgHandler);
        while(count($this->channel->callbacks)) {
            $this->channel->wait();
        }
    }

    /**
     * sendACK - send the acknowledgement for the received message
     *
     * @param [PhpAmqpLib\Message\AMQPMessage] $message  Message received by the message handler
     * @return void
     */
    public static function sendACK($message){
        $message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
    }

    /**
     * Closes the channel and connection with RabbitMQ.
     */
    public function __destruct(){
      $this->channel->close();
      $this->connection->close();
    }

}
<?php
namespace com\linways\base\request;

use com\linways\base\dto\PageSupport;

class BaseRequest extends PageSupport
{

    public $sortBy;

    public $sortOrder = "ASC";
    /**
     * get full data for export
     * @var boolean
     */
    public $isExport=FALSE;
    
    public $createdBy;
    public $updatedBy;
}<?php

namespace  com\linways\base\service;

use com\linways\base\email\PHPMailer;
use com\linways\base\exception\EmailException;
use com\linways\base\dto\Email;

class EmailService 
{
    // /Condition 1 - Presence of a static member variable
    private static $_instance = null;
    
    // /Condition 2 - Locked down the constructor
    private function __construct()
    {}
    // Prevent any oustide instantiation of this class
    
    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {}
    // Prevent any copy of this object
    
    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (! is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();
        return self::$_instance;
    }
    /**
     *
     * @param Email $email
     */
    private function validateSendEmail($email)
    {
        if($email != NULL)
        {
            if($email->from == NULL  || empty($email->from->email))
            {
                throw new EmailException(EmailException::EMAIL_FROM_NOT_DEFINED,"Email from is not defined.");
                
            }
            
            if(($email->to == NULL  || empty($email->to->email)) && (empty($email->toList) || count($email->toList) == 0))
            {
                throw new EmailException(EmailException::EMAIL_TO_NOT_DEFINED,"Email to is not defined.");
            
            }
        }
        else {
            
            throw new EmailException(EmailException::EMAIL_OBJECT__NOT_DEFINED,"Email Object is null.");
        }
        
    }
    /**
     * Method for sending email in AMS
     * @param Email $email
     * @return true/false
     * 
     * parammeters to set 
     * <p>From,to,subject,body</p> other parameted will take the default value if custom required set those values 
     * .For more info check Email.php class
     */
    public function sendEmail($email)
    {
        
        //Validate send email
        $this->validateSendEmail($email);
        //Currenlty only single reply to possible
        
        $mail=new PHPMailer();
        
        $mail->SMTPAuth   = true;                  		// enable SMTP authentication
        $mail->SMTPSecure = "ssl";
        
        //Set email mailer type
        switch ($email->mailerType) {
            case Email::SMTP:
                $mail = $this->setSMTPMailerTypeConfig($mail);
                break;
            case Email::SEND_MAIL:
                $mail = $this->setSendEmailMailerTypeConfig($mail);
                break;
            case Email::MAIL:
                $mail = $this->setMailMailerTypeConfig($mail);
                break;
            default:
                $mail = $this->setSMTPMailerTypeConfig();
                break;
        }
        
        $mail->From = $email->from->email;
        $mail->FromName   = $email->from->name;
        $mail->Subject    = $email->subject;
        $mail->Body       = $email->body;                 // HTML Body
        $mail->AltBody    = $email->body;    					// Text Body
        // set word wrap
        $mail->WordWrap   = $email->wordWrap;
        
        if(!empty($email->to))
        {
            $mail->AddAddress($email->to->email,$email->to->name);
            $mail->AddReplyTo($email->to->email,$email->to->name);
            
        }
       
        $mail->IsHTML($email->isHtml); // send as HTML
        //Email To List  
        if(!empty($email->toList))
        {
            foreach ($email->toList as $emailTo) {
                $mail->AddAddress($emailTo->email,$emailTo->name);
                $mail->AddReplyTo($emailTo->email,$emailTo->name);
            }
        }
        
        //Email CC List
        if(!empty($email->ccList))
        {
            foreach ($email->ccList as $emailCC) {
                $mail->AddCC($emailCC->email,$emailCC->name);
            }
        }
        
        $isSuccess = $mail->Send();
        
        if(!$isSuccess)
        {
            throw new EmailException(EmailException::EMAIL_SEND_FAILED,$mail->ErrorInfo);
        }
        
        return $isSuccess;
    }
    /**
     * set SMTP mailerType configuration as per mailer type
     * @param PHPMailer $mail
     */
    private  function setSMTPMailerTypeConfig($mail)
    {
        global  $SMTPHost, $SMTPPort, $mailID, $mailpassword;
        //
        if($mail != null)
        {
            $mail->Mailer = Email::SMTP;
            // sets the prefix to the servier
            $mail->Host       = $SMTPHost;      				// sets GMAIL as the SMTP server
            $mail->Port       = $SMTPPort;                  // set the SMTP port
            $mail->Username   = $mailID;  						// GMAIL username
            $mail->Password   = $mailpassword;            	// GMAIL password
        }
        
        return $mail;
    }
    
    /**
     * 
     * set sendemail mailerType configuration as per mailer type.
     * <p> Should configure these parameter in conf.php</p>
     * <p> $SEND_EMAIL_HOST, $SEND_EMAIL_PORT, $SEND_EMAIL_USERNAME, $SEND_EMAIL_PASSWORD;
        </p>
     * @param PHPMailer $mail
     */
    private  function setSendEmailMailerTypeConfig($mail)
    {
        global  $SEND_EMAIL_HOST, $SEND_EMAIL_PORT, $SEND_EMAIL_USERNAME, $SEND_EMAIL_PASSWORD;
        //
        if($mail != null)
        {
            $mail->Mailer = Email::SEND_MAIL;
            // sets the prefix to the servier
            $mail->Host       = $SEND_EMAIL_HOST;      				// sets GMAIL as the SMTP server
            $mail->Port       = $SEND_EMAIL_PORT;                  // set the SMTP port
            $mail->Username   = $SEND_EMAIL_USERNAME;  						// GMAIL username
            $mail->Password   = $SEND_EMAIL_PASSWORD;            	// GMAIL password
        }
    
        return $mail;
    }
    /**
     *
     * set sendemail mailerType configuration as per mailer type.
     * <p> Should configure these parameter in conf.php</p>
     * <p>$SEND_MAIL_HOST, $SEND_MAIL_PORT, $SEND_MAIL_USERNAME, $SEND_MAIL_PASSWORD;
       </p>
     * @param PHPMailer $mail
     */
    private  function setMailMailerTypeConfig($mail)
    {
        global  $SEND_MAIL_HOST, $SEND_MAIL_PORT, $SEND_MAIL_USERNAME, $SEND_MAIL_PASSWORD;
        //
        if($mail != null)
        {
            $mail->Mailer = Email::SEND_MAIL;
            // sets the prefix to the servier
            $mail->Host       = $SEND_MAIL_HOST;      				
            $mail->Port       = $SEND_MAIL_PORT;                  
            $mail->Username   = $SEND_MAIL_USERNAME;  						
            $mail->Password   = $SEND_MAIL_PASSWORD;            	
        }
    
        return $mail;
    }
}
?><?php
namespace com\linways\base\helper;

class ResultHandler
{
    /**
     * Method for sending success response
     *
     * @param
     *            $data
     * @param string $msg
     */
    public static function result($msg = null, $data = "")
    {
        echo json_encode(array(
            'success' => true,
            'msg' => $msg,
            'data' => $data,
            'errorCode' => NULL
        ));
    }
    
    /**
     * Method for sending fault response
     *
     * @param string $msg
     * @param string $errorCode
     * @param
     *            $data
     */
    public static function fault($msg = "", $data = null, $errorCode = "")
    {
        echo json_encode(array(
            'success' => false,
            'msg' => $msg,
            'data' => $data,
            'errorCode' => $errorCode
        ));
    }
}<?php
namespace com\linways\base\exception;

class SQLException extends CoreException
{
    
}<?php
namespace com\linways\base\exception;
use com\linways\base\exception\CoreException;

class TaskQueueException extends CoreException
{
    const CONF_ARRAY_REQUIRED="CONF_ARRAY_IS_REQUIRED";
    const RABBITMQ_CREDENTIALS_REQUIRED = "RABBITMQ_CREDENTIALS_REQUIRED";
    const RABBITMQ_CONNECTION_FAILED = "RABBITMQ_CONNECTION_FAILED";
    const COMMAND_REQUIRED = "COMMAND_REQUIRED";
    const MESSAGE_HANDLER_REQUIRED = "MESSAGE_HANDLER_REQUIRED";    
}<?php
namespace com\linways\base\exception;
use com\linways\base\exception\CoreException;

class EmailException extends CoreException
{
    const EMAIL_FROM_NOT_DEFINED="EMAIL_FROM_NOT_DEFINED";
    const EMAIL_TO_NOT_DEFINED="EMAIL_TO_NOT_DEFINED";
    const EMAIL_OBJECT__NOT_DEFINED="EMAIL_OBJECT__NOT_DEFINED";
    const EMAIL_SEND_FAILED="EMAIL_SEND_FAILED";
}<?php
namespace com\linways\base\exception;

use com\linways\base\exception\CoreException;

class MapperException extends CoreException
{
    const MAPPER_NOT_DEFINED= "MAPPER_NOT_DEFINED";
    const MAPPER_PRIMARY_KEY_NOT_DEFINED = "MAPPER_PRIMARY_KEY_NOT_DEFINED";
    const MAPPER_PRIMARY_KEY_COLUMN_NOT_DEFINED = "MAPPER_PRIMARY_KEY_COLUMN_NOT_DEFINED";
}<?php
namespace com\linways\base\exception;

class CoreException extends \Exception
{

    const SERVICE_NOT_DEFINED = "SERVICE_NOT_DEFINED";

    const INTERNAL_SERVICE_FAILURE = "INTERNAL_SERVICE_FAILURE";

    const QUERY_EXECUTION_FAILED = "QUERY_EXECUTION_FAILED";

    const CONNECTION_TIMEOUT = "CONNECTION_TIMEOUT";

    const CONNECTION_FAILED = "CONNECTION_FAILED";

    const SORT_ORDER_NOT_DEFINED = "SORT_ORDER_NOT_DEFINED";

    const SORTBY_NOT_DEFINED = "SORTBY_NOT_DEFINED";

    const INVALID_REQUEST = "INVALID_REQUEST";

    const DUPLICATE_ENTRY = "DUPLICATE_ENTRY";

    const INVALID_LOGIN = "INVALID_LOGIN";

    const DB_CONFIG_NOT_SET = "DB_CONFIG_NOT_SET";

    const DB_CONFIG_FILE_PATH_NOT_DEFINED_IN_SERVER = "DB_CONFIG_FILE_PATH_NOT_DEFINED_IN_SERVER";

    /**
     * Exception code
     *
     * @var string
     */
    protected $code;

    /**
     *
     * @param $message [optional]            
     * @param $code [optional]            
     */
    public function __construct($code = null, $message = null)
    {
        $this->message = $message;
        $this->code = $code;
        $this->logError();
    }

    /**
     * Log exception in error log
     */
    private function logError()
    {
        $errorMsg = "Error Code : '" . $this->code . "' . Error Message : " . $this->message . " Error Trace : " . $this->getTraceAsString();
        error_log($errorMsg);
    }
}<?php
namespace com\linways\base\email;

/*~ class.phpmailer.php
 .---------------------------------------------------------------------------.
 |  Software: PHPMailer - PHP email class                                    |
 |   Version: 2.0.0 rc1                                                      |
 |   Contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
 |      Info: http://phpmailer.sourceforge.net                               |
 |   Support: http://sourceforge.net/projects/phpmailer/                     |
 | ------------------------------------------------------------------------- |
 |    Author: Andy Prevost (project admininistrator)                         |
 |    Author: Brent R. Matzelle (original founder)                           |
 | Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
 | Copyright (c) 2001-2003, Brent R. Matzelle                                |
 | ------------------------------------------------------------------------- |
 |   License: Distributed under the Lesser General Public License (LGPL)     |
 |            http://www.gnu.org/copyleft/lesser.html                        |
 | This program is distributed in the hope that it will be useful - WITHOUT  |
 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
 | FITNESS FOR A PARTICULAR PURPOSE.                                         |
 | ------------------------------------------------------------------------- |
 | We offer a number of paid services (www.codeworxtech.com):                |
 | - Web Hosting on highly optimized fast and secure servers                 |
 | - Technology Consulting                                                   |
 | - Oursourcing (highly qualified programmers and graphic designers)        |
 '---------------------------------------------------------------------------'

 /**
 * PHPMailer - PHP email transport class
 * @package PHPMailer
 * @author Andy Prevost
 * @copyright 2004 - 2007 Andy Prevost
 */

class PHPMailer {

    /////////////////////////////////////////////////
    // PROPERTIES, PUBLIC
    /////////////////////////////////////////////////

    /**
     * Email priority (1 = High, 3 = Normal, 5 = low).
    * @var int
    */
    public $Priority          = 3;

    /**
     * Sets the CharSet of the message.
     * @var string
     */
    public $CharSet           = 'iso-8859-1';

    /**
     * Sets the Content-type of the message.
     * @var string
     */
    public $ContentType       = 'text/plain';

    /**
     * Sets the Encoding of the message. Options for this are "8bit",
     * "7bit", "binary", "base64", and "quoted-printable".
     * @var string
     */
    public $Encoding          = '8bit';

    /**
     * Holds the most recent mailer error message.
     * @var string
     */
    public $ErrorInfo         = '';

    /**
     * Sets the From email address for the message.
     * @var string
     */
    public $From              = 'root@localhost';

    /**
     * Sets the From name of the message.
     * @var string
     */
    public $FromName          = 'Root User';

    /**
     * Sets the Sender email (Return-Path) of the message.  If not empty,
     * will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
     * @var string
     */
    public $Sender            = '';

    /**
     * Sets the Subject of the message.
     * @var string
     */
    public $Subject           = '';

    /**
     * Sets the Body of the message.  This can be either an HTML or text body.
     * If HTML then run IsHTML(true).
     * @var string
     */
    public $Body              = '';

    /**
     * Sets the text-only body of the message.  This automatically sets the
     * email to multipart/alternative.  This body can be read by mail
     * clients that do not have HTML email capability such as mutt. Clients
     * that can read HTML will view the normal Body.
     * @var string
     */
    public $AltBody           = '';

    /**
     * Sets word wrapping on the body of the message to a given number of
     * characters.
     * @var int
     */
    public $WordWrap          = 0;

    /**
     * Method to send mail: ("mail", "sendmail", or "smtp").
     * @var string
     */
    public $Mailer            = 'mail';

    /**
     * Sets the path of the sendmail program.
     * @var string
     */
    public $Sendmail          = '/usr/sbin/sendmail';

    /**
     * Path to PHPMailer plugins.  This is now only useful if the SMTP class
     * is in a different directory than the PHP include path.
     * @var string
     */
    public $PluginDir         = '';

    /**
     * Holds PHPMailer version.
     * @var string
     */
    public $Version           = "2.0.0 rc1";

    /**
     * Sets the email address that a reading confirmation will be sent.
     * @var string
     */
    public $ConfirmReadingTo  = '';

    /**
     * Sets the hostname to use in Message-Id and Received headers
     * and as default HELO string. If empty, the value returned
     * by SERVER_NAME is used or 'localhost.localdomain'.
     * @var string
     */
    public $Hostname          = '';

    /////////////////////////////////////////////////
    // PROPERTIES FOR SMTP
    /////////////////////////////////////////////////

    /**
     * Sets the SMTP hosts.  All hosts must be separated by a
    * semicolon.  You can also specify a different port
    * for each host by using this format: [hostname:port]
    * (e.g. "smtp1.example.com:25;smtp2.example.com").
    * Hosts will be tried in order.
    * @var string
    */
    public $Host        = 'localhost';

    /**
     * Sets the default SMTP server port.
     * @var int
     */
    public $Port        = 25;

    /**
     * Sets the SMTP HELO of the message (Default is $Hostname).
     * @var string
     */
    public $Helo        = '';

    /**
     * Sets connection prefix.
     * Options are "", "ssl" or "tls"
     * @var string
     */
    public $SMTPSecure = "";

    /**
     * Sets SMTP authentication. Utilizes the Username and Password variables.
     * @var bool
     */
    public $SMTPAuth     = false;

    /**
     * Sets SMTP username.
     * @var string
     */
    public $Username     = '';

    /**
     * Sets SMTP password.
     * @var string
     */
    public $Password     = '';

    /**
     * Sets the SMTP server timeout in seconds. This function will not
     * work with the win32 version.
     * @var int
     */
    public $Timeout      = 10;

    /**
     * Sets SMTP class debugging on or off.
     * @var bool
     */
    public $SMTPDebug    = false;

    /**
     * Prevents the SMTP connection from being closed after each mail
     * sending.  If this is set to true then to close the connection
     * requires an explicit call to SmtpClose().
     * @var bool
     */
    public $SMTPKeepAlive = false;

    /**
     * Provides the ability to have the TO field process individual
     * emails, instead of sending to entire TO addresses
     * @var bool
     */
    public $SingleTo = false;

    /////////////////////////////////////////////////
    // PROPERTIES, PRIVATE
    /////////////////////////////////////////////////

    private $smtp            = NULL;
    private $to              = array();
    private $cc              = array();
    private $bcc             = array();
    private $ReplyTo         = array();
    private $attachment      = array();
    private $CustomHeader    = array();
    private $message_type    = '';
    private $boundary        = array();
    private $language        = array();
    private $error_count     = 0;
    private $LE              = "\n";

    /////////////////////////////////////////////////
    // METHODS, VARIABLES
    /////////////////////////////////////////////////

    /**
     * Sets message type to HTML.
    * @param bool $bool
    * @return void
    */
    public function IsHTML($bool) {
        if($bool == true) {
            $this->ContentType = 'text/html';
        } else {
            $this->ContentType = 'text/plain';
        }
    }

    /**
     * Sets Mailer to send message using SMTP.
     * @return void
     */
    public function IsSMTP() {
        $this->Mailer = 'smtp';
    }

    /**
     * Sets Mailer to send message using PHP mail() function.
     * @return void
     */
    public function IsMail() {
        $this->Mailer = 'mail';
    }

    /**
     * Sets Mailer to send message using the $Sendmail program.
     * @return void
     */
    public function IsSendmail() {
        $this->Mailer = 'sendmail';
    }

    /**
     * Sets Mailer to send message using the qmail MTA.
     * @return void
     */
    public function IsQmail() {
        $this->Sendmail = '/var/qmail/bin/sendmail';
        $this->Mailer = 'sendmail';
    }

    /////////////////////////////////////////////////
    // METHODS, RECIPIENTS
    /////////////////////////////////////////////////

    /**
     * Adds a "To" address.
    * @param string $address
    * @param string $name
    * @return void
    */
    public function AddAddress($address, $name = '') {
        $cur = count($this->to);
        $this->to[$cur][0] = trim($address);
        $this->to[$cur][1] = $name;
    }

    /**
     * Adds a "Cc" address. Note: this public function works
     * with the SMTP mailer on win32, not with the "mail"
     * mailer.
     * @param string $address
     * @param string $name
     * @return void
     */
    public function AddCC($address, $name = '') {
        $cur = count($this->cc);
        $this->cc[$cur][0] = trim($address);
        $this->cc[$cur][1] = $name;
    }

    /**
     * Adds a "Bcc" address. Note: this function works
     * with the SMTP mailer on win32, not with the "mail"
     * mailer.
     * @param string $address
     * @param string $name
     * @return void
     */
    public function AddBCC($address, $name = '') {
        $cur = count($this->bcc);
        $this->bcc[$cur][0] = trim($address);
        $this->bcc[$cur][1] = $name;
    }

    /**
     * Adds a "Reply-to" address.
     * @param string $address
     * @param string $name
     * @return void
     */
    public function AddReplyTo($address, $name = '') {
        $cur = count($this->ReplyTo);
        $this->ReplyTo[$cur][0] = trim($address);
        $this->ReplyTo[$cur][1] = $name;
    }

    /////////////////////////////////////////////////
    // METHODS, MAIL SENDING
    /////////////////////////////////////////////////

    /**
     * Creates message and assigns Mailer. If the message is
    * not sent successfully then it returns false.  Use the ErrorInfo
    * variable to view description of the error.
    * @return bool
    */
    public function Send() {
        $header = '';
        $body = '';
        $result = true;

        if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
            $this->SetError($this->Lang('provide_address'));
            return false;
        }

        /* Set whether the message is multipart/alternative */
        if(!empty($this->AltBody)) {
            $this->ContentType = 'multipart/alternative';
        }

        $this->error_count = 0; // reset errors
        $this->SetMessageType();
        $header .= $this->CreateHeader();
        $body = $this->CreateBody();

        if($body == '') {
            return false;
        }

        /* Choose the mailer */
        switch($this->Mailer) {
            case 'sendmail':
                $result = $this->SendmailSend($header, $body);
                break;
            case 'smtp':
                $result = $this->SmtpSend($header, $body);
                break;
            case 'mail':
                $result = $this->MailSend($header, $body);
                break;
            default:
                $result = $this->MailSend($header, $body);
                break;
                //$this->SetError($this->Mailer . $this->Lang('mailer_not_supported'));
                //$result = false;
                //break;
        }

        return $result;
    }

    /**
     * Sends mail using the $Sendmail program.
     * @access private
     * @return bool
     */
    public function SendmailSend($header, $body) {
        if ($this->Sender != '') {
            $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
        } else {
            $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail));
        }

        if(!@$mail = popen($sendmail, 'w')) {
            $this->SetError($this->Lang('execute') . $this->Sendmail);
            return false;
        }

        fputs($mail, $header);
        fputs($mail, $body);

        $result = pclose($mail) >> 8 & 0xFF;
        if($result != 0) {
            $this->SetError($this->Lang('execute') . $this->Sendmail);
            return false;
        }

        return true;
    }

    /**
     * Sends mail using the PHP mail() function.
     * @access private
     * @return bool
     */
    public function MailSend($header, $body) {

        $to = '';
        for($i = 0; $i < count($this->to); $i++) {
            if($i != 0) { $to .= ', '; }
            $to .= $this->AddrFormat($this->to[$i]);
        }

        $toArr = split(',', $to);

        if ($this->Sender != '' && strlen(ini_get('safe_mode'))< 1) {
            $old_from = ini_get('sendmail_from');
            ini_set('sendmail_from', $this->Sender);
            $params = sprintf("-oi -f %s", $this->Sender);
            if ($this->SingleTo === true && count($toArr) > 1) {
                foreach ($toArr as $key => $val) {
                    $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
                }
            } else {
                $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
            }
        } else {
            if ($this->SingleTo === true && count($toArr) > 1) {
                foreach ($toArr as $key => $val) {
                    $rt = @mail($val, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header, $params);
                }
            } else {
                $rt = @mail($to, $this->EncodeHeader($this->SecureHeader($this->Subject)), $body, $header);
            }
        }

        if (isset($old_from)) {
            ini_set('sendmail_from', $old_from);
        }

        if(!$rt) {
            $this->SetError($this->Lang('instantiate'));
            return false;
        }

        return true;
    }

    /**
     * Sends mail via SMTP using PhpSMTP (Author:
     * Chris Ryan).  Returns bool.  Returns false if there is a
     * bad MAIL FROM, RCPT, or DATA input.
     * @access private
     * @return bool
     */
    public function SmtpSend($header, $body) {
//         include_once($this->PluginDir . 'class.smtp.php');
        $error = '';
        $bad_rcpt = array();

        if(!$this->SmtpConnect()) {
            return false;
        }

        $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
        if(!$this->smtp->Mail($smtp_from)) {
            $error = $this->Lang('from_failed') . $smtp_from;
            $this->SetError($error);
            $this->smtp->Reset();
            return false;
        }

        /* Attempt to send attach all recipients */
        for($i = 0; $i < count($this->to); $i++) {
            if(!$this->smtp->Recipient($this->to[$i][0])) {
                $bad_rcpt[] = $this->to[$i][0];
            }
        }
        for($i = 0; $i < count($this->cc); $i++) {
            if(!$this->smtp->Recipient($this->cc[$i][0])) {
                $bad_rcpt[] = $this->cc[$i][0];
            }
        }
        for($i = 0; $i < count($this->bcc); $i++) {
            if(!$this->smtp->Recipient($this->bcc[$i][0])) {
                $bad_rcpt[] = $this->bcc[$i][0];
            }
        }

        if(count($bad_rcpt) > 0) { // Create error message
            for($i = 0; $i < count($bad_rcpt); $i++) {
                if($i != 0) {
                    $error .= ', ';
                }
                $error .= $bad_rcpt[$i];
            }
            $error = $this->Lang('recipients_failed') . $error;
            $this->SetError($error);
            $this->smtp->Reset();
            return false;
        }

        if(!$this->smtp->Data($header . $body)) {
            $this->SetError($this->Lang('data_not_accepted'));
            $this->smtp->Reset();
            return false;
        }
        if($this->SMTPKeepAlive == true) {
            $this->smtp->Reset();
        } else {
            $this->SmtpClose();
        }

        return true;
    }

    /**
     * Initiates a connection to an SMTP server.  Returns false if the
     * operation failed.
     * @access private
     * @return bool
     */
    public function SmtpConnect() {
        if($this->smtp == NULL) {
            $this->smtp = new SMTP();
        }

        $this->smtp->do_debug = $this->SMTPDebug;
        $hosts = explode(';', $this->Host);
        $index = 0;
        $connection = ($this->smtp->Connected());

        /* Retry while there is no connection */
        while($index < count($hosts) && $connection == false) {
            $hostinfo = array();
            if(eregi('^(.+):([0-9]+)$', $hosts[$index], $hostinfo)) {
                $host = $hostinfo[1];
                $port = $hostinfo[2];
            } else {
                $host = $hosts[$index];
                $port = $this->Port;
            }

            if($this->smtp->Connect(((!empty($this->SMTPSecure))?$this->SMTPSecure.'://':'').$host, $port, $this->Timeout)) {
                if ($this->Helo != '') {
                    $this->smtp->Hello($this->Helo);
                } else {
                    $this->smtp->Hello($this->ServerHostname());
                }

                $connection = true;
                if($this->SMTPAuth) {
                    if(!$this->smtp->Authenticate($this->Username, $this->Password)) {
                        $this->SetError($this->Lang('authenticate'));
                        $this->smtp->Reset();
                        $connection = false;
                    }
                }
            }
            $index++;
        }
        if(!$connection) {
            $this->SetError($this->Lang('connect_host'));
        }

        return $connection;
    }

    /**
     * Closes the active SMTP session if one exists.
     * @return void
     */
    public function SmtpClose() {
        if($this->smtp != NULL) {
            if($this->smtp->Connected()) {
                $this->smtp->Quit();
                $this->smtp->Close();
            }
        }
    }

    /**
     * Sets the language for all class error messages.  Returns false
     * if it cannot load the language file.  The default language type
     * is English.
     * @param string $lang_type Type of language (e.g. Portuguese: "br")
     * @param string $lang_path Path to the language file directory
     * @access public
     * @return bool
     */
    public function SetLanguage($lang_type, $lang_path = 'language/') {
        if(file_exists($lang_path.'phpmailer.lang-'.$lang_type.'.php')) {
            include($lang_path.'phpmailer.lang-'.$lang_type.'.php');
        } elseif (file_exists($lang_path.'phpmailer.lang-en.php')) {
            include($lang_path.'phpmailer.lang-en.php');
        } else {
            $this->SetError('Could not load language file');
            return false;
        }
        $this->language = $PHPMAILER_LANG;

        return true;
    }

    /////////////////////////////////////////////////
    // METHODS, MESSAGE CREATION
    /////////////////////////////////////////////////

    /**
     * Creates recipient headers.
    * @access private
    * @return string
    */
    public function AddrAppend($type, $addr) {
        $addr_str = $type . ': ';
        $addr_str .= $this->AddrFormat($addr[0]);
        if(count($addr) > 1) {
            for($i = 1; $i < count($addr); $i++) {
                $addr_str .= ', ' . $this->AddrFormat($addr[$i]);
            }
        }
        $addr_str .= $this->LE;

        return $addr_str;
    }

    /**
     * Formats an address correctly.
     * @access private
     * @return string
     */
    public function AddrFormat($addr) {
        if(empty($addr[1])) {
            $formatted = $this->SecureHeader($addr[0]);
        } else {
            $formatted = $this->EncodeHeader($this->SecureHeader($addr[1]), 'phrase') . " <" . $this->SecureHeader($addr[0]) . ">";
        }

        return $formatted;
    }

    /**
     * Wraps message for use with mailers that do not
     * automatically perform wrapping and for quoted-printable.
     * Original written by philippe.
     * @access private
     * @return string
     */
    public function WrapText($message, $length, $qp_mode = false) {
        $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE;

        $message = $this->FixEOL($message);
        if (substr($message, -1) == $this->LE) {
            $message = substr($message, 0, -1);
        }

        $line = explode($this->LE, $message);
        $message = '';
        for ($i=0 ;$i < count($line); $i++) {
            $line_part = explode(' ', $line[$i]);
            $buf = '';
            for ($e = 0; $e<count($line_part); $e++) {
                $word = $line_part[$e];
                if ($qp_mode and (strlen($word) > $length)) {
                    $space_left = $length - strlen($buf) - 1;
                    if ($e != 0) {
                        if ($space_left > 20) {
                            $len = $space_left;
                            if (substr($word, $len - 1, 1) == '=') {
                                $len--;
                            } elseif (substr($word, $len - 2, 1) == '=') {
                                $len -= 2;
                            }
                            $part = substr($word, 0, $len);
                            $word = substr($word, $len);
                            $buf .= ' ' . $part;
                            $message .= $buf . sprintf("=%s", $this->LE);
                        } else {
                            $message .= $buf . $soft_break;
                        }
                        $buf = '';
                    }
                    while (strlen($word) > 0) {
                        $len = $length;
                        if (substr($word, $len - 1, 1) == '=') {
                            $len--;
                        } elseif (substr($word, $len - 2, 1) == '=') {
                            $len -= 2;
                        }
                        $part = substr($word, 0, $len);
                        $word = substr($word, $len);

                        if (strlen($word) > 0) {
                            $message .= $part . sprintf("=%s", $this->LE);
                        } else {
                            $buf = $part;
                        }
                    }
                } else {
                    $buf_o = $buf;
                    $buf .= ($e == 0) ? $word : (' ' . $word);

                    if (strlen($buf) > $length and $buf_o != '') {
                        $message .= $buf_o . $soft_break;
                        $buf = $word;
                    }
                }
            }
            $message .= $buf . $this->LE;
        }

        return $message;
    }

    /**
     * Set the body wrapping.
     * @access private
     * @return void
     */
    public function SetWordWrap() {
        if($this->WordWrap < 1) {
            return;
        }

        switch($this->message_type) {
            case 'alt':
                /* fall through */
            case 'alt_attachments':
                $this->AltBody = $this->WrapText($this->AltBody, $this->WordWrap);
                break;
            default:
                $this->Body = $this->WrapText($this->Body, $this->WordWrap);
                break;
        }
    }

    /**
     * Assembles message header.
     * @access private
     * @return string
     */
    public function CreateHeader() {
        $result = '';

        /* Set the boundaries */
        $uniq_id = md5(uniqid(time()));
        $this->boundary[1] = 'b1_' . $uniq_id;
        $this->boundary[2] = 'b2_' . $uniq_id;

        $result .= $this->HeaderLine('Date', $this->RFCDate());
        if($this->Sender == '') {
            $result .= $this->HeaderLine('Return-Path', trim($this->From));
        } else {
            $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
        }

        /* To be created automatically by mail() */
        if($this->Mailer != 'mail') {
            if(count($this->to) > 0) {
                $result .= $this->AddrAppend('To', $this->to);
            } elseif (count($this->cc) == 0) {
                $result .= $this->HeaderLine('To', 'undisclosed-recipients:;');
            }
            if(count($this->cc) > 0) {
                $result .= $this->AddrAppend('Cc', $this->cc);
            }
        }

        $from = array();
        $from[0][0] = trim($this->From);
        $from[0][1] = $this->FromName;
        $result .= $this->AddrAppend('From', $from);

        /* sendmail and mail() extract Cc from the header before sending */
        if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->cc) > 0)) {
            $result .= $this->AddrAppend('Cc', $this->cc);
        }

        /* sendmail and mail() extract Bcc from the header before sending */
        if((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) {
            $result .= $this->AddrAppend('Bcc', $this->bcc);
        }

        if(count($this->ReplyTo) > 0) {
            $result .= $this->AddrAppend('Reply-to', $this->ReplyTo);
        }

        /* mail() sets the subject itself */
        if($this->Mailer != 'mail') {
            $result .= $this->HeaderLine('Subject', $this->EncodeHeader($this->SecureHeader($this->Subject)));
        }

        $result .= sprintf("Message-ID: <%s@%s>%s", $uniq_id, $this->ServerHostname(), $this->LE);
        $result .= $this->HeaderLine('X-Priority', $this->Priority);
        $result .= $this->HeaderLine('X-Mailer', 'PHPMailer (phpmailer.sourceforge.net) [version ' . $this->Version . ']');

        if($this->ConfirmReadingTo != '') {
            $result .= $this->HeaderLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
        }

        // Add custom headers
        for($index = 0; $index < count($this->CustomHeader); $index++) {
            $result .= $this->HeaderLine(trim($this->CustomHeader[$index][0]), $this->EncodeHeader(trim($this->CustomHeader[$index][1])));
        }
        $result .= $this->HeaderLine('MIME-Version', '1.0');

        switch($this->message_type) {
            case 'plain':
                $result .= $this->HeaderLine('Content-Transfer-Encoding', $this->Encoding);
                $result .= sprintf("Content-Type: %s; charset=\"%s\"", $this->ContentType, $this->CharSet);
                break;
            case 'attachments':
                /* fall through */
            case 'alt_attachments':
                if($this->InlineImageExists()){
                    $result .= sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->LE, $this->LE, $this->boundary[1], $this->LE);
                } else {
                    $result .= $this->HeaderLine('Content-Type', 'multipart/mixed;');
                    $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
                }
                break;
            case 'alt':
                $result .= $this->HeaderLine('Content-Type', 'multipart/alternative;');
                $result .= $this->TextLine("\tboundary=\"" . $this->boundary[1] . '"');
                break;
        }

        if($this->Mailer != 'mail') {
            $result .= $this->LE.$this->LE;
        }

        return $result;
    }

    /**
     * Assembles the message body.  Returns an empty string on failure.
     * @access private
     * @return string
     */
    public function CreateBody() {
        $result = '';

        $this->SetWordWrap();

        switch($this->message_type) {
            case 'alt':
                $result .= $this->GetBoundary($this->boundary[1], '', 'text/plain', '');
                $result .= $this->EncodeString($this->AltBody, $this->Encoding);
                $result .= $this->LE.$this->LE;
                $result .= $this->GetBoundary($this->boundary[1], '', 'text/html', '');
                $result .= $this->EncodeString($this->Body, $this->Encoding);
                $result .= $this->LE.$this->LE;
                $result .= $this->EndBoundary($this->boundary[1]);
                break;
            case 'plain':
                $result .= $this->EncodeString($this->Body, $this->Encoding);
                break;
            case 'attachments':
                $result .= $this->GetBoundary($this->boundary[1], '', '', '');
                $result .= $this->EncodeString($this->Body, $this->Encoding);
                $result .= $this->LE;
                $result .= $this->AttachAll();
                break;
            case 'alt_attachments':
                $result .= sprintf("--%s%s", $this->boundary[1], $this->LE);
                $result .= sprintf("Content-Type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->LE, $this->boundary[2], $this->LE.$this->LE);
                $result .= $this->GetBoundary($this->boundary[2], '', 'text/plain', '') . $this->LE; // Create text body
                $result .= $this->EncodeString($this->AltBody, $this->Encoding);
                $result .= $this->LE.$this->LE;
                $result .= $this->GetBoundary($this->boundary[2], '', 'text/html', '') . $this->LE; // Create the HTML body
                $result .= $this->EncodeString($this->Body, $this->Encoding);
                $result .= $this->LE.$this->LE;
                $result .= $this->EndBoundary($this->boundary[2]);
                $result .= $this->AttachAll();
                break;
        }
        if($this->IsError()) {
            $result = '';
        }

        return $result;
    }

    /**
     * Returns the start of a message boundary.
     * @access private
     */
    public function GetBoundary($boundary, $charSet, $contentType, $encoding) {
        $result = '';
        if($charSet == '') {
            $charSet = $this->CharSet;
        }
        if($contentType == '') {
            $contentType = $this->ContentType;
        }
        if($encoding == '') {
            $encoding = $this->Encoding;
        }
        $result .= $this->TextLine('--' . $boundary);
        $result .= sprintf("Content-Type: %s; charset = \"%s\"", $contentType, $charSet);
        $result .= $this->LE;
        $result .= $this->HeaderLine('Content-Transfer-Encoding', $encoding);
        $result .= $this->LE;

        return $result;
    }

    /**
     * Returns the end of a message boundary.
     * @access private
     */
    public function EndBoundary($boundary) {
        return $this->LE . '--' . $boundary . '--' . $this->LE;
    }

    /**
     * Sets the message type.
     * @access private
     * @return void
     */
    public function SetMessageType() {
        if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) {
            $this->message_type = 'plain';
        } else {
            if(count($this->attachment) > 0) {
                $this->message_type = 'attachments';
            }
            if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) {
                $this->message_type = 'alt';
            }
            if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) {
                $this->message_type = 'alt_attachments';
            }
        }
    }

    /* Returns a formatted header line.
     * @access private
     * @return string
     */
    public function HeaderLine($name, $value) {
        return $name . ': ' . $value . $this->LE;
    }

    /**
     * Returns a formatted mail line.
     * @access private
     * @return string
     */
    public function TextLine($value) {
        return $value . $this->LE;
    }

    /////////////////////////////////////////////////
    // CLASS METHODS, ATTACHMENTS
    /////////////////////////////////////////////////

    /**
     * Adds an attachment from a path on the filesystem.
    * Returns false if the file could not be found
    * or accessed.
    * @param string $path Path to the attachment.
    * @param string $name Overrides the attachment name.
    * @param string $encoding File encoding (see $Encoding).
    * @param string $type File extension (MIME) type.
    * @return bool
    */
    public function AddAttachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {
        if(!@is_file($path)) {
            $this->SetError($this->Lang('file_access') . $path);
            return false;
        }

        $filename = basename($path);
        if($name == '') {
            $name = $filename;
        }

        $cur = count($this->attachment);
        $this->attachment[$cur][0] = $path;
        $this->attachment[$cur][1] = $filename;
        $this->attachment[$cur][2] = $name;
        $this->attachment[$cur][3] = $encoding;
        $this->attachment[$cur][4] = $type;
        $this->attachment[$cur][5] = false; // isStringAttachment
        $this->attachment[$cur][6] = 'attachment';
        $this->attachment[$cur][7] = 0;

        return true;
    }

    /**
     * Attaches all fs, string, and binary attachments to the message.
     * Returns an empty string on failure.
     * @access private
     * @return string
     */
    public function AttachAll() {
        /* Return text of body */
        $mime = array();

        /* Add all attachments */
        for($i = 0; $i < count($this->attachment); $i++) {
            /* Check for string attachment */
            $bString = $this->attachment[$i][5];
            if ($bString) {
                $string = $this->attachment[$i][0];
            } else {
                $path = $this->attachment[$i][0];
            }

            $filename    = $this->attachment[$i][1];
            $name        = $this->attachment[$i][2];
            $encoding    = $this->attachment[$i][3];
            $type        = $this->attachment[$i][4];
            $disposition = $this->attachment[$i][6];
            $cid         = $this->attachment[$i][7];

            $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE);
            $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE);
            $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE);

            if($disposition == 'inline') {
                $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE);
            }

            $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", $disposition, $name, $this->LE.$this->LE);

            /* Encode as string attachment */
            if($bString) {
                $mime[] = $this->EncodeString($string, $encoding);
                if($this->IsError()) {
                    return '';
                }
                $mime[] = $this->LE.$this->LE;
            } else {
                $mime[] = $this->EncodeFile($path, $encoding);
                if($this->IsError()) {
                    return '';
                }
                $mime[] = $this->LE.$this->LE;
            }
        }

        $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE);

        return join('', $mime);
    }

    /**
     * Encodes attachment in requested format.  Returns an
     * empty string on failure.
     * @access private
     * @return string
     */
    public function EncodeFile ($path, $encoding = 'base64') {
        if(!@$fd = fopen($path, 'rb')) {
            $this->SetError($this->Lang('file_open') . $path);
            return '';
        }
        $magic_quotes = get_magic_quotes_runtime();
        set_magic_quotes_runtime(0);
        $file_buffer = fread($fd, filesize($path));
        $file_buffer = $this->EncodeString($file_buffer, $encoding);
        fclose($fd);
        set_magic_quotes_runtime($magic_quotes);

        return $file_buffer;
    }

    /**
     * Encodes string to requested format. Returns an
     * empty string on failure.
     * @access private
     * @return string
     */
    public function EncodeString ($str, $encoding = 'base64') {
        $encoded = '';
        switch(strtolower($encoding)) {
            case 'base64':
                /* chunk_split is found in PHP >= 3.0.6 */
                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
                break;
            case '7bit':
            case '8bit':
                $encoded = $this->FixEOL($str);
                if (substr($encoded, -(strlen($this->LE))) != $this->LE)
                    $encoded .= $this->LE;
                    break;
            case 'binary':
                $encoded = $str;
                break;
            case 'quoted-printable':
                $encoded = $this->EncodeQP($str);
                break;
            default:
                $this->SetError($this->Lang('encoding') . $encoding);
                break;
        }
        return $encoded;
    }

    /**
     * Encode a header string to best of Q, B, quoted or none.
     * @access private
     * @return string
     */
    public function EncodeHeader ($str, $position = 'text') {
        $x = 0;

        switch (strtolower($position)) {
            case 'phrase':
                if (!preg_match('/[\200-\377]/', $str)) {
                    /* Can't use addslashes as we don't know what value has magic_quotes_sybase. */
                    $encoded = addcslashes($str, "\0..\37\177\\\"");
                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
                        return ($encoded);
                    } else {
                        return ("\"$encoded\"");
                    }
                }
                $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
                break;
            case 'comment':
                $x = preg_match_all('/[()"]/', $str, $matches);
                /* Fall-through */
            case 'text':
            default:
                $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
                break;
        }

        if ($x == 0) {
            return ($str);
        }

        $maxlen = 75 - 7 - strlen($this->CharSet);
        /* Try to select the encoding which should produce the shortest output */
        if (strlen($str)/3 < $x) {
            $encoding = 'B';
            $encoded = base64_encode($str);
            $maxlen -= $maxlen % 4;
            $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
        } else {
            $encoding = 'Q';
            $encoded = $this->EncodeQ($str, $position);
            $encoded = $this->WrapText($encoded, $maxlen, true);
            $encoded = str_replace('='.$this->LE, "\n", trim($encoded));
        }

        $encoded = preg_replace('/^(.*)$/m', " =?".$this->CharSet."?$encoding?\\1?=", $encoded);
        $encoded = trim(str_replace("\n", $this->LE, $encoded));

        return $encoded;
    }

    /**
     * Encode string to quoted-printable.
     * @access private
     * @return string
     */
    public function EncodeQP ($str) {
        $encoded = $this->FixEOL($str);
        if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
            $encoded .= $this->LE;
        }

        /* Replace every high ascii, control and = characters */
        $encoded = preg_replace('/([\000-\010\013\014\016-\037\075\177-\377])/e', "'='.sprintf('%02X', ord('\\1'))", $encoded);
        /* Replace every spaces and tabs when it's the last character on a line */
        $encoded = preg_replace("/([\011\040])".$this->LE."/e", "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded);

        /* Maximum line length of 76 characters before CRLF (74 + space + '=') */
        $encoded = $this->WrapText($encoded, 74, true);

        return $encoded;
    }

    /**
     * Encode string to q encoding.
     * @access private
     * @return string
     */
    public function EncodeQ ($str, $position = 'text') {
        /* There should not be any EOL in the string */
        $encoded = preg_replace("[\r\n]", '', $str);

        switch (strtolower($position)) {
            case 'phrase':
                $encoded = preg_replace("/([^A-Za-z0-9!*+\/ -])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
                break;
            case 'comment':
                $encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded);
            case 'text':
            default:
                /* Replace every high ascii, control =, ? and _ characters */
                $encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e',
                "'='.sprintf('%02X', ord('\\1'))", $encoded);
                break;
        }

        /* Replace every spaces to _ (more readable than =20) */
        $encoded = str_replace(' ', '_', $encoded);

        return $encoded;
    }

    /**
     * Adds a string or binary attachment (non-filesystem) to the list.
     * This method can be used to attach ascii or binary data,
     * such as a BLOB record from a database.
     * @param string $string String attachment data.
     * @param string $filename Name of the attachment.
     * @param string $encoding File encoding (see $Encoding).
     * @param string $type File extension (MIME) type.
     * @return void
     */
    public function AddStringAttachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream') {
        /* Append to $attachment array */
        $cur = count($this->attachment);
        $this->attachment[$cur][0] = $string;
        $this->attachment[$cur][1] = $filename;
        $this->attachment[$cur][2] = $filename;
        $this->attachment[$cur][3] = $encoding;
        $this->attachment[$cur][4] = $type;
        $this->attachment[$cur][5] = true; // isString
        $this->attachment[$cur][6] = 'attachment';
        $this->attachment[$cur][7] = 0;
    }

    /**
     * Adds an embedded attachment.  This can include images, sounds, and
     * just about any other document.  Make sure to set the $type to an
     * image type.  For JPEG images use "image/jpeg" and for GIF images
     * use "image/gif".
     * @param string $path Path to the attachment.
     * @param string $cid Content ID of the attachment.  Use this to identify
     *        the Id for accessing the image in an HTML form.
     * @param string $name Overrides the attachment name.
     * @param string $encoding File encoding (see $Encoding).
     * @param string $type File extension (MIME) type.
     * @return bool
     */
    public function AddEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream') {

        if(!@is_file($path)) {
            $this->SetError($this->Lang('file_access') . $path);
            return false;
        }

        $filename = basename($path);
        if($name == '') {
            $name = $filename;
        }

        /* Append to $attachment array */
        $cur = count($this->attachment);
        $this->attachment[$cur][0] = $path;
        $this->attachment[$cur][1] = $filename;
        $this->attachment[$cur][2] = $name;
        $this->attachment[$cur][3] = $encoding;
        $this->attachment[$cur][4] = $type;
        $this->attachment[$cur][5] = false;
        $this->attachment[$cur][6] = 'inline';
        $this->attachment[$cur][7] = $cid;

        return true;
    }

    /**
     * Returns true if an inline attachment is present.
     * @access private
     * @return bool
     */
    public function InlineImageExists() {
        $result = false;
        for($i = 0; $i < count($this->attachment); $i++) {
            if($this->attachment[$i][6] == 'inline') {
                $result = true;
                break;
            }
        }

        return $result;
    }

    /////////////////////////////////////////////////
    // CLASS METHODS, MESSAGE RESET
    /////////////////////////////////////////////////

    /**
     * Clears all recipients assigned in the TO array.  Returns void.
    * @return void
    */
    public function ClearAddresses() {
        $this->to = array();
    }

    /**
     * Clears all recipients assigned in the CC array.  Returns void.
     * @return void
     */
    public function ClearCCs() {
        $this->cc = array();
    }

    /**
     * Clears all recipients assigned in the BCC array.  Returns void.
     * @return void
     */
    public function ClearBCCs() {
        $this->bcc = array();
    }

    /**
     * Clears all recipients assigned in the ReplyTo array.  Returns void.
     * @return void
     */
    public function ClearReplyTos() {
        $this->ReplyTo = array();
    }

    /**
     * Clears all recipients assigned in the TO, CC and BCC
     * array.  Returns void.
     * @return void
     */
    public function ClearAllRecipients() {
        $this->to = array();
        $this->cc = array();
        $this->bcc = array();
    }

    /**
     * Clears all previously set filesystem, string, and binary
     * attachments.  Returns void.
     * @return void
     */
    public function ClearAttachments() {
        $this->attachment = array();
    }

    /**
     * Clears all custom headers.  Returns void.
     * @return void
     */
    public function ClearCustomHeaders() {
        $this->CustomHeader = array();
    }

    /////////////////////////////////////////////////
    // CLASS METHODS, MISCELLANEOUS
    /////////////////////////////////////////////////

    /**
     * Adds the error message to the error container.
    * Returns void.
    * @access private
    * @return void
    */
    private function SetError($msg) {
        $this->error_count++;
        $this->ErrorInfo = $msg;
    }

    /**
     * Returns the proper RFC 822 formatted date.
     * @access private
     * @return string
     */
    private static function RFCDate() {
        $tz = date('Z');
        $tzs = ($tz < 0) ? '-' : '+';
        $tz = abs($tz);
        $tz = (int)($tz/3600)*100 + ($tz%3600)/60;
        $result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);

        return $result;
    }

    /**
     * Returns the server hostname or 'localhost.localdomain' if unknown.
     * @access private
     * @return string
     */
    private function ServerHostname() {
        if (!empty($this->Hostname)) {
            $result = $this->Hostname;
        } elseif (isset($_SERVER['SERVER_NAME'])) {
            $result = $_SERVER['SERVER_NAME'];
        } else {
            $result = "localhost.localdomain";
        }

        return $result;
    }

    /**
     * Returns a message in the appropriate language.
     * @access private
     * @return string
     */
    private function Lang($key) {
        if(count($this->language) < 1) {
            $this->SetLanguage('en'); // set the default language
        }

        if(isset($this->language[$key]))
        {
            return $this->language[$key];
        }
        else
        {
            return 'Language string failed to load: ' . $key;
        }
    }

    /**
     * Returns true if an error occurred.
     * @return bool
     */
    public function IsError() {
        return ($this->error_count > 0);
    }

    /**
     * Changes every end of line from CR or LF to CRLF.
     * @access private
     * @return string
     */
    private function FixEOL($str) {
        $str = str_replace("\r\n", "\n", $str);
        $str = str_replace("\r", "\n", $str);
        $str = str_replace("\n", $this->LE, $str);
        return $str;
    }

    /**
     * Adds a custom header.
     * @return void
     */
    public function AddCustomHeader($custom_header) {
        $this->CustomHeader[] = explode(':', $custom_header, 2);
    }

    /**
     * Evaluates the message and returns modifications for inline images and backgrounds
     * @access public
     * @return $message
     */
    public function MsgHTML($message) {
        preg_match_all("/(src|background)=\"(.*)\"/Ui", $message, $images);
        if(isset($images[2])) {
            foreach($images[2] as $i => $url) {
                $filename  = basename($url);
                $directory = dirname($url);
                $cid       = 'cid:' . md5($filename);
                $fileParts = split("\.", $filename);
                $ext       = $fileParts[1];
                $mimeType  = $this->_mime_types($ext);
                $message = preg_replace("/".$images[1][$i]."=\"".preg_quote($url, '/')."\"/Ui", $images[1][$i]."=\"".$cid."\"", $message);
                $this->AddEmbeddedImage($url, md5($filename), $filename, 'base64', $mimeType);
            }
        }
        $this->IsHTML(true);
        $this->Body = $message;
        $textMsg = trim(strip_tags($message));
        if ( !empty($textMsg) && empty($this->AltBody) ) {
            $this->AltBody = $textMsg;
        }
        if ( empty($this->AltBody) ) {
            $this->AltBody = 'To view this email message, open the email in with HTML compatibility!' . "\n\n";
        }
    }

    /**
     * Gets the mime type of the embedded or inline image
     * @access private
     * @return mime type of ext
     */
    public function _mime_types($ext = '') {
        $mimes = array(
            'hqx'  =>  'application/mac-binhex40',
            'cpt'   =>  'application/mac-compactpro',
            'doc'   =>  'application/msword',
            'bin'   =>  'application/macbinary',
            'dms'   =>  'application/octet-stream',
            'lha'   =>  'application/octet-stream',
            'lzh'   =>  'application/octet-stream',
            'exe'   =>  'application/octet-stream',
            'class' =>  'application/octet-stream',
            'psd'   =>  'application/octet-stream',
            'so'    =>  'application/octet-stream',
            'sea'   =>  'application/octet-stream',
            'dll'   =>  'application/octet-stream',
            'oda'   =>  'application/oda',
            'pdf'   =>  'application/pdf',
            'ai'    =>  'application/postscript',
            'eps'   =>  'application/postscript',
            'ps'    =>  'application/postscript',
            'smi'   =>  'application/smil',
            'smil'  =>  'application/smil',
            'mif'   =>  'application/vnd.mif',
            'xls'   =>  'application/vnd.ms-excel',
            'ppt'   =>  'application/vnd.ms-powerpoint',
            'wbxml' =>  'application/vnd.wap.wbxml',
            'wmlc'  =>  'application/vnd.wap.wmlc',
            'dcr'   =>  'application/x-director',
            'dir'   =>  'application/x-director',
            'dxr'   =>  'application/x-director',
            'dvi'   =>  'application/x-dvi',
            'gtar'  =>  'application/x-gtar',
            'php'   =>  'application/x-httpd-php',
            'php4'  =>  'application/x-httpd-php',
            'php3'  =>  'application/x-httpd-php',
            'phtml' =>  'application/x-httpd-php',
            'phps'  =>  'application/x-httpd-php-source',
            'js'    =>  'application/x-javascript',
            'swf'   =>  'application/x-shockwave-flash',
            'sit'   =>  'application/x-stuffit',
            'tar'   =>  'application/x-tar',
            'tgz'   =>  'application/x-tar',
            'xhtml' =>  'application/xhtml+xml',
            'xht'   =>  'application/xhtml+xml',
            'zip'   =>  'application/zip',
            'mid'   =>  'audio/midi',
            'midi'  =>  'audio/midi',
            'mpga'  =>  'audio/mpeg',
            'mp2'   =>  'audio/mpeg',
            'mp3'   =>  'audio/mpeg',
            'aif'   =>  'audio/x-aiff',
            'aiff'  =>  'audio/x-aiff',
            'aifc'  =>  'audio/x-aiff',
            'ram'   =>  'audio/x-pn-realaudio',
            'rm'    =>  'audio/x-pn-realaudio',
            'rpm'   =>  'audio/x-pn-realaudio-plugin',
            'ra'    =>  'audio/x-realaudio',
            'rv'    =>  'video/vnd.rn-realvideo',
            'wav'   =>  'audio/x-wav',
            'bmp'   =>  'image/bmp',
            'gif'   =>  'image/gif',
            'jpeg'  =>  'image/jpeg',
            'jpg'   =>  'image/jpeg',
            'jpe'   =>  'image/jpeg',
            'png'   =>  'image/png',
            'tiff'  =>  'image/tiff',
            'tif'   =>  'image/tiff',
            'css'   =>  'text/css',
            'html'  =>  'text/html',
            'htm'   =>  'text/html',
            'shtml' =>  'text/html',
            'txt'   =>  'text/plain',
            'text'  =>  'text/plain',
            'log'   =>  'text/plain',
            'rtx'   =>  'text/richtext',
            'rtf'   =>  'text/rtf',
            'xml'   =>  'text/xml',
            'xsl'   =>  'text/xml',
            'mpeg'  =>  'video/mpeg',
            'mpg'   =>  'video/mpeg',
            'mpe'   =>  'video/mpeg',
            'qt'    =>  'video/quicktime',
            'mov'   =>  'video/quicktime',
            'avi'   =>  'video/x-msvideo',
            'movie' =>  'video/x-sgi-movie',
            'doc'   =>  'application/msword',
            'word'  =>  'application/msword',
            'xl'    =>  'application/excel',
            'eml'   =>  'message/rfc822'
        );
        return ( ! isset($mimes[strtolower($ext)])) ? 'application/x-unknown-content-type' : $mimes[strtolower($ext)];
    }

    /**
     * Set (or reset) Class Objects (variables)
     *
     * Usage Example:
     * $page->set('X-Priority', '3');
     *
     * @access public
     * @param string $name Parameter Name
     * @param mixed $value Parameter Value
     * NOTE: will not work with arrays, there are no arrays to set/reset
     */
    public function set ( $name, $value = '' ) {
        if ( isset($this->$name) ) {
            $this->$name = $value;
        } else {
            $this->SetError('Cannot set or reset variable ' . $name);
            return false;
        }
    }

    /**
     * Read a file from a supplied filename and return it.
     *
     * @access public
     * @param string $filename Parameter File Name
     */
    public function getFile($filename) {
        $return = '';
        if ($fp = fopen($filename, 'rb')) {
            while (!feof($fp)) {
                $return .= fread($fp, 1024);
            }
            fclose($fp);
            return $return;
        } else {
            return false;
        }
    }

    /**
     * Strips newlines to prevent header injection.
     * @access private
     * @param string $str String
     * @return string
     */
    public function SecureHeader($str) {
        $str = trim($str);
        $str = str_replace("\r", "", $str);
        $str = str_replace("\n", "", $str);
        return $str;
    }

}<?php
namespace com\linways\base\email;
/*~ class.smtp.php
 .---------------------------------------------------------------------------.
 |  Software: PHPMailer - PHP email class                                    |
 |   Version: 2.0.0 rc1                                                      |
 |   Contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
 |      Info: http://phpmailer.sourceforge.net                               |
 |   Support: http://sourceforge.net/projects/phpmailer/                     |
 | ------------------------------------------------------------------------- |
 |    Author: Andy Prevost (project admininistrator)                         |
 |    Author: Brent R. Matzelle (original founder)                           |
 | Copyright (c) 2004-2007, Andy Prevost. All Rights Reserved.               |
 | Copyright (c) 2001-2003, Brent R. Matzelle                                |
 | ------------------------------------------------------------------------- |
 |   License: Distributed under the Lesser General Public License (LGPL)     |
 |            http://www.gnu.org/copyleft/lesser.html                        |
 | This program is distributed in the hope that it will be useful - WITHOUT  |
 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
 | FITNESS FOR A PARTICULAR PURPOSE.                                         |
 | ------------------------------------------------------------------------- |
 | We offer a number of paid services (www.codeworxtech.com):                |
 | - Web Hosting on highly optimized fast and secure servers                 |
 | - Technology Consulting                                                   |
 | - Oursourcing (highly qualified programmers and graphic designers)        |
 '---------------------------------------------------------------------------'

 /**
 * SMTP is rfc 821 compliant and implements all the rfc 821 SMTP
 * commands except TURN which will always return a not implemented
 * error. SMTP also provides some utility methods for sending mail
 * to an SMTP server.
 * @package PHPMailer
 * @author Chris Ryan
 */

class SMTP {
    /**
     *  SMTP server port
     *  @var int
     */
    public $SMTP_PORT = 25;

    /**
     *  SMTP reply line ending
     *  @var string
     */
    public $CRLF = "\r\n";

    /**
     *  Sets whether debugging is turned on
     *  @var bool
     */
    public $do_debug;       // the level of debug to perform

    /**
     *  Sets VERP use on/off (default is off)
     *  @var bool
     */
    public $do_verp = false;

    /**#@+
     * @access private
     */
    private $smtp_conn;      // the socket to the server
    private $error;          // error if any on the last call
    private $helo_rply;      // the reply the server sent to us for HELO
    /**#@-*/

    /**
     * Initialize the class so that the data is in a known state.
     * @access public
     * @return void
     */
    public function __construct() {
        $this->smtp_conn = 0;
        $this->error = null;
        $this->helo_rply = null;

        $this->do_debug = 0;
    }

    /*************************************************************
     *                    CONNECTION FUNCTIONS                  *
     ***********************************************************/

    /**
     * Connect to the server specified on the port specified.
     * If the port is not specified use the default SMTP_PORT.
     * If tval is specified then a connection will try and be
     * established with the server for that number of seconds.
     * If tval is not specified the default is 30 seconds to
     * try on the connection.
     *
     * SMTP CODE SUCCESS: 220
     * SMTP CODE FAILURE: 421
     * @access public
     * @return bool
     */
    public function Connect($host,$port=0,$tval=30) {
        /* set the error val to null so there is no confusion */
        $this->error = null;

        /* make sure we are __not__ connected */
        if($this->connected()) {
            /* ok we are connected! what should we do?
             * for now we will just give an error saying we
             * are already connected
             */
            $this->error = array("error" => "Already connected to a server");
            return false;
        }

        if(empty($port)) {
            $port = $this->SMTP_PORT;
        }

        /* connect to the smtp server */
        $this->smtp_conn = fsockopen($host,    // the host of the server
            $port,    // the port to use
            $errno,   // error number if any
            $errstr,  // error message if any
            $tval);   // give up after ? secs
            /* verify we connected properly */
            if(empty($this->smtp_conn)) {
                $this->error = array("error" => "Failed to connect to server",
                    "errno" => $errno,
                    "errstr" => $errstr);
                if($this->do_debug >= 1) {
                    echo "SMTP -> ERROR: " . $this->error["error"] .
                    ": $errstr ($errno)" . $this->CRLF;
                }
                return false;
            }

            /* sometimes the SMTP server takes a little longer to respond
             * so we will give it a longer timeout for the first read
             * - Windows still does not have support for this timeout function
             */
            if(substr(PHP_OS, 0, 3) != "WIN")
                socket_set_timeout($this->smtp_conn, $tval, 0);

                /* get any announcement stuff */
                $announce = $this->get_lines();

                /* set the timeout  of any socket functions at 1/10 of a second */
                //if(function_exists("socket_set_timeout"))
                //   socket_set_timeout($this->smtp_conn, 0, 100000);

                if($this->do_debug >= 2) {
                    echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce;
                }

                return true;
    }

    /**
     * Performs SMTP authentication.  Must be run after running the
     * Hello() method.  Returns true if successfully authenticated.
     * @access public
     * @return bool
     */
    public function Authenticate($username, $password) {
        // Start authentication
        fputs($this->smtp_conn,"AUTH LOGIN" . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($code != 334) {
            $this->error =
            array("error" => "AUTH not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        // Send encoded username
        fputs($this->smtp_conn, base64_encode($username) . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($code != 334) {
            $this->error =
            array("error" => "Username not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        // Send encoded password
        fputs($this->smtp_conn, base64_encode($password) . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($code != 235) {
            $this->error =
            array("error" => "Password not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        return true;
    }

    /**
     * Returns true if connected to a server otherwise false
     * @access private
     * @return bool
     */
    public function Connected() {
        if(!empty($this->smtp_conn)) {
            $sock_status = socket_get_status($this->smtp_conn);
            if($sock_status["eof"]) {
                // hmm this is an odd situation... the socket is
                // valid but we are not connected anymore
                if($this->do_debug >= 1) {
                    echo "SMTP -> NOTICE:" . $this->CRLF .
                    "EOF caught while checking if connected";
                }
                $this->Close();
                return false;
            }
            return true; // everything looks good
        }
        return false;
    }

    /**
     * Closes the socket and cleans up the state of the class.
     * It is not considered good to use this function without
     * first trying to use QUIT.
     * @access public
     * @return void
     */
    public function Close() {
        $this->error = null; // so there is no confusion
        $this->helo_rply = null;
        if(!empty($this->smtp_conn)) {
            // close the connection and cleanup
            fclose($this->smtp_conn);
            $this->smtp_conn = 0;
        }
    }

    /***************************************************************
     *                        SMTP COMMANDS                       *
     *************************************************************/

    /**
     * Issues a data command and sends the msg_data to the server
     * finializing the mail transaction. $msg_data is the message
     * that is to be send with the headers. Each header needs to be
     * on a single line followed by a <CRLF> with the message headers
     * and the message body being seperated by and additional <CRLF>.
     *
     * Implements rfc 821: DATA <CRLF>
     *
     * SMTP CODE INTERMEDIATE: 354
     *     [data]
     *     <CRLF>.<CRLF>
     *     SMTP CODE SUCCESS: 250
     *     SMTP CODE FAILURE: 552,554,451,452
     * SMTP CODE FAILURE: 451,554
     * SMTP CODE ERROR  : 500,501,503,421
     * @access public
     * @return bool
     */
    public function Data($msg_data) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Data() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"DATA" . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 354) {
            $this->error =
            array("error" => "DATA command not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        /* the server is ready to accept data!
         * according to rfc 821 we should not send more than 1000
         * including the CRLF
         * characters on a single line so we will break the data up
         * into lines by \r and/or \n then if needed we will break
         * each of those into smaller lines to fit within the limit.
         * in addition we will be looking for lines that start with
         * a period '.' and append and additional period '.' to that
         * line. NOTE: this does not count towards are limit.
         */

        // normalize the line breaks so we know the explode works
        $msg_data = str_replace("\r\n","\n",$msg_data);
        $msg_data = str_replace("\r","\n",$msg_data);
        $lines = explode("\n",$msg_data);

        /* we need to find a good way to determine is headers are
         * in the msg_data or if it is a straight msg body
         * currently I am assuming rfc 822 definitions of msg headers
         * and if the first field of the first line (':' sperated)
         * does not contain a space then it _should_ be a header
         * and we can process all lines before a blank "" line as
         * headers.
         */
        $field = substr($lines[0],0,strpos($lines[0],":"));
        $in_headers = false;
        if(!empty($field) && !strstr($field," ")) {
            $in_headers = true;
        }

        $max_line_length = 998; // used below; set here for ease in change

        while(list(,$line) = @each($lines)) {
            $lines_out = null;
            if($line == "" && $in_headers) {
                $in_headers = false;
            }
            // ok we need to break this line up into several smaller lines
            while(strlen($line) > $max_line_length) {
                $pos = strrpos(substr($line,0,$max_line_length)," ");

                // Patch to fix DOS attack
                if(!$pos) {
                    $pos = $max_line_length - 1;
                }

                $lines_out[] = substr($line,0,$pos);
                $line = substr($line,$pos + 1);
                /* if we are processing headers we need to
                 * add a LWSP-char to the front of the new line
                 * rfc 822 on long msg headers
                 */
                if($in_headers) {
                    $line = "\t" . $line;
                }
            }
            $lines_out[] = $line;

            // now send the lines to the server
            while(list(,$line_out) = @each($lines_out)) {
                if(strlen($line_out) > 0)
                {
                    if(substr($line_out, 0, 1) == ".") {
                        $line_out = "." . $line_out;
                    }
                }
                fputs($this->smtp_conn,$line_out . $this->CRLF);
            }
        }

        // ok all the message data has been sent so lets get this
        // over with aleady
        fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "DATA not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return true;
    }

    /**
     * Expand takes the name and asks the server to list all the
     * people who are members of the _list_. Expand will return
     * back and array of the result or false if an error occurs.
     * Each value in the array returned has the format of:
     *     [ <full-name> <sp> ] <path>
     * The definition of <path> is defined in rfc 821
     *
     * Implements rfc 821: EXPN <SP> <string> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE FAILURE: 550
     * SMTP CODE ERROR  : 500,501,502,504,421
     * @access public
     * @return string array
     */
    public function Expand($name) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Expand() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "EXPN not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        // parse the reply and place in our array to return to user
        $entries = explode($this->CRLF,$rply);
        while(list(,$l) = @each($entries)) {
            $list[] = substr($l,4);
        }

        return $list;
    }

    /**
     * Sends the HELO command to the smtp server.
     * This makes sure that we and the server are in
     * the same known state.
     *
     * Implements from rfc 821: HELO <SP> <domain> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE ERROR  : 500, 501, 504, 421
     * @access public
     * @return bool
     */
    public function Hello($host="") {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Hello() without being connected");
            return false;
        }

        // if a hostname for the HELO was not specified determine
        //a suitable one to send
        if(empty($host)) {
            // we need to determine some sort of appopiate default
            // to send to the server
            $host = "localhost";
        }

        // Send extended hello first (RFC 2821)
        if(!$this->SendHello("EHLO", $host))
        {
            if(!$this->SendHello("HELO", $host))
                return false;
        }

        return true;
    }

    /**
     * Sends a HELO/EHLO command.
     * @access private
     * @return bool
     */
    public function SendHello($hello, $host) {
        fputs($this->smtp_conn, $hello . " " . $host . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER: " . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => $hello . " not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        $this->helo_rply = $rply;

        return true;
    }

    /**
     * Gets help information on the keyword specified. If the keyword
     * is not specified then returns generic help, ussually contianing
     * A list of keywords that help is available on. This function
     * returns the results back to the user. It is up to the user to
     * handle the returned data. If an error occurs then false is
     * returned with $this->error set appropiately.
     *
     * Implements rfc 821: HELP [ <SP> <string> ] <CRLF>
     *
     * SMTP CODE SUCCESS: 211,214
     * SMTP CODE ERROR  : 500,501,502,504,421
     * @access public
     * @return string
     */
    public function Help($keyword="") {
        $this->error = null; // to avoid confusion

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Help() without being connected");
            return false;
        }

        $extra = "";
        if(!empty($keyword)) {
            $extra = " " . $keyword;
        }

        fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 211 && $code != 214) {
            $this->error =
            array("error" => "HELP not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        return $rply;
    }

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command.
     *
     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,421
     * @access public
     * @return bool
     */
    public function Mail($from) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Mail() without being connected");
            return false;
        }

        $useVerp = ($this->do_verp ? "XVERP" : "");
        fputs($this->smtp_conn,"MAIL FROM:<" . $from . ">" . $useVerp . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "MAIL not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return true;
    }

    /**
     * Sends the command NOOP to the SMTP server.
     *
     * Implements from rfc 821: NOOP <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE ERROR  : 500, 421
     * @access public
     * @return bool
     */
    public function Noop() {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Noop() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"NOOP" . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "NOOP not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return true;
    }

    /**
     * Sends the quit command to the server and then closes the socket
     * if there is no error or the $close_on_error argument is true.
     *
     * Implements from rfc 821: QUIT <CRLF>
     *
     * SMTP CODE SUCCESS: 221
     * SMTP CODE ERROR  : 500
     * @access public
     * @return bool
     */
    public function Quit($close_on_error=true) {
        $this->error = null; // so there is no confusion

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Quit() without being connected");
            return false;
        }

        // send the quit command to the server
        fputs($this->smtp_conn,"quit" . $this->CRLF);

        // get any good-bye messages
        $byemsg = $this->get_lines();

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg;
        }

        $rval = true;
        $e = null;

        $code = substr($byemsg,0,3);
        if($code != 221) {
            // use e as a tmp var cause Close will overwrite $this->error
            $e = array("error" => "SMTP server rejected quit command",
                "smtp_code" => $code,
                "smtp_rply" => substr($byemsg,4));
            $rval = false;
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $e["error"] . ": " .
                    $byemsg . $this->CRLF;
            }
        }

        if(empty($e) || $close_on_error) {
            $this->Close();
        }

        return $rval;
    }

    /**
     * Sends the command RCPT to the SMTP server with the TO: argument of $to.
     * Returns true if the recipient was accepted false if it was rejected.
     *
     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250,251
     * SMTP CODE FAILURE: 550,551,552,553,450,451,452
     * SMTP CODE ERROR  : 500,501,503,421
     * @access public
     * @return bool
     */
    public function Recipient($to) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Recipient() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"RCPT TO:<" . $to . ">" . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250 && $code != 251) {
            $this->error =
            array("error" => "RCPT not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return true;
    }

    /**
     * Sends the RSET command to abort and transaction that is
     * currently in progress. Returns true if successful false
     * otherwise.
     *
     * Implements rfc 821: RSET <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE ERROR  : 500,501,504,421
     * @access public
     * @return bool
     */
    public function Reset() {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Reset() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"RSET" . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "RSET failed",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }

        return true;
    }

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command. This command
     * will send the message to the users terminal if they are logged
     * in.
     *
     * Implements rfc 821: SEND <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,502,421
     * @access public
     * @return bool
     */
    public function Send($from) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Send() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "SEND not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return true;
    }

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command. This command
     * will send the message to the users terminal if they are logged
     * in and send them an email.
     *
     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,502,421
     * @access public
     * @return bool
     */
    public function SendAndMail($from) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called SendAndMail() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "SAML not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return true;
    }

    /**
     * Starts a mail transaction from the email address specified in
     * $from. Returns true if successful or false otherwise. If True
     * the mail transaction is started and then one or more Recipient
     * commands may be called followed by a Data command. This command
     * will send the message to the users terminal if they are logged
     * in or mail it to them if they are not.
     *
     * Implements rfc 821: SOML <SP> FROM:<reverse-path> <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE SUCCESS: 552,451,452
     * SMTP CODE SUCCESS: 500,501,502,421
     * @access public
     * @return bool
     */
    public function SendOrMail($from) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called SendOrMail() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250) {
            $this->error =
            array("error" => "SOML not accepted from server",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return true;
    }

    /**
     * This is an optional command for SMTP that this class does not
     * support. This method is here to make the RFC821 Definition
     * complete for this class and __may__ be implimented in the future
     *
     * Implements from rfc 821: TURN <CRLF>
     *
     * SMTP CODE SUCCESS: 250
     * SMTP CODE FAILURE: 502
     * SMTP CODE ERROR  : 500, 503
     * @access public
     * @return bool
     */
    public function Turn() {
        $this->error = array("error" => "This method, TURN, of the SMTP ".
            "is not implemented");
        if($this->do_debug >= 1) {
            echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF;
        }
        return false;
    }

    /**
     * Verifies that the name is recognized by the server.
     * Returns false if the name could not be verified otherwise
     * the response from the server is returned.
     *
     * Implements rfc 821: VRFY <SP> <string> <CRLF>
     *
     * SMTP CODE SUCCESS: 250,251
     * SMTP CODE FAILURE: 550,551,553
     * SMTP CODE ERROR  : 500,501,502,421
     * @access public
     * @return int
     */
    public function Verify($name) {
        $this->error = null; // so no confusion is caused

        if(!$this->connected()) {
            $this->error = array(
                "error" => "Called Verify() without being connected");
            return false;
        }

        fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF);

        $rply = $this->get_lines();
        $code = substr($rply,0,3);

        if($this->do_debug >= 2) {
            echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply;
        }

        if($code != 250 && $code != 251) {
            $this->error =
            array("error" => "VRFY failed on name '$name'",
                "smtp_code" => $code,
                "smtp_msg" => substr($rply,4));
            if($this->do_debug >= 1) {
                echo "SMTP -> ERROR: " . $this->error["error"] .
                ": " . $rply . $this->CRLF;
            }
            return false;
        }
        return $rply;
    }

    /*******************************************************************
     *                       INTERNAL FUNCTIONS                       *
     ******************************************************************/

    /**
     * Read in as many lines as possible
     * either before eof or socket timeout occurs on the operation.
     * With SMTP we can tell if we have more lines to read if the
     * 4th character is '-' symbol. If it is a space then we don't
     * need to read anything else.
     * @access private
     * @return string
     */
    public function get_lines() {
        $data = "";
        while($str = @fgets($this->smtp_conn,515)) {
            if($this->do_debug >= 4) {
                echo "SMTP -> get_lines(): \$data was \"$data\"" .
                $this->CRLF;
                echo "SMTP -> get_lines(): \$str is \"$str\"" .
                $this->CRLF;
            }
            $data .= $str;
            if($this->do_debug >= 4) {
                echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF;
            }
            // if the 4th character is a space then we are done reading
            // so just break the loop
            if(substr($str,3,1) == " ") { break; }
        }
        return $data;
    }

}<?php
$DB_HOST   = 'localhost';
$DB_USER   = 'root';
$DB_PASSWD = 'root';
$DB_NAME   = 'ams_qa';

?>
<?php
$DB_HOST = 'localhost';
$DB_USER = 'root';
$DB_PASSWD = 'root';
$DB_NAME = 'nucleus_test';

 ?>
<?php 

namespace test\utils;

use com\linways\nucleus\core\utils\user\UserServiceUtils;
use com\linways\base\test\BaseTestCase;


class UserServiceUtilsTest extends BaseTestCase{

    public function testGetSalt(){
        $response = UserServiceUtils::getSalt();
        $this->assertNotEmpty($response);
        $this->assertInternalType('string',$response);
        $this->assertEquals(16, strlen($response));

        
        }




}<?php
require_once "../../vendor/autoload.php";

use com\linways\nucleus\core\ams_task_queue\AMSTaskQueue;

$RABBITMQ_HOST     = 'localhost';
$RABBITMQ_USER     = 'guest';
$RABBITMQ_PASSWORD = 'guest';

$taskQueue = new AMSTaskQueue();
$params    = ['className'             => 'test\scripts\testServiceClass', 'methodName'             => 'printHelloWorld', 'methodParams'             => ['gabriel', 'garcia', 'marquez']];
$conf      = ['DB_PROFESIONAL_CONFIG' => '/var/www/html/Professional/ams/db_prof_conf.php', 'AMS_CONFIG' => '/var/www/html/Professional/ams/conf.php', 'NUCLEUS_CONF' => '/var/www/html/Professional/ams/nucleus_conf.php'];
$taskQueue->enqueue('EXECUTE SERVICE', $params, $conf);




/**************************Service Test********************************/
// $params = ['className'             => 'com\linways\core\ams\professional\service\notification\NotificationService', 'methodName'             => 'getNotificationTypeIdFromNotificationTypeCode', 'methodParams'             => ['WORKFLOW_REQUEST']];
// $conf   = ['DB_PROFESIONAL_CONFIG' => '/var/www/html/Professional/ams/db_prof_conf.php', 'AMS_CONFIG' => '/var/www/html/Professional/ams/conf.php', 'NUCLEUS_CONF' => '/var/www/html/Professional/ams/nucleus_conf.php'];
// $taskQueue->enqueue('EXECUTE SERVICE', $params, $conf);





/************************Hook Test*****************************/
// $params = [];
// $params = ['hookName'              => 'wf_get_message_details_for_HOUR_SWAP', 'hookParams'              => 15];
// $conf   = ['DB_PROFESIONAL_CONFIG' => '/var/www/html/Professional/ams/db_prof_conf.php', 'AMS_CONFIG' => '/var/www/html/Professional/ams/conf.php', 'NUCLEUS_CONF' => '/var/www/html/Professional/ams/nucleus_conf.php'];
// $taskQueue->enqueue('EXECUTE HOOK', $params, $conf);
<?php

namespace test\scripts;

use com\linways\nucleus\core\service\BaseService;

class testServiceClass extends BaseService {

	private static $_instance = null;

	// /Condition 2 - Locked down the constructor
	private function __construct() {}

	// Prevent any oustide instantiation of this class

	// /Condition 3 - Prevent any object or instance of that class to be cloned
	private function __clone() {}

	// Prevent any copy of this object

	// /Condition 4 - Have a single globally accessible static method
	public static function getInstance() {
		if (!is_object(self::$_instance))
		// or if( is_null(self::$_instance) ) or if( self::$_instance == null )
		{
			self::$_instance = new self();
		}

		return self::$_instance;
	}

	public function printHelloWorld($fname, $mname, $lname) {
		echo "Welcome:$fname $mname $lname ";
		// return "Welcome to ooty.. nice to meet you!";
		// throw new TaskQueueException('could not print hello world', 101);
	}

}
<?php

$schedulerServiceInstance = SchedulerService::getInstance();
<?php 
namespace test\service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\service\PermissionService;

class PermissionServiceTest extends BaseTestCase {

     protected function setUp()
     {
         $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
         putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
         putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
     }

     protected function tearDown()
     {
         $this->clearDBTable('api_access_token_permission');
         $this->clearDBTable('api_access_token');
         $this->clearDBTable('api_organization_details');
         $this->clearDBTable('colleges');
     }

     public function prepare(){
         $apiAccessTokenServiceTest = new APIAccessTokenServiceTest();
         $result = $apiAccessTokenServiceTest->testSuccessForCreateAPIClientIdAndSecretId();
         return $result['accessId'];
     }
    
     public function testSuccessForaddPermissionToAccessId(){
        $APIAccessId = $this->prepare();
        $permissionCode = "view_students";
        $result = PermissionService::getInstance()->addPermissionToAccessId($APIAccessId, $permissionCode);
        $this->assertInternalType('integer', $result);
        return $result;
     }

    /**
     *@expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode com\linways\nucleus\core\exception\NucleusException::INVALID_PERMISSION_ID
     */
     public function testFailureForaddPermissionToAccessId(){
         $APIAccessId = $this->prepare();
         $permissionCode = "cancel_students";
         $res = PermissionService::getInstance()->addPermissionToAccessId($APIAccessId, $permissionCode);
         return $this->assertFalse($res);
     }
     
     public function testSuccessForAPIHasPermission(){
         $APIAccessId = $this->prepare();
         $permissionCode = "view_students";
         $result = PermissionService::getInstance()->addPermissionToAccessId($APIAccessId, $permissionCode);
         $res = PermissionService::getInstance()->APIHasPermission($APIAccessId, $permissionCode);
         $this->assertNotEmpty($res);
         $this->assertInternalType('string',$res);
     }
     
     public function testFailureForAPIHasPermission(){
         $APIAccessId = $this->prepare();
         $permissionCode = "edit_students";
         $result = PermissionService::getInstance()->addPermissionToAccessId($APIAccessId, 'view_students');
         $res = PermissionService::getInstance()->APIHasPermission($APIAccessId, $permissionCode);
         $this->assertEmpty($res);
     }

    /**
     *@expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode com\linways\nucleus\core\exception\NucleusException::INVALID_PERMISSION_ID
     */
     public function testFailureForAPIHasPermissionForNonExistingPermissionCode(){
         $APIAccessId = $this->prepare();
         $permissionCode = "delete_table";
         $result = PermissionService::getInstance()->addPermissionToAccessId($APIAccessId, 'view_students');
         $res = PermissionService::getInstance()->APIHasPermission($APIAccessId, $permissionCode);
     }
 }
?><?php
namespace test\service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\constants\Scope;
use com\linways\nucleus\core\dto\Addon;
use com\linways\nucleus\core\dto\AddonAttribute;
use com\linways\nucleus\core\dto\AddonAttributeValue;
use com\linways\nucleus\core\dto\AddonEnabled;
use com\linways\nucleus\core\service\AddonAttributeService;
use com\linways\nucleus\core\service\AddonService;

class AddonServiceTest extends BaseTestCase
{

    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
    }

    protected function tearDown()
    {

        $this->clearDBTable('colleges');
        $this->clearDBTable('addon_enabled');
        $this->clearDBTable('addon_attr_value');
        $this->clearDBTable('addon');
        $this->clearDBTable('addon_attr');
        $this->clearDBTable('addon_data_store');
    }

    public function getAddonObject()
    {
        $addon = null;
        $addonAttributes = [];

        $addon = new Addon();

        $addon->name = 'greythr';
        $addon->description = 'greythr interface';
        $addon->url = 'https://www.greythr.com/';
        $addon->packageName = "greythr";
        $addon->scope = "INSTITUTION";

        $addonAttribute = new AddonAttribute();
        $addonAttribute->type = "credential";
        $addonAttribute->name = "access_id";
        $addonAttribute->code = "accessId";
        $addonAttribute->required = "1";
        $addonAttributes[] = $addonAttribute;

        $addonAttribute = new AddonAttribute();
        $addonAttribute->type = "credential";
        $addonAttribute->name = "access_key";
        $addonAttribute->code = "accessKey";
        $addonAttribute->required = "1";
        $addonAttributes[] = $addonAttribute;

        $addon->addonAttribute = $addonAttributes;
        return $addon;
    }

    public function getAddonEnabledObject($addon){
        $addonEnabled = null;

        $addonEnabled = new AddonEnabled();
        $addonEnabled->addonId = $addon['addonId'];
        $addonEnabled->scopeId = "X123";

        $attributes = $addon['attributesId'];

        $addon_attr_value = new AddonAttributeValue();
        $addon_attr_value->addonAttributeId = $attributes[0];
        $addon_attr_value->value = 'FGHJKL465';
        $addonAttributes[] = $addon_attr_value;

        $addon_attr_value = new AddonAttributeValue();
        $addon_attr_value->addonAttributeId = $attributes[1];
        $addon_attr_value->value = "AQWERTTY78946";
        $addonAttributes[] = $addon_attr_value;

        $addonEnabled->addonAttributes = $addonAttributes;

        return $addonEnabled;
    }

    public function testSuccessForCreateAddon()
    {
        $addon = $this->getAddonObject();
        $result = AddonService::getInstance()->createAddon($addon);
        $this->assertNotEmpty($result);
        return $result;
    }

    public function testSuccessForActivateAddon()
    {
        $result = null;
        $addon = null;
        $addonObject =null;
        $addonEnabled = null;

        $addon = $this->testSuccessForCreateAddon();

        $addonEnabled = $this->getAddonEnabledObject($addon);

        $college = AddonTestHelpers::getInstance()->createCollege("PRO");

        $result = AddonService::getInstance()->activateAddon($addonEnabled, "PRO");

        $this->assertNotEmpty( $result);

        return $result;
    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode 1452
     */
    public function testFailureForActivateAddon()
    {
        mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
        $result = null;
        $addon = null;
        $addonObject =null;
        $addonEnabled = null;

        $addonObject = $this->getAddonObject();
        $addon = AddonService::getInstance()->createAddon($addonObject);

        $addon['addonId'] = 78;

        $addonEnabled = $this->getAddonEnabledObject($addon);

        $college = AddonTestHelpers::getInstance()->createCollege("PRO");

        $result = AddonService::getInstance()->activateAddon($addonEnabled, "PRO");

        return $result;

    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode com\linways\nucleus\core\exception\NucleusException::INVALID_COLLEGE_CODE
     */
    public function testFailureForActivateAddonWithInvalidCollegeCode()
    {
        $result = null;
        $addon = null;
        $addonObject =null;
        $addonEnabled = null;

        $addonObject = $this->getAddonObject();
        $addon = AddonService::getInstance()->createAddon($addonObject);

        $addon['addonId'] = '78A';

        $addonEnabled = $this->getAddonEnabledObject($addon);

        $college = AddonTestHelpers::getInstance()->createCollege("PRO");

        $result = AddonService::getInstance()->activateAddon($addonEnabled, "QRS");

    }

    public function testSuccessForUpdateAttributeValues()
    {

        $enabledAddon = null;
        $addonAttributesValues =[];
        $addonAttributeValue = NULL;

        $enabledAddon = $this->testSuccessForActivateAddon();
        $enabledAddonId = $enabledAddon['id'];
        $attributesValuesId = $enabledAddon['attributesValuesId'];
        $addonAttributeValue = new AddonAttributeValue();
        $addonAttributeValue->value = "QWERTYPOIU789";
        $addonAttributeValue->id = $attributesValuesId[0];

        $addonAttributesValues[] = $addonAttributeValue;

        $addonAttributeValue = new AddonAttributeValue();
        $addonAttributeValue->value = "REDPET";
        $addonAttributeValue->id = $attributesValuesId[1];

        $addonAttributesValues[] = $addonAttributeValue;
        $result = AddonAttributeService::getInstance()->updateAddonAttributeValue($addonAttributesValues,$enabledAddonId);
        
        return $this->assertTrue($result);
    }

   
    public function testSuccessForDisableAddon()
    {
        $enabledAddon = null;

        $enabledAddon = $this->testSuccessForActivateAddon();

        $result = AddonService::getInstance()->disableAddon($enabledAddon['id']);
        return $this->assertTrue($result);
    }

    public function testSuccessForGetEnabledAddonsService()
    {
        $collegeCode = "PRO";
        $scopeName = Scope::INSTITUTION;

        $enabledAddon = $this->testSuccessForActivateAddon();

        $res = AddonService::getInstance()->getAddonsEnabled($collegeCode, $scopeName);
        return $this->assertInternalType('array', $res);
    }

    public function testFailureForGetEnabledAddonsService()
    {
        $collegeCode = "QRS";
        $scopeName = Scope::INSTITUTION;

        $enabledAddon = $this->testSuccessForActivateAddon();
        $res = AddonService::getInstance()->getAddonsEnabled($collegeCode, $scopeName);

        return $this->assertCount(0, $res);
    }

    public function testSuccessForGetEnabledAddonByPackageNameAndCollegeCode(){
        $collegeCode = "PRO";
        $packageName = 'greythr';

        $enabledAddon = $this->testSuccessForActivateAddon();
        $res = AddonService::getInstance()->getEnabledAddonByPackageNameAndCollegeCode($packageName,$collegeCode);

        return $this->assertInternalType('array', $res);

    }
    public function testFailureForGetEnabledAddonByPackageNameAndCollegeCode(){
        $collegeCode = "PRO";
        $packageName = 'awesome';

        $enabledAddon = $this->testSuccessForActivateAddon();
        $res = AddonService::getInstance()->getEnabledAddonByPackageNameAndCollegeCode($packageName,$collegeCode);

        return $this->assertCount(0, $res);

    }

    public function testSuccessForGetAllAddonWithEnabledFlag(){
        $collegeCode = "PRO";

        $enabledAddon = $this->testSuccessForActivateAddon();
        $result = AddonService::getInstance()->getAllAddonWithEnabledFlag($collegeCode);

        $this->assertInternalType('array', $result);
        $this->assertNotEmpty($result);

    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode com\linways\nucleus\core\exception\NucleusException::INVALID_COLLEGE_CODE
     */
    public function testFailureForGetAllAddonWithEnabledFlag(){
        $collegeCode = "QRS";

        $enabledAddon = $this->testSuccessForActivateAddon();
        $result = AddonService::getInstance()->getAllAddonWithEnabledFlag($collegeCode);

    }

    public function testSuccessForGetAddonByIdAndAttributeType(){
        $addonId=null;
        $attrType = "credential";

        $addon = $this->testSuccessForCreateAddon();
        $addonId = $addon['addonId'];
        $addonEnabled = $this->getAddonEnabledObject($addon);

        $college = AddonTestHelpers::getInstance()->createCollege("PRO");

        $result = AddonService::getInstance()->activateAddon($addonEnabled, "PRO");

        $addonList =AddonService::getInstance()->getAddonByIdAndAttributeType($addonId,$attrType);

        $this->assertInternalType('array', $result);
        $this->assertNotEmpty($result);
    }

    public function testFailureForGetAddonByIdAndAttributeType(){
        $addonId=null;
        $attrType = "credential";

        $addon = $this->testSuccessForCreateAddon();
        $addonId = '12A';
        $addonEnabled = $this->getAddonEnabledObject($addon);

        $college = AddonTestHelpers::getInstance()->createCollege("PRO");

        $result = AddonService::getInstance()->activateAddon($addonEnabled, "PRO");

        $addonList =AddonService::getInstance()->getAddonByIdAndAttributeType($addonId,$attrType);

        $this->assertInternalType('array', $addonList);
        $this->assertEmpty($addonList);
    }

    public function testSuccessForGetAllAddons(){
        $addon = $this->testSuccessForActivateAddon();
        $addonList = AddonService::getInstance()->getAllAddons();
        $this->assertInternalType('array', $addonList);
        $this->assertNotEmpty($addonList);
    }

    public function testFailureForGetAllAddons(){
        $addonList = AddonService::getInstance()->getAllAddons();
        $this->assertInternalType('array', $addonList);
        $this->assertEmpty($addonList);
    }

    public function testSuccessForgetEnabledAddonWithAddonDataStoreByPackageNameAndCollegeCode(){
        $addonDataStoreServiceTest = new AddonDataStoreServiceTest();
        $addonDataStore = $addonDataStoreServiceTest->testSuccessForPutData();
        $packageName = 'greythr';
        $collegeCode = 'PRO';
        $enabledAddon  = AddonService::getInstance()->getEnabledAddonWithAddonDataStoreByPackageNameAndCollegeCode($packageName,$collegeCode);
        $this->assertInternalType('array',$enabledAddon);
        $this->assertNotEmpty($enabledAddon);

    }
}

<?php 
namespace test\service\user;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\service\user\UserService;
use com\linways\nucleus\core\dto\user\User;
use com\linways\nucleus\core\exception\NucleusException;

class UserServiceTest extends BaseTestCase{

     protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
    }

    protected function tearDown()
    {
        // $this->clearDBTable('nucleus_user');
    }

    public function testInstanceForServiceCanBeAquired(){
        $userService = UserService::getInstance();

        $this->assertInstanceOf('com\\linways\\nucleus\\core\\service\\user\\UserService',$userService, "Cannot Get Instance For UserService");
        return $userService;
    }

 public static function newUserObject(){
    $user = new User();
    $user->name = 'chacko';
    $user->email = 'chako@mathematics.com';
    $user->username = 'bababayalla';
    // $user->password =  'hmmm..pokko';
    $user->password =  'linways';
    
    $response[] = $user;

    $firstUser = new User();
    $firstUser->name = 'gadheyan';
    $firstUser->email = 'gadheyan@linways.com';
    $firstUser->username = 'gadheyan';
    // $firstUser->password =  'hai';
    $firstUser->password =  'linways';
    
    $response[] = $firstUser;
    
    $secondUser = new User();
    $secondUser->name = 'nambiar';
    $secondUser->email = 'nambiar@kingofsmuggling.com';
    $secondUser->username = 'narcos';
    // $secondUser->password =  'thuglife';
    $secondUser->password =  'linways';

    $response[] = $secondUser;
    
    return $response;


 }
/**
 * @depends testInstanceForServiceCanBeAquired
 *  @group ignore
 */
    public function testCreateNewUserForSuccess(UserService $userService){
        $response = $userService->createNewUser(self::newUserObject()[1]);
        $this->assertNotEmpty( $response);
        $this->assertInternalType('integer', $response);
    }

// /**
//  * @depends testInstanceForServiceCanBeAquired
//  * @expectedExceptionCode DUPLICATE_ENTRY
//  * @expectedException com\linways\nucleus\core\exception\NucleusException
//  */
//     public function testCreateNewUserForDuplicateEntry(UserService $userService){
//          $userService->createNewUser(self::newUserObject()[1]);
//          $userService->createNewUser(self::newUserObject()[1]);
   
//     }

/**
 * @depends testInstanceForServiceCanBeAquired
 * 
 */
public function testGetAllPermissions(UserService $userService){
    $response = $userService->getAllPermissions('1');
    print_r($response);
    $this->assertInternalType('array', $response);
    $this->assertNotEmpty($response);
}
    
/**
 * @depends testInstanceForServiceCanBeAquired
 * 
 */
     public function testuserHasPermissionForSuccess(UserService $userService)
     {
        $response = $userService->userHasPermission('1', ['VIEW_DASHBOARD']);
            print_r($response);
        $this->assertTrue($response);
         
     }
        /**
        * 
        * @depends testInstanceForServiceCanBeAquired
        * 
        */
     public function testuserHasPermissionForFailure(UserService $userService)
     {
        $response = $userService->checkUserHasPermission('1', ['VIEW_NOTHING']);
            print_r($response);
        $this->assertFalse($response);
         
     }
     
     
}
<?php
namespace service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\service\SingleSignonService;
use com\linways\nucleus\core\dto\SingleSignonLog;

/**
 * @author gadheyan
 */
class SingleSignonServiceTest extends BaseTestCase
{
    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
    }

    protected function tearDown()
    {
        // $this->clearDBTable('colleges');
    }

    public function testInstanceForServiceCanBeAquired(){
        $singleSignonService = SingleSignonService::getInstance();

        $this->assertInstanceOf('com\\linways\\nucleus\\core\\service\\SingleSignonService',$singleSignonService, "Cannot Get Instance For SingleSignonService");
        return $singleSignonService;
    }

  /**
   *
   * @depends testInstanceForServiceCanBeAquired
   * @author gadheyan
   */
    public function testGenerateToken(SingleSignonService $singleSignonService){
        $response = $singleSignonService->generateToken();
        // $this->assertInstanceOf('\stdClass',$response, "'response' is not an instance of stdClass");
        // $this->assertObjectHasAttribute('id', $response);
        // print_r($response);
        // die();
        $this->assertNotEmpty($response, "'token' is empty");
        $this->assertInternalType('string',$response, "'token' is not string");

        return array($singleSignonService, $response);
    }

  /**
   *
   * @depends testGenerateToken
   * @author gadheyan
   */
    public function testValidateToken($dependency){
        $singleSignonService = $dependency[0];
        $token = $dependency[1];
        // print_r($dependency);die();
        $response = $singleSignonService->validateToken($token);
        // print_r($response);die();
        $this->assertInternalType('array',$response, "'response' is not an array");
        $this->assertNotEmpty($response, "'response' is empty");
        $this->assertArrayHasKey('email', $response);
        $this->assertArrayHasKey('iss', $response);
        $this->assertArrayHasKey('exp', $response);
        $this->assertArrayHasKey('iat', $response);


        // print_r($response);

    }
    /**
     *
     * @depends testInstanceForServiceCanBeAquired
     * @author gadheyan
     */
    public function testLogSingleSignonDetails(SingleSignonService $singleSignonService){
        $singleSignonLog = new SingleSignonLog();
        $singleSignonLog->userId = '0';
        $singleSignonLog->collegeCode = 'PRO';
        $singleSignonLog->loggedInAs = 'ADMIN';
 
        $response = $singleSignonService->logSingleSignonDetails($singleSignonLog);
        $this->assertNotEmpty($response,'Response is empty');
        $this->assertInternalType('integer', $response, 'Returned id is not integer!');
    }
}
<?php
namespace service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\service\ConfigurationService;

/**
 * @author gadheyan
 */
class ConfigurationServiceTest extends BaseTestCase
{
    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
    }

    protected function tearDown()
    {
        // $this->clearDBTable('colleges');
    }

    public function testInstanceForServiceCanBeAquired(){
        $configurationService = ConfigurationService::getInstance();

        $this->assertInstanceOf('com\\linways\\nucleus\\core\\service\\ConfigurationService',$configurationService, "Cannot Get Instance For ConfigurationService");
        return $configurationService;
    }

  /**
   *
   * @depends testInstanceForServiceCanBeAquired
   * @author gadheyan
   */
    public function testGetMultipleConfigurations(ConfigurationService $configurationService){
        $response = $configurationService->getMultipleConfigurations(array('email','username','password'));
        $this->assertInstanceOf('\stdClass',$response, "'response' is not an instance of stdClass");
        // $this->assertObjectHasAttribute('id', $response);
    }
}
<?php

namespace test\service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\dto\ApiOrganizationDetails;
use com\linways\nucleus\core\service\APIAccessTokenService;


class APIAccessTokenServiceTest extends BaseTestCase
{

    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
    }

    protected function tearDown()
    {

        $this->clearDBTable('api_access_token');
        $this->clearDBTable('api_organization_details');
        $this->clearDBTable('colleges');
    }

    public function getOrganizationObject()
    {
        $organization = new ApiOrganizationDetails();
        $organization->name = 'Linways Technologies Pvt. Ltd.';
        $organization->address = 'Khafji Tower, 132/323 , asdas dad,  ad asd 670234';
        $organization->email = 'test@email.com';
        $organization->contactNo = '9873468936';
        return $organization;
    }

    public function testSuccessForCreateOrganization()
    {
        $organization = $this->getOrganizationObject();
        $result = APIAccessTokenService::getInstance()->createOrganization($organization);
        $this->assertInternalType('integer',$result);
        $this->assertNotEmpty($result);
        return $result;
    }

    public function testSuccessForCreateAPIClientIdAndSecretId()
    {
        $orgId = $this->testSuccessForCreateOrganization();
        $college = AddonTestHelpers::getInstance()->createCollege("PRO");
        $result = APIAccessTokenService::getInstance()->createAPIClientIdAndSecret($orgId, $college);
        $this->assertInternalType('array',$result);
        $this->assertNotEmpty($result);
        return $result;
    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode 1054
     */
    public function testFailureForCreateAPIClientIdAndSecretIdByInvalidCollegeId()
    {
        $orgId = $this->testSuccessForCreateOrganization();
        $college = AddonTestHelpers::getInstance()->createCollege("PRO");
        $result = APIAccessTokenService::getInstance()->createAPIClientIdAndSecret($orgId, '45EA');
        $this->assertInternalType('array',$result);
        $this->assertNotEmpty($result);
    }

    public function testSuccessForGetAPIAccessToken()
    {
        $orgId = $this->testSuccessForCreateOrganization();
        $college = AddonTestHelpers::getInstance()->createCollege("PRO");
        $result = APIAccessTokenService::getInstance()->createAPIClientIdAndSecret($orgId, $college);
        $accessToken = APIAccessTokenService::getInstance()->getAPIAccessToken($result['accessId']);
        return $this->assertNotNull($accessToken);
    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode 8
     */
    public function testFailureForGetAPIAccessTokenByInvalidAccessId()
    {
        $orgId = $this->testSuccessForCreateOrganization();
        $college = AddonTestHelpers::getInstance()->createCollege("PRO");
        $result = APIAccessTokenService::getInstance()->createAPIClientIdAndSecret($orgId, $college);
        $accessToken = APIAccessTokenService::getInstance()->getAPIAccessToken("AWER132");

    }
}<?php
/**
 * Created by PhpStorm.
 * User: jithin
 * Date: 30/10/17
 * Time: 2:33 PM
 */

namespace test\service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\dto\AddonDataStore;
use com\linways\nucleus\core\service\AddonDataStoreSevice;

class AddonDataStoreServiceTest extends BaseTestCase
{
    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
    }

    protected function tearDown()
    {

        $this->clearDBTable('colleges');
        $this->clearDBTable('addon_enabled');
        $this->clearDBTable('addon_attr_value');
        $this->clearDBTable('addon');
        $this->clearDBTable('addon_attr');
        $this->clearDBTable('addon_data_store');

    }
    public function getData(){
        $addonServiceTest  = new AddonServiceTest();
        $enabledAddon = $addonServiceTest->testSuccessForActivateAddon();
        $addonDataStore = new AddonDataStore();
        $addonDataStore->enabledAddonId = $enabledAddon['id'];
        $addonDataStore->key = "Access Key";
        $addonDataStore->value = "asdsadaqweqasdas";
        return $addonDataStore;
    }
    public function testSuccessForPutData(){
        $addonDataStore = $this->getData();
        $result = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $this->assertInternalType('int', $result);
        $this->assertNotEmpty( $result);
        return $result;

    }

    public function testSuccessForGetData(){
        $addonDataStore = $this->getData();
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->getData($addonDataStore->enabledAddonId,$addonDataStore->key);
        $this->assertInternalType('array',$result);
        $this->assertNotEmpty( $result);
    }


    public function testFailureForGetData(){
        $addonDataStore = $this->getData();
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->getData("2AW",$addonDataStore->key);
        $this->assertInternalType('array',$result);
        $this->assertEmpty( $result);
    }

    public function testFailureForGetDataWithInvalidKey(){
        $addonDataStore = $this->getData();
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->getData($addonDataStore->enabledAddonId,"clientId");
        $this->assertInternalType('array',$result);
        $this->assertEmpty( $result);
    }

    public function testSuccessForDeleteData(){
        $addonDataStore = $this->getData();
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->deleteData($addonDataStore->enabledAddonId,$addonDataStore->key);
        $this->assertTrue($result);
    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode com\linways\nucleus\core\exception\NucleusException::INVALID_KEY
     */
    public function testFailureForDeleteDataByEmptyKey(){
        $addonDataStore = $this->getData();
        $key ="";
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->deleteData($addonDataStore->enabledAddonId,$key);
    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode com\linways\nucleus\core\exception\NucleusException::INVALID_ENABLED_ADDON_ID
     */
    public function testFailureForDeleteDataByEmptyEnabledAddonId(){
        $addonDataStore = $this->getData();
        $id ="";
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->deleteData($id,$addonDataStore->key);
    }

    public function testSuccessForDeleteAll(){
        $addonDataStore = $this->getData();
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->deleteAll($addonDataStore->enabledAddonId);
        $this->assertTrue($result);
    }

    /**
     * @expectedException com\linways\nucleus\core\exception\NucleusException
     * @expectedExceptionCode com\linways\nucleus\core\exception\NucleusException::INVALID_ENABLED_ADDON_ID
     */
    public function testFailureForDeleteAllByEmptyEnabledAddonId(){
        $addonDataStore = $this->getData();
        $id ="";
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $result = AddonDataStoreSevice::getInstance()->deleteAll($id);
    }

    public function testSuccessForUpdateData(){
        $addonDataStore = $this->getData();
        $dataStore = AddonDataStoreSevice::getInstance()->putData($addonDataStore);
        $addonDataStore->id = $dataStore;
        $addonDataStore->value = "ABCDEFGH";
        $result = AddonDataStoreSevice::getInstance()->updateData($addonDataStore);
        $this->assertTrue($result);
    }

}
<?php
namespace test\service;

use com\linways\nucleus\core\dto\College;
use com\linways\nucleus\core\service\CollegeService;
use com\linways\nucleus\exception\NucleusException;

class AddonTestHelpers
{
    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {}

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {}

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (! is_object(self::$_instance))
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
                self::$_instance = new self();

                return self::$_instance;
    }


    public function createCollege($collegeCode){
        $college =null;
        $result=null;

        $college = new College();
        $college->code = 'PRO';
        $college->name = 'Professional';
        $college->dbUsername = 'root';
        $college->dbPassword = 'root';
        $college->dbHost = 'localhost';
        $result = CollegeService::getInstance()->createCollege($college);

        return $result;
    }
}
<?php

namespace test\service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\dto\College;
use com\linways\nucleus\core\service\CollegeService;

class CollegeServiceTest extends BaseTestCase
{

    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
    }

    protected function tearDown()
    {
        $this->clearDBTable('colleges');
        $this->clearDBTable('clients');
    }

    public function getCollege()
    {
        $college = new College();
        $client = new ClientServiceTest();
        $college->code = 'PRO';
        $college->name = 'Linways';
        $college->dbUsername = 'root';
        $college->dbPassword = 'root';
        $college->dbHost = 'localhost';
        $college->clientId = $client->testCreateClient();
        return $college;
    }

    public function testCreateCollege()
    {
        $data = array();
        $college = $this->getCollege();
        $data['clientId'] = $college->clientId;
        $result = CollegeService::getInstance()->createCollege($college);
        $data['collegeId'] = $result;
        $this->assertInternalType('integer', $result);
        return $data;
    }

    public function testSuccessForgetAllColleges()
    {
        $id = $this->testCreateCollege();
        $college = $this->getCollege();
        $result = CollegeService::getInstance()->getAllColleges();
        $this->assertEquals(1, count($result));
        $this->assertEquals($college->dbUsername, $result[0]->dbUsername);
    }

    public function testgetBillingColleges()
    {
        $this->markTestIncomplete('This test has not been implemented yet.
      since addling client id  to college is not implemented');
    }


    public function testSuccessForGetCountOfCollegesByClientId()
    {
        $collegeDetails = $this->testCreateCollege();
        $result = CollegeService::getInstance()->getCountOfCollegesByClientId($collegeDetails['clientId']);
        $this->assertInternalType('integer', $result);
        $this->assertEquals('1', $result);
    }

    public function testFailureForGetCountOfCollegesByClientId()
    {
        $collegeDetails = $this->testCreateCollege();
        $result = CollegeService::getInstance()->getCountOfCollegesByClientId(245);
        $this->assertInternalType('integer', $result);
        $this->assertEquals('0', $result);
    }

    // /**
    //  * @expectedException \com\linways\nucleus\core\exception\NucleusException
    //  * @expectedExceptionCode \com\linways\nucleus\core\exception\NucleusException::CLIENT_ID_REQUIRED
    //  */
    // public function testFailureByEmptyIdForGetCountOfCollegesByClientId()
    // {
    //     $collegeDetails = $this->testCreateCollege();
    //     $clientId = '';
    //     $result = CollegeService::getInstance()->getCountOfCollegesByClientId($clientId);
    // }

    public function testSuccessForDeleteCollege()
    {
        $collegeDetails = $this->testCreateCollege();
        $college = new College();
        $college->id = $collegeDetails['collegeId'];
        $result = CollegeService::getInstance()->deleteCollege($college);
        $this->assertInternaltype('boolean', $result);
        $this->assertEquals(true, $result);
    }

    public function testSuccessGetCollgeByCode()
    {
      $collegeDetails = $this->testCreateCollege();
      $result = CollegeService::getInstance()->getCollegeByCollegeCode('PRO');
      $this->assertEquals('Linways', $result->name);
    }
}
<?php

namespace service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\dto\Expense;
use com\linways\nucleus\core\service\ExpenseService;
use test\service\ExpenseCategoryServiceTest;

class ExpenseServiceTest extends BaseTestCase
{

    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
        $this->clearDBTable('expenses');

    }

    protected function tearDown()
    {
        $this->clearDBTable('expense_category');
    }

    public function getExpense()
    {
        $expenseCategory = new ExpenseCategoryServiceTest();
        $expense = new Expense();
        $expense->item = 'Linways';
        $expense->amount = '232';
        $expense->date = date('Y-m-d H:i:s');
        $expense->expenseCategoryId = $expenseCategory->testCreateExpenseCategory();
        return $expense;
    }

    public function testCreateExpense()
    {
        $expense = $this->getExpense();
        $result = ExpenseService::getInstance()->createExpense($expense);
        $this->assertInternalType('integer', $result);
    }

    public function testUpsertInsert()
    {
        $expense = $this->getExpense();
        $result = ExpenseService::getInstance()->upsertExpense($expense);
        $this->assertInternalType('integer', $result);
    }

    public function testUpsertUpdate()
    {
        $expense = $this->getExpense();
        $id = ExpenseService::getInstance()->createExpense($expense);;
        $expense1 = $this->getExpense();
        $expense1->id = $id;
        $expense1->item = "Edited Name";
        $result = ExpenseService::getInstance()->upsertExpense($expense1);
        $this->assertDatabaseHas('expenses', [
            "item" => $expense1->item
        ]);
    }

    public function testDeleteExpense()
    {
        $expense = $this->getExpense();
        $result = ExpenseService::getInstance()->createExpense($expense);
        $this->assertInternalType('integer', $result);
        $res = ExpenseService::getInstance()->deleteExpense($result);
        $this->assertInternalType('null', $res);
    }

    public function testGetTotalExpenseByCategoryAndMonth()
    {
        $this->markTestIncomplete('This is not implemented');
    }

    public function testAverageExpenseForSingleTenant()
    {
        $this->markTestIncomplete('This is not implemented');
    }

    public function testGetTotalIncomeFromBillingCustomers()
    {
        $this->markTestIncomplete('This is not implemented');
    }

    public function testSuccessForGetAllExpensesOfSingleMonth()
    {
        $this->testCreateExpense();
        $result = ExpenseService::getInstance()->getAllExpensesOfSingleMonth(2018, 01);
        $this->assertInternalType('array', $result);
        $this->assertNotEmpty($result);
    }

    public function testFailureForGetAllExpensesOfSingleMonth()
    {
        $this->testCreateExpense();
        $result = ExpenseService::getInstance()->getAllExpensesOfSingleMonth(2014, 12);
        $this->assertInternalType('array', $result);
        $this->assertEmpty($result);
    }

}
<?php
/**
 * Created by PhpStorm.
 * User: jithin
 * Date: 4/1/18
 * Time: 11:25 AM
 */

namespace test\service;


use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\dto\ExpenseCategory;
use com\linways\nucleus\core\service\ExpenseCategoryService;

class ExpenseCategoryServiceTest extends BaseTestCase
{
    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
        $this->clearDBTable('expenses');

    }

    protected function tearDown()
    {
        $this->clearDBTable('expense_category');
    }

    public function getExpenseCategory()
    {
        $expenseCategory = new ExpenseCategory();
        $expenseCategory->name = 'CHT';
        return $expenseCategory;
    }

    public function testCreateExpenseCategory()
    {
        $expenseCategory = $this->getExpenseCategory();
        $result = ExpenseCategoryService::getInstance()->createExpenseCategory($expenseCategory);
        $this->assertInternalType('integer', $result);
        $this->assertEquals('1', $result);
        return $result;
    }
}<?php

namespace test\service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\service\SchedulerService;
use Cron\CronExpression;

class SchedulerServiceTest extends BaseTestCase {
	protected function setUp() {
		$nucleusTestDBConfPath = __DIR__ .'/../db_conf/nucleus_db_conf.php';
		putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
		putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
	}

	protected function tearDown() {
		// $this->clearDBTable('scheduled_task');
	}

	public function getScheduleArgs() {
		$args         = new \stdClass();
		$args->method = 'SomeClass::SomeMethod';
		$args->params = [
			'param1' => 'value1',
			'param2' => 'value2'
		];
		$args->frequency = '0 4 * */1 *';
		$args->code      = 'UNIQUE_CODE';
		date_default_timezone_set('Asia/Kolkata');
		$startDate             = time();
		$args->nextExecutionAt = date('Y-m-d H:i:s', strtotime('+1 hour', $startDate));
		return $args;
	}

	public function testSuccessfulSchedule() {
		// $this->assertFalse(True);
		$schedule = $this->getScheduleArgs();
		$id       = SchedulerService::getInstance()->addTask($schedule);
		$this->assertInternalType('integer', $id);
		return $id;
	}

	public function testSuccessfulScheduleWithFrequencyOnly() {
		$schedule                  = $this->getScheduleArgs();
		$schedule->code            = null;
		$schedule->nextExecutionAt = null;
		$id                        = SchedulerService::getInstance()->addTask($schedule);
		return $this->assertInternalType('integer', $id);
	}
	public function testSuccessfulScheduleWithNextExecutionAtOnly() {
		$schedule            = $this->getScheduleArgs();
		$schedule->frequency = null;
		$schedule->code      = null;
		$id                  = SchedulerService::getInstance()->addTask($schedule);
		return $this->assertInternalType('integer', $id);
	}
	/**
	 * @expectedException InvalidArgumentException
	 */
	public function testScheduleFailureWithoutFrequencyAndNextExecutionAt() {
		$schedule                  = $this->getScheduleArgs();
		$schedule->frequency       = null;
		$schedule->code            = null;
		$schedule->nextExecutionAt = null;
		$id                        = SchedulerService::getInstance()->addTask($schedule);
	}

	/**
	 * @expectedException InvalidArgumentException
	 */
	public function testScheduleFailureWithoutmethod() {
		$schedule         = $this->getScheduleArgs();
		$schedule->method = null;
		$id               = SchedulerService::getInstance()->addTask($schedule);
	}

	/**
	 * @expectedException mysqli_sql_exception
	 * @expectedExceptionCode 1062
	 */
	public function testShouldFailOnNonUniqueCode() {
		mysqli_report(MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT);
		$schedule = $this->getScheduleArgs();
		SchedulerService::getInstance()->addTask($schedule);
		$schedule = $this->getScheduleArgs();
		SchedulerService::getInstance()->addTask($schedule);
	}

	public function testScheduleDeletionByIdSuccess() {
		$id  = $this->testSuccessfulSchedule();
		$res = SchedulerService::getInstance()->deleteScheduledTaskById($id);
		$this->assertDatabaseHasNot('scheduled_task', [
				'id' => $id
			]);
	}

	/**
	 * @expectedException InvalidArgumentException
	 */
	public function testScheduleDeletionFailureWithoutId() {
		SchedulerService::getInstance()->deleteScheduledTaskById(null);
	}

	public function testScheduleDeletionByCodeSuccess() {
		$schedule = $this->getScheduleArgs();
		$id       = SchedulerService::getInstance()->addTask(clone($schedule));
		$res      = SchedulerService::getInstance()->deleteScheduledTaskByCode($schedule->code);
		$this->assertDatabaseHasNot('scheduled_task', [
				'code' => $schedule->code
			]);
	}

	/**
	 * @expectedException InvalidArgumentException
	 */
	public function testScheduleDeletionFailureWithoutCode() {
		SchedulerService::getInstance()->deleteScheduledTaskByCode(null);
	}

	public function testGetScheduleById() {
		$id       = $this->testSuccessfulSchedule();
		$schedule = $this->getScheduleArgs();
		$result   = SchedulerService::getInstance()->getScheduledTaskById($id);
		$this->assertEquals($schedule->code, $result->code);
	}

	public function testCalculateAndUpdateNextExecutionAtSuccess() {
		$id       = $this->testSuccessfulSchedule();
		$schedule = SchedulerService::getInstance()->getScheduledTaskById($id);
		$cronExp  = CronExpression::factory($schedule->frequency);
		SchedulerService::getInstance()->updateNextExecution($schedule);
		$this->assertDatabaseHas('scheduled_task', [
				'id'                => $schedule->id,
				'next_execution_at' => $cronExp->getNextRunDate()->format('Y-m-d H:i:s'),
				'last_executed_at'  => $schedule->nextExecutionAt
			]);
	}

	public function testGetDueTasks() {
		$this->testSuccessfulSchedule();// next exec time at +1 hr (NOT DUE)
		$schedule                  = $this->getScheduleArgs();
		$schedule->code            = null;
		$schedule->nextExecutionAt = date('Y-m-d H:i:s');// next exec now (DUE)
		SchedulerService::getInstance()->addTask($schedule);
		$this->testSuccessfulScheduleWithFrequencyOnly();// null next exec at (NOT DUE)
		$res = SchedulerService::getInstance()->getDueTasks();
		$this->assertEquals(1, sizeof($res));
	}
}
<?php

namespace test\service;

use com\linways\base\test\BaseTestCase;
use com\linways\nucleus\core\dto\Client;
use com\linways\nucleus\core\service\ClientService;

class ClientServiceTest extends BaseTestCase
{

    protected function setUp()
    {
        $nucleusTestDBConfPath = __DIR__ . '/../db_conf/nucleus_db_conf.php';
        putenv("TEST_DB_CONFIG=$nucleusTestDBConfPath");
        putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");
        $this->clearDBTable('clients');

    }

    protected function tearDown()
    {
        $this->clearDBTable('clients');

    }

    public function getClient()
    {
        $client = new Client();
        $client->name = 'Linways';
        $client->address = 'Linways technologies pvt. ltd.';
        $client->totalAnnualIncome = 125000;
        $client->serviceStartDate = date('Y-m-d H:i:s');
        $client->billingStartDate = date('Y-m-d H:i:s');
        return $client;
    }

    public function testCreateClient()
    {
        $client = $this->getClient();
        $result = ClientService::getInstance()->createClient($client);
        $this->assertInternalType('integer', $result);
        return $result;
    }

    public function testgetAllClients()
    {
        $id = $this->testCreateClient();
        $client = $this->getClient();
        $result = ClientService::getInstance()->getAllClients();
        $this->assertEquals(1, count($result));
        $this->assertEquals($client->name, $result[0]->name);
    }

    public function testUpsertInsert()
    {
        $client = $this->getClient();
        $result = ClientService::getInstance()->upsertClient($client);
        $this->assertInternalType('integer', $result);
    }

    public function testUpsertUpdate()
    {
        $client = $this->getClient();
        $id = ClientService::getInstance()->createClient($client);;
        $client1 = $this->getClient();
        $client1->id = $id;
        $client1->name = "Edited Name";
        $result = ClientService::getInstance()->upsertClient($client1);
        $this->assertDatabaseHas('clients', [
            "name" => $client1->name
        ]);
    }

    public function testDeleteClient()
    {
        $client = $this->getClient();
        $result = ClientService::getInstance()->createClient($client);
        $this->assertInternalType('integer', $result);
        $res = ClientService::getInstance()->deleteClient($result);
        $this->assertInternalType('null', $res);
    }


}
<?php


use Phinx\Migration\AbstractMigration;

class ExpenseCategoryAuditFields extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("
        ALTER TABLE `expense_category` 
        ADD COLUMN `created_by` INT NOT NULL AFTER `name`,
        ADD COLUMN `created_at` DATETIME NOT NULL AFTER `created_by`,
        ADD COLUMN `updated_by` INT NOT NULL AFTER `created_at`,
        ADD COLUMN `updated_at` DATETIME NOT NULL AFTER `updated_by`;

        ");

    }
}
<?php


use Phinx\Migration\AbstractMigration;

class CollegeDbAuditFields extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("ALTER TABLE `colleges` 
            ADD COLUMN `created_by` INT(11) NULL AFTER `client_id`,
            ADD COLUMN `created_at` DATETIME NULL AFTER `created_by`,
            ADD COLUMN `updated_by` INT(11) NULL AFTER `created_at`,
            ADD COLUMN `updated_at` DATETIME NULL AFTER `updated_by`;
        ");
    }
}
<?php


use Phinx\Migration\AbstractMigration;

class SchedulerMigrations extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("CREATE TABLE `scheduled_task` (
            `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
            `code` varchar(50) DEFAULT NULL,
            `conf` varchar(1000) NOT NULL DEFAULT '[]',
            `params` varchar(2000) NOT NULL DEFAULT '[]',
            `frequency` varchar(100) DEFAULT NULL,
            `next_execution_at` datetime DEFAULT NULL,
            `last_executed_at` datetime DEFAULT NULL,
            `created_at` datetime DEFAULT NULL,
            PRIMARY KEY (`id`),
            UNIQUE KEY `code` (`code`)
          )");
    }
}
<?php
use Phinx\Migration\AbstractMigration;

class AddonsMigration extends AbstractMigration
{

    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     * createTable
     * renameTable
     * addColumn
     * renameColumn
     * addIndex
     * addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("CREATE TABLE `addon` (
                        `id` INT NOT NULL AUTO_INCREMENT,
                        `name` VARCHAR(250) NOT NULL,
                        `description` VARCHAR(1024) NULL DEFAULT NULL,
                        `url` VARCHAR(500) NOT NULL,
                        `package_name` VARCHAR(150) NOT NULL,
                        `scope` VARCHAR(150) NOT NULL,
                        `created_by` VARCHAR(65) NULL DEFAULT NULL,
                        `created_date` DATETIME NULL DEFAULT NULL,
                        `update_by` VARCHAR(65) NULL DEFAULT NULL,
                        `updated_date` DATETIME NULL DEFAULT NULL,
                        PRIMARY KEY (`id`),
                        UNIQUE INDEX `package_name_UNIQUE` (`package_name` ASC));
                        ");

            $this->execute("CREATE TABLE `addon_attr` (
                              `id` INT NOT NULL AUTO_INCREMENT,
                              `type` VARCHAR(150) NOT NULL,
                              `name` VARCHAR(150) NOT NULL,
                              `code` VARCHAR(250) NOT NULL,
                              `addon_id` INT NOT NULL,
                              `required` TINYINT NULL DEFAULT 0,
                              `data_type` VARCHAR(45) NULL DEFAULT 'string',
                              `created_by` VARCHAR(75) NULL,
                              `updated_by` VARCHAR(75) NULL,
                              `created_date` DATETIME NULL,
                              `updated_date` DATETIME NULL,
                              PRIMARY KEY (`id`),
                              UNIQUE INDEX `UNIQUE` (`code` ASC, `addon_id` ASC),
                              INDEX `addon_id_idx` (`addon_id` ASC),
                              CONSTRAINT `addon_id`
                                FOREIGN KEY (`addon_id`)
                                REFERENCES `addon` (`id`)
                                ON DELETE CASCADE
                                ON UPDATE NO ACTION);");

        $this->execute("CREATE TABLE `addon_enabled` (
                      `id` INT NOT NULL AUTO_INCREMENT,
                      `addon_id` INT NOT NULL,
                      `college_id` INT NOT NULL,
                      `scope_id` VARCHAR(150) NOT NULL,
                      `created_by` VARCHAR(65) NULL,
                      `created_date` DATETIME NULL,
                      `updated_by` VARCHAR(65) NULL,
                      `updated_date` DATETIME NULL,
                      PRIMARY KEY (`id`),
                      INDEX `addon_id_idx` (`addon_id` ASC),
                      INDEX `college_id_idx` (`college_id` ASC),
                      UNIQUE INDEX `UNIQUE` (`addon_id` ASC, `college_id` ASC),
                      CONSTRAINT `addon_id_fdx`
                        FOREIGN KEY (`addon_id`)
                        REFERENCES `addon` (`id`)
                        ON DELETE CASCADE
                        ON UPDATE NO ACTION,
                      CONSTRAINT `college_id_fdx`
                        FOREIGN KEY (`college_id`)
                        REFERENCES `colleges` (`id`)
                        ON DELETE CASCADE
                        ON UPDATE NO ACTION);
                    ");
        $this->execute("CREATE TABLE `addon_attr_value` (
                      `id` INT NOT NULL AUTO_INCREMENT,
                      `addon_attr_id` INT NOT NULL,
                      `value` VARCHAR(1024) NULL,
                      `enabled_addon_id` INT NOT NULL,
                      `created_by` VARCHAR(65) NULL,
                      `created_date` DATETIME NULL,
                      `updated_by` VARCHAR(65) NULL,
                      `updated_date` DATETIME NULL,
                      PRIMARY KEY (`id`),
                      INDEX `fk_addon_attr_value_1_idx` (`addon_attr_id` ASC),
                      INDEX `fk_addon_attr_value_2_idx` (`enabled_addon_id` ASC),
                      CONSTRAINT `fk_addon_attr_value_1`
                        FOREIGN KEY (`addon_attr_id`)
                        REFERENCES `addon_attr` (`id`)
                        ON DELETE CASCADE
                        ON UPDATE NO ACTION,
                      CONSTRAINT `fk_addon_attr_value_2`
                        FOREIGN KEY (`enabled_addon_id`)
                        REFERENCES `addon_enabled` (`id`)
                        ON DELETE CASCADE
                        ON UPDATE NO ACTION);
                    ");


        $this->execute("CREATE TABLE `addon_data_store` (
                      `id` INT NOT NULL AUTO_INCREMENT,
                      `enabled_addon_id` INT NOT NULL,
                      `key` VARCHAR(250) NOT NULL,
                      `value` VARCHAR(500) NULL,
                      `created_by` VARCHAR(65) NULL,
                      `created_date` DATETIME NULL,
                      `updated_by` VARCHAR(65) NULL,
                      `updated_date` DATETIME NULL,
                      PRIMARY KEY (`id`),
                      UNIQUE INDEX `UNIQUE` (`enabled_addon_id` ASC, `key` ASC),
                      CONSTRAINT `enabledAddonId`
                        FOREIGN KEY (`enabled_addon_id`)
                        REFERENCES `addon_enabled` (`id`)
                        ON DELETE CASCADE 
                        ON UPDATE NO ACTION);

                    ");
    }
}
<?php

use Phinx\Migration\AbstractMigration;

class TablesForSsoFeature extends AbstractMigration {
	/**
	 * Change Method.
	 *
	 * Write your reversible migrations using this method.
	 *
	 * More information on writing migrations is available here:
	 * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
	 *
	 * The following commands can be used in this method and Phinx will
	 * automatically reverse them when rolling back:
	 *
	 *    createTable
	 *    renameTable
	 *    addColumn
	 *    renameColumn
	 *    addIndex
	 *    addForeignKey
	 *
	 * Remember to call "create()" or "update()" and NOT "save()" when working
	 * with the Table class.
	 */
	public function change() {
		$this->execute("CREATE TABLE `configuration` (
		    `id` INT NOT NULL AUTO_INCREMENT,
			`key` VARCHAR(64) NOT NULL,
			`value` LONGTEXT NOT NULL,
			PRIMARY KEY (`id`));
			 
			ALTER TABLE `configuration` 
			ADD UNIQUE INDEX `Key` (`key` ASC);

			ALTER TABLE `configuration` 
			CHANGE COLUMN `key` `conf_key` VARCHAR(64) NOT NULL ;

			ALTER TABLE `configuration` 
			CHANGE COLUMN `value` `conf_value` LONGTEXT NOT NULL ;
			
			CREATE TABLE `single_signon_log` (
  		    `id` INT NOT NULL AUTO_INCREMENT,
		    `user_id` INT NOT NULL,
		    `college_code` VARCHAR(8) NOT NULL,
		    `logged_in_as` VARCHAR(45) NOT NULL,
		    `login_time` DATETIME NOT NULL,
		    PRIMARY KEY (`id`));
			
			ALTER TABLE `single_signon_log` 
			CHANGE COLUMN `college_code` `college_code` VARCHAR(32) NOT NULL ;
			ALTER TABLE `single_signon_log` 
			CHANGE COLUMN `logged_in_as` `logged_in_as` VARCHAR(128)  NOT NULL ;



			  ");
			

	}
}
<?php


use Phinx\Migration\AbstractMigration;

class InitialMigration extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will 
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $options = $this->adapter->getOptions();
        exec("mysql -u {$options['user']} -p{$options['pass']}  {$options['name']}< ". __DIR__. "/InitialSeed.sql");
    }
}
<?php


use Phinx\Migration\AbstractMigration;

class CollegeDbFields extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
      $this->execute("ALTER TABLE colleges
          ADD COLUMN `db_username` VARCHAR(50) NOT NULL,
          ADD COLUMN `db_password` VARCHAR(50) NOT NULL,
          ADD COLUMN `db_host` VARCHAR(50) NOT NULL DEFAULT 'localhost';");
    }
}
<?php


use Phinx\Migration\AbstractMigration;

class TablesForUserModuleInNucleus extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("
        CREATE TABLE `nucleus_user` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(64) NOT NULL,
        `email` VARCHAR(256) NOT NULL,
        `user_name` VARCHAR(128) NOT NULL,
        `password` VARCHAR(32) NOT NULL,
        `salt` VARCHAR(16) NOT NULL,
        PRIMARY KEY (`id`));

        ALTER TABLE `nucleus_user` 
        ADD UNIQUE INDEX `nucleus_user_email` (`email` ASC);
        
        ALTER TABLE `nucleus_user` 
        DROP INDEX `nucleus_user_email` ,
        ADD UNIQUE INDEX `un_nucleus_user_email` (`email` ASC),
        ADD UNIQUE INDEX `un_nucleus_user_username` (`user_name` ASC);
        
        ALTER TABLE `nucleus_user` 
        ADD INDEX `un_nucleus_user_password` (`password` ASC);


        CREATE TABLE `permission` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(128) NOT NULL,
        `type` VARCHAR(128) NOT NULL,
        PRIMARY KEY (`id`));

        CREATE TABLE `user_permission` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `nucleus_user_id` INT NULL,
        `permission_id` INT NULL,
        PRIMARY KEY (`id`));

        ALTER TABLE `user_permission` 
        ADD INDEX `fk_nucleus_user_id_permission_id_idx` (`nucleus_user_id` ASC);
        ALTER TABLE `user_permission` 
        ADD CONSTRAINT `fk_nucleus_user_id_permission_id`
          FOREIGN KEY (`nucleus_user_id`)
          REFERENCES `nucleus_user` (`id`)
          ON DELETE CASCADE
          ON UPDATE NO ACTION;

        ALTER TABLE `user_permission` 
        DROP FOREIGN KEY `fk_nucleus_user_id_permission_id`;
        ALTER TABLE `user_permission` 
        ADD CONSTRAINT `fk_user_permission_nucleus_user_id`
        FOREIGN KEY (`nucleus_user_id`)
        REFERENCES `nucleus_user` (`id`)
        ON DELETE CASCADE
        ON UPDATE NO ACTION;

        
        

        ALTER TABLE `user_permission` 
        ADD INDEX `fk_user_permission_permission_id_idx` (`permission_id` ASC);
        ALTER TABLE `user_permission` 
        ADD CONSTRAINT `fk_user_permission_permission_id`
        FOREIGN KEY (`permission_id`)
        REFERENCES `permission` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION;


        CREATE TABLE `group` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(45) NOT NULL,
        `created_by` INT NOT NULL,
        `created_date` DATETIME NOT NULL,
        `updated_by` INT NOT NULL,
        `updated_date` VARCHAR(45) NULL,
        PRIMARY KEY (`id`));

        CREATE TABLE `group_permission` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `group_id` INT NOT NULL,
        `permission_id` INT NOT NULL,
        PRIMARY KEY (`id`),
        INDEX `fk_group_permission_group_idx` (`group_id` ASC),
        INDEX `fk_group_permission_permission_idx` (`permission_id` ASC),
        CONSTRAINT `fk_group_permission_group`
        FOREIGN KEY (`group_id`)
        REFERENCES `group` (`id`)
        ON DELETE CASCADE
        ON UPDATE NO ACTION,
        CONSTRAINT `fk_group_permission_permission`
        FOREIGN KEY (`permission_id`)
        REFERENCES `permission` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION);

        ALTER TABLE `group_permission` 
        ADD COLUMN `created_by` INT NOT NULL AFTER `permission_id`,
        ADD COLUMN `created_date` DATETIME NOT NULL AFTER `created_by`,
        ADD COLUMN `updated_by` INT NOT NULL AFTER `created_date`,
        ADD COLUMN `updated_date` DATETIME NOT NULL AFTER `updated_by`;
        ALTER TABLE `nucleus_user` 
        ADD COLUMN `created_by` INT NOT NULL AFTER `salt`,
        ADD COLUMN `created_date` DATETIME NOT NULL AFTER `created_by`,
        ADD COLUMN `updated_by` INT NOT NULL AFTER `created_date`,
        ADD COLUMN `updated_date` DATETIME NOT NULL AFTER `updated_by`;
        ALTER TABLE `permission` 
        ADD COLUMN `created_by` INT NOT NULL AFTER `type`,
        ADD COLUMN `updated_by` INT NOT NULL AFTER `created_by`,
        ADD COLUMN `created_date` DATETIME NOT NULL AFTER `updated_by`,
        ADD COLUMN `updated_date` DATETIME NOT NULL AFTER `created_date`;

        ALTER TABLE `user_permission` 
        ADD COLUMN `created_by` INT NOT NULL AFTER `permission_id`,
        ADD COLUMN `updated_by` INT NOT NULL AFTER `created_by`,
        ADD COLUMN `created_date` DATETIME NOT NULL AFTER `updated_by`,
        ADD COLUMN `updated_date` DATETIME NOT NULL AFTER `created_date`;

        ALTER TABLE `nucleus_user` 
        CHANGE COLUMN `user_name` `username` VARCHAR(128) NOT NULL ;
       
        ALTER TABLE `nucleus_user` 
        CHANGE COLUMN `salt` `salt` VARCHAR(32) NOT NULL ;

        ALTER TABLE `nucleus_user` 
        ADD INDEX `un_nucleus_user_salt` (`salt` ASC);

        CREATE TABLE `user_group` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `group_id` INT NOT NULL,
        `nucleus_user_id` INT NOT NULL,
        PRIMARY KEY (`id`));

        ALTER TABLE `user_group` 
        ADD INDEX `fk_user_group_group_idx` (`group_id` ASC),
        ADD INDEX `fk_user_group_nucleus_user_id_idx` (`nucleus_user_id` ASC);
        ALTER TABLE `user_group` 
        ADD CONSTRAINT `fk_user_group_group_id`
        FOREIGN KEY (`group_id`)
        REFERENCES `group` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION,
        ADD CONSTRAINT `fk_user_group_nucleus_user_id`
        FOREIGN KEY (`nucleus_user_id`)
        REFERENCES `nucleus_user` (`id`)
        ON DELETE NO ACTION
        ON UPDATE NO ACTION;

        ALTER TABLE `user_group` 
        DROP FOREIGN KEY `fk_user_group_group_id`,
        DROP FOREIGN KEY `fk_user_group_nucleus_user_id`;
        ALTER TABLE `user_group` 
        ADD CONSTRAINT `fk_user_group_group_id`
        FOREIGN KEY (`group_id`)
        REFERENCES `group` (`id`)
        ON DELETE CASCADE
        ON UPDATE NO ACTION,
        ADD CONSTRAINT `fk_user_group_nucleus_user_id`
        FOREIGN KEY (`nucleus_user_id`)
        REFERENCES `nucleus_user` (`id`)
        ON DELETE CASCADE
        ON UPDATE NO ACTION;




            ");

    }
}
<?php


use Phinx\Migration\AbstractMigration;

class CollegeDbIsActiveFlag extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("
            ALTER TABLE `colleges` 
            ADD COLUMN `isActive` TINYINT NOT NULL DEFAULT 1 AFTER `updated_at`;
        ");

    }
}
<?php


use Phinx\Migration\AbstractMigration;

class ClientTableMigrations extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
      $this->execute("CREATE TABLE `clients` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(300) NOT NULL,
        `address` VARCHAR(500) NULL,
        `total_annual_income` INT(10) NULL,
        `service_start_date` DATE NULL,
        `billing_start_date` DATE NULL,
        `created_by` INT NOT NULL,
        `created_at` DATETIME NOT NULL,
        `updated_by` INT NULL,
        `updated_at` DATETIME NOT NULL,
        PRIMARY KEY (`id`));

        CREATE TABLE `expense_category` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(100) NOT NULL,
        PRIMARY KEY (`id`));

        CREATE TABLE `expenses` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `item` VARCHAR(255) NOT NULL,
        `amount` FLOAT NOT NULL,
        `expense_category_id` INT NOT NULL,
        `date` DATETIME NOT NULL,
        PRIMARY KEY (`id`),
        INDEX `fk_expense_category_idx` (`expense_category_id` ASC),
        CONSTRAINT `fk_expense_category`
          FOREIGN KEY (`expense_category_id`)
          REFERENCES `expense_category` (`id`)
          ON DELETE NO ACTION
          ON UPDATE NO ACTION);

          ALTER TABLE `colleges`
          ADD COLUMN `client_id` INT NULL AFTER `db_host`,
          ADD INDEX `fk_client_idx` (`client_id` ASC);
          ALTER TABLE `colleges`
          ADD CONSTRAINT `fk_client`
            FOREIGN KEY (`client_id`)
            REFERENCES `clients` (`id`)
            ON DELETE NO ACTION
            ON UPDATE NO ACTION;

      ");
    }
}
<?php


use Phinx\Migration\AbstractMigration;

class DummySuperUser extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("


        INSERT INTO `nucleus_user` (`id`,`name`,`email`,`username`,`password`,`salt`,`created_by`,`created_date`,`updated_by`,`updated_date`) VALUES (3,'superuser','superuser@linways.com','superuser','ee5e04ff863b21478d91f0c75576f4fd','bQOwJ4s5XgmFq2x9',0,'2018-01-16 04:15:46',0,'2018-01-16 04:15:46');


            INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (1,'DASHBOARD_VIEW','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (2,'TENANTS_VIEW','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (3,'CLIENTS_VIEW_CLIENT_LIST','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (4,'CLIENTS_BATCH_UPDATE','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (5,'CLIENTS_CREATE_OR_UPDATE','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (6,'CLIENTS_SAVE_CLIENT_DETAILS','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (7,'CLIENTS_DELETE_CLIENT_DETAILS','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (8,'EXPENSES_ADD','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (9,'EXPENSES_BATCH_UPDATE','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (10,'EXPENSES_VIEW','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (11,'EXPENSES_RENDER_REPORTS_BASE_TEMPLATE','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (12,'EXPENSES_GET_EXPENSES_FOR_SINGLE_TENANT','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (13,'EXPENSES_GET_TRIAL_DETAILS','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (14,'EXPENSES_GET_BASIC_DETAILS_OF_CLIENT','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (15,'EXPENSES_GET_CLIENTS_ON_TRIAL_AND_TENANTS','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (16,'EXPENSES_GET_CATEGORIZED_EXPENSES','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (17,'TENANTS_GENERATE_TOKEN_AND_REDIRECT_AS_ADMIN','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (18,'TENANTS_LIST','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (19,'TENANTS_CREATE_OR_UPDATE','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (20,'TENANTS_SAVE_TENANT_DETAILS','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `permission` (`id`,`name`,`type`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (21,'TENANTS_DELETE_TENANT_DETAILS','',0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');




INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (0,3,3,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (3,3,1,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (6,3,2,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (8,3,4,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (9,3,5,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (10,3,6,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (11,3,7,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (12,3,8,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (13,3,9,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (14,3,10,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (15,3,12,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (16,3,13,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (17,3,14,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (18,3,15,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (19,3,16,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (20,3,17,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (21,3,18,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (22,3,19,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (23,3,20,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');
INSERT INTO `user_permission` (`id`,`nucleus_user_id`,`permission_id`,`created_by`,`updated_by`,`created_date`,`updated_date`) VALUES (24,3,21,0,0,'0000-00-00 00:00:00','0000-00-00 00:00:00');






            ");
    }
}
<?php


use Phinx\Migration\AbstractMigration;

class ApiAccessKeyMigration extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
        $this->execute("CREATE TABLE `api_organization_details` (
            `id` INT NOT NULL AUTO_INCREMENT,
            `name` VARCHAR(250) NULL,
            `address` VARCHAR(500) NULL,
            `email` VARCHAR(250) NULL,
            `contactNo` VARCHAR(45) NULL,
            `notes` VARCHAR(500) NULL,
            PRIMARY KEY (`id`));
          ");
        
        $this->execute("CREATE TABLE `api_access_token` (
            `id` INT NOT NULL AUTO_INCREMENT,
            `access_id` VARCHAR(20) NOT NULL,
            `access_token` VARCHAR(1024) NOT NULL,
            `college_id` INT NOT NULL,
            `api_organization_details_id` INT NOT NULL,
            `issued_at` DATETIME NOT NULL,
            `is_active` TINYINT NOT NULL DEFAULT 1,
            PRIMARY KEY (`id`),
            UNIQUE INDEX `access_id_UNIQUE` (`access_id` ASC),
            INDEX `fk1_idx` (`college_id` ASC),
            INDEX `fk2_idx` (`api_organization_details_id` ASC),
            CONSTRAINT `fk1`
              FOREIGN KEY (`college_id`)
              REFERENCES `colleges` (`id`)
              ON DELETE CASCADE
              ON UPDATE NO ACTION,
            CONSTRAINT `fk2`
              FOREIGN KEY (`api_organization_details_id`)
              REFERENCES `api_organization_details` (`id`)
              ON DELETE CASCADE
              ON UPDATE NO ACTION);
          ");
        
        $this->execute("CREATE TABLE `api_permission` (
            `id` INT NOT NULL AUTO_INCREMENT,
            `name` VARCHAR(100) NOT NULL,
            `code` VARCHAR(100) NOT NULL,
            PRIMARY KEY (`id`),
            UNIQUE INDEX `code_UNIQUE` (`code` ASC));
          ");

        $this->execute("CREATE TABLE `api_access_token_permission` (
            `id` INT NOT NULL AUTO_INCREMENT,
            `api_access_token_id` INT NOT NULL,
            `api_permission_id` INT NOT NULL,
            PRIMARY KEY (`id`),
            INDEX `fk1_idx` (`api_access_token_id` ASC),
            INDEX `fk2_idx` (`api_permission_id` ASC),
            CONSTRAINT `aatpf11`
              FOREIGN KEY (`api_access_token_id`)
              REFERENCES `api_access_token` (`id`)
              ON DELETE NO ACTION
              ON UPDATE NO ACTION,
            CONSTRAINT `aatpf2`
              FOREIGN KEY (`api_permission_id`)
              REFERENCES `api_permission` (`id`)
              ON DELETE NO ACTION
              ON UPDATE NO ACTION);
          ");
    }
}
<?php


use Phinx\Seed\AbstractSeed;

class DummySuperUser extends AbstractSeed
{
    /**
     * Run Method.
     *
     * Write your database seeder using this method.
     *
     * More information on writing seeders is available here:
     * http://docs.phinx.org/en/latest/seeding.html
     */
    public function run()
    {

    }
}
<?php


use Phinx\Seed\AbstractSeed;

class CollegeSeed extends AbstractSeed
{
    /**
     * Run Method.
     *
     * Write your database seeder using this method.
     *
     * More information on writing seeders is available here:
     * http://docs.phinx.org/en/latest/seeding.html
     */
    public function run()
    {
        $data = array(
            array(
                'college_code' => 'PRO',
                'college_name' => 'Linways Technologies',
                'college_db' => 'pro_db'
            ),
        );

        $posts = $this->table('colleges');
        $posts->insert($data)
              ->save();
    }
}
<?php


use Phinx\Seed\AbstractSeed;

class AddonSeed extends AbstractSeed
{
    /**
     * Run Method.
     *
     * Write your database seeder using this method.
     *
     * More information on writing seeders is available here:
     * http://docs.phinx.org/en/latest/seeding.html
     */
    public function run()
    {
      $data = array(
          array(
              'name'    => 'GreytHR',
              'description' => 'GreytHR provides payroll software with fast and accurate payroll processing. It covers every aspect of payroll management including statutory compliance.',
              'url' => 'http://greythr.com',
              'package_name' => 'greythr',
              'scope' => 'INSTITUTION',
              'created_date' => date('Y-m-d H:i:s'),
              'updated_date' => date('Y-m-d H:i:s')
          )
      );

      $posts = $this->table('addon');
      $posts->insert($data)
            ->save();
    }
}
<?php


use Phinx\Seed\AbstractSeed;

class GreythrSeedData extends AbstractSeed
{
    /**
     * Run Method.
     *
     * Write your database seeder using this method.
     *
     * More information on writing seeders is available here:
     * http://docs.phinx.org/en/latest/seeding.html
     */
    public function run()
    {

    }
}
<?php


use Phinx\Seed\AbstractSeed;

class PermissionSeed extends AbstractSeed
{
    /**
     * Run Method.
     *
     * Write your database seeder using this method.
     *
     * More information on writing seeders is available here:
     * http://docs.phinx.org/en/latest/seeding.html
     */
    public function run()
    {
        $data = array(
            array(
                'name'    => 'View Students',
                'code' => 'view_students',
            ),
            array(
                'name'    => 'View Faculties',
                'code' => 'view_faculties',
            ),
            array(
                'name'    => 'Edit Students',
                'code' => 'edit_students',
            ),
            array(
                'name'    => 'Edit Faculties',
                'code' => 'edit_faculties',
            )
        );

        $posts = $this->table('api_permission');
        $posts->insert($data)
              ->save();
    }
}
#!/usr/bin/env php
<?php
require_once __DIR__ . "/../vendor/autoload.php";
use com\linways\nucleus\core\service\CollegeService;
use \Phinx\Console\PhinxApplication;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;
error_reporting(E_ALL & ~E_WARNING ); // prevent showing warnings

//Path to nucleus DB conf
$nucleusTestDBConfPath = __DIR__ . '/../test/db_conf/nucleus_db_conf.php';
putenv("NUCLEUS_CONF=$nucleusTestDBConfPath");

$dbs = [];
$target = null;
$seedName = null;
$command = null;
$dbUsername = 'root';
$dbPassword = 'root';
$dbHost = 'localhost';
$commandArgs = [];
$migrationFile = null;
if(count($argv)<2){
  // If no operations specified
  $argv[1] = 'help';
}
else{
  $args = $argv;
  $command = $args[1];
  $flagStartIndex = null;
  for($i=3; $i<$argc; $i++){
    if(($args[$i] == '-s')|| ($args[$i] == '-S')){
      $seedName = $args[$i+1];
      $flagStartIndex = $flagStartIndex? $flagStartIndex: $i;
    }
    elseif(($args[$i] == '-t') || ($args[$i] == '-T')){
      $target = $args[$i+1];
      $flagStartIndex = $flagStartIndex? $flagStartIndex: $i;
    }
    elseif(($args[$i] == '-f') || ($args[$i] == '-F')){
      $migrationFile = $args[$i+1];
      $flagStartIndex = $flagStartIndex? $flagStartIndex: $i;
    }
    elseif(strtolower($args[$i]) == '-dbuser'){
      $dbUsername = $args[$i+1];
      $flagStartIndex = $flagStartIndex? $flagStartIndex: $i;
    }
    elseif(strtolower($args[$i]) == '-dbpass'){
      $dbPassword = $args[$i+1];
      $flagStartIndex = $flagStartIndex? $flagStartIndex: $i;
    }
    elseif(strtolower($args[$i]) == '-dbhost'){
      $dbHost = $args[$i+1];
      $flagStartIndex = $flagStartIndex? $flagStartIndex: $i;
    }
  }
  $flagStartIndex = $flagStartIndex? $flagStartIndex: 3; // for taking a single available arg
  if($argc > 2){
    for($i=2; $i<$flagStartIndex; $i++){
      $commandArgs[] = $args[$i];
    }
  }

}

switch ($command){
    case 'migrate':     //  MIGRATE
        $dbs = prepareDBArray($commandArgs, $command);
        foreach($dbs as $dbDetails){
          $app = new PhinxApplication();
          $wrap = setPhinxVariables('Migrating',$app, $dbDetails);
          try{
            if($target)
              $response = $wrap->getMigrate(null, $target);
            else
              $response = $wrap->getMigrate();
            print_r($response);
          }catch(Exception $e){
            echo "\033[31m ERROR: ";
            echo $e->getMessage();
            echo "\033[0m\n";
            exit(1);
          }

          echo "\n";
          unset($wrap);
          unset($app);
          unset($response);
      }
      break;
    case 'seed':         // SEED
        $dbs = prepareDBArray($commandArgs, $command);
        foreach($dbs as $dbDetails){
          $app = new PhinxApplication();
          $wrap = setPhinxVariables('Seeding', $app, $dbDetails);
          try{
            $response = $wrap->getSeed();
            print_r($response);
          }catch(Exception $e){
            echo "\033[31m ERROR: ";
            echo $e->getMessage();
            echo "\033[0m\n";
            exit(1);
          }
          echo "<br/>";
          unset($wrap);
          unset($app);
          unset($response);
        }
        break;
    case 'migrate-fake':    // MIGRATE FAKE
      if(empty($migrationFile)){
        echo "Name of the migration file is required\n";
        echo "USAGE: manger.php migrate-fake [ALL] / [[DB1 [DB2]] -f 1231234_migration_file\n";
        exit(1);
      }
      list($version, $migarationName) = explode ('_', $migrationFile, 2);
      $migarationName .= '-faked';
      $dbs = prepareDBArray($commandArgs, $command);
      foreach($dbs as $dbDetails){
         if(fakeMigration($version, $migarationName, $dbDetails))
            echo "Faked Migration $migrationFile for " . $dbDetails['dbName'] + "\n";
      }
      break;
    case 'create-migration':       // CREATE MIGRATION
      $app = new PhinxApplication();
      $command = ['create'];
      if(empty($commandArgs)){
        echo "Not enough arguments (missing: 'name')\n";
        echo "USAGE: manager.php create-migration NameOfMigration\n";
        exit(1);
      }
      $command += ["name" => $commandArgs[0]];
      try{
        $response = executeRun($command, $app);
        print_r($response);
      }catch(Exception $e){
        echo "\033[31m ERROR: ";
        echo $e->getMessage();
        echo "\033[0m\n";
        exit(1);
      }
      break;
    case 'create-seed':          // CREATE SEED
      $app = new PhinxApplication();
      $command = ['seed:create'];
      if(empty($commandArgs)){
        echo "Not enough arguments (missing: 'name')\n";
        echo "USAGE: manager.php create-seed NameOfSeedFile\n";
        exit(1);
      }
      $command += ["name" => $commandArgs[0]];
      try{
        $response = executeRun($command, $app);
        print_r($response);
      }catch(Exception $e){
        echo "\033[31m ERROR: ";
        echo $e->getMessage();
        echo "\033[0m\n";
        exit(1);
      }
      break;
    case 'help':          // HELP
      echo "\n";
      $mask = "%-20s %-s \n";
      printf($mask, 'migrate', 'To run the migrations');
      printf($mask, 'migrate-fake', 'To fake a migration (mark migration as done)');
      printf($mask, 'seed', 'To add seed values to the database');
      printf($mask, 'create-migration', 'To create a new migration file');
      printf($mask, 'create-seed', 'To create a new seed file');
      print("Refer https://devdocs.linways.com/books/linways-ams-developer-docs/chapter/database-migrator\n");
      break;
    default :
      echo "invalid option. Use `php manager.php help`";
}
/**
 * Prepare the array of databases according to the command params
 * @param  array $commandArgs commandline arguments
 * @param  array $command name of the command
 * @return array       array of database names;
 */
function prepareDBArray($commandArgs, $command){
  if(empty($commandArgs)){
    echo "Database name or 'ALL' should be specified as second argument\n";
    echo "use \n `manager.php $command DB_NAME_1 [DB_NAME_2 ... DB_NAME_N] [-t target] [-s seedFileName] [-f migrationFile]` \nor\n `manager.php $command ALL [-t target] [-s seedVersion]` \n";
    $mask = "%-20s %s \n";
    printf($mask, 'DB_NAME', 'To run the migrations against specified database');
    printf($mask, 'ALL', 'To run migrations against all databases');
    printf($mask, '-t', '(optional) To pass the target version of the migration. (The timestamp in the file name is the version)');
    printf($mask, '-s', '(optional) To pass the name of the seed file. (Name of the seed file)');
    printf($mask, '-dbhost', '(optional) Database hostname, Default: localhost');
    printf($mask, '-dbuser', '(optional) Database username, Default: root');
    printf($mask, '-dbpass', '(optional) Database password, Default: root');
    print("Refer https://devdocs.linways.com/books/linways-ams-developer-docs/chapter/database-migrator\n");
    exit();
  }
  if($commandArgs == ['ALL'] || $commandArgs == ['all']){
    $allColleges = CollegeService::getInstance()->getAllColleges();
    foreach ($allColleges as $college) {
      $dbs[] = [
          'dbName' => $college->collegeDb,
          'dbUsername' => $college->dbUsername,
          'dbPassword' => $college->dbPassword,
          'dbHost' => $college->dbHost
        ];
    }
    if(empty($dbs)){
      echo "ERROR: No entries found on college database.\n";
      exit(1);
    }
  }else{
    foreach($commandArgs as $dbName){
      global $dbUsername,$dbPassword,$dbHost;
      $dbs[] = [
        'dbName' => $dbName,
        'dbUsername' => $dbUsername,
        'dbPassword' => $dbPassword,
        'dbHost' => $dbHost
      ];
    }
  }
  return $dbs;
}
/**
 * Return true if $haystack starts with $needle, else false
 * @param  string $haystack
 * @param  string $needle   key to be searched
 * @return boolean         True if $haystack starts with $needle else False
 */
function startsWith($haystack, $needle)
{
     $length = strlen($needle);
     return (substr($haystack, 0, $length) === $needle);
}

/**
 * sets server env variables and returns the wrapper object
 * @param string $action    Name of the action for displaying only
 * @param string $dbDetails Database Details
 * @param object $app       Object of PhinxApplication
 * @return object           Phinx wrapper object
 */
function setPhinxVariables($action, $app, $dbDetails){
  $_SERVER['PHINX_DBNAME'] = $dbDetails['dbName'];
  $_SERVER['PHINX_DBUSER'] = $dbDetails['dbUsername'];
  $_SERVER['PHINX_DBPASS'] = $dbDetails['dbPassword'];
  $_SERVER['PHINX_DBHOST'] = $dbDetails['dbHost'];
  $_SERVER['PHINX_CONFIG_DIR'] = __DIR__ . '/../';
  echo "============= $action :".$dbDetails['dbName']." ==========\n";
  $wrap = new \Phinx\Wrapper\TextWrapper($app, array(
              // 'config_path' => __DIR__. '/../../../phinx.yml',
              'parser' => 'yaml'
  ));
  return $wrap;
}

/**
 * To execue a phinx command
 * @param  array            $command [description]
 * @param  PhinxApplication $app     [description]
 * @return [type]                    [description]
 */
function executeRun(array $command, PhinxApplication $app)
{
    // Output will be written to a temporary stream, so that it can be
    // collected after running the command.
    $stream = fopen('php://temp', 'w+');
    // Execute the command, capturing the output in the temporary stream
    // and storing the exit code for debugging purposes.
    $exit_code = $app->doRun(new ArrayInput($command), new StreamOutput($stream));
    // Get the output of the command and close the stream, which will
    // destroy the temporary file.
    $result = stream_get_contents($stream, -1, 0);
    fclose($stream);
    return $result;
}
/**
 * creates an entry in migration table with $version and $name
 * @param  string $version       version of the migration (timestamp prefix of the migration file)
 * @param  string $migrationName name of the migration
 * @param  array  $dbDetails     database connection details
 * @return boolean
 */
function fakeMigration($version, $migrationName, $dbDetails){
  $db = new mysqli($dbDetails['dbHost'], $dbDetails['dbUsername'], $dbDetails['dbPassword'], $dbDetails['dbName']);
  if($db->connect_errno > 0){
      die('Unable to connect to database [' . $db->connect_error . ']');
  }

  $createTable = "CREATE TABLE IF NOT EXISTS `db_migrations` (
  `version` bigint(20) NOT NULL,
  `migration_name` varchar(100) DEFAULT NULL,
  `start_time` timestamp NULL DEFAULT NULL,
  `end_time` timestamp NULL DEFAULT NULL,
  `breakpoint` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`version`))";
  $db->query($createTable);

  //TODO read migration table from .yml configuration
  $migrationTableName = 'db_migrations';
  $sql = "INSERT INTO `$migrationTableName`
  (`version`, `migration_name`, `start_time`, `end_time`)
  VALUES ('$version', '$migrationName', NOW(), NOW())";

  if(!$result = $db->query($sql)){
      echo("There was an error running the query [$db->error]\n");
      exit(0);
  }
  else
    return true;
}
{
  "name": "linways/nucleus-core",
  "description": "Project for tenant and api access management for professional",
  "type": "project",

  "require": {
    "linways/linways-base": "^1.0.0",
    "robmorgan/phinx": "^0.9.1",
    "firebase/php-jwt": "^5.0",
    "dragonmantank/cron-expression": "^1.2"

  },

  "autoload": {
    "psr-4": {
      "com\\linways\\": "src/com/linways",
      "test\\": "test/"
    }
  },

  "repositories": [ 
      {
      "type": "vcs",
      "url": "git@gitlab.com:linways/linways-base.git",
      "name": "linways-base"
      }

      ],


  "require-dev": {
    "phpunit/phpunit": "^5.7"
  }
}
<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

class Addon extends BaseDTO
{
    /**
     * 
     * @var integer
     */
    public $id;
    
    /**
     * 
     * @var string
     */
    public $name;
    
    /**
     *
     * @var string
     */
    public $description;
  
    /**
     *
     * @var string
     */
    public $scope;
    
    /**
     *
     * @var string
     */
    public $url;
    
    /**
     *
     * @var string
     */
    public $packageName;
    
    /**
     * 
     * @var AddonAttribute
     */
    public $addonAttribute;
    
    /**
     * 
     * @var College
     */
    public $college;

    /**
     * @var integer
     */
    public $enabledAddonId;

    
}

<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

class AddonAttributeValue extends BaseDTO
{
    /**
     * 
     * @var integer
     */
    public $id;
    
    /**
     * 
     * @var integer
     */
    public $addonAttributeId;
    
    /**
     * 
     * @var AddonAttribute
     */
    public $addonAttribute;
    
    /**
     * 
     * @var string
     */
    public $value;
    
    /**
     * 
     * @var integer
     */
    public $addonEnabledId;
}

<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

class AddonAttribute extends BaseDTO
{
    /**
     * 
     * @var integer
     */
   public $id;
   
   /**
    * 
    * @var integer
    */
   public $addonId;
   
   /**
    * 
    * @var Addon
    */
   public $addon;
   
   /**
     * 
     * @var string
     */
   public $type;
   
   /**
    * 
    * @var string
    */
   public $name;
   
   /**
    *
    * @var string
    */
   public $code;
   
   /**
    *
    * @var string
    */
   public $dataType;
   
   /**
    * 
    * @var boolean
    */
   public $required;
   
   /**
    * 
    * @var AddonAttributeValue
    */
   public $addonAttributeValue;
}

<?php
/**
 * Created by PhpStorm.
 * User: jithin
 * Date: 1/11/17
 * Time: 3:23 PM
 */

namespace com\linways\nucleus\core\dto;


use com\linways\base\dto\BaseDTO;

class ApiOrganizationDetails extends BaseDTO
{
    /**
     * @var integer
     */
    public $id;

    /**
     * @var string
     */
    public $name;

    /**
     * @var string
     */
    public $address;

    /**
     * @var string
     */
    public $contactNo;

    /**
     * @var string
     */
    public $email;

    /**
     * @var string
     */
    public $notes;

}<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

class AddonEnabled extends BaseDTO
{
    /**
     * 
     * @var integer
     */
    public $id;
    
    /**
     * 
     * @var integer
     */
    public $addonId;
    
    /**
     * 
     * @var Addon
     */
    public $addon;
    
    /**
     * 
     * @var integer
     */
    public $collegeId;
    
    /**
     * 
     * @var College
     */
    public $college;
    
    /**
     * scope id is null when scope is "INSTITUTION"
     * @var string
     */
    public $scopeId;
    
    /**
     * 
     * @var [AddonAttribute]
     */
    public $addonAttributes=[];

    /**
     * @var [AddonDataStore]
     */
    public $addonDataStore =[];
}

<?php 
namespace com\linways\nucleus\core\dto\user;

use com\linways\base\dto\BaseDTO;

class User extends BaseDTO{
   
    public $id;
    public $name;
    public $email;
    public $username;
    public $password;
    public $salt;

}<?php
/**
 * Created by PhpStorm.
 * User: jithin
 * Date: 30/10/17
 * Time: 3:49 PM
 */

namespace com\linways\nucleus\core\dto;


use com\linways\base\dto\BaseDTO;

class AddonDataStore extends BaseDTO
{
    /**
     * @var integer
     */
    public $id;

    /**
     * @var integer
     */
    public $enabledAddonId;

    /**
     * @var string
     */
    public $key;

    /**
     * @var string
     */
    public $value;

}<?php
namespace com\linways\nucleus\core\dto;

use DateTime;
use com\linways\base\dto\BaseDTO;

class Expense extends BaseDTO
{
    /**
     *
     * @var int
     */
    public $id;

    /**
     *
     * @var string
     */
    public $item;

    /**
     *
     * @var float
     */
    public $amount;

    /**
     *
     * @var ExpenseCategory
     */
    public $expenseCategory;

    /**
     *
     * @var int
     */
    public $expenseCategoryId;

    /**
     *
     * @var DateTime
     */
    public $date;

}
<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

/**
 * DTO for schedule
 */

class ScheduledTask extends BaseDTO {
	/**
	 *
	 * @var int
	 */
	public $id;

	/**
	 *
	 * @var string
	 */
	public $code;

	/**
	 *
	 * @var string
	 */
	public $conf;

	/**
	 *
	 * @var string
	 */
	public $params;

	/**
	 *
	 * @var string - The cron expression
	 */
	public $frequency;

	/**
	 *
	 * @var \DateTime
	 */
	public $nextExecutionAt;

	/**
	 *
	 * @var \DateTime
	 */
	public $lastExecutedAt;

	/**
	 *
	 * @var \DateTime
	 */
	public $createdAt;
}
<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

class College extends BaseDTO
{
    /**
     *
     * @var int
     */
    public $id;

    /**
     *
     * @var string
     */
    public $code;

    /**
     *
     * @var string
     */
    public $name;

    /**
     *
     * @var string
     */
    public $collegeDb;

    /**
     * @var string
     */
    public $dbUsername;
    /**
     * @var string
     */
    public $dbPassword;
    /**
     * @var string
     */
    public $dbHost;
    /**
     * @var int
     */
    public $clientId;
    /**
     * @var Client
     */
    public $client;

    /**
     *
     * @var AddonEnabled
     */
    public $addonsEnabled = [];

}
<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

class ExpenseCategory extends BaseDTO
{
    /**
     *
     * @var int
     */
    public $id;

    /**
     *
     * @var string
     */
    public $name;

}
<?php
namespace com\linways\nucleus\core\dto;

use com\linways\base\dto\BaseDTO;

class SingleSignonLog extends BaseDTO{
    public $id;
    public $userId;
    public $collegeCode;
    public $loggedInAs;
    public $loginTime;
}<?php
namespace com\linways\nucleus\core\dto;

use DateTime;
use com\linways\base\dto\BaseDTO;

/**
 * Client details. (not tenant info)
 */
class Client extends BaseDTO
{
    /**
     *
     * @var int
     */
    public $id;

    /**
     *
     * @var string
     */
    public $name;

    /**
     *
     * @var string
     */
    public $address;

    /**
     *
     * @var int
     */
    public $totalAnnualIncome;

    /**
     *
     * @var DateTime
     */
    public $serviceStartDate;

    /**
     *
     * @var DateTime
     */
    public $billingStartDate;

}
<?php
namespace com\linways\nucleus\core\ams_task_queue;

use com\linways\base\queue\TaskQueue;

/**
 * @author gadheyan
 */

class AMSTaskQueue extends TaskQueue {

	public function __construct() {

		$conf                          = [];
		$conf['DB_PROFESIONAL_CONFIG'] = getenv('DB_PROFESIONAL_CONFIG');
		$conf['AMS_CONFIG']            = getenv('AMS_CONFIG');
		$conf['NUCLEUS_CONF']          = getenv('NUCLEUS_CONF');
		$nucleusPath = $conf['NUCLEUS_CONF'];
		require_once $nucleusPath;
        // require_once $conf['NUCLEUS_CONF'];
		parent::__construct($conf);
	}
}

<?php
namespace com\linways\nucleus\core\constants;

class Scope
{
    const INSTITUTION = "INSTITUTION";
}

<?php
namespace com\linways\nucleus\core\constants;

class SingleSignonConstant{
    const ADMIN = "ADMIN";
}<?php

namespace com\linways\nucleus\core\mapper;

use com\linways\base\mapper\IMapper;
use com\linways\base\mapper\Result;
use com\linways\base\mapper\ResultMap;

class ClientServiceMapper implements IMapper
{

    private $mapper = [];
    private static $_instance = null;

    private function __construct()
    {
    }

    private function __clone()
    {
    }

    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            self::$_instance = new self();
        return self::$_instance;
    }

    const GET_ALL_CLIENTS = "GET_ALL_CLIENTS";
    const GET_BASIC_DETAILS_OF_ALL_CLIENTS = "GET_BASIC_DETAILS_OF_ALL_CLIENTS";

    public function getMapper()
    {
        if (empty($this->mapper)) {
            $this->mapper[self::GET_ALL_CLIENTS] = $this->getAllClients();
            $this->mapper[self::GET_BASIC_DETAILS_OF_ALL_CLIENTS] = $this->getBasicDetailsOfAllClients();
        }

        return $this->mapper;
    }

    private function getAllClients()
    {
        $mapper = null;

        $mapper = new ResultMap("getAllClients", 'com\linways\nucleus\core\dto\Client', 'id', 'id');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('name', 'name');
        $mapper->results[] = new Result('address', 'address');
        $mapper->results[] = new Result('totalAnnualIncome', 'total_annual_income');
        $mapper->results[] = new Result('serviceStartDate', 'service_start_date');
        $mapper->results[] = new Result('billingStartDate', 'billing_start_date');
        $mapper->results[] = new Result('createdBy', 'created_by');
        $mapper->results[] = new Result('createdAt', 'created_at');
        $mapper->results[] = new Result('updatedBy', 'updated_by');
        $mapper->results[] = new Result('updatedAt', 'updated_at');

        return $mapper;
    }

    private function getBasicDetailsOfAllClients()
    {
        $mapper = null;
        $mapper = new ResultMap("getAllBasicDetailsOfClients", 'com\linways\nucleus\core\dto\Client', 'id', 'id');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('name', 'name');
        return $mapper;
    }
}
<?php
namespace com\linways\nucleus\core\mapper;

use com\linways\base\mapper\IMapper;
use com\linways\base\mapper\Result;
use com\linways\base\mapper\ResultMap;

class SchedulerServiceMapper implements IMapper
{

    private $mapper = [];

    // /Condition 1 - Presence of a static member variable
    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {}

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {}

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (! is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();

        return self::$_instance;
    }

    const GET_SCHEDULE_MAPPER = "GET_SCHEDULE_MAPPER";

    public function getMapper()
    {
        if (empty($this->mapper)) {
            $this->mapper[self::GET_SCHEDULE_MAPPER] = $this->getScheduleMapper();
        }

        return $this->mapper;
    }

    private function getScheduleMapper(){
        $mapper = null;
        $mapper = new ResultMap("scheduleMapper", 'com\linways\nucleus\core\dto\ScheduledTask', 'id', 'id');
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('code', 'code');
        $mapper->results[] = new Result('conf', 'conf');
        $mapper->results[] = new Result('params', 'params');
        $mapper->results[] = new Result('frequency', 'frequency');
        $mapper->results[] = new Result('nextExecutionAt', 'next_execution_at');
        $mapper->results[] = new Result('lastExecutedAt', 'last_executed_at');
        $mapper->results[] = new Result('createdAt', 'created_at');
        return $mapper;

    }
}
<?php
namespace com\linways\nucleus\core\mapper;

use com\linways\base\mapper\IMapper;
use com\linways\base\mapper\Result;
use com\linways\base\mapper\ResultMap;

class CollegeServiceMapper implements IMapper
{

    private $mapper = [];

    // /Condition 1 - Presence of a static member variable
    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {}

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {}

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (! is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();

        return self::$_instance;
    }

    const GET_ALL_COLLEGES = "GET_ALL_COLLEGES";
    const GET_BILLING_COLLEGES = "GET_BILLING_COLLEGES";

    public function getMapper()
    {
        if (empty($this->mapper)) {
          $this->mapper[self::GET_ALL_COLLEGES] = $this->getAllCollegesMapper();
            $this->mapper[self::GET_BILLING_COLLEGES] = $this->getBillingCollegesMapper();
        }

        return $this->mapper;
    }

    private function getAllCollegesMapper()
    {
        $mapper = null;
        $mapper = new ResultMap("getAllCollegesMapper", 'com\linways\nucleus\core\dto\College', 'id', 'id');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('code', 'college_code');
        $mapper->results[] = new Result('name', 'college_name');
        $mapper->results[] = new Result('collegeDb', 'college_db');
        $mapper->results[] = new Result('dbUsername', 'db_username');
        $mapper->results[] = new Result('dbPassword', 'db_password');
        $mapper->results[] = new Result('dbHost', 'db_host');
        return $mapper;
    }
    private function getClientMapper(){
      $mapper = null;
      $mapper = new ResultMap("getAllCollegesMapper", 'com\linways\nucleus\core\dto\Client', 'id', 'id');
      $mapper->results = [];
      $mapper->results[] = new Result('id', 'id');
      $mapper->results[] = new Result('name', 'name');
      $mapper->results[] = new Result('address', 'address');
      $mapper->results[] = new Result('sericeStartDate', 'service_start_date');
      $mapper->results[] = new Result('billingStartDate', 'billing_start_date');
      return $mapper;
    }

    private function getBillingCollegesMapper(){
      $mapper = null;
      $mapper = new ResultMap("getAllCollegesMapper", 'com\linways\nucleus\core\dto\College', 'id', 'id');
      $mapper->results = [];
      $mapper->results[] = new Result('id', 'id');
      $mapper->results[] = new Result('code', 'college_code');
      $mapper->results[] = new Result('name', 'college_name');
      $mapper->results[] = new Result('client', 'expenseCategory', Result::OBJECT, $this->getClientMapper());
      return $mapper;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: jithin
 * Date: 30/10/17
 * Time: 2:29 PM
 */

namespace com\linways\nucleus\core\mapper;


use com\linways\base\mapper\IMapper;
use com\linways\base\mapper\Result;
use com\linways\base\mapper\ResultMap;

class AddonDataStoreMapper implements IMapper
{
    private $mapper = [];

    // /Condition 1 - Presence of a static member variable
    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
    }

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {
    }

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (!is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();

        return self::$_instance;
    }

    const GET_ADDON_DATA_STORE_MAPPER = "GET_ADDON_DATA_STORE_MAPPER";

    public function getMapper()
    {
        if (empty($this->mapper)) {
            $this->mapper[self::GET_ADDON_DATA_STORE_MAPPER] = $this->getAddonDataStoreMapper();
        }
        return $this->mapper;
    }

    protected function getAddonDataStoreMapper(){
        $mapper = null;
        $mapper = new ResultMap('getAddonDataStoreMapper','com\linways\nucleus\core\dto\AddonDataStore','id','id');
        $mapper->results[] = new Result('id','id');
        $mapper->results[] = new Result('enabledAddonId','enabled_addon_id');
        $mapper->results[] = new Result('key','key');
        $mapper->results[] = new Result('value','value');
        return$mapper;
    }
}<?php
namespace com\linways\nucleus\core\mapper;

use com\linways\base\mapper\IMapper;
use com\linways\base\mapper\Result;
use com\linways\base\mapper\ResultMap;

class ExpenseServiceMapper implements IMapper
{

    private $mapper = [];
    private static $_instance = null;

    private function __construct()
    {}

    private function __clone()
    {}
    public static function getInstance()
    {
        if (! is_object(self::$_instance))
            self::$_instance = new self();
        return self::$_instance;
    }

    const GET_ALL_EXPENSES = "GET_ALL_EXPENSES";
    const GET_EXPENSE_CATEGORY_MAPPER = "GET_EXPENSE_CATEGORY_MAPPER";

    public function getMapper()
    {
        if (empty($this->mapper)) {
          $this->mapper[self::GET_ALL_EXPENSES] = $this->getAllExpenses();
            $this->mapper[self::GET_EXPENSE_CATEGORY_MAPPER] = $this->getExpenseCategoryMapper();
        }

        return $this->mapper;
    }

    private function getExpenseCategoryMapper(){
      $mapper = null;

      $mapper = new ResultMap("getExpenseCategoryMapper", 'com\linways\nucleus\core\dto\ExpenseCategory', 'id', 'id');
      $mapper->results = [];
      $mapper->results[] = new Result('id', 'id');
      $mapper->results[] = new Result('name', 'name');
      return $mapper;
    }
    private function getAllExpenses()
    {
        $mapper = null;

        $mapper = new ResultMap("getAllClients", 'com\linways\nucleus\core\dto\Expense', 'id', 'id');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('item', 'item');
        $mapper->results[] = new Result('amount', 'amount');
        $mapper->results[] = new Result('date', 'date');
        $mapper->results[] = new Result('expenseCategory', 'expenseCategory', Result::OBJECT, $this->getExpenseCategoryMapper());
        return $mapper;
    }
}
<?php

namespace com\linways\nucleus\core\mapper;

use com\linways\base\mapper\IMapper;
use com\linways\base\mapper\Result;
use com\linways\base\mapper\ResultMap;
use Klein\Request;

class AddonServiceMapper implements IMapper
{

    private $mapper = [];

    // /Condition 1 - Presence of a static member variable
    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
    }

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {
    }

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (!is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();

        return self::$_instance;
    }

    const GET_ADDONS_ENABLED = "GET_ADDONS_ENABLED";
    const GET_ALL_ADDONS = "GET_ALL_ADDONS";
    const GET_ALL_ADDON_WITH_ENABLED_FLAG = "GET_ALL_ADDON_WITH_ENABLED_FLAG";
    const GET_ENABLED_ADDON_BY_PACKAGE_NAME_AND_COLLEGE_CODE = "GET_ENABLED_ADDON_BY_PACKAGE_NAME_AND_COLLEGE_CODE";
    const GET_ENABLED_ADDON_BY_ADDONID_AND_COLLEGECODE = "GET_ENABLED_ADDON_BY_ADDONID_AND_COLLEGECODE";
    const GET_ENABLED_ADDON_WITH_ADDON_DATA_STORE_MAPPER = "GET_ENABLED_ADDON_WITH_ADDON_DATA_STORE_MAPPER";

    public function getMapper()
    {
        if (empty($this->mapper)) {
            $this->mapper[self::GET_ADDONS_ENABLED] = $this->getAddonsEnabledMapper();
            $this->mapper[self::GET_ALL_ADDONS] = $this->getAllAddonsMapper();
            $this->mapper[self::GET_ENABLED_ADDON_BY_ADDONID_AND_COLLEGECODE] = $this->getAllAddonsWithEnabledAddonIdMapper();
            $this->mapper[self::GET_ALL_ADDON_WITH_ENABLED_FLAG] = $this->getAllAddonWithEnabledFlagMapper();
            $this->mapper[self::GET_ENABLED_ADDON_BY_PACKAGE_NAME_AND_COLLEGE_CODE] = $this->getEnabledAddonByPackageNameAndCollegeCodeMapper();
            $this->mapper[self::GET_ENABLED_ADDON_WITH_ADDON_DATA_STORE_MAPPER] = $this->getEnabledAddonWithDataStoreMapper();
        }

        return $this->mapper;
    }

    /**
     * Mapping ADDON with description (id, name, description, url, package_name, scope)
     * @return ResultMap|null
     */
    private function getAddonsMapper()
    {
        $mapper = null;
        $mapper = new ResultMap("getAddonsMapper", 'com\linways\nucleus\core\dto\Addon', 'id', 'id');
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('name', 'name');
        $mapper->results[] = new Result('description', 'description');
        $mapper->results[] = new Result('url', 'url');
        $mapper->results[] = new Result('packageName', 'package_name');
        $mapper->results[] = new Result('scope', 'scope');
        return $mapper;
    }

    /**
     * Mapping ADDON with ENABLED ADDON ID. (id, name, description, url, package_name, scope, enabledAddonId)
     * @return ResultMap|null
     */
    private function getAllAddonWithEnabledFlagMapper()
    {
        $mapper = null;
        $mapper = new ResultMap("getAllAddonWithEnabledFlagMapper", 'com\linways\nucleus\core\dto\Addon', 'id', 'id');
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('name', 'name');
        $mapper->results[] = new Result('description', 'description');
        $mapper->results[] = new Result('url', 'url');
        $mapper->results[] = new Result('packageName', 'package_name');
        $mapper->results[] = new Result('scope', 'scope');
        $mapper->results[] = new Result('enabledAddonId', 'enabledAddonId');
        return $mapper;

    }

    /**
     * Mapping ADDON only. (addonId,addonName,url, packageName, scope)
     * @return ResultMap|null
     */
    private function getAddonMapper()
    {
        $mapper = null;

        $mapper = new ResultMap("getAddonMapper", 'com\linways\nucleus\core\dto\Addon', 'id', 'addonId');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'addonId');
        $mapper->results[] = new Result('name', 'addonName');
        $mapper->results[] = new Result('url', 'url');
        $mapper->results[] = new Result('packageName', 'packageName');
        $mapper->results[] = new Result('scope', 'scope');

        return $mapper;
    }

    /**
     * Mapping ADDONs with ATTRIBUTES (id, name, description, url, package_name, scope, getAttributeMapper)
     * @return ResultMap|null
     */
    private function getAllAddonsMapper()
    {
        $mapper = null;
        $mapper = new ResultMap("getAllAddonsMapper", 'com\linways\nucleus\core\dto\Addon', 'id', 'id');
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('name', 'name');
        $mapper->results[] = new Result('description', 'description');
        $mapper->results[] = new Result('url', 'url');
        $mapper->results[] = new Result('packageName', 'package_name');
        $mapper->results[] = new Result('scope', 'scope');
        $mapper->results[] = new Result('attributes', 'attributes', Result::OBJECT_ARRAY, $this->getAddonAttributeMapper());
        return $mapper;
    }

    /**
     * Mapping ADDONs with ENABLED ADDON ID (id, name, description, url, package_name, scope,enabledAddonId, getAttributeMapper )
     * @return ResultMap|null
     */
    private function getAllAddonsWithEnabledAddonIdMapper()
    {
        $mapper = null;
        $mapper = new ResultMap("getAllAddonsWithEnabledAddonIdMapper", 'com\linways\nucleus\core\dto\Addon', 'id', 'id');
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('name', 'name');
        $mapper->results[] = new Result('description', 'description');
        $mapper->results[] = new Result('url', 'url');
        $mapper->results[] = new Result('packageName', 'package_name');
        $mapper->results[] = new Result('scope', 'scope');
        $mapper->results[] = new Result('enabledAddonId', 'enabledAddonId');
        $mapper->results[] = new Result('attributes', 'attributes', Result::OBJECT_ARRAY, $this->getAttributeMapper());
        return $mapper;
    }

    /**
     * Mapping ENABLED ADDONs. (enabledAddonId, scope_id, collegeId, getAddonsMapper)
     * @return ResultMap|null
     */
    private function getAddonsEnabledMapper()
    {
        $mapper = null;

        $mapper = null;
        $mapper = new ResultMap("getAddonsEnabledMapper", 'com\linways\nucleus\core\dto\AddonEnabled', 'id', 'id');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'enabledAddonId');
        $mapper->results[] = new Result('scope_id', 'scope_id');
        $mapper->results[] = new Result('collegeId', 'collegeId');
        $mapper->results[] = new Result('addon', 'addon', Result::OBJECT, $this->getAddonsMapper());
        return $mapper;
    }

    /**
     * Mapping ENABLED ADDONs.(id,scopeId,addon, getAttributeMapper)
     * @return ResultMap|null
     */
    private function getEnabledAddonByPackageNameAndCollegeCodeMapper()
    {
        $mapper = null;

        $mapper = new ResultMap("getEnabledAddonByPackageNameAndCollegeCode", 'com\linways\nucleus\core\dto\AddonEnabled', 'id', 'id');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('scopeId', 'scopeId');
        $mapper->results[] = new Result('addon', 'addon', Result::OBJECT, $this->getAddonMapper());
        $mapper->results[] = new Result('addonAttribute', 'addonAttribute', Result::OBJECT_ARRAY, $this->getAttributeMapper());
        return $mapper;
    }

    private function getEnabledAddonWithDataStoreMapper(){
        $mapper = null;

        $mapper = new ResultMap("getEnabledAddonByPackageNameAndCollegeCode", 'com\linways\nucleus\core\dto\AddonEnabled', 'id', 'id');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'id');
        $mapper->results[] = new Result('scopeId', 'scopeId');
        $mapper->results[] = new Result('addon', 'addon', Result::OBJECT, $this->getAddonMapper());
        $mapper->results[] = new Result('addonAttribute', 'addonAttribute', Result::OBJECT_ARRAY, $this->getAttributeMapper());
        $mapper->results[] = new Result('addonDataStore', 'addonDataStore', Result::OBJECT_ARRAY, $this->getAddonDataStoreMapper());
        return $mapper;
    }

    /**
     * Mapping ADDON ATTRIBUTE only. (attributeId, attrName, attrCode, required, data_type, attrType)
     * @return ResultMap|null
     */
    public function getAddonAttributeMapper(){
        $mapper = null;

        $mapper = new ResultMap("getAddonAttributeMapper", 'com\linways\nucleus\core\dto\AddonAttribute', 'id', 'attributeId');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'attributeId');
        $mapper->results[] = new Result('name', 'attrName');
        $mapper->results[] = new Result('code', 'attrCode');
        $mapper->results[] = new Result('required', 'required');
        $mapper->results[] = new Result('dataType', 'data_type');
        $mapper->results[] = new Result('type', 'attrType');

        return $mapper;
    }

    /**
     * mapping ADDON ATTRIBUTES.(attributeId, attrName, attrCode, required, data_type, attrType, getAttributeValuesMapper)
     * @return ResultMap|null
     */
    private function getAttributeMapper()
    {
        $mapper = null;

        $mapper = new ResultMap("getAttributeMapper", 'com\linways\nucleus\core\dto\AddonAttribute', 'id', 'attributeId');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'attributeId');
        $mapper->results[] = new Result('name', 'attrName');
        $mapper->results[] = new Result('code', 'attrCode');
        $mapper->results[] = new Result('required', 'required');
        $mapper->results[] = new Result('dataType', 'data_type');
        $mapper->results[] = new Result('type', 'attrType');
        $mapper->results[] = new Result('addonAttributeValue', 'addonAttributeValue', Result::OBJECT, $this->getAttributeValuesMapper());

        return $mapper;
    }

    /**
     * Mapping ADDON ATTRIBUTE VALUES. (attributeValueId, value)
     * @return ResultMap|null
     */
    private function getAttributeValuesMapper()
    {
        $mapper = null;

        $mapper = new ResultMap('getAttributeValuesMapper', 'com\linways\nucleus\core\dto\AddonAttributeValue', 'id', 'attributeValueId');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'attributeValueId');
        $mapper->results[] = new Result('value', 'value');

        return $mapper;
    }


    /**
     * Mapping ADDON_DATA_STORE only. ( addonDataStoreId, addonDataStoreValue, addonDataStoreKey,enabledAddonId)
     * @return ResultMap|null
     */
    private function getAddonDataStoreMapper(){
        $mapper = null;

        $mapper = new ResultMap('getAddonDataStoreMapper', 'com\linways\nucleus\core\dto\AddonDataStore', 'id', 'addonDataStoreId');
        $mapper->results = [];
        $mapper->results[] = new Result('id', 'addonDataStoreId');
        $mapper->results[] = new Result('value', 'addonDataStoreValue');
        $mapper->results[] = new Result('key', 'addonDataStoreKey');
        $mapper->results[] = new Result('enabledAddonId', 'enabledAddonId');

        return $mapper;
    }

}
<?php 
namespace com\linways\nucleus\core\utils\user;

class UserServiceUtils{



    function getSalt($length = 16) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

}<?php
namespace com\linways\nucleus\core\utils;


class StringUtils {
  /**
   * Convert empty string to string 'NULL'
   * @param  string $item
   * @return string
   */
    public static function stringifyNull($item){
      return $item ? "'$item'": 'NULL';
    }

}
<?php
namespace com\linways\nucleus\core\utils;

use \Firebase\JWT\JWT;

class JWTUtils {
    public static function createToken($organizationId, $collegeId){
        global $JWT_SECRET;
        $accessId = bin2hex(openssl_random_pseudo_bytes(10));
        // JWT specs recommend usage of short keys.
        $jwtPayload =  array(
            "aId" => $accessId,  // aid is for access Id
            "cId" => $collegeId, // College ID 
            "oId" => $organizationId,  //OrganizationID
            "iat" => time(),
        );
        return [$accessId, JWT::encode($jwtPayload, $JWT_SECRET)];
    }

}<?php
use com\linways\base\request\BaseRequest;

/**
 * Request object for ScheduleService::schedule
 */
class ScheduleRequest extends BaseRequest
{

  /**
   *
   * @var string Parameters required for execution of the method
   */
  public $params;

  /**
   *
   * @var string Name of the method to be executed.
   */
  public $method;

  /**
   *
   * @var string (Optional) Cron job schedule string representing when the task should work
   */
  public $frequency;

  /**
   *
   * @var string  (Optional) Unique code used to identify scheduled task (Useful for named tasks)
   */
  public $code;

  /**
   *
   * @var \DateTime (Optional) To set next execution time. (For one time tasks set this and leave frequency null)
   */
  public $nextExecutionAt;

}
<?php 
namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\exception\NucleusException;

class ConfigurationService extends BaseService {

    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct() {
    }
    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone() {
    }
    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance() {
        if (!is_object(self::$_instance))
        // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
        {self::$_instance = new self();
        }

        return self::$_instance;
    }


/**
 * [getConfigurationByKey method to get configuration as key-value pair]
 * @author gadheyan
 * @param  [type] $key [description]
 * @return [type]      [description]
 */
    public function getConfigurationByKey($key){
        $sql = "";
        $response = null;

        $key = $this->realEscapeString($key);

        $sql = "SELECT conf_key as `key`, conf_value as value FROM configuration WHERE key='$key'";

        try {
          $response = $this->executeQueryForObject($sql);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $response;
    }

    public function getMultipleConfigurations($key){
        $sql = "";
        $response = new \StdClass;
        $result = null;

 
        foreach ($key as $item) {
            $item = $this->realEscapeString($item);
        }
        $key = "'".implode("', '" , $key)."'";
        // print_r($key);die();

        $sql = "SELECT conf_key as `key`, conf_value as value FROM configuration WHERE conf_key IN ($key)";

        try {
          $result = $this->executeQueryForList($sql);
          foreach ($result as $configuration) {
              $response->{$configuration->key} = $configuration->value;
          }
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $response;
    }


}<?php

namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\exception\NucleusException;
use Exception;

class PermissionService extends BaseService
{

    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
    }

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {
    }

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();

        return self::$_instance;
    }

    /**
     * return permission id by permission code
     * @param string $permissionCode
     * @return integer|NULL
     * @throws NucleusException
     */
    private function getPermissionIdByPermissionCode($permissionCode)
    {
        $id = NULL;
        $permissionCode = $this->realEscapeString($permissionCode);
        $query = "SELECT id FROM api_permission WHERE code='" . $permissionCode . "'";
        try {
            $result = $this->executeQueryForObject($query);
            if ($result)
                $id = $result->id;

        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $id;
    }

    /**
     * return apiAccessTokenId by accessId
     * @param integer $APIAccessId
     * @return integer|NULL
     * @throws NucleusException
     */
    private function getAPIAccessTokenIdByAccessId($APIAccessId)
    {
        $id = NULL;

        $APIAccessId = $this->realEscapeString($APIAccessId);
        $query = "SELECT `id` FROM `api_access_token` WHERE `access_id`='" . $APIAccessId . "'";
        try {
            $result = $this->executeQueryForObject($query);
            if ($result)
                $id = $result->id;
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $id;
    }

    /**
     * Insert permission to access id
     * @param integer $APIAccessId
     * @param string $permissionCode
     * @return bool
     * @throws NucleusException
     */
    public function addPermissionToAccessId($APIAccessId, $permissionCode)
    {
        $id = null;

        $APIAccessId = $this->realEscapeString($APIAccessId);
        $permissionCode = $this->realEscapeString($permissionCode);
        $permissionId = $this->getPermissionIdByPermissionCode($permissionCode);
        $APIAccessTokenId = $this->getAPIAccessTokenIdByAccessId($APIAccessId);
        if (empty($permissionId))
            throw new NucleusException(NucleusException::INVALID_PERMISSION_ID, "Invalid permission id");
        if (empty($APIAccessTokenId))
            throw new NucleusException(NucleusException::INVALID_API_ACCESS_TOKEN_ID, "Invalid api access token id");

        $query = "INSERT INTO api_access_token_permission (api_access_token_id,api_permission_id) VALUES ($APIAccessTokenId,$permissionId)";
        try {
            $result = $this->executeQueryForObject($query, true);
            $id = $result;
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $id;
    }

    /**
     * checking api has permission. return api_access_token_permission id
     * @param integer $APIAccessId
     * @param string $permissionCode
     * @return integer|null
     * @throws NucleusException
     */
    public function APIHasPermission($APIAccessId, $permissionCode)
    {
        $id = null;

        $APIAccessId = $this->realEscapeString($APIAccessId);
        $permissionCode = $this->realEscapeString($permissionCode);
        $APIAccessTokenId = $this->getAPIAccessTokenIdByAccessId($APIAccessId);
        $permissionId = $this->getPermissionIdByPermissionCode($permissionCode);

        if (empty($permissionId))
            throw new NucleusException(NucleusException::INVALID_PERMISSION_ID, "Invalid permission id");
        if (empty($APIAccessTokenId))
            throw new NucleusException(NucleusException::INVALID_API_ACCESS_TOKEN_ID, "Invalid api access token id");
        $query = "SELECT id FROM api_access_token_permission WHERE api_access_token_id=$APIAccessTokenId AND api_permission_id=$permissionId";
        try {
            $result = $this->executeQueryForObject($query);
            if ($result)
                $id = $result->id;
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $id;
    }
}<?php

namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\dto\Addon;
use com\linways\nucleus\core\dto\AddonEnabled;
use com\linways\nucleus\core\exception\NucleusException;
use com\linways\nucleus\core\mapper\AddonServiceMapper;

class AddonService extends BaseService
{

    private static $_instance = null;

    private $mapper = [];

    private function __construct()
    {
        $this->mapper = AddonServiceMapper::getInstance()->getMapper();
    }

    private function __clone()
    {
    }

    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            self::$_instance = new self();
        return self::$_instance;
    }

    /**
     * creating addon entry to the database
     *
     * @param Addon $addon
     * @throws NucleusException
     * @return array $data
     */
    public function createAddon($addon)
    {
        $data = [];

        $addon->name = $this->realEscapeString($addon->name);
        $addon->description = $this->realEscapeString($addon->description);
        $addon->url = $this->realEscapeString($addon->url);
        $addon->packageName = $this->realEscapeString($addon->packageName);
        $addon->scope = $this->realEscapeString($addon->scope);

        $query = "INSERT INTO `addon` (
                    `name`,
                    `description`,
                    `url`,
                    `package_name`,
                    `scope`,
                    `updated_date`,
                    `created_date`) VALUES (' $addon->name ',
                    '$addon->description',
                    '$addon->url',
                    '$addon->packageName',
                    '$addon->scope',
                    utc_timestamp(),
                    utc_timestamp())";

        $result = $this->executeQueryForObject($query, true);
        $data['addonId'] = $result;
        $res = AddonAttributeService::getInstance()->createAddonAttribute($addon->addonAttribute, $result);
        $data['attributesId'] = $res;

        return $data;
    }

    public function updateAddon($addon)
    {
        $status = false;

        // TODO

        return status;
    }

    public function deleteAddon($addonId)
    {
        $status = false;

        // TODO

        return $status;
    }

    /**
     * return college id of corresponding college code
     * @param string $collegeCode
     * @throws \Exception
     * @return integer|NULL
     */
    private function getCollegeIdByCollegeCode($collegeCode)
    {
        $id = NULL;
        if (empty($collegeCode))
            return $id;
        $query = "SELECT id FROM `colleges` WHERE `college_code`='$collegeCode'";
        $result = $this->executeQueryForObject($query);
        if ($result)
            $id = $result->id;

        return $id;
    }

    /**
     * Activating addon by addon id and college code
     * @param AddonEnabled $addonEnabled
     * @param string $collegeCode
     * @throws NucleusException
     * @return array
     */
    public function activateAddon($addonEnabled, $collegeCode)

    {
        $enabledAddon = [];
        $collegeId = 0;

        $collegeCode = $this->realEscapeString($collegeCode);
        $addonEnabled->scopeId = $this->realEscapeString($addonEnabled->scopeId);
        $addonEnabled->addonId = $this->realEscapeString($addonEnabled->addonId);

        if (empty($addonEnabled->scopeId))
            throw new NucleusException(NucleusException::INVALID_SCOPE_ID, "Invalid/empty scope id");

        if (empty($addonEnabled->addonId))
            throw new NucleusException(NucleusException::INVALID_ADDON_ID, "Invalid/empty addon id");

        if (empty($collegeCode))
//            throw new NucleusException(NucleusException::INVALID_COLLEGE_CODE, "Invalid/empty college code");
            throw new \InvalidArgumentException("TEST MESSAGE");
        $collegeId = $this->getCollegeIdByCollegeCode($collegeCode);

        if (empty($collegeId))
            throw new NucleusException(NucleusException::INVALID_COLLEGE_CODE, "Invalid/empty college code");

        $query = "INSERT INTO `addon_enabled` (`college_id`,
                `addon_id`,
                `scope_id`,
                `created_date`,
                `updated_date`) VALUES ('$collegeId',
                '$addonEnabled->addonId',
                '$addonEnabled->scopeId',
                utc_timestamp(),
                utc_timestamp())";
        try {

            $res = $this->executeQueryForObject($query, true);
            $enabledAddon['id'] = $res;
            $addonAttributes = $addonEnabled->addonAttributes;
            $result = AddonAttributeService::getInstance()->createAddonAttributeValue($addonAttributes, $res);
            $enabledAddon['attributesValuesId'] = $result;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }


        return $enabledAddon;
    }

    public function updateCredentials($updateDetails)
    {
        $status = false;

        // TODO

        return $status;
    }

    /**
     * Disable addon by enabled addon id
     * @param $enabledAddonId
     * @return bool
     * @throws \Exception
     */
    public function disableAddon($enabledAddonId)
    {
        $status = false;

        $enabledAddonId = $this->realEscapeString($enabledAddonId);
        $query = "DELETE FROM `addon_enabled` WHERE `id` ='$enabledAddonId' ";

        $result = AddonAttributeService::getInstance()->deleteAddonAttributeValues($enabledAddonId);
        $res = $this->executeQueryForObject($query, true);
        $status = true;
        return $status;
    }

    /**
     * Method for getting enabled addons of a college
     *
     * @param string $collegeCode
     * @param string $scopeName
     * @param int $scopeId
     * @throws \Exception
     * @return [Addon]
     */
    public function getAddonsEnabled($collegeCode, $scopeName, $scopeId = NULL)
    {
        $addons = [];

        $collegeCode = $this->realEscapeString($collegeCode);
        $scopeName = $this->realEscapeString($scopeName);
        $scopeId = $this->realEscapeString($scopeId);

        $query = "SELECT a.*, ae.id AS enabledAddonId, ae.scope_id ,ae.college_id AS collegeId FROM
                 addon_enabled  ae INNER JOIN addon a ON ae.addon_id=a.id
                 INNER JOIN colleges c ON c.id=ae.college_id WHERE a.id>0";

        if (empty($collegeCode)) {
            throw new NucleusException(NucleusException::INVALID_COLLEGE_CODE, "Invalid college code");
        } else {
            $query .= " AND c.college_code='$collegeCode' ";
        }
        if (!empty($scopeName)) {
            $query .= " AND a.scope='$scopeName' ";
        }
        if (!empty($scopeId)) {
            $query .= " AND ae.scope_id='$scopeId' ";
        }
        try {
            $addons = $this->executeQueryForList($query, $this->mapper[AddonServiceMapper::GET_ADDONS_ENABLED]);
        } catch (\Exception $e) {

            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $addons;
    }

    /**\
     * returns all addons
     * @throws NucleusException
     * @return [Addon]
     */
    public function getAllAddons()
    {
        $addons = [];

        $query = "SELECT a.*, aa.id AS attributeId, aa.type AS attrType, aa.name AS attrName,
                  aa.code AS attrCode, aa.required , aa.data_type
                  FROM addon a LEFT JOIN addon_attr aa ON aa.addon_id=a.id WHERE a.id>0";

        try {
            $addons = $this->executeQueryForList($query, $this->mapper[AddonServiceMapper::GET_ALL_ADDONS]);
        } catch (\Exception $e) {

            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $addons;
    }

    /**
     * returns a particular addon by addon id and type.
     * @param integer $addonId
     * @param string $attrType (optional)
     * @return Addon
     * @throws NucleusException
     */
    public function getAddonByIdAndAttributeType($addonId, $attrType = NULL)
    {
        $addons = [];

        $addonId = $this->realEscapeString($addonId);
        $attrType = $this->realEscapeString($attrType);

        $query = "SELECT a.*, aa.id AS attributeId, aa.type AS attrType, aa.name AS attrName,
                  aa.code AS attrCode, aa.required, aa.data_type
                  FROM addon a LEFT JOIN addon_attr aa ON aa.addon_id=a.id WHERE a.id>0";
        if (empty($addonId))
            throw new NucleusException(NucleusException::INVALID_ADDON_ID, "Invalid addon id");
        else
            $query .= " AND a.id='$addonId'";

        if ($attrType)
            $query .= " AND (aa.type='$attrType' OR aa.type is null)";


        try {
            $addons = $this->executeQueryForList($query, $this->mapper[AddonServiceMapper::GET_ALL_ADDONS]);
        } catch (\Exception $e) {

            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $addons;
    }

    public function getEnabledAddonByAddonIdAndCollegeCode($addonId,$collegeCode,$attrType=NULL){
        $addons=[];

        $addonId = $this->realEscapeString($addonId);
        $attrType = $this->realEscapeString($attrType);
        $collegeCode = $this->realEscapeString($collegeCode);

        $collegeId = $this->getCollegeIdByCollegeCode($collegeCode);

        if(empty($collegeId))
            throw new NucleusException(NucleusException::INVALID_COLLEGE_CODE,"Invalid college code");

        $query = "SELECT a.*, aa.id AS attributeId, aa.type AS attrType, aa.name AS attrName, aa.code AS attrCode,
                  aa.required AS required, ae.id as enabledAddonId, aa.data_type, aav.id as attributeValueId ,aav.value FROM addon a
                  LEFT JOIN addon_enabled ae ON ae.addon_id = a.id LEFT JOIN addon_attr aa ON aa.addon_id=a.id
                  LEFT JOIN addon_attr_value aav ON aav.addon_attr_id = aa.id WHERE a.id>0 AND (aav.enabled_addon_id = ae.id OR aav.enabled_addon_id is null)";
        if($addonId)
            $query.=" AND ae.addon_id = '$addonId'";
        else
            throw new NucleusException(NucleusException::INVALID_ADDON_ID,"Invalid Addon Id");

        if($collegeId)
            $query.=" AND ae.college_id = '$collegeId'";

        if($attrType)
            $query .= " AND (aa.type='$attrType' OR aa.type is null)";
        try{
            $addons = $this->executeQueryForList($query, $this->mapper[AddonServiceMapper::GET_ENABLED_ADDON_BY_ADDONID_AND_COLLEGECODE]);
        }catch (\Exception $e){
            throw new NucleusException($e->getCode(),$e->getMessage());
        }
        return $addons;
    }

    /**
     * Returns all available addons with enabled addon id ,if addon is enabled for the given college
     * @param string $collegeCode
     * @return [Addon]
     * @throws NucleusException
     */
    public function getAllAddonWithEnabledFlag($collegeCode)
    {

        $addons = [];

        $collegeCode = $this->realEscapeString($collegeCode);

        $collegeId = $this->getCollegeIdByCollegeCode($collegeCode);
        if (empty($collegeId)) {
            throw new NucleusException(NucleusException::INVALID_COLLEGE_CODE, "Invalid college code");
        }
//        (CASE WHEN ae.addon_id IS NULL THEN 0 ELSE 1 END) AS isEnabled

        $query = "SELECT a.*, ae.id as enabledAddonId
                  FROM addon  a LEFT JOIN addon_enabled ae ON ae.addon_id = a.id AND
                  ae.college_id = '$collegeId'";

        try {
            $addons = $this->executeQueryForList($query, $this->mapper[AddonServiceMapper::GET_ALL_ADDON_WITH_ENABLED_FLAG]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $addons;
    }

    /**
     * return enabled addon by package name and college code
     * @param string $packageName
     * @param string $collegeCode
     * @return [AddonEnabled]
     * @throws NucleusException
     */
    public function getEnabledAddonByPackageNameAndCollegeCode($packageName, $collegeCode)
    {
        $addon = NULL;

        $collegeCode = $this->realEscapeString($collegeCode);
        $packageName = $this->realEscapeString($packageName);

        $collegeId = $this->getCollegeIdByCollegeCode($collegeCode);
        if (empty($collegeId)) {
            throw new NucleusException(NucleusException::INVALID_COLLEGE_CODE, "Invalid college code");
        }

        $query = "SELECT ae.id, ae.scope_id as scopeId, a.id as addonId, a.name as addonName, a.url,
                  a.scope , a.package_name as packageName ,aa.type as attrType, aa.id as attributeId,
                  aa.name as attrName, aa.code as attrCode, aa.required, aa.data_type, aav.id as attributeValueId,
                  aav.value FROM addon_enabled ae INNER JOIN addon a ON ae.addon_id=a.id
                  LEFT JOIN addon_attr aa ON aa.addon_id=ae.addon_id LEFT JOIN addon_attr_value
                  aav ON aav.addon_attr_id = aa.id WHERE a.package_name='$packageName' AND ae.college_id='$collegeId' AND (aav.enabled_addon_id = ae.id OR aav.enabled_addon_id is null)";
        try {
            $addon = $this->executeQueryForList($query, $this->mapper[AddonServiceMapper::GET_ENABLED_ADDON_BY_PACKAGE_NAME_AND_COLLEGE_CODE]);

        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $addon;
    }

    public function getEnabledAddonWithAddonDataStoreByPackageNameAndCollegeCode($packageName,$collegeCode){
        $addon = NULL;

        $collegeCode = $this->realEscapeString($collegeCode);
        $packageName = $this->realEscapeString($packageName);

        $collegeId = $this->getCollegeIdByCollegeCode($collegeCode);
        if (empty($collegeId)) {
            throw new NucleusException(NucleusException::INVALID_COLLEGE_CODE, "Invalid college code");
        }

        $query = "SELECT ae.id, ae.scope_id as scopeId, a.id as addonId, a.name as addonName, a.url,
                  a.scope ,a.package_name as packageName ,aa.type as attrType, aa.id as attributeId,
                  aa.name as attrName, aa.code as attrCode, aa.required, aa.data_type,
                  aav.id as attributeValueId, aav.value, ads.id as addonDataStoreId,
                  ads.enabled_addon_id as enabledAddonId, ads.value as addonDataStoreValue,
                  ads.key as addonDataStoreKey FROM addon_enabled ae INNER JOIN addon a
                  ON ae.addon_id=a.id LEFT JOIN addon_attr aa ON aa.addon_id=ae.addon_id
                  LEFT JOIN addon_attr_value aav ON aav.addon_attr_id = aa.id LEFT JOIN addon_data_store ads
                  ON ads.enabled_addon_id = ae.id WHERE a.package_name='$packageName' AND
                  ae.college_id='$collegeId' AND (aav.enabled_addon_id = ae.id OR aav.enabled_addon_id is null)";
        try {
            $addon = $this->executeQueryForList($query, $this->mapper[AddonServiceMapper::GET_ENABLED_ADDON_WITH_ADDON_DATA_STORE_MAPPER]);

        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $addon;
    }

}
<?php 
namespace com\linways\nucleus\core\service\user;


use com\linways\nucleus\core\dto\user\User;
use com\linways\nucleus\core\utils\user\UserServiceUtils;
use com\linways\nucleus\core\service\BaseService;
use com\linways\nucleus\core\exception\NucleusException;

class UserService extends BaseService{

    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {}

    // Prevent any oustide instantiation of this class
    
    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {}

    // Prevent any copy of this object
    
    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (! is_object(self::$_instance))
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();
        
        return self::$_instance;
    }


/**
 * [createNewUser description]
 * @param  User   $user [description]
 * @return [type]       [description]
 * @author gadheyan
 */
    public function createNewUser(User $user){
        $sql = "";

        $user->salt = UserServiceUtils::getSalt();
        $user->password = md5($user->password.$user->salt);


        $user = $this->realEscapeObject($user);

        $sql = "INSERT INTO `nucleus_user` (`name`, `email`, `username`, `password`, `salt`, `created_by`, `created_date`, `updated_by`, `updated_date`) VALUES ('$user->name', '$user->email', '$user->username', '$user->password', '$user->salt', '$user->createdBy', UTC_TIMESTAMP(), '$user->updatedBy', UTC_TIMESTAMP());";

        try {
            $index = $this->executeQueryForObject($sql, true);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $index;
    }

/**
 * [login description]
 * @param  User   $user [description]
 * @return [type]       [description]
 * @author gadheyan
 */
    public function login(User $user){
         $sql = "";
         $result = null;
         $user = $this->realEscapeObject($user);
         $sql = "SELECT id, name, email, username FROM nucleus_user WHERE (email = '$user->email' OR username = '$user->username') AND password = md5(concat('$user->password', salt));";
        try {
            $result = $this->executeQueryForObject($sql);
                    $_SESSION['userId'] = '1';
        // $_SESSION['userId'] = $result['id'];
        $_SESSION['email'] = $result->email;
        $_SESSION['name'] = $result->name;
        $_SESSION['username'] = $result->username;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $result;
    }


/**
 * [getAllPermissions description]
 * @param  [type] $userId [description]
 * @return [type]         [description]
 * @author gadheyan
 */
    public function getAllPermissions($userId){
        $sql = "";
        $permissions = [];
        $response = [];

        $userId = $this->realEscapeString($userId);

        $sql = "SELECT 
                    pr.name as name
                FROM
                    nucleus_user nu
                        INNER JOIN
                    user_permission up ON nu.id = up.nucleus_user_id
                        AND nu.id = '$userId'
                        INNER JOIN
                    permission pr ON pr.id = up.permission_id 
                UNION 
                SELECT 
                    pr.name
                FROM
                    nucleus_user nu
                        INNER JOIN
                    user_group ug ON nu.id = ug.nucleus_user_id
                        AND nu.id = '$userId'
                        INNER JOIN
                    group_permission gp ON gp.group_id = ug.group_id
                        INNER JOIN
                    permission pr ON pr.id = gp.permission_id;";


        try {
           $permissions = $this->executeQueryForList($sql); 
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        foreach ($permissions as $permission) {
            $response[] = $permission->name;
        }
        return $response;

    }


    public function userHasPermission($userId, $permissions){
        
        $userPermissions = $this->getAllPermissions($userId);
        foreach ($permissions as $permission) {
            if(!in_array($permission, $userPermissions)){
                return false;
            }
        }
        return true;

    }




}<?php

namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\dto\ExpenseCategory;
use com\linways\nucleus\core\exception\NucleusException;
use com\linways\nucleus\core\mapper\ExpenseServiceMapper;
use com\linways\nucleus\core\utils\StringUtils;
use Phinx\Util\Util;

class ExpenseCategoryService extends BaseService
{

    private static $_instance = null;

    private function __construct()
    {
        $this->mapper = ExpenseServiceMapper::getInstance()->getMapper();
    }

    private function __clone()
    {
    }

    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            self::$_instance = new self();
        return self::$_instance;
    }

    /**
     * returns all expenses in the database
     * @return array array of client object
     * @throws NucleusException
     */
    public function getAllExpenseCategories()
    {
        $expenseCategories = [];
        $query = "SELECT * from `expense_category`";
        try {
            $expenseCategories = $this->executeQueryForList($query, $this->mapper[ExpenseServiceMapper::GET_EXPENSE_CATEGORY_MAPPER]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $expenseCategories;
    }

    /**
     * @param ExpenseCategory $expenseCategory
     * @return null|\stdClass
     * @throws NucleusException
     */
    public function createExpenseCategory($expenseCategory)
    {
        $id = null;
        $expenseCategory = $this->realEscapeObject($expenseCategory);

        /**
         * Remove below code after include user management
         */
        $expenseCategory->createdBy = $expenseCategory->createdBy ? $expenseCategory->createdBy : 0;
        $expenseCategory->updatedBy = $expenseCategory->updatedBy ? $expenseCategory->updatedBy : 0;

        if (empty($expenseCategory->name))
            throw new NucleusException(NucleusException::INVALID_EXPENSE_CATEGORY_NAME, "Category name is required");
        $query = "INSERT INTO `expense_category`
                  (`name`, `created_by`, `created_at`, `updated_by`, `updated_at`) 
                  VALUES ('" . $expenseCategory->name . "',$expenseCategory->createdBy,UTC_TIMESTAMP(),
                  $expenseCategory->updatedBy,UTC_TIMESTAMP()) ";
        try {
            $id = $this->executeQueryForObject($query, true);

        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $id;
    }


}
<?php
namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\service\ConfigurationService;
use com\linways\nucleus\core\exception\NucleusException;
use \Firebase\JWT\JWT;
use com\linways\nucleus\core\dto\SingleSignonLog;

class SingleSignonService extends BaseService {

  private static $_instance = null;

  // /Condition 2 - Locked down the constructor
  private function __construct() {
  }
  // Prevent any oustide instantiation of this class

  // /Condition 3 - Prevent any object or instance of that class to be cloned
  private function __clone() {
  }
  // Prevent any copy of this object

  // /Condition 4 - Have a single globally accessible static method
  public static function getInstance() {
    if (!is_object(self::$_instance))
    // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
    {
            self::$_instance = new self();
    }

    return self::$_instance;
  }

    const USER_EMAIL_FOR_SINGLE_SIGN_ON_AS_ADMIN = "USER_EMAIL_FOR_SINGLE_SIGN_ON_AS_ADMIN";
    const SHARED_KEY_FOR_SINGLE_SIGN_ON = "SHARED_KEY_FOR_SINGLE_SIGN_ON";//stored in configuration table in nucleus database.("vkn wrote pitamahan")

    /**
     * [generateToken generate a jwt for authentication.]
     * @return [string] [json web token]
     */
    public function generateToken(){
        $key = "";
        $token = "";
        $email = "";
        $credentials = null;

        $credentials = ConfigurationService::getInstance()->getMultipleConfigurations(array(self::USER_EMAIL_FOR_SINGLE_SIGN_ON_AS_ADMIN, self::SHARED_KEY_FOR_SINGLE_SIGN_ON));
        $token = array(
        "email" => $credentials->{ self::USER_EMAIL_FOR_SINGLE_SIGN_ON_AS_ADMIN }, // email address of logged-in user
        "iss" => "nucleus.linways.com",
        "exp" => time() + (3 *60), // expires 3 minutes from now
        "iat" => time()
        );

        return JWT::encode( $token, $credentials->{self::SHARED_KEY_FOR_SINGLE_SIGN_ON} );
    }


/**
 * [validateToken description]
 * @author gadheyan
 * @param  [string] $token [description]
 * @return [array]        [description]
 */
    public function validateToken($token){
        $credentials = null;

        $credentials = ConfigurationService::getInstance()->getMultipleConfigurations(array(self::SHARED_KEY_FOR_SINGLE_SIGN_ON));

        return (array) JWT::decode($token, $credentials->{self::SHARED_KEY_FOR_SINGLE_SIGN_ON}, array('HS256'));

    }


    public function logSingleSignonDetails(SingleSignonLog $singleSignonLog){
        $sql = "";
        $response = NULL;
        $singleSignonLog = $this->realEscapeObject($singleSignonLog);

        $sql = "INSERT INTO `single_signon_log` (`user_id`, `college_code`, `logged_in_as`, `login_time`) VALUES ('$singleSignonLog->userId', '$singleSignonLog->collegeCode', '$singleSignonLog->loggedInAs', UTC_TIMESTAMP());";
        try {
            $response = $this->executeQueryForObject($sql, true);
        } catch (\Exception $e) {
             throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $response;
    }
}
<?php
namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\dto\AddonAttribute;
use com\linways\nucleus\core\exception\NucleusException;

class AddonAttributeService extends BaseService
{

    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {}

    // Prevent any oustide instantiation of this class
    
    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {}

    // Prevent any copy of this object
    
    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (! is_object(self::$_instance))
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();
        
        return self::$_instance;
    }

    /**
     *
     * @param AddonAttribute $addonAttributes
     * @param integer $addonId
     * @throws NucleusException
     * @return [integer]|NULL
     */
    public function createAddonAttribute($addonAttributes, $addonId)
    {
        $id = [];
        
        $addonId = $this->realEscapeString($addonId);
        
        try {
            foreach ($addonAttributes as $addonAttribute) {
                $addonAttribute->type = $this->realEscapeString($addonAttribute->type);
                $addonAttribute->name = $this->realEscapeString($addonAttribute->name);
                $addonAttribute->code = $this->realEscapeString($addonAttribute->code);
                $addonAttribute->required = $this->realEscapeString($addonAttribute->required);
                $addonAttribute->dataType = $this->realEscapeString($addonAttribute->dataType);
                
                if (empty($addonAttribute->type) || empty($addonAttribute->type) || empty($addonAttribute->name) || empty($addonAttribute->code))
                    throw new NucleusException("Invalid or empty fields");
                
                $addonAttribute->dataType = empty($addonAttribute->dataType) ? NULL : $addonAttribute->dataType;
                $addonAttribute->required = empty($addonAttribute->required) ? NULL : $addonAttribute->required;
                
                $query = "INSERT INTO `addon_attr` (`type`,
                `name`,
                `code`,
                `addon_id`,
                `required`,
                `data_type`,
                `created_date`,
                `updated_date`) VALUES ('$addonAttribute->type',
                '$addonAttribute->name',
                '$addonAttribute->code',
                '$addonId',
                '$addonAttribute->required',
                '$addonAttribute->dataType',
                utc_timestamp(),
                utc_timestamp())";
                
                $result = $this->executeQueryForObject($query, true);
                $id[] = $result;
            }
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode, $e->getMessage);
        }
        
        return $id;
    }

    /**
     *
     * @param AddonAttribute $addonAttribute
     * @param integer $addonId
     * @return boolean
     */
    public function updateAddonAttribute($addonAttribute, $addonId)
    {
        $status = false;
        
        // TODO
        
        return $status;
    }

    /**
     *
     * @param integer $addonAttributeId
     * @return boolean
     */
    public function deleteAddonAttribute($addonAttributeId)
    {
        $status = false;
        
        // TODO
        
        return $status;
    }

    /**
     *
     * @param integer $addonAttributeId
     * @param integer $addonId
     * @param string $code
     * @param string $type
     * @param string $dataType
     * @return array
     */
    public function getAddonAttributes($addonAttributeId, $addonId, $code = NULL, $type = NULL, $dataType = NULL)
    {
        $addonAttributes = [];
        
        return $addonAttributes = [];
    }

    /**
     *
     * @param [AddonAttributeValue] $addonAttrValue
     * @param integer $addonEnbledId
     * @throws NucleusException
     * @return boolean
     */
    public function createAddonAttributeValue($addonAttrValue, $addonEnbledId)
    {
        $id = [];
        $sql = NULL;
        $addonEnbledId = $this->realEscapeString($addonEnbledId);
        $query = "INSERT INTO `addon_attr_value` (`enabled_addon_id`,`addon_attr_id`,`value`,`created_date`,`updated_date`) VALUES('$addonEnbledId',";
        
        try {
            foreach ($addonAttrValue as $attributeValue) {
                $attributeValue->value = $this->realEscapeString($attributeValue->value);
                $attributeValue->addonAttributeId = $this->realEscapeString($attributeValue->addonAttributeId);
                $sql = $query;
                $sql = $sql . "'$attributeValue->addonAttributeId','$attributeValue->value',utc_timestamp(),utc_timestamp())";
                $result = $this->executeQueryForObject($sql, true);
                $id[] = $result;
            }
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $id;
    }

    /**
     *
     * @param [AddonAttributeValue] $addonAttrValue
     * @throws NucleusException
     * @return boolean
     */
    public function updateAddonAttributeValue($addonAttributeValues,$enabledAddonid)
    {
        $status = false;
        $enabledAddonid = $this->realEscapeString($enabledAddonid);
        $query = "UPDATE `addon_attr_value` SET ";
        $sql = NULL;
        
        try {
            foreach ($addonAttributeValues as $attributeValue) {
                $attributeValue->value = $this->realEscapeString($attributeValue->value);
                $attributeValue->id = $this->realEscapeString($attributeValue->id);
                $sql = $query;
                $sql = $sql . "`value`='$attributeValue->value',
                            `updated_date`=utc_timestamp()
                            WHERE `id`='$attributeValue->id' AND enabled_addon_id = '$enabledAddonid'";
                $result = $this->executeQueryForObject($sql, true);
            }
            $status = true;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $status;
    }

    /**
     *
     * @param integer $enabledAddonId
     * @throws NucleusException
     * @return boolean
     */
    public function deleteAddonAttributeValues($enabledAddonId)
    {
        $status = false;
        $enabledAddonId = $this->realEscapeString($enabledAddonId);
        
        $query = "DELETE FROM `addon_attr_value` WHERE `enabled_addon_id`='$enabledAddonId'";
        try {
            $result = $this->executeQueryForObject($query, true);
            $status = true;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode, $e->getMessage);
        }
        return $status;
    }

    /**
     * return addon attribute id by addon attribute code and addon id
     *
     * @param string $code
     * @param integer $addonId
     * @return integer
     * @throws NucleusException
     */
    public function getAttributeIdByCodeAndAddonId($code, $addonId)
    {
        $id = NULL;
        
        $query = "SELECT id FROM `addon_attr` WHERE  `id`>0";
        if (empty($code))
            throw new NucleusException(NucleusException::INVALID_ATTRIBUTE_CODE, "Invalid attribte code");
        else
            $query .= " AND `code`='$code'";
        
        if (empty($addonId))
            throw new NucleusException(NucleusException::INVALID_ADDON_ID, "Invalid Addon Id");
        else
            $query .= " AND `addon_id`='$addonId'";
        
        try {
            $result = $this->executeQueryForObject($query);
            $id = $result->id;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        
        return $id;
    }
}

<?php

namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\dto\Expense;
use com\linways\nucleus\core\mapper\ExpenseServiceMapper;
use com\linways\nucleus\core\exception\NucleusException;

class ExpenseService extends BaseService
{

    private static $_instance = null;

    private function __construct()
    {
        $this->mapper = ExpenseServiceMapper::getInstance()->getMapper();
    }

    private function __clone()
    {
    }

    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            self::$_instance = new self();
        return self::$_instance;
    }

    /**
     * returns all expenses in the database
     * @return array array of client object
     * @throws NucleusException
     */
    public function getAllExpenses()
    {
        $expenses = [];
        $query = "SELECT e.*, ec.id as categoryId, ec.name as categoryName
        FROM `expenses` as e inner join `expense_category` as ec
        ON e.expense_category_id = ec.id";
        try {
            $expenses = $this->executeQueryForList($query, $this->mapper[ExpenseServiceMapper::GET_ALL_CLIENTS]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $expenses;
    }


    /**
     *
     * @param Expense $expense
     * @throws NucleusException
     * @return int | NULL
     */
    public function createExpense($expense)
    {
        $id = null;
        $expense = $this->realEscapeObject($expense);

        if (empty($expense->item) || empty($expense->amount)) {
            throw new NucleusException("ITEM NAME AND AMOUNT IS REQUIRED", "ITEM NAME AND AMOUNT IS REQUIRED");
        }

        $query = "INSERT INTO `expenses` (`item`,`amount`,`expense_category_id`, `date`)
        VALUES ('$expense->item', '$expense->amount', '$expense->expenseCategoryId', '$expense->date')";
        try {
            $id = $this->executeQueryForObject($query, true);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $id;
    }

    /**
     * Updates a client if exists otherwise creates a new client
     * @param  Expense $expense client object
     * @return int | NucleusException    id of the updated item
     * @throws NucleusException
     */
    public function upsertExpense($expense)
    {
        if ($expense->id) {
            $expense = $this->realEscapeObject($expense);

            if (empty($expense->item) && empty($expense->amount)) {

                if (!empty($expense->id)) {
                    return $this->deleteExpense($expense->id);
                } else {
                    throw new NucleusException(NucleusException::CLIENT_NAME_REQUIRED, "Expense item, amount and date is required");
                }
            }

            $query = "UPDATE `expenses` SET
          `item`='$expense->item',
          `amount`='$expense->amount',
          `expense_category_id`= $expense->expenseCategoryId,
          `date`= '$expense->date' where `id` = $expense->id";
            try {
                $this->executeQueryForObject($query, true);
            } catch (\Exception $e) {
                throw new NucleusException($e->getCode(), $e->getMessage());
            }
            return $expense->id;
        } else {
            //if ID does not exists calls create method
            return $this->createExpense($expense);
        }
    }

    /**
     * Delete a client using clientID
     * @param  int $expenseId id of the client to be deleted
     * @return  null | NucleusException    True if successfully executed.
     * @throws NucleusException
     */
    public function deleteExpense($expenseId)
    {
        $expenseId = $this->realEscapeString($expenseId);
        if (empty($expenseId)) {
            throw new NucleusException(NucleusException::CLIENT_ID_REQUIRED, "Expense id is required");
        }
        $query = "DELETE FROM `expenses` where `id`=$expenseId";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return null;
    }

    /**
     * Return total expenses by category for a given month and year
     * @param string $date
     * @return object     with amount and category
     * @throws NucleusException
     */
    public function getTotalExpenseByCategoryAndMonth($date)
    {
        $expenses = [];
        $date = $this->realEscapeString($date);
        $year = date("Y", strtotime($date));
        $month = date("m", strtotime($date));
        $query = "SELECT SUM(ex.amount) AS amount, ec.name FROM
        expenses AS ex INNER JOIN expense_category AS ec ON
        ex.expense_category_id = ec.id WHERE
        YEAR(date)='" . $year . "' AND MONTH(date)='" . $month . "' group by ex.expense_category_id";
        try {
            $expenses = $this->executeQueryForList($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $expenses;
    }

    /**
     * returns total income from billing customers
     * @param string $date
     * @return mixed
     * @throws NucleusException
     */
    public function getTotalIncomeFromBillingCustomers($date)
    {
        $date = $this->realEscapeString($date);
        $endDate = date("Y-m-d", strtotime($date));
        $query = "SELECT SUM(total_annual_income) AS annualIncome
        FROM clients WHERE  DATE(billing_start_date)<='" . $endDate . "'";
        try {
            $income = $this->executeQueryForObject($query);
            return $income->annualIncome;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
    }

    /**
     * Calculate average expense for single tenant
     * @param string $date
     * @return \stdClass
     * @throws NucleusException
     */
    public function averageExpenseForSingleTenant($date)
    {
        $numberOfBillingTenants = count(CollegeService::getInstance()->getBillingColleges($date));
        $categoryWiseExpenses = $this->getTotalExpenseByCategoryAndMonth($date);
        $annualIncome = $this->getTotalIncomeFromBillingCustomers($date);
        $averageAnnualIncomePerTenant = $annualIncome / $numberOfBillingTenants;
        $averageAnnualExpensePerTenant = 0;
        foreach ($categoryWiseExpenses as $category) {
            //converting monthly expense to annual expense per tenant
            $category->amount = ($category->amount / $numberOfBillingTenants) * 12;
            $averageAnnualExpensePerTenant += $category->amount;
        }
        $response = new \stdClass();
        $response->averageAnnualIncomePerTenant = $averageAnnualIncomePerTenant;
        $response->averageAnnualExpensePerTenant = $averageAnnualExpensePerTenant;
        $response->numberOfBillingTenants = $numberOfBillingTenants;
        $response->categoryWiseExpenses = $categoryWiseExpenses;
        return $response;

    }

    /**
     * Returns all expenses in a month
     * @param integer $year
     * @param integer $month
     * @return null|Object
     * @throws NucleusException
     * @throws \Exception
     */
    public function getAllExpensesOfSingleMonth($year, $month)
    {
        $data = null;
        $year = $this->realEscapeString($year);
        $month = $this->realEscapeString($month);
        if (empty($month) || empty($year))
            throw new NucleusException(NucleusException::INVALID_DATE, "Date required");

        $query = "SELECT e.id,e.item,e.amount, DATE(e.date) as date, ec.name as category FROM `expenses` e 
                  INNER JOIN `expense_category` ec ON e.expense_category_id=ec.id WHERE
                  YEAR(e.date)=$year AND MONTH(e.date)=$month";

        try {
            $data = $this->executeQueryForList($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $data;
    }
}
<?php
/**
 * Created by PhpStorm.
 * User: jithin
 * Date: 30/10/17
 * Time: 2:23 PM
 */

namespace com\linways\nucleus\core\service;


use com\linways\nucleus\core\dto\AddonDataStore;
use com\linways\nucleus\core\exception\NucleusException;
use com\linways\nucleus\core\mapper\AddonDataStoreMapper;
use Exception;

class AddonDataStoreSevice extends BaseService
{
    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
        $this->mapper = AddonDataStoreMapper::getInstance()->getMapper();
    }

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {
    }

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();

        return self::$_instance;
    }

    /**
     * returns addon data store details
     * @param integer $enabledAddonId
     * @param string $key
     * @return AddonDataStore $data
     * @throws NucleusException
     */
    public function getData($enabledAddonId, $key)
    {
        $data = NULL;

        $enabledAddonId = $this->realEscapeString($enabledAddonId);
        $key = $this->realEscapeString($key);

        $query = "SELECT * FROM `addon_data_store` WHERE `id`>0";

        if(empty($key))
            throw new NucleusException(NucleusException::INVALID_KEY,"Invalid Key value");
        else
            $query .= " AND `key` = '$key'";

        if(empty($enabledAddonId))
            throw new NucleusException(NucleusException::INVALID_ENABLED_ADDON_ID,"Invalid enabled addon id");
        else
            $query.=" AND `enabled_addon_id` = '$enabledAddonId'";
        try {
            $data = $this->executeQueryForList($query, $this->mapper[AddonDataStoreMapper::GET_ADDON_DATA_STORE_MAPPER]);
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $data;
    }

    /**
     * Insert details into addon_data_store table.
     * @param AddonDataStore $addonDataStore
     * @return integer
     * @throws NucleusException
     */
    public function putData($addonDataStore)
    {
        $result = NULL;

        $addonDataStore->value = $this->realEscapeString($addonDataStore->value);
        $addonDataStore->key = $this->realEscapeString($addonDataStore->key);
        $addonDataStore->enabledAddonId = $this->realEscapeString($addonDataStore->enabledAddonId);

        $query = "INSERT INTO `addon_data_store` (`id`, `enabled_addon_id`, `key`, `value`, `created_by`, 
                  `created_date`, `updated_by`, `updated_date`) VALUES (NULL, '$addonDataStore->enabledAddonId', 
                  '$addonDataStore->key', '$addonDataStore->value',NULL, utc_timestamp(),NULL, utc_timestamp())";

        try {
            $result = $this->executeQueryForObject($query, true);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $result;
    }

    /**
     * Deleting data from addon_data_store
     * @param integer $enabledAddonId
     * @param string $key
     * @return bool
     * @throws NucleusException
     */
    public function deleteData($enabledAddonId, $key)
    {
        $status = false;
        $enabledAddonId = $this->realEscapeString($enabledAddonId);
        $key = $this->realEscapeString($key);
        if (empty($enabledAddonId))
            throw new NucleusException(NucleusException::INVALID_ENABLED_ADDON_ID, "Invalid enabled addon id");
        if (empty($key))
            throw new NucleusException(NucleusException::INVALID_KEY, "Invalid key");
        $query = "DELETE FROM `addon_data_store` WHERE `enabled_addon_id`='$enabledAddonId' AND `key`='$key'";

        try {
            $result = $this->executeQueryForObject($query, true);
            $status = true;
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $status;
    }

    /**
     * Deleting all addon_store_data by enabled addon id
     * @param integer $enabledAddonId
     * @return boolean $status
     * @throws NucleusException
     */
    public function deleteAll($enabledAddonId)
    {
        $status = false;
        $enabledAddonId = $this->realEscapeString($enabledAddonId);
        if (empty($enabledAddonId))
            throw new NucleusException(NucleusException::INVALID_ENABLED_ADDON_ID, "Invalid enabled addon id");

        $query = "DELETE FROM `addon_data_store` WHERE `enabled_addon_id`='$enabledAddonId'";
        try {
            $result = $this->executeQueryForObject($query, true);
            $status = true;
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $status;
    }

    /**
     * updating addon_data_store fields.
     * @param AddonDataStore $addonDataStore
     * @return bool
     * @throws NucleusException
     */
    public function updateData($addonDataStore){
        $status = false;
        $addonDataStore->id = $this->realEscapeString($addonDataStore->id);
        $addonDataStore->key = $this->realEscapeString($addonDataStore->key);
        $query = "UPDATE `addon_data_store` SET `value` = '$addonDataStore->value',
                  `updated_date` = utc_timestamp() WHERE `id` ='$addonDataStore->id'";
        try{
            $this->executeQueryForObject($query,true);
            $status = true;
        }catch(Exception $e){
            throw new NucleusException($e->getCode(),$e->getMessage());
        }
        return $status;
    }

    /**
     * return addon data store value by enabled addon id and key
     * @param integer $enabledAddonId
     * @param string $key
     * @return null|Object
     * @throws NucleusException
     */
    public function getAddonDataStoreValueByKeyAndEnabledAddonId($enabledAddonId,$key){
        $data = null;
        $enabledAddonId = $this->realEscapeString($enabledAddonId);
        $key = $this->realEscapeString($key);
        $query = "SELECT value FROM `addon_data_store` WHERE `id`>";
        if(empty($key))
            throw new NucleusException(NucleusException::INVALID_KEY,"Invalid Key value");
        else
            $query .= " AND `key` = '$key'";

        if(empty($enabledAddonId))
            throw new NucleusException(NucleusException::INVALID_ENABLED_ADDON_ID,"Invalid enabled addon id");
        else
            $query.=" AND `enabled_addon_id` = '$enabledAddonId'";
        try{
            $data = $this->executeQueryForObject($query,true);
        }catch (Exception $e){
            throw new NucleusException($e->getCode(),$e->getMessage());
        }

        return $data;
    }


}<?php

namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\dto\ApiOrganizationDetails;
use com\linways\nucleus\core\exception\NucleusException;
use com\linways\nucleus\core\utils\JWTUtils;
use Exception;

class APIAccessTokenService extends BaseService
{
    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
    }
    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {
    }
    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self ();

        return self::$_instance;
    }

    /**Create organization
     * @param ApiOrganizationDetails $organization
     * @return integer|null
     * @throws NucleusException
     */
    public function createOrganization($organization)
    {
        $id =null;
        $organization->notes = isset($organization->notes) ? $this->realEscapeString($organization->notes) : null;
        $organization->name = $this->realEscapeString($organization->name);
        $organization->address = $this->realEscapeString($organization->address);
        $organization->contactNo = $this->realEscapeString($organization->contactNo);
        $organization->email = $this->realEscapeString($organization->email);

        $query = "INSERT INTO api_organization_details(name ,address, email, contactNo, notes) 
                  VALUES ('$organization->name','$organization->address','$organization->email', 
                  '$organization->contactNo','$organization->notes')";
        try {
            $result = $this->executeQueryForObject($query, true);
            $id = $result;
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $id;
    }

    /**
     * creating client id and secret id for api
     * @param integer $organizationId
     * @param integer $collegeId
     * @return array
     * @throws NucleusException
     */
    public function createAPIClientIdAndSecret($organizationId, $collegeId)
    {
        $data = [];
        list($accessId, $accessToken) = JWTUtils::createToken($organizationId, $collegeId);
        $query = "INSERT INTO api_access_token (access_id, access_token, college_id, api_organization_details_id, issued_at, is_active) 
		VALUES ('$accessId', '$accessToken', $collegeId, $organizationId, NOW(), true)";
        try {
            $res = $this->executeQueryForObject($query, true);
            $data = [
                'accessId' => $accessId,
                'accessToken' => $accessToken
            ];
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $data;
    }

    /**
     * return api access token
     * @param integer$accessId
     * @return string|null
     * @throws NucleusException
     */
    public function getAPIAccessToken($accessId)
    {
        $token = null;
        $accessId = $this->realEscapeString($accessId);
        if (empty($accessId))
            throw new NucleusException(NucleusException::INVALID_ACCESS_ID, "Invalid access id");

        $query = "SELECT access_token from api_access_token where access_id='$accessId'";
        try {
            $result = $this->executeQueryForObject($query);
            $token = $result->access_token;
        } catch (Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $token;
    }
}<?php
namespace com\linways\nucleus\core\service;

use com\linways\base\connection\IMySqlConnector;
use com\linways\base\connection\MySqlConnector;
use com\linways\base\connection\MySqlQuery;
use com\linways\nucleus\core\exception\NucleusException;

class BaseService extends MySqlQuery implements IMySqlConnector {
	/**
	 * @var MySqlConnector
	 */
	public $mySqlConnector;

	/**
	 * Establish a MYSQL Connection
	 * If connection and db properties not set this method
	 * help to establish the connection.
	 * This will be called from MySqlQuery or also you can call externally
	 */
	public function establishConnection() {
		$this->mySqlConnector = MySqlConnector::getInstance();
		/**
		 * This condition is mandatory to check multiple database connections.
		 * If not added then connection will be reused from the first project ie projectA service
		 * invoked first then mysqlconnection will established based on the projectA DB configuration
		 * after that if we invoke projectB service mysqlConnection will be resused from ProjectA bz of
		 * singleton instance of mysqlconnection
		 * to avoid this situation we need to set 'instanceof' attribute value in setDBConnectionProperties() method
		 */
		if ($this->mySqlConnector->instanceOf != null && !$this->mySqlConnector->instanceOf instanceof BaseService) {
			$this->mySqlConnector->closeConnection();
		}

		if (!$this->mySqlConnector->connection) {

			$this->setDBConnectionProperties();
		}
		$this->connection = $this->mySqlConnector->getConnection();
	}
	/**
	 * Set DB Connection properties
	 */
	protected function setDBConnectionProperties() {

		// check DB configurations
		$dbconfigFilePath = getenv('NUCLEUS_CONF');

		//check file configured correctly
		if (empty($dbconfigFilePath)) {
			throw new NucleusException(NucleusException::DB_CONFIG_FILE_PATH_NOT_DEFINED_IN_SERVER, "Database config for nucleus is not found. Please set env `NUCLEUS_CONF`");
		}
		//include db configuration file to load connection properties
		require $dbconfigFilePath;
		// global  $DB_HOST,$DB_USER,$DB_PASSWD,$DB_NAME;
		if (empty($DB_HOST) || empty($DB_USER) || empty($DB_PASSWD) || empty($DB_NAME)) {
			throw new NucleusException(NucleusException::DB_CONFIG_NOT_SET, "Nucleus  DB configuration not set properly. Please set DB_HOST, DB_USER, DB_PASSWD and DB_NAME");
		}

		//Set DB Connection properties
		$this->mySqlConnector->DB_HOST   = $DB_HOST;
		$this->mySqlConnector->DB_USER   = $DB_USER;
		$this->mySqlConnector->DB_PASSWD = $DB_PASSWD;
		$this->mySqlConnector->DB_NAME   = $DB_NAME;

		$this->mySqlConnector->instanceOf = $this;
	}
}
<?php

namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\utils\StringUtils;
use com\linways\nucleus\core\dto\Client;
use com\linways\nucleus\core\exception\NucleusException;
use com\linways\nucleus\core\mapper\ClientServiceMapper;
use Symfony\Component\Config\Definition\Exception\Exception;

class ClientService extends BaseService
{

    private static $_instance = null;

    private function __construct()
    {
        $this->mapper = ClientServiceMapper::getInstance()->getMapper();
    }

    private function __clone()
    {
    }

    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            self::$_instance = new self();
        return self::$_instance;
    }

    /**
     *
     * @param Client $client
     * @throws NucleusException
     * @return int | NULL
     */
    public function createClient($client)
    {
        $id = null;
        $client = $this->realEscapeObject($client);

        if (empty($client->name)) {
            throw new NucleusException(NucleusException::CLIENT_NAME_REQUIRED, "Client name is required");
        }

        $query = "INSERT INTO `clients` (`name`,
        `address`,`total_annual_income`, `service_start_date`,
        `billing_start_date`, `created_by`, `created_at`, `updated_by`, `updated_at`)
        VALUES ('$client->name',
        '$client->address'," .
            StringUtils::stringifyNull($client->totalAnnualIncome) . "," .
            StringUtils::stringifyNull($client->serviceStartDate) . "," .
            StringUtils::stringifyNull($client->billingStartDate) . ", 0, UTC_TIMESTAMP(), 0, UTC_TIMESTAMP())";
        try {
            $id = $this->executeQueryForObject($query, true);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $id;
    }

    /**
     * return all client details
     * @param null $date
     * @return array|Object
     * @throws NucleusException
     */
    public function getAllClients($date = NULL)
    {
        $clients = [];
        $date = $this->realEscapeString($date);
        $endDate = date("Y-m-d", strtotime($date));
        $query = "SELECT * FROM `clients`";
        if ($date) {
            $query .= "WHERE service_start_date<='" . $endDate . "'";
        }
        try {
            $clients = $this->executeQueryForList($query, $this->mapper[ClientServiceMapper::GET_ALL_CLIENTS]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }

        return $clients;
    }

    /**
     * Updates a client if exists otherwise creates a new client
     * @param  Client $client client object
     * @return int | NucleusException    id of the updated item
     * @throws NucleusException
     */
    public function upsertClient($client)
    {
        if ($client->id) {
            $client = $this->realEscapeObject($client);
            if (empty($client->name)) {
                if (!empty($client->id) && empty($client->address) && empty($client->totalAnnualIncome) && empty($client->serviceStartDate) && empty($client->billingStartDate)) {
                    return $this->deleteClient($client->id);
                } else {
                    throw new NucleusException(NucleusException::CLIENT_NAME_REQUIRED, "Client name is required");
                }
            }

            $query = "UPDATE `clients` SET
          `name`='$client->name',
          `address`='$client->address',
          `total_annual_income`=" . StringUtils::stringifyNull($client->totalAnnualIncome) . ",
          `service_start_date`=" . StringUtils::stringifyNull($client->serviceStartDate) . ",
          `billing_start_date`=" . StringUtils::stringifyNull($client->billingStartDate) . ",
          `updated_by`=0,
          `updated_at`=UTC_TIMESTAMP() where `id`=$client->id";

            try {
                $this->executeQueryForObject($query, true);
            } catch (\Exception $e) {
                throw new NucleusException($e->getCode(), $e->getMessage());
            }
            return $client->id;
        } else {
            //if ID does not exists calls create method
            return $this->createClient($client);
        }
    }

    /**
     * Delete a client using clientID
     * @param  int $clientId id of the client to be deleted
     * @return  null | NucleusException    True if successfully executed.
     * @throws NucleusException
     */
    public function deleteClient($clientId)
    {
        $clientId = $this->realEscapeString($clientId);
        if (empty($clientId)) {
            throw new NucleusException(NucleusException::CLIENT_ID_REQUIRED, "Client id is required");
        }
        if (CollegeService::getInstance()->getCountOfCollegesByClientId($clientId)) {
            throw new NucleusException(NucleusException::NOT_EMPTY_CLIENT, "This client contain active colleges. Remove colleges and try again");
        }
        $query = "DELETE FROM `clients` where `id`=$clientId";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return null;
    }

    /**
     * Returns client details by client id
     * @param  integer $clientId
     * @return \stdClass Object
     * @throws NucleusException
     */
    public function getClientById($clientId)
    {
        $data = null;
        $clientId = $this->realEscapeString($clientId);
        if (empty($clientId)) {
            throw new NucleusException(NucleusException::CLIENT_ID_REQUIRED . "Client id is required");
        }
        $query = 'SELECT * FROM `clients` WHERE id=' . $clientId;
        try {
            $data = $this->executeQueryForObject($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $data;

    }

    public function getBasicDetailsOfAllClients()
    {
        $clients = null;
        $query = "SELECT id,name FROM `clients` ORDER BY name";
        try {
            $clients = $this->executeQueryForList($query, $this->mapper[ClientServiceMapper::GET_BASIC_DETAILS_OF_ALL_CLIENTS]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $clients;
    }

    /**
     * Return count of clients and average trial period
     * @throws NucleusException
     */
    public function getCountOfClientsAndAverageTrialPeriod($date)
    {
        $data = null;
        $date = $this->realEscapeString($date);
        $endDate = date("Y-m-d", strtotime($date));

        $query = "SELECT COUNT(id) AS count, AVG(DATEDIFF(`billing_start_date`,`service_start_date`)) 
                  as averageDate FROM `clients` 
                  WHERE service_start_date<='" . $endDate . "' AND billing_start_date<='" . $endDate . "'";

        try {
            $data = $this->executeQueryForObject($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $data;
    }

    /**
     * Return number clients on trial and number of tenants in single client
     * @return null
     * @throws NucleusException
     */
    public function getClientsOnTrialAndCountOfTenantsInSingleClient($date)
    {
        $result = null;
        $date = $this->realEscapeString($date);
        $endDate = date("Y-m-d", strtotime($date));
        try {
            $query = "SELECT COUNT(DISTINCT c.id) as clientCount, COUNT(DISTINCT cg.id) as tenantCount FROM 
                      `clients` c INNER JOIN colleges cg ON cg.client_id=c.id WHERE 
                      DATE(c.service_start_date)<='" . $endDate . "' AND (
                      c.billing_start_date IS NULL OR DATE(c.billing_start_date)>'" . $endDate . "')";
            $result = $this->executeQueryForObject($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $result;
    }


}
<?php
namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\exception\SchedulerException;
use com\linways\nucleus\core\mapper\SchedulerServiceMapper;
use Cron\CronExpression;

/**
 * Services for managing scheduled tasks.
 */

class SchedulerService extends BaseService {

	private static $_instance = null;

	/** Condition 2 - Locked down the constructor */
	private function __construct() {
		$this->mapper = SchedulerServiceMapper::getInstance()->getMapper();
	}

	// Prevent any oustide instantiation of this class

	/** Condition 3 - Prevent any object or instance of that class to be cloned */
	private function __clone() {
	}

	// Prevent any copy of this object

	/** Condition 4 - Have a single globally accessible static method */
	public static function getInstance() {
		if (!is_object(self::$_instance)) {
			// or if( is_null(self::$_instance) ) or if( self::$_instance == null )
			self::$_instance = new self();
		}

		return self::$_instance;
	}

	/**
	 * getConfigArrayForAMS - get all configuration file paths (required for linways professional) form the environment and  pushes into an array
	 * All files in this array will be included when executing scheduled task to get college context.
	 *
	 * @return string - json encoded array of configuration files
	 */
	private function getConfigArrayForAMS() {
		$conf = [];
		if (getenv('AMS_CONFIG')) {
			$conf[] = getenv('AMS_CONFIG');
		}
		if (getenv('DB_PROFESIONAL_CONFIG')) {
			$conf[] = getenv('DB_PROFESIONAL_CONFIG');
		}
		if (getenv('NUCLEUS_CONF')) {
			$conf[] = getenv('NUCLEUS_CONF');
		}

		return json_encode($conf);

	}

	/**
	 * schedule - Schedules a task to a future date.
	 *
	 * @param ScheduleRequest $scheduleRequest
	 * @return int id of the created schedule
	 */
	public function addTask($scheduleRequest) {
		//TODO : Add validations
		//args = $method, $params=[], $frequency=null, $code=null, $nextExecutionAt=null
		if (empty($scheduleRequest->frequency) && empty($scheduleRequest->nextExecutionAt)) {
			throw new \InvalidArgumentException("frequency and/or nextExecutionAt not specified.");
		}
		if (empty($scheduleRequest->method)) {
			throw new \InvalidArgumentException("Method is required.");
		}

		if ($scheduleRequest->frequency && !CronExpression::isValidExpression($scheduleRequest->frequency)) {
			throw new SchedulerException(SchedulerException::INVALID_FREQUENCY_STRING, "frequency is not a valid cron expression");
		}
		$scheduleRequest->nextExecutionAt = empty($scheduleRequest->nextExecutionAt)?'NULL':"'$scheduleRequest->nextExecutionAt'";
		$scheduleRequest->code            = empty($scheduleRequest->code)?'NULL':"'$scheduleRequest->code'";

		$scheduleRequest->param['method'] = $scheduleRequest->method;
		$scheduleRequest->conf            = $this->getConfigArrayForAMS();
		$scheduleRequest->params          = json_encode($scheduleRequest->params);
		$sql                              = "INSERT INTO scheduled_task ( code, conf, params, frequency, next_execution_at, created_at)
        VALUES ($scheduleRequest->code, '$scheduleRequest->conf', '$scheduleRequest->params', '$scheduleRequest->frequency', $scheduleRequest->nextExecutionAt, UTC_TIMESTAMP())";
		return $this->executeQueryForObject($sql, true);
	}

	/**
	 * Returns Schedule item by given id
	 * @param  int $id id of the schedule item
	 * @return ScheduledTask
	 */
	public function getScheduledTaskById($id) {
		//TODO validation

		$sql = "SELECT * FROM scheduled_task WHERE id=$id";
		return $this->executeQueryForObject($sql, FALSE, $this->mapper[SchedulerServiceMapper::GET_SCHEDULE_MAPPER]);
	}

	/**
	 * Returns Schedule item for a given unique code
	 * @param  string $code Unique code of the scheduled task
	 * @return ScheduledTask
	 */
	public function getScheduledTaskByCode($code) {
		//TODO validation

		$sql = "SELECT * FROM scheduled_task WHERE code=$code";
		return $this->executeQueryForObject($sql, FALSE, $this->mapper[SchedulerServiceMapper::GET_SCHEDULE_MAPPER]);
	}

	/**
	 * delete a schedule using id
	 * @param  int $id id of the schedule item in the database
	 * @return bool    true is deletion is successful else returns false
	 */
	public function deleteScheduledTaskById($id) {
		//     TODO : Add validations
		if (empty($id)) {
			throw new \InvalidArgumentException("id of the schedule is required.");
		}

		$sql = "DELETE from scheduled_task WHERE id=$id";
		$this->executeQueryForObject($sql, true);
		return true;
	}

	/**
	 * Delete a schedule using unique code
	 * @param  string $code unique code for the schedule item to be deleted
	 * @return bool         true is deletion is successful else returns false
	 */
	public function deleteScheduledTaskByCode($code) {
		//     TODO : Add valcodeations
		if (empty($code)) {
			throw new \InvalidArgumentException("id of the schedule is required.");
		}

		$sql = "DELETE from scheduled_task WHERE code='$code'";
		$this->executeQueryForObject($sql, true);
		return true;
	}

	/**
	 * Calculate next execution timestamp using frequency string(cron job scheduler string)
	 * and updates nextExecutionAt and lastUpdatedAt fields
	 * @param  ScheduledTask $scheduledTask  Scheduled task object
	 * @return true|SchedulerException     [description]
	 */
	public function updateNextExecution($scheduledTask) {
		$cronExp         = CronExpression::factory($scheduledTask->frequency);
		$nextExecutionAt = $cronExp->getNextRunDate()->format('Y-m-d H:i:s');
		$sql             = "UPDATE scheduled_task SET
              next_execution_at='$nextExecutionAt',
              last_executed_at='$scheduledTask->nextExecutionAt'
              WHERE id=$scheduledTask->id";
		return $this->executeQuery($sql, TRUE);
	}

	/**
	 * returns all tasks whose next_execution_at is in the past.
	 * Since we are setting next_execution_at as NULL if we are done with a
	 * Task, unnecessary tasks will not come. Since we are running all tasks
	 * as async chances for overlap of tasks are less.
	 * @return SchedulerException[]
	 */
	public function getDueTasks() {
		$sql = "SELECT * FROM scheduled_task WHERE next_execution_at <= NOW()";
		return $this->executeQueryForList($sql, $this->mapper[SchedulerServiceMapper::GET_SCHEDULE_MAPPER]);
	}
}
<?php

namespace com\linways\nucleus\core\service;

use com\linways\nucleus\core\dto\College;
use com\linways\nucleus\core\exception\NucleusException;
use com\linways\nucleus\core\mapper\CollegeServiceMapper;
use com\linways\nucleus\core\utils\StringUtils;
use Symfony\Component\Config\Definition\Exception\Exception;

class CollegeService extends BaseService
{

    private static $_instance = null;

    // /Condition 2 - Locked down the constructor
    private function __construct()
    {
        $this->mapper = CollegeServiceMapper::getInstance()->getMapper();
    }

    // Prevent any oustide instantiation of this class

    // /Condition 3 - Prevent any object or instance of that class to be cloned
    private function __clone()
    {
    }

    // Prevent any copy of this object

    // /Condition 4 - Have a single globally accessible static method
    public static function getInstance()
    {
        if (!is_object(self::$_instance))
            // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
            self::$_instance = new self();

        return self::$_instance;
    }

    /**
     *
     * @param College $college
     * @throws NucleusException
     * @return integer|NULL
     */
    public function createCollege($college)
    {
        $id = null;
        $college->code = $this->realEscapeString($college->code);
        $college->collegeDb = $this->realEscapeString($college->collegeDb);
        $college->name = $this->realEscapeString($college->name);
        $college->dbUsername = $this->realEscapeString($college->dbUsername);
        $college->dbPassword = $this->realEscapeString($college->dbPassword);
        $college->clientId = $this->realEscapeString($college->clientId);
        $college->createdBy = $this->realEscapeString($college->createdBy);
        $college->updatedBy = $this->realEscapeString($college->updatedBy);
        $college->dbHost = $college->dbHost ? $this->realEscapeString($college->dbHost) : 'localhost';
        if (empty($college->dbUsername) || empty($college->dbPassword) || empty($college->name) || empty($college->code)) {
            throw new NucleusException(NucleusException::DB_USERNAME_AND_PASSWORD_REQUIRED, "College code, name ,DB username and DBpassword is required");
        }

        $query = "INSERT INTO colleges (`college_code`,`college_name`,`college_db`, `db_username`, `db_password`, `db_host`,`client_id`,`created_by`,`created_at`,`updated_by`,`updated_at`)
                VALUES ('$college->code',
                '$college->name',
                '$college->collegeDb',
                '$college->dbUsername',
                '$college->dbPassword',
                '$college->dbHost',
                '$college->clientId',
                '$college->createdBy',
                UTC_TIMESTAMP(),
                '$college->updatedBy',
                UTC_TIMESTAMP()

              )";
        try {
            $id = $this->executeQueryForObject($query, true);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $id;
    }

    /**
     * Returns  all Colleges/tenants
     * @return College[]
     * @throws NucleusException
     */
    public function getAllColleges()
    {
        $colleges = [];
        $query = "SELECT * FROM colleges WHERE isActive=1";
        try {
            // $colleges = $this->executeQueryForList($query, $this->mapper[CollegeServiceMapper::GET_ALL_COLLEGES]);
            $colleges = $this->executeQueryForList($query, $this->mapper[CollegeServiceMapper::GET_ALL_COLLEGES]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $colleges;
    }

    /**
     * returns all colleges under billing (Does not inclues the one in trial)
     * @return College[]
     * @throws NucleusException
     */
    public function getBillingColleges($date)
    {
        $colleges = [];
        $date = $this->realEscapeString($date);
        $endDate = date("Y-m-d", strtotime($date));
        $query = "SELECT clg.id, clg.college_code, clg.college_name, cli.id AS client_id, cli.service_start_date, cli.billing_start_date
                  FROM colleges AS clg INNER JOIN clients AS cli ON clg.client_id = cli.id WHERE
                  DATE(cli.billing_start_date)<='" . $endDate . "' AND clg.isActive=1";
        try {
            $colleges = $this->executeQueryForList($query, $this->mapper[CollegeServiceMapper::GET_BILLING_COLLEGES]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $colleges;
    }

    /**
     * returns number of colleges available in client
     * @param integer $clientId
     * @return int
     * @throws NucleusException
     */
    public function getCountOfCollegesByClientId($clientId)
    {
        $data = 0;
        $clientId = $this->realEscapeString($clientId);
        if (empty($clientId))
            throw new NucleusException(NucleusException::CLIENT_ID_REQUIRED, "Client id required");

        $query = "SELECT COUNT(id) as count FROM `colleges` WHERE client_id=$clientId";
        try {
            $result = $this->executeQueryForObject($query);
            if ($result)
                $data = $result->count;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return (int)$data;
    }

    /**
     * Returns college details by college id
     * @param $collegeId
     * @return null|\stdClass
     * @throws NucleusException
     */
    public function getCollegeByCollegeId($collegeId)
    {
        $data = null;
        $collegeId = $this->realEscapeString($collegeId);
        if (empty($collegeId))
            throw new NucleusException(NucleusException::TENANT_ID_IS_REQUIRED, "Tenant id is required");
        $query = "SELECT * FROM `colleges` WHERE id=$collegeId";
        try {
            $data = $this->executeQueryForObject($query);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $data;
    }

    /**
     * Returns college details by college code
     * @param  string $collegeCode [description]
     * @return null|\stdClass
     * @throws NucleusException
     */
    public function getCollegeByCollegeCode($collegeCode)
    {
        $data = null;
        $collegeCode = $this->realEscapeString($collegeCode);
        if (empty($collegeCode))
            throw new NucleusException(NucleusException::COLLEGE_CODE_IS_REQUIRED, "College code is required");
        $query = "SELECT * FROM `colleges` WHERE college_code='$collegeCode'";
        try {
            $data = $this->executeQueryForObject($query, false, $this->mapper[CollegeServiceMapper::GET_ALL_COLLEGES]);
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $data;
    }

    /**
     * @param College $college
     * @return mixed
     * @throws NucleusException
     */
    public function upsertCollege($college)
    {
        if ($college->id) {
            $college = $this->realEscapeObject($college);
            if (empty($college->name)) {
                if (!empty($college->id) && empty($college->code) && empty($college->collegeDb) && empty($college->dbPassword) && empty($college->dbHost) && empty($college->dbUserName)) {
                    return $this->deleteCollege($college);
                } else {
                    throw new NucleusException(NucleusException::REQUIRED_FIELDS_MISSING, "Empty fields submitted");
                }
            }
            $query = "UPDATE `colleges` SET
                      `college_code`='" . $college->code . "',
                      `college_name`='" . $college->name . "',
                      `college_db`='" . $college->collegeDb . "',
                      `db_username`='" . $college->dbUsername . "',
                      `db_password`='" . $college->dbPassword . "',
                      `db_host`='" . $college->dbHost . "',
                      `client_id`='" . $college->clientId . "',
                      `updated_by`=" . StringUtils::stringifyNull($college->updatedBy) . ",
                      `updated_at`=UTC_TIMESTAMP()
                      WHERE `id`='" . $college->id . "'";
            try {
                $this->executeQueryForObject($query, true);
            } catch (\Exception $e) {
                throw new NucleusException($e->getCode(), $e->getMessage());
            }
            return $college->id;
        } else {
            //if ID does not exists calls create method
            return $this->createCollege($college);
        }
    }

    /**
     * Deleting college/tenant by resetI isActive flag
     * @param College $college
     * @return bool true|false
     * @throws NucleusException
     */
    public function deleteCollege($college)
    {
        $status = false;
        $college = $this->realEscapeObject($college);
        if (empty($college->id))
            throw new NucleusException(NucleusException::TENANT_ID_IS_REQUIRED, "Tenant id is required");
        $query = "UPDATE `colleges` SET
                  `isActive`=0,
                  `updated_by`= " . StringUtils::stringifyNull($college->updatedBy) . ",
                  `updated_at`= UTC_TIMESTAMP()
                  WHERE id=$college->id";
        try {
            $this->executeQuery($query);
            $status = true;
        } catch (\Exception $e) {
            throw new NucleusException($e->getCode(), $e->getMessage());
        }
        return $status;
    }

}
<?php
namespace com\linways\nucleus\core\exception;

use com\linways\base\exception\CoreException;

class TaskQueueWorkerException extends CoreException {
	const CLASS_NOT_FOUND           = "CLASS_NOT_FOUND";
	const METHOD_NOT_FOUND_IN_CLASS = "METHOD_NOT_FOUND_IN_CLASS";
}

?><?php
namespace com\linways\nucleus\core\exception;

use com\linways\base\exception\CoreException;

class SchedulerException extends CoreException
{
    const INVALID_FREQUENCY_STRING = "INVALID_FREQUENCY_STRING";
}
<?php

namespace com\linways\nucleus\core\exception;

use com\linways\base\exception\CoreException;

class NucleusException extends CoreException
{
    const INVALID_SCOPE = "INVALID_SCOPE";
    const INVALID_SCOPE_ID = "INVALID_SCOPE_ID";
    const INVALID_ADDON_ID = "INVALID_ADDON_ID";
    const INVALID_COLLEGE_CODE = "INVALID_COLLEGE_CODE";
    const DB_CONFIG_FILE_PATH_NOT_DEFINED_IN_SERVER = "DB_CONFIG_FILE_PATH_NOT_DEFINED_IN_SERVER";
    const INVALID_ATTRIBUTE_CODE = "INVALID_ATTRIBUTE_CODE";
    const DB_CONFIG_NOT_SET = "DB_CONFIG_NOT_SET";
    const INVALID_ENABLED_ADDON_ID = "INVALID_ENABLED_ADDON_ID";
    const INVALID_KEY = "INVALID_KEY";
    const INVALID_PERMISSION_ID = "INVALID_PERMISSION_ID";
    const INVALID_API_ACCESS_TOKEN_ID = "INVALID_API_ACCESS_TOKEN_ID";
    const INVALID_ACCESS_ID = "INVALID_ACCESS_ID";
    const DB_USERNAME_AND_PASSWORD_REQUIRED = "DB_USERNAME_AND_PASSWORD_REQUIRED";
    const CLIENT_NAME_REQUIRED = "CLIENT_NAME_REQUIRED";
    const CLIENT_ID_REQUIRED = "CLIENT_ID_REQUIRED";
    const NOT_EMPTY_CLIENT = "NOT_EMPTY_CLIENT";
    const TENANT_ID_IS_REQUIRED = "TENANT_ID_IS_REQUIRED";
    const COLLEGE_CODE_IS_REQUIRED = "COLLEGE_CODE_IS_REQUIRED";
    const REQUIRED_FIELDS_MISSING ="REQUIRED_FIELDS_MISSING";
    const INVALID_DATE = "INVALID_DATE";
    const INVALID_EXPENSE_CATEGORY_NAME = "INVALID_EXPENSE_CATEGORY_NAME";
}
{
    "name": "mtdowling/cron-expression",
    "type": "library",
    "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
    "keywords": ["cron", "schedule"],
    "license": "MIT",
    "authors": [{
        "name": "Michael Dowling",
        "email": "mtdowling@gmail.com",
        "homepage": "https://github.com/mtdowling"
    }],
    "require": {
        "php": ">=5.3.2"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.0|~5.0"
    },
    "autoload": {
        "psr-4": {
            "Cron\\": "src/Cron/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/Cron/"
        }
    }
}<?php

namespace Cron;

use InvalidArgumentException;

/**
 * CRON field factory implementing a flyweight factory
 * @link http://en.wikipedia.org/wiki/Cron
 */
class FieldFactory
{
    /**
     * @var array Cache of instantiated fields
     */
    private $fields = array();

    /**
     * Get an instance of a field object for a cron expression position
     *
     * @param int $position CRON expression position value to retrieve
     *
     * @return FieldInterface
     * @throws InvalidArgumentException if a position is not valid
     */
    public function getField($position)
    {
        if (!isset($this->fields[$position])) {
            switch ($position) {
                case 0:
                    $this->fields[$position] = new MinutesField();
                    break;
                case 1:
                    $this->fields[$position] = new HoursField();
                    break;
                case 2:
                    $this->fields[$position] = new DayOfMonthField();
                    break;
                case 3:
                    $this->fields[$position] = new MonthField();
                    break;
                case 4:
                    $this->fields[$position] = new DayOfWeekField();
                    break;
                case 5:
                    $this->fields[$position] = new YearField();
                    break;
                default:
                    throw new InvalidArgumentException(
                        $position . ' is not a valid position'
                    );
            }
        }

        return $this->fields[$position];
    }
}
<?php

namespace Cron;

use DateTime;

/**
 * Day of month field.  Allows: * , / - ? L W
 *
 * 'L' stands for "last" and specifies the last day of the month.
 *
 * The 'W' character is used to specify the weekday (Monday-Friday) nearest the
 * given day. As an example, if you were to specify "15W" as the value for the
 * day-of-month field, the meaning is: "the nearest weekday to the 15th of the
 * month". So if the 15th is a Saturday, the trigger will fire on Friday the
 * 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If
 * the 15th is a Tuesday, then it will fire on Tuesday the 15th. However if you
 * specify "1W" as the value for day-of-month, and the 1st is a Saturday, the
 * trigger will fire on Monday the 3rd, as it will not 'jump' over the boundary
 * of a month's days. The 'W' character can only be specified when the
 * day-of-month is a single day, not a range or list of days.
 *
 * @author Michael Dowling <mtdowling@gmail.com>
 */
class DayOfMonthField extends AbstractField
{
    /**
     * Get the nearest day of the week for a given day in a month
     *
     * @param int $currentYear  Current year
     * @param int $currentMonth Current month
     * @param int $targetDay    Target day of the month
     *
     * @return \DateTime Returns the nearest date
     */
    private static function getNearestWeekday($currentYear, $currentMonth, $targetDay)
    {
        $tday = str_pad($targetDay, 2, '0', STR_PAD_LEFT);
        $target = DateTime::createFromFormat('Y-m-d', "$currentYear-$currentMonth-$tday");
        $currentWeekday = (int) $target->format('N');

        if ($currentWeekday < 6) {
            return $target;
        }

        $lastDayOfMonth = $target->format('t');

        foreach (array(-1, 1, -2, 2) as $i) {
            $adjusted = $targetDay + $i;
            if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) {
                $target->setDate($currentYear, $currentMonth, $adjusted);
                if ($target->format('N') < 6 && $target->format('m') == $currentMonth) {
                    return $target;
                }
            }
        }
    }

    public function isSatisfiedBy(DateTime $date, $value)
    {
        // ? states that the field value is to be skipped
        if ($value == '?') {
            return true;
        }

        $fieldValue = $date->format('d');

        // Check to see if this is the last day of the month
        if ($value == 'L') {
            return $fieldValue == $date->format('t');
        }

        // Check to see if this is the nearest weekday to a particular value
        if (strpos($value, 'W')) {
            // Parse the target day
            $targetDay = substr($value, 0, strpos($value, 'W'));
            // Find out if the current day is the nearest day of the week
            return $date->format('j') == self::getNearestWeekday(
                $date->format('Y'),
                $date->format('m'),
                $targetDay
            )->format('j');
        }

        return $this->isSatisfied($date->format('d'), $value);
    }

    public function increment(DateTime $date, $invert = false)
    {
        if ($invert) {
            $date->modify('previous day');
            $date->setTime(23, 59);
        } else {
            $date->modify('next day');
            $date->setTime(0, 0);
        }

        return $this;
    }

    /**
     * Validates that the value is valid for the Day of the Month field
     * Days of the month can contain values of 1-31, *, L, or ? by default. This can be augmented with lists via a ',',
     * ranges via a '-', or with a '[0-9]W' to specify the closest weekday.
     *
     * @param string $value
     * @return bool
     */
    public function validate($value)
    {
        // Allow wildcards and a single L
        if ($value === '?' || $value === '*' || $value === 'L') {
            return true;
        }

        // If you only contain numbers and are within 1-31
        if ((bool) preg_match('/^\d{1,2}$/', $value) && ($value >= 1 && $value <= 31)) {
            return true;
        }

        // If you have a -, we will deal with each of your chunks
        if ((bool) preg_match('/-/', $value)) {
            // We cannot have a range within a list or vice versa
            if ((bool) preg_match('/,/', $value)) {
                return false;
            }

            $chunks = explode('-', $value);
            foreach ($chunks as $chunk) {
                if (!$this->validate($chunk)) {
                    return false;
                }
            }

            return true;
        }

        // If you have a comma, we will deal with each value
        if ((bool) preg_match('/,/', $value)) {
            // We cannot have a range within a list or vice versa
            if ((bool) preg_match('/-/', $value)) {
                return false;
            }

            $chunks = explode(',', $value);
            foreach ($chunks as $chunk) {
                if (!$this->validate($chunk)) {
                    return false;
                }
            }

            return true;
        }

        // If you contain a /, we'll deal with it
        if ((bool) preg_match('/\//', $value)) {
            $chunks = explode('/', $value);
            foreach ($chunks as $chunk) {
                if (!$this->validate($chunk)) {
                    return false;
                }
            }
            return true;
        }

        // If you end in W, make sure that it has a numeric in front of it
        if ((bool) preg_match('/^\d{1,2}W$/', $value)) {
            return true;
        }

        return false;
    }
}
<?php

namespace Cron;

use DateTime;


/**
 * Year field.  Allows: * , / -
 */
class YearField extends AbstractField
{
    public function isSatisfiedBy(DateTime $date, $value)
    {
        return $this->isSatisfied($date->format('Y'), $value);
    }

    public function increment(DateTime $date, $invert = false)
    {
        if ($invert) {
            $date->modify('-1 year');
            $date->setDate($date->format('Y'), 12, 31);
            $date->setTime(23, 59, 0);
        } else {
            $date->modify('+1 year');
            $date->setDate($date->format('Y'), 1, 1);
            $date->setTime(0, 0, 0);
        }

        return $this;
    }

    public function validate($value)
    {
        return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value);
    }
}
<?php

namespace Cron;
use DateTime;

/**
 * CRON field interface
 */
interface FieldInterface
{
    /**
     * Check if the respective value of a DateTime field satisfies a CRON exp
     *
     * @param DateTime $date  DateTime object to check
     * @param string   $value CRON expression to test against
     *
     * @return bool Returns TRUE if satisfied, FALSE otherwise
     */
    public function isSatisfiedBy(DateTime $date, $value);

    /**
     * When a CRON expression is not satisfied, this method is used to increment
     * or decrement a DateTime object by the unit of the cron field
     *
     * @param DateTime $date   DateTime object to change
     * @param bool     $invert (optional) Set to TRUE to decrement
     *
     * @return FieldInterface
     */
    public function increment(DateTime $date, $invert = false);

    /**
     * Validates a CRON expression for a given field
     *
     * @param string $value CRON expression value to validate
     *
     * @return bool Returns TRUE if valid, FALSE otherwise
     */
    public function validate($value);
}
<?php

namespace Cron;

/**
 * Abstract CRON expression field
 */
abstract class AbstractField implements FieldInterface
{
    /**
     * Check to see if a field is satisfied by a value
     *
     * @param string $dateValue Date value to check
     * @param string $value     Value to test
     *
     * @return bool
     */
    public function isSatisfied($dateValue, $value)
    {
        if ($this->isIncrementsOfRanges($value)) {
            return $this->isInIncrementsOfRanges($dateValue, $value);
        } elseif ($this->isRange($value)) {
            return $this->isInRange($dateValue, $value);
        }

        return $value == '*' || $dateValue == $value;
    }

    /**
     * Check if a value is a range
     *
     * @param string $value Value to test
     *
     * @return bool
     */
    public function isRange($value)
    {
        return strpos($value, '-') !== false;
    }

    /**
     * Check if a value is an increments of ranges
     *
     * @param string $value Value to test
     *
     * @return bool
     */
    public function isIncrementsOfRanges($value)
    {
        return strpos($value, '/') !== false;
    }

    /**
     * Test if a value is within a range
     *
     * @param string $dateValue Set date value
     * @param string $value     Value to test
     *
     * @return bool
     */
    public function isInRange($dateValue, $value)
    {
        $parts = array_map('trim', explode('-', $value, 2));

        return $dateValue >= $parts[0] && $dateValue <= $parts[1];
    }

    /**
     * Test if a value is within an increments of ranges (offset[-to]/step size)
     *
     * @param string $dateValue Set date value
     * @param string $value     Value to test
     *
     * @return bool
     */
    public function isInIncrementsOfRanges($dateValue, $value)
    {
        $parts = array_map('trim', explode('/', $value, 2));
        $stepSize = isset($parts[1]) ? (int) $parts[1] : 0;

        if ($stepSize === 0) {
            return false;
        }

        if (($parts[0] == '*' || $parts[0] === '0')) {
            return (int) $dateValue % $stepSize == 0;
        }

        $range = explode('-', $parts[0], 2);
        $offset = $range[0];
        $to = isset($range[1]) ? $range[1] : $dateValue;
        // Ensure that the date value is within the range
        if ($dateValue < $offset || $dateValue > $to) {
            return false;
        }

        if ($dateValue > $offset && 0 === $stepSize) {
          return false;
        }

        for ($i = $offset; $i <= $to; $i+= $stepSize) {
            if ($i == $dateValue) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns a range of values for the given cron expression
     *
     * @param string $expression The expression to evaluate
     * @param int $max           Maximum offset for range
     *
     * @return array
     */
    public function getRangeForExpression($expression, $max)
    {
        $values = array();

        if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) {
            if (!$this->isIncrementsOfRanges($expression)) {
                list ($offset, $to) = explode('-', $expression);
                $stepSize = 1;
            }
            else {
                $range = array_map('trim', explode('/', $expression, 2));
                $stepSize = isset($range[1]) ? $range[1] : 0;
                $range = $range[0];
                $range = explode('-', $range, 2);
                $offset = $range[0];
                $to = isset($range[1]) ? $range[1] : $max;
            }
            $offset = $offset == '*' ? 0 : $offset;
            for ($i = $offset; $i <= $to; $i += $stepSize) {
                $values[] = $i;
            }
            sort($values);
        }
        else {
            $values = array($expression);
        }

        return $values;
    }

}
<?php

namespace Cron;

use DateTime;


/**
 * Minutes field.  Allows: * , / -
 */
class MinutesField extends AbstractField
{
    public function isSatisfiedBy(DateTime $date, $value)
    {
        return $this->isSatisfied($date->format('i'), $value);
    }

    public function increment(DateTime $date, $invert = false, $parts = null)
    {
        if (is_null($parts)) {
            if ($invert) {
                $date->modify('-1 minute');
            } else {
                $date->modify('+1 minute');
            }
            return $this;
        }

        $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts);
        $minutes = array();
        foreach ($parts as $part) {
            $minutes = array_merge($minutes, $this->getRangeForExpression($part, 59));
        }

        $current_minute = $date->format('i');
        $position = $invert ? count($minutes) - 1 : 0;
        if (count($minutes) > 1) {
            for ($i = 0; $i < count($minutes) - 1; $i++) {
                if ((!$invert && $current_minute >= $minutes[$i] && $current_minute < $minutes[$i + 1]) ||
                    ($invert && $current_minute > $minutes[$i] && $current_minute <= $minutes[$i + 1])) {
                    $position = $invert ? $i : $i + 1;
                    break;
                }
            }
        }

        if ((!$invert && $current_minute >= $minutes[$position]) || ($invert && $current_minute <= $minutes[$position])) {
            $date->modify(($invert ? '-' : '+') . '1 hour');
            $date->setTime($date->format('H'), $invert ? 59 : 0);
        }
        else {
            $date->setTime($date->format('H'), $minutes[$position]);
        }

        return $this;
    }

    public function validate($value)
    {
        return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value);
    }
}
<?php

namespace Cron;

use DateTime;

/**
 * Month field.  Allows: * , / -
 */
class MonthField extends AbstractField
{
    public function isSatisfiedBy(DateTime $date, $value)
    {
        // Convert text month values to integers
        $value = str_ireplace(
            array(
                'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
                'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'
            ),
            range(1, 12),
            $value
        );

        return $this->isSatisfied($date->format('m'), $value);
    }

    public function increment(DateTime $date, $invert = false)
    {
        if ($invert) {
            $date->modify('last day of previous month');
            $date->setTime(23, 59);
        } else {
            $date->modify('first day of next month');
            $date->setTime(0, 0);
        }

        return $this;
    }

    public function validate($value)
    {
        return (bool) preg_match('/^[\*,\/\-0-9A-Z]+$/', $value);
    }
}
<?php

namespace Cron;
use DateTime;
use DateTimeZone;


/**
 * Hours field.  Allows: * , / -
 */
class HoursField extends AbstractField
{
    public function isSatisfiedBy(DateTime $date, $value)
    {
        return $this->isSatisfied($date->format('H'), $value);
    }

    public function increment(DateTime $date, $invert = false, $parts = null)
    {
        // Change timezone to UTC temporarily. This will
        // allow us to go back or forwards and hour even
        // if DST will be changed between the hours.
        if (is_null($parts) || $parts == '*') {
            $timezone = $date->getTimezone();
            $date->setTimezone(new DateTimeZone('UTC'));
            if ($invert) {
                $date->modify('-1 hour');
            } else {
                $date->modify('+1 hour');
            }
            $date->setTimezone($timezone);

            $date->setTime($date->format('H'), $invert ? 59 : 0);
            return $this;
        }

        $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts);
        $hours = array();
        foreach ($parts as $part) {
            $hours = array_merge($hours, $this->getRangeForExpression($part, 23));
        }

        $current_hour = $date->format('H');
        $position = $invert ? count($hours) - 1 : 0;
        if (count($hours) > 1) {
            for ($i = 0; $i < count($hours) - 1; $i++) {
                if ((!$invert && $current_hour >= $hours[$i] && $current_hour < $hours[$i + 1]) ||
                    ($invert && $current_hour > $hours[$i] && $current_hour <= $hours[$i + 1])) {
                    $position = $invert ? $i : $i + 1;
                    break;
                }
            }
        }

        $hour = $hours[$position];
        if ((!$invert && $date->format('H') >= $hour) || ($invert && $date->format('H') <= $hour)) {
            $date->modify(($invert ? '-' : '+') . '1 day');
            $date->setTime($invert ? 23 : 0, $invert ? 59 : 0);
        }
        else {
            $date->setTime($hour, $invert ? 59 : 0);
        }

        return $this;
    }

    public function validate($value)
    {
        return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value);
    }
}
<?php

namespace Cron;

use DateTime;
use DateTimeImmutable;
use DateTimeZone;
use Exception;
use InvalidArgumentException;
use RuntimeException;

/**
 * CRON expression parser that can determine whether or not a CRON expression is
 * due to run, the next run date and previous run date of a CRON expression.
 * The determinations made by this class are accurate if checked run once per
 * minute (seconds are dropped from date time comparisons).
 *
 * Schedule parts must map to:
 * minute [0-59], hour [0-23], day of month, month [1-12|JAN-DEC], day of week
 * [1-7|MON-SUN], and an optional year.
 *
 * @link http://en.wikipedia.org/wiki/Cron
 */
class CronExpression
{
    const MINUTE = 0;
    const HOUR = 1;
    const DAY = 2;
    const MONTH = 3;
    const WEEKDAY = 4;
    const YEAR = 5;

    /**
     * @var array CRON expression parts
     */
    private $cronParts;

    /**
     * @var FieldFactory CRON field factory
     */
    private $fieldFactory;

    /**
     * @var int Max iteration count when searching for next run date
     */
    private $maxIterationCount = 1000;

    /**
     * @var array Order in which to test of cron parts
     */
    private static $order = array(self::YEAR, self::MONTH, self::DAY, self::WEEKDAY, self::HOUR, self::MINUTE);

    /**
     * Factory method to create a new CronExpression.
     *
     * @param string $expression The CRON expression to create.  There are
     *                           several special predefined values which can be used to substitute the
     *                           CRON expression:
     *
     *      `@yearly`, `@annually` - Run once a year, midnight, Jan. 1 - 0 0 1 1 *
     *      `@monthly` - Run once a month, midnight, first of month - 0 0 1 * *
     *      `@weekly` - Run once a week, midnight on Sun - 0 0 * * 0
     *      `@daily` - Run once a day, midnight - 0 0 * * *
     *      `@hourly` - Run once an hour, first minute - 0 * * * *
     * @param FieldFactory $fieldFactory Field factory to use
     *
     * @return CronExpression
     */
    public static function factory($expression, FieldFactory $fieldFactory = null)
    {
        $mappings = array(
            '@yearly' => '0 0 1 1 *',
            '@annually' => '0 0 1 1 *',
            '@monthly' => '0 0 1 * *',
            '@weekly' => '0 0 * * 0',
            '@daily' => '0 0 * * *',
            '@hourly' => '0 * * * *'
        );

        if (isset($mappings[$expression])) {
            $expression = $mappings[$expression];
        }

        return new static($expression, $fieldFactory ?: new FieldFactory());
    }

    /**
     * Validate a CronExpression.
     *
     * @param string $expression The CRON expression to validate.
     *
     * @return bool True if a valid CRON expression was passed. False if not.
     * @see \Cron\CronExpression::factory
     */
    public static function isValidExpression($expression)
    {
        try {
            self::factory($expression);
        } catch (InvalidArgumentException $e) {
            return false;
        }

        return true;
    }

    /**
     * Parse a CRON expression
     *
     * @param string       $expression   CRON expression (e.g. '8 * * * *')
     * @param FieldFactory $fieldFactory Factory to create cron fields
     */
    public function __construct($expression, FieldFactory $fieldFactory)
    {
        $this->fieldFactory = $fieldFactory;
        $this->setExpression($expression);
    }

    /**
     * Set or change the CRON expression
     *
     * @param string $value CRON expression (e.g. 8 * * * *)
     *
     * @return CronExpression
     * @throws \InvalidArgumentException if not a valid CRON expression
     */
    public function setExpression($value)
    {
        $this->cronParts = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
        if (count($this->cronParts) < 5) {
            throw new InvalidArgumentException(
                $value . ' is not a valid CRON expression'
            );
        }

        foreach ($this->cronParts as $position => $part) {
            $this->setPart($position, $part);
        }

        return $this;
    }

    /**
     * Set part of the CRON expression
     *
     * @param int    $position The position of the CRON expression to set
     * @param string $value    The value to set
     *
     * @return CronExpression
     * @throws \InvalidArgumentException if the value is not valid for the part
     */
    public function setPart($position, $value)
    {
        if (!$this->fieldFactory->getField($position)->validate($value)) {
            throw new InvalidArgumentException(
                'Invalid CRON field value ' . $value . ' at position ' . $position
            );
        }

        $this->cronParts[$position] = $value;

        return $this;
    }

    /**
     * Set max iteration count for searching next run dates
     *
     * @param int $maxIterationCount Max iteration count when searching for next run date
     *
     * @return CronExpression
     */
    public function setMaxIterationCount($maxIterationCount)
    {
        $this->maxIterationCount = $maxIterationCount;
        
        return $this;
    }

    /**
     * Get a next run date relative to the current date or a specific date
     *
     * @param string|\DateTime $currentTime      Relative calculation date
     * @param int              $nth              Number of matches to skip before returning a
     *                                           matching next run date.  0, the default, will return the current
     *                                           date and time if the next run date falls on the current date and
     *                                           time.  Setting this value to 1 will skip the first match and go to
     *                                           the second match.  Setting this value to 2 will skip the first 2
     *                                           matches and so on.
     * @param bool             $allowCurrentDate Set to TRUE to return the current date if
     *                                           it matches the cron expression.
     *
     * @return \DateTime
     * @throws \RuntimeException on too many iterations
     */
    public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
    {
        return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate);
    }

    /**
     * Get a previous run date relative to the current date or a specific date
     *
     * @param string|\DateTime $currentTime      Relative calculation date
     * @param int              $nth              Number of matches to skip before returning
     * @param bool             $allowCurrentDate Set to TRUE to return the
     *                                           current date if it matches the cron expression
     *
     * @return \DateTime
     * @throws \RuntimeException on too many iterations
     * @see \Cron\CronExpression::getNextRunDate
     */
    public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
    {
        return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate);
    }

    /**
     * Get multiple run dates starting at the current date or a specific date
     *
     * @param int              $total            Set the total number of dates to calculate
     * @param string|\DateTime $currentTime      Relative calculation date
     * @param bool             $invert           Set to TRUE to retrieve previous dates
     * @param bool             $allowCurrentDate Set to TRUE to return the
     *                                           current date if it matches the cron expression
     *
     * @return array Returns an array of run dates
     */
    public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false)
    {
        $matches = array();
        for ($i = 0; $i < max(0, $total); $i++) {
            try {
                $matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate);
            } catch (RuntimeException $e) {
                break;
            }
        }

        return $matches;
    }

    /**
     * Get all or part of the CRON expression
     *
     * @param string $part Specify the part to retrieve or NULL to get the full
     *                     cron schedule string.
     *
     * @return string|null Returns the CRON expression, a part of the
     *                     CRON expression, or NULL if the part was specified but not found
     */
    public function getExpression($part = null)
    {
        if (null === $part) {
            return implode(' ', $this->cronParts);
        } elseif (array_key_exists($part, $this->cronParts)) {
            return $this->cronParts[$part];
        }

        return null;
    }

    /**
     * Helper method to output the full expression.
     *
     * @return string Full CRON expression
     */
    public function __toString()
    {
        return $this->getExpression();
    }

    /**
     * Determine if the cron is due to run based on the current date or a
     * specific date.  This method assumes that the current number of
     * seconds are irrelevant, and should be called once per minute.
     *
     * @param string|\DateTime $currentTime Relative calculation date
     *
     * @return bool Returns TRUE if the cron is due to run or FALSE if not
     */
    public function isDue($currentTime = 'now')
    {
        if ('now' === $currentTime) {
            $currentDate = date('Y-m-d H:i');
            $currentTime = strtotime($currentDate);
        } elseif ($currentTime instanceof DateTime) {
            $currentDate = clone $currentTime;
            // Ensure time in 'current' timezone is used
            $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
            $currentDate = $currentDate->format('Y-m-d H:i');
            $currentTime = strtotime($currentDate);
        } elseif ($currentTime instanceof DateTimeImmutable) {
            $currentDate = DateTime::createFromFormat('U', $currentTime->format('U'));
            $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
            $currentDate = $currentDate->format('Y-m-d H:i');
            $currentTime = strtotime($currentDate);
        } else {
            $currentTime = new DateTime($currentTime);
            $currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0);
            $currentDate = $currentTime->format('Y-m-d H:i');
            $currentTime = $currentTime->getTimeStamp();
        }

        try {
            return $this->getNextRunDate($currentDate, 0, true)->getTimestamp() == $currentTime;
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Get the next or previous run date of the expression relative to a date
     *
     * @param string|\DateTime $currentTime      Relative calculation date
     * @param int              $nth              Number of matches to skip before returning
     * @param bool             $invert           Set to TRUE to go backwards in time
     * @param bool             $allowCurrentDate Set to TRUE to return the
     *                                           current date if it matches the cron expression
     *
     * @return \DateTime
     * @throws \RuntimeException on too many iterations
     */
    protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false)
    {
        if ($currentTime instanceof DateTime) {
            $currentDate = clone $currentTime;
        } elseif ($currentTime instanceof DateTimeImmutable) {
            $currentDate = DateTime::createFromFormat('U', $currentTime->format('U'));
            $currentDate->setTimezone($currentTime->getTimezone());
        } else {
            $currentDate = new DateTime($currentTime ?: 'now');
            $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
        }

        $currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), 0);
        $nextRun = clone $currentDate;
        $nth = (int) $nth;

        // We don't have to satisfy * or null fields
        $parts = array();
        $fields = array();
        foreach (self::$order as $position) {
            $part = $this->getExpression($position);
            if (null === $part || '*' === $part) {
                continue;
            }
            $parts[$position] = $part;
            $fields[$position] = $this->fieldFactory->getField($position);
        }

        // Set a hard limit to bail on an impossible date
        for ($i = 0; $i < $this->maxIterationCount; $i++) {

            foreach ($parts as $position => $part) {
                $satisfied = false;
                // Get the field object used to validate this part
                $field = $fields[$position];
                // Check if this is singular or a list
                if (strpos($part, ',') === false) {
                    $satisfied = $field->isSatisfiedBy($nextRun, $part);
                } else {
                    foreach (array_map('trim', explode(',', $part)) as $listPart) {
                        if ($field->isSatisfiedBy($nextRun, $listPart)) {
                            $satisfied = true;
                            break;
                        }
                    }
                }

                // If the field is not satisfied, then start over
                if (!$satisfied) {
                    $field->increment($nextRun, $invert, $part);
                    continue 2;
                }
            }

            // Skip this match if needed
            if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
                $this->fieldFactory->getField(0)->increment($nextRun, $invert, isset($parts[0]) ? $parts[0] : null);
                continue;
            }

            return $nextRun;
        }

        // @codeCoverageIgnoreStart
        throw new RuntimeException('Impossible CRON expression');
        // @codeCoverageIgnoreEnd
    }
}
<?php

namespace Cron;

use DateTime;
use InvalidArgumentException;


/**
 * Day of week field.  Allows: * / , - ? L #
 *
 * Days of the week can be represented as a number 0-7 (0|7 = Sunday)
 * or as a three letter string: SUN, MON, TUE, WED, THU, FRI, SAT.
 *
 * 'L' stands for "last". It allows you to specify constructs such as
 * "the last Friday" of a given month.
 *
 * '#' is allowed for the day-of-week field, and must be followed by a
 * number between one and five. It allows you to specify constructs such as
 * "the second Friday" of a given month.
 */
class DayOfWeekField extends AbstractField
{
    public function isSatisfiedBy(DateTime $date, $value)
    {
        if ($value == '?') {
            return true;
        }

        // Convert text day of the week values to integers
        $value = $this->convertLiterals($value);

        $currentYear = $date->format('Y');
        $currentMonth = $date->format('m');
        $lastDayOfMonth = $date->format('t');

        // Find out if this is the last specific weekday of the month
        if (strpos($value, 'L')) {
            $weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L')));
            $tdate = clone $date;
            $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth);
            while ($tdate->format('w') != $weekday) {
                $tdateClone = new DateTime();
                $tdate = $tdateClone
                    ->setTimezone($tdate->getTimezone())
                    ->setDate($currentYear, $currentMonth, --$lastDayOfMonth);
            }

            return $date->format('j') == $lastDayOfMonth;
        }

        // Handle # hash tokens
        if (strpos($value, '#')) {
            list($weekday, $nth) = explode('#', $value);

            // 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601
            if ($weekday === '0') {
                $weekday = 7;
            }

            // Validate the hash fields
            if ($weekday < 0 || $weekday > 7) {
                throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given");
            }
            if ($nth > 5) {
                throw new InvalidArgumentException('There are never more than 5 of a given weekday in a month');
            }
            // The current weekday must match the targeted weekday to proceed
            if ($date->format('N') != $weekday) {
                return false;
            }

            $tdate = clone $date;
            $tdate->setDate($currentYear, $currentMonth, 1);
            $dayCount = 0;
            $currentDay = 1;
            while ($currentDay < $lastDayOfMonth + 1) {
                if ($tdate->format('N') == $weekday) {
                    if (++$dayCount >= $nth) {
                        break;
                    }
                }
                $tdate->setDate($currentYear, $currentMonth, ++$currentDay);
            }

            return $date->format('j') == $currentDay;
        }

        // Handle day of the week values
        if (strpos($value, '-')) {
            $parts = explode('-', $value);
            if ($parts[0] == '7') {
                $parts[0] = '0';
            } elseif ($parts[1] == '0') {
                $parts[1] = '7';
            }
            $value = implode('-', $parts);
        }

        // Test to see which Sunday to use -- 0 == 7 == Sunday
        $format = in_array(7, str_split($value)) ? 'N' : 'w';
        $fieldValue = $date->format($format);

        return $this->isSatisfied($fieldValue, $value);
    }

    public function increment(DateTime $date, $invert = false)
    {
        if ($invert) {
            $date->modify('-1 day');
            $date->setTime(23, 59, 0);
        } else {
            $date->modify('+1 day');
            $date->setTime(0, 0, 0);
        }

        return $this;
    }

    public function validate($value)
    {
        $value = $this->convertLiterals($value);

        foreach (explode(',', $value) as $expr) {
            if (!preg_match('/^(\*|[0-7](L?|#[1-5]))([\/\,\-][0-7]+)*$/', $expr)) {
                return false;
            }
        }

        return true;
    }

    private function convertLiterals($string)
    {
        return str_ireplace(
            array('SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'),
            range(0, 6),
            $string
        );
    }
}
<?php

namespace linways\cli\utls;

use com\linways\nucleus\core\dto\College;
use Phinx\Console\PhinxApplication;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Command\Command;
use com\linways\nucleus\core\service\CollegeService;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;

class MigrationUtils{

  /**
   * To ask the password for the database for migrations
   * @param  Command          $command [description]
   * @param  InputInterface  $input   [description]
   * @param  OutputInterface $output  [description]
   */
  public static function askDbPassword(Command $command, InputInterface $input, OutputInterface $output){
    $helper = $command->getHelper('question');
    $question = new Question('Database password: ');
    $question->setHidden(true);
    $question->setHiddenFallback(false);
    $pass = $helper->ask($input, $output, $question);
    return $pass;
  }

  /**
   * returns an array of database details for running migrations migrations
   * @param  string $collegeCode
   * @param  boolean $migrateAll  true if migrating all databases
   * @param  object $db
   * @return College[]
   */
  public static function getDbDetailsForMigration($collegeCode,$migrateAll, $db ){
    $dbsToMigrate = [];
    if(!empty($collegeCode)){
      //get the details about college
      $singleDb = CollegeService::getInstance()->getCollegeByCollegeCode($collegeCode);
      $dbsToMigrate[] = $singleDb;
    }else if (!empty($migrateAll)){
      //get all college info
      $dbsToMigrate = CollegeService::getInstance()->getAllColleges();
    }else if (!empty($db->name)){
      //migrate for a single database
      $singleDb = new College();
      $singleDb->collegeDb = $db->name;
      $singleDb->dbHost = $db->host;
      $singleDb->dbUsername = $db->username;
      $singleDb->dbPassword = $db->password;
      $dbsToMigrate[] = $singleDb;
    }
    return $dbsToMigrate;
  }

  /**
   * To execue a phinx command
   * @param  array            $command [description]
   * @param  PhinxApplication $app     [description]
   * @return [type]                    [description]
   */
  function executeRun(array $command, PhinxApplication $app)
  {
      // Output will be written to a temporary stream, so that it can be
      // collected after running the command.
      $stream = fopen('php://temp', 'w+');
      // Execute the command, capturing the output in the temporary stream
      // and storing the exit code for debugging purposes.
      $exit_code = $app->doRun(new ArrayInput($command), new StreamOutput($stream));
      // Get the output of the command and close the stream, which will
      // destroy the temporary file.
      $result = stream_get_contents($stream, -1, 0);
      fclose($stream);
      return $result;
  }
}
<?php

namespace linways\cli\command\db;

use linways\cli\utls\MigrationUtils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

use linways\cli\service\db\MigrateService;

class MigrateCommand extends Command{

    protected function configure(){
        $this->setName("db:migrate")
             ->setDescription("To run migrations against required databases.")
             ->setHelp("
Examples:
`<fg=blue;options=bold>db:migrate PRO</>` For running migrations for a college with code PRO
`<fg=blue;options=bold>db:migrate --all</>` For running migrations on all colleges using nucleus
`<fg=blue;options=bold>db:migrate --db=pro_db -u db_username -p</>` For running migrations on a specific database
`<fg=blue;options=bold>db:migrate --all --target=20171018082229</>` For migrating till a particular migration.
             ")
             ->addArgument('college_code', InputArgument::OPTIONAL, 'College code for migration. Works only with nucleus.')
             ->addOption('all','a', InputOption::VALUE_NONE, 'For executing migrations against all colleges in nucleus.')
             ->addOption('db', 'd', InputOption::VALUE_REQUIRED, 'for executing migrations against a single db (Nucleus not required)')
             ->addOption('target', 't', InputOption::VALUE_REQUIRED, 'If you want to run upto a particular migration. Target version is the time stamp in the migration file name (first part of the name)')
             ->addOption('host', null, InputOption::VALUE_REQUIRED, 'Database host name/ip.', 'localhost')
             ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Database user name.', 'root')
             ->addOption('pass', 'p', InputOption::VALUE_NONE, 'Prompt for database password. <comment>[default: "root"]</comment>');
    }

    protected function execute(InputInterface $input, OutputInterface $output){
        $collegeCode = $input->getArgument('college_code');
        $migrateAll = $input->getOption('all');
        $target = $input->getOption('target');
        $db = new \stdClass();
        $db->name = $input->getOption('db');
        $db->host = $input->getOption('host');
        $db->username = $input->getOption('user');
        $db->password = $input->getOption('pass');
        if(empty($collegeCode) && empty($migrateAll) && empty($db->name)){
          $output->writeln("Run `db:migrate --help` for usage");
          exit(1);
        }
        if(!$db->password )
          $db->password  = 'root'; //for setting the default password
        else
          $db->password = MigrationUtils::askDbPassword($this, $input, $output);
        $dbsToMigrate = MigrationUtils::getDbDetailsForMigration($collegeCode, $migrateAll, $db);
        try{
          foreach ($dbsToMigrate as $db) {
            $output->writeln('<question>============= Migrating :<options=bold;fg=black;bg=yellow>'.$db->collegeDb .' ['. $db->code.']</>==========</question>');
            $response = MigrateService::migrateDb($db->collegeDb, $target, $db->dbHost, $db->dbUsername, $db->dbPassword);
            print_r($response);
            $output->writeln('<options=bold;fg=black;bg=green>âœ“ DONE</>');
          }
        }catch(\Exception $e){
          $output->writeln('<error>'. $e->getMessage(). '</error>');
        }
    }
}
<?php

namespace linways\cli\command\db;

use linways\cli\service\db\MigrateFakeService;
use linways\cli\utls\MigrationUtils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class MigrateFakeCommand extends Command{

    protected function configure(){
        $this->setName("db:migrate-fake")
             ->setDescription("To fake a migration (mark migration as done)")
             ->setHelp("
Examples:
`<fg=blue;options=bold>db:migrate-fake 20171018082229_scheduler_migrations PRO </>` For faking 'scheduler_migrations' for a college with code PRO
`<fg=blue;options=bold>db:migrate-fake 20171018082229_scheduler_migrations --all </>` For faking 'scheduler_migrations' on all colleges using nucleus
`<fg=blue;options=bold>db:migrate-fake 20171018082229_scheduler_migrations --db=pro_db -u db_username -p </>` For faking 'scheduler_migrations' on a specific database
`<fg=blue;options=bold>db:migrate-fake 20171018082229_scheduler_migrations PRO --revert</>` For removing the particular migration from migration table. Effectively setting this migration as 'not migrated'
             ")
             ->addArgument('migration_file_name', InputArgument::REQUIRED, 'File name of the migration to be faked. <fg=red;>eg: 20171018082229_scheduler_migrations</>')
             ->addArgument('college_code', InputArgument::OPTIONAL, 'College code for faking the migration. Works only with nucleus.')
             ->addOption('all','a', InputOption::VALUE_NONE, 'For faking migrations against all colleges in nucleus.')
             ->addOption('db', 'd', InputOption::VALUE_REQUIRED, 'for faking migrations against a single db (Nucleus not required)')
             ->addOption('revert','r', InputOption::VALUE_NONE, 'For setting a migrations as NOT DONE. NB: This will not revert the actual database changes.')
             ->addOption('host', null, InputOption::VALUE_REQUIRED, 'Database host name/ip.', 'localhost')
             ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Database user name.', 'root')
             ->addOption('pass', 'p', InputOption::VALUE_NONE, 'promt for database password. <comment>[default: "root"]</comment>');
    }

    protected function execute(InputInterface $input, OutputInterface $output){
        $migrationFile = $input->getArgument('migration_file_name');
        $collegeCode = $input->getArgument('college_code');
        $migrateAll = $input->getOption('all');
        $revert = $input->getOption('revert');
        $db = new \stdClass();
        $db->name = $input->getOption('db');
        $db->host = $input->getOption('host');
        $db->username = $input->getOption('user');
        $db->password = $input->getOption('pass');

        if(empty($collegeCode) && empty($migrateAll) && empty($db->name)){
          $output->writeln("Run `db:migrate-fake --help` for usage");
          exit(1);
        }
        if(!$db->password)
          $db->password = 'root'; //for setting the default password
        else
          $db->password = MigrationUtils::askDbPassword($this, $input, $output);
        $dbsToMigrate = MigrationUtils::getDbDetailsForMigration($collegeCode, $migrateAll, $db);
        list($version, $migarationName) = explode ('_', $migrationFile, 2);
        $migarationName .= '-faked';
        try{
          foreach ($dbsToMigrate as $db) {
            if($revert){
              $response = MigrateFakeService::fakeRevert($version, $migarationName, $db);
              $output->writeln('<question>============= Reverting Migration(FAKE) :<options=bold;fg=black;bg=yellow>'.$db->collegeDb .' ['. $db->code.']['.$migrationFile.']</>==========</question>');
              $output->writeln('Note that this will not revert the actual database changes caused by this migration.');
            }
            else{
              $output->writeln('<question>============= Migrating(FAKE) :<options=bold;fg=black;bg=yellow>'.$db->collegeDb .' ['. $db->code.']['.$migrationFile.']</>==========</question>');
              $response = MigrateFakeService::fakeMigration($version, $migarationName, $db);
            }
            if($response == false)
              $output->writeln("<comment>$version Already migrated</comment>");
            $output->writeln('<options=bold;fg=black;bg=green>âœ“ DONE</>');
          }
        }catch(\Exception $e){
          $output->writeln('<error>'. $e->getMessage(). '</error>');
        }
    }
}
<?php

namespace linways\cli\command\db;

use Phar;
use Exception;
use linways\cli\utls\MigrationUtils;
use Phinx\Console\PhinxApplication;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;

class CreateMigrationCommand extends Command{

    protected function configure(){
        $this->setName("db:create-migration")
             ->setDescription("To create a new migration file.")
             ->setHelp("
Examples:
`<fg=blue;options=bold>db:create-migration NameForMigrationInCamelCase</>` For creating a new migration file
             ")
             ->addArgument('migration_name', InputArgument::REQUIRED, 'Name of the migration in camel case.');
    }

    protected function execute(InputInterface $input, OutputInterface $output){
        $collegeCode = $input->getArgument('migration_name');
        $app = new PhinxApplication();
        $command = ['create'];
        $command += ["name" => $collegeCode];
        if(Phar::running(true))
          $command += ["--template" => Phar::running(true) ."/MigrationCustom.template.php.dist"];
        else
        $command += ["--template" => "./MigrationCustom.template.php.dist"];
        try{
          $response = MigrationUtils::executeRun($command, $app);
          print_r($response);
          $output->writeln('<options=bold;fg=black;bg=green>âœ“ DONE</>');
        }catch(Exception $e){
          $output->writeln('<error>'. $e->getMessage(). '</error>');
          exit(1);
        }
    }
}
<?php

namespace linways\cli\command\git;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

class UpdateStyleGuideCommand extends Command{

    protected function configure(){
        $this->setName("git:update-styleguide")
             ->setDescription("To fetch the new version of the style guide from gitlab")
             ->setHelp("
Examples:
`<fg=blue;options=bold>git:update-styleguide</>` For updating the style guide 
`<fg=blue;options=bold>git:update-styleguide -p public/libcommon/styleguide</>` Update the styleguide which is at different location
             ")
             ->addOption('path', 'p', InputOption::VALUE_REQUIRED, 'Path to which the styleguide repo to be pulled', 'libcommon/styleguide')
             ->addOption('repo', 'r', InputOption::VALUE_REQUIRED, 'SSH repository url of style guide', 'git@gitlab.com:linways/styleguide.git')
             ->addOption('branch', 'b', InputOption::VALUE_REQUIRED, 'Branch name to pull', 'master');
    }

    protected function execute(InputInterface $input, OutputInterface $output){
        $path = $input->getOption('path');
        $repo = $input->getOption('repo');
        $branch = $input->getOption('branch');
        $isDirty = !empty(exec("git status --porcelain"));
        if($isDirty){
            $output->writeln('<error> Git is dirty. Please commit all changes for pulling the styleguide</error>');
            exit(1);
        }
        $command = "git subtree pull --prefix " . $path . " " . $repo . " " . $branch . " --squash";
        $output->writeln("Executing " . $command );
        echo exec($command);
    }
}
<?php
namespace linways\cli\service\db;
use Exception;
use Symfony\Component\Yaml\Yaml;

class MigrateFakeService{
  /**
   * creates an entry in migration table with $version and $name
   * @param  string $version       version of the migration (timestamp prefix of the migration file)
   * @param  string $migrationName name of the migration
   * @param  array  $dbDetails     database connection details
   * @return boolean
   */
  public static function fakeMigration($version, $migrationName, $dbDetails){
    $db = new \mysqli($dbDetails->dbHost, $dbDetails->dbUsername, $dbDetails->dbPassword, $dbDetails->collegeDb);
    if($db->connect_errno > 0){
        $e = new \Exception('Unable to connect to database [' . $db->connect_error . ']');
        throw $e;
        exit(0);
    }

    // read migration table name from .yml configuration
    if(empty(getenv('PHINX_CONF')))
      throw new \Exception("Environment variable `PHINX_CONF` not found. define it in `.cli.env` file on project root.");

    $phixConf = Yaml::parseFile(getenv('PHINX_CONF'));
    $migrationTableName = $phixConf ["environments"]["default_migration_table"];
    $createTable = "CREATE TABLE IF NOT EXISTS `$migrationTableName` (
    `version` bigint(20) NOT NULL,
    `migration_name` varchar(100) DEFAULT NULL,
    `start_time` timestamp NULL DEFAULT NULL,
    `end_time` timestamp NULL DEFAULT NULL,
    `breakpoint` tinyint(1) NOT NULL DEFAULT '0',
    PRIMARY KEY (`version`))";
    $db->query($createTable);

    $sqlCheckMigrated = "SELECT * from `$migrationTableName` where version='$version'";
    $result = $db->query($sqlCheckMigrated);
    if($result->num_rows > 0){
      return false;
    }else{
      $sql = "INSERT INTO `$migrationTableName`
      (`version`, `migration_name`, `start_time`, `end_time`)
      VALUES ('$version', '$migrationName', NOW(), NOW())";
      if(!$result = $db->query($sql)){
        $e = new \Exception("There was an error running the query [$db->error]\n");
        throw $e;
        exit(0);
      }
      else
        return true;
    }
  }

  /**
   * Removes the entry from migration table.
   * @param  string $version        version of the migration (timestamp prefix of the migration file)
   * @param  string $migrationName  name of the migration
   * @param  array  $dbDetails      database connection details
   * @return true|Exception
   */
  public static function fakeRevert($version, $migrationName, $dbDetails){
    $db = new \mysqli($dbDetails->dbHost, $dbDetails->dbUsername, $dbDetails->dbPassword, $dbDetails->collegeDb);
    if($db->connect_errno > 0){
        $e = new \Exception('Unable to connect to database [' . $db->connect_error . ']');
        throw $e;
        exit(0);
    }

    // read migration table name from .yml configuration
    if(empty(getenv('PHINX_CONF')))
      throw new \Exception("Environment variable `PHINX_CONF` not found. define it in `.cli.env` file on project root.");

    $phixConf = Yaml::parseFile(getenv('PHINX_CONF'));
    $migrationTableName = $phixConf ["environments"]["default_migration_table"];
    $sql = "delete from `$migrationTableName` where version='$version'";
    if(!$result = $db->query($sql)){
      throw new \Exception("There was an error running the query [$db->error]\n");
      exit(0);
    }
    else
      return true;
  }
}
<?php
namespace linways\cli\service\db;
use Phinx\Console\PhinxApplication;

class MigrateService{
  /**
   * migrate a single database using username and password
   * @param  string $dbName   [description]
   * @param  string $target   target migration version
   * @param  string $host     [description]
   * @param  string $username [description]
   * @param  string $password [description]
   * @return [type]           [description]
   */
  public static function migrateDb($dbName, $target, $host, $username, $password){
    $app = new PhinxApplication();
    $_SERVER['PHINX_DBNAME'] = $dbName;
    $_SERVER['PHINX_DBHOST'] = $host;
    $_SERVER['PHINX_DBUSER'] = $username;
    $_SERVER['PHINX_DBPASS'] = $password;
    $_SERVER['PHINX_CONFIG_DIR'] = dirname (getenv('PHINX_CONF'));
    if(empty($_SERVER['PHINX_CONFIG_DIR']))
      throw new \Exception("Environment variable `PHINX_CONF` not found. define it in `.cli.env` file on project root.");

    $wrap = new \Phinx\Wrapper\TextWrapper($app, array(
                // 'config_path' => __DIR__. '/../../../phinx.yml',
                'parser' => 'yaml'
    ));
    try{
      if($target)
        $response = $wrap->getMigrate(null, $target);
      else
        $response = $wrap->getMigrate();
    }catch(\Exception $e){
      throw $e;
      exit(1);
    }
    unset($wrap);
    unset($app);
    return $response;
  }
}
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Console\Application;
use linways\cli\command\db\MigrateCommand;
use linways\cli\command\db\CreateMigrationCommand;
use linways\cli\command\db\MigrateFakeCommand;
use linways\cli\command\git\UpdateStyleGuideCommand;
use Symfony\Component\Dotenv\Dotenv;
error_reporting(E_ALL ^ E_NOTICE);  

$app = new Application();
$dotenv = new Dotenv();
$dotenv->load('.cli.env');
$app->add(new MigrateCommand());
$app->add(new MigrateFakeCommand());
$app->add(new CreateMigrationCommand());
$app->add(new UpdateStyleGuideCommand());
$app->run();
<?php
$namespaceDefinition

use $useClassName;

class $className extends $baseClassName
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
      // write your SQL inside the double quotes
      $this->execute("

      ");
    }
}
€<²Ýé5ô}`‹¾òyzÍaFï   GBMB