#!/usr/bin/env php X codecept.phar2vendor/phpseclib/phpseclib/phpseclib/bootstrap.phpGbtn 8vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php3"Gb3"jX1vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.phpXfGbXf69*1vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.phpGb0vendor/phpseclib/phpseclib/phpseclib/Net/SCP.phpGb1vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php$^Gb$^*C8vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.phpGbf42vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.phpw0Gbw0Ií5vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.phpGb'Ф7vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.phpVGbVs7vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.phpGJGbGJ+y6vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.phpnmGbnm;,2vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.phppGbpw3vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.phpYGbY6pa8vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.phpUGbUf [2vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.phpGb2ä2vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.phpGb"2vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php Gb %C$3vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php8Gb8+2vendor/phpseclib/phpseclib/phpseclib/File/X509.php;uGb;u?2vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php"Gb".7:vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.phpGb_*ё2vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php)dGb)dc-M9vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php$Gb$[Bvendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.phpp Gbp %vendor/webmozart/assert/src/Mixin.php6Gb6uF&vendor/webmozart/assert/src/Assert.phpHkGbHk2vendor/php-webdriver/webdriver/lib/WebDriverBy.php[Gb[ (5vendor/php-webdriver/webdriver/lib/WebDriverAlert.phpGbZ=A=vendor/php-webdriver/webdriver/lib/WebDriverSearchContext.phpGb\Τ<vendor/php-webdriver/webdriver/lib/WebDriverCapabilities.phpAGbAA^ 6vendor/php-webdriver/webdriver/lib/WebDriverAction.phpbGbbGvendor/php-webdriver/webdriver/lib/AbstractWebDriverCheckboxOrRadio.phpGb :-vendor/php-webdriver/webdriver/lib/Cookie.php Gb [5vendor/php-webdriver/webdriver/lib/Net/URLChecker.phpqGbqġڤ6vendor/php-webdriver/webdriver/lib/WebDriverRadios.phpnGbnhCvendor/php-webdriver/webdriver/lib/WebDriverNavigationInterface.phpGbv ä6vendor/php-webdriver/webdriver/lib/WebDriverSelect.phpGbxL-:vendor/php-webdriver/webdriver/lib/WebDriverNavigation.phpGbf4vendor/php-webdriver/webdriver/lib/WebDriverKeys.php Gb |_qK8vendor/php-webdriver/webdriver/lib/WebDriverPlatform.phpGb9vendor/php-webdriver/webdriver/lib/JavaScriptExecutor.phpGb⌤7vendor/php-webdriver/webdriver/lib/WebDriverOptions.phpGb65vendor/php-webdriver/webdriver/lib/WebDriverPoint.php_Gb_?vendor/php-webdriver/webdriver/lib/WebDriverSelectInterface.php@Gb@_4vendor/php-webdriver/webdriver/lib/WebDriverWait.php;Gb;Avendor/php-webdriver/webdriver/lib/Remote/UselessFileDetector.phpGbk&>vendor/php-webdriver/webdriver/lib/Remote/RemoteWebElement.phpR$GbR$[#?vendor/php-webdriver/webdriver/lib/Remote/RemoteTouchScreen.php:Gb:`e4<vendor/php-webdriver/webdriver/lib/Remote/RemoteKeyboard.phpGbygҤAvendor/php-webdriver/webdriver/lib/Remote/RemoteExecuteMethod.phpRGbRm;vendor/php-webdriver/webdriver/lib/Remote/DriverCommand.phpDGbDNfAvendor/php-webdriver/webdriver/lib/Remote/RemoteTargetLocator.php Gb X/Dvendor/php-webdriver/webdriver/lib/Remote/CustomWebDriverCommand.phpmGbmKvendor/php-webdriver/webdriver/lib/Remote/Service/DriverCommandExecutor.phpGb`Cvendor/php-webdriver/webdriver/lib/Remote/Service/DriverService.php Gb IZEvendor/php-webdriver/webdriver/lib/Remote/WebDriverCapabilityType.php(Gb(tc)Ѥ>vendor/php-webdriver/webdriver/lib/Remote/WebDriverCommand.php_Gb_!Uv:vendor/php-webdriver/webdriver/lib/Remote/FileDetector.phpuGbuAvendor/php-webdriver/webdriver/lib/Remote/HttpCommandExecutor.php>Gb>);vendor/php-webdriver/webdriver/lib/Remote/ExecuteMethod.phpGbt5f ?vendor/php-webdriver/webdriver/lib/Remote/LocalFileDetector.phpGbn.pBvendor/php-webdriver/webdriver/lib/Remote/WebDriverBrowserType.phpGb1P_=vendor/php-webdriver/webdriver/lib/Remote/RemoteWebDriver.phpO&GbO&4%[X9vendor/php-webdriver/webdriver/lib/Remote/RemoteMouse.phpPGbP K:vendor/php-webdriver/webdriver/lib/Remote/RemoteStatus.phpGb=]<vendor/php-webdriver/webdriver/lib/Remote/JsonWireCompat.php>Gb>st?vendor/php-webdriver/webdriver/lib/Remote/WebDriverResponse.phpGbr-Avendor/php-webdriver/webdriver/lib/Remote/DesiredCapabilities.phpGbxlAvendor/php-webdriver/webdriver/lib/WebDriverExpectedCondition.phpMGbM56vendor/php-webdriver/webdriver/lib/WebDriverWindow.phpU GbU #?8vendor/php-webdriver/webdriver/lib/WebDriverKeyboard.phpGbe%?vendor/php-webdriver/webdriver/lib/WebDriverHasInputDevices.phpGbDh=vendor/php-webdriver/webdriver/lib/WebDriverTargetLocator.phpiGbig8פAvendor/php-webdriver/webdriver/lib/Chrome/ChromeDriverService.phpGbUy;vendor/php-webdriver/webdriver/lib/Chrome/ChromeOptions.phpGbޤBvendor/php-webdriver/webdriver/lib/Chrome/ChromeDevToolsDriver.php@Gb@)|:vendor/php-webdriver/webdriver/lib/Chrome/ChromeDriver.phpLGbL ;vendor/php-webdriver/webdriver/lib/Support/XPathEscaper.phpGb/2>Jvendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriver.phpGbsKvendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebElement.phpGb3=+Tvendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriverNavigation.phpLGbLs[ޤBvendor/php-webdriver/webdriver/lib/Internal/WebDriverLocatable.phpGbFz8vendor/php-webdriver/webdriver/lib/WebDriverTimeouts.phpGbD =vendor/php-webdriver/webdriver/lib/WebDriverEventListener.phprGbr"9vendor/php-webdriver/webdriver/lib/WebDriverDimension.phpGbZ,:vendor/php-webdriver/webdriver/lib/WebDriverDispatcher.phpKGbKЭ@;vendor/php-webdriver/webdriver/lib/Local/LocalWebDriver.phpGbՏ%7vendor/php-webdriver/webdriver/lib/WebDriverElement.phpGbOI8vendor/php-webdriver/webdriver/lib/WebDriverUpAction.phpGb.rMvendor/php-webdriver/webdriver/lib/Exception/InvalidCookieDomainException.phpuGbu@8sEvendor/php-webdriver/webdriver/lib/Exception/NoAlertOpenException.phpoGboWBvendor/php-webdriver/webdriver/lib/Exception/ExpectedException.phpjGbjj驤Pvendor/php-webdriver/webdriver/lib/Exception/ElementNotInteractableException.phpxGbxwNvendor/php-webdriver/webdriver/lib/Exception/UnsupportedOperationException.phpvGbv:Fvendor/php-webdriver/webdriver/lib/Exception/UnknownErrorException.phpnGbn8LFvendor/php-webdriver/webdriver/lib/Exception/NoSuchCookieException.phpoGboe?hGvendor/php-webdriver/webdriver/lib/Exception/ScriptTimeoutException.phpoGbo3CSvendor/php-webdriver/webdriver/lib/Exception/IMEEngineActivationFailedException.php{Gb{FEvendor/php-webdriver/webdriver/lib/Exception/NoSuchAlertException.phpmGbmW Bvendor/php-webdriver/webdriver/lib/Exception/NoStringException.phpjGbjuqJvendor/php-webdriver/webdriver/lib/Exception/InvalidSessionIdException.phpsGbs>Evendor/php-webdriver/webdriver/lib/Exception/XPathLookupException.phpmGbmaTWFvendor/php-webdriver/webdriver/lib/Exception/NoSuchWindowException.phpnGbn>Nvendor/php-webdriver/webdriver/lib/Exception/ElementNotSelectableException.phpGb: Mvendor/php-webdriver/webdriver/lib/Exception/UnexpectedAlertOpenException.phpuGbuIvendor/php-webdriver/webdriver/lib/Exception/JavascriptErrorException.phpqGbqi.Mvendor/php-webdriver/webdriver/lib/Exception/InvalidElementStateException.phpvGbvsHKvendor/php-webdriver/webdriver/lib/Exception/ElementNotVisibleException.phpsGbs-mOvendor/php-webdriver/webdriver/lib/Exception/StaleElementReferenceException.phpwGbwޢRJvendor/php-webdriver/webdriver/lib/Exception/IndexOutOfBoundsException.phprGbr Gvendor/php-webdriver/webdriver/lib/Exception/UnknownMethodException.phpoGboj8Ovendor/php-webdriver/webdriver/lib/Exception/UnrecognizedExceptionException.phptGbttmWIvendor/php-webdriver/webdriver/lib/Exception/InvalidSelectorException.phpqGbqvKrAvendor/php-webdriver/webdriver/lib/Exception/TimeoutException.phpiGbiHvendor/php-webdriver/webdriver/lib/Exception/NoSuchDocumentException.phpsGbsMbФOvendor/php-webdriver/webdriver/lib/Exception/UnableToCaptureScreenException.phpwGbwL}>Qvendor/php-webdriver/webdriver/lib/Exception/ElementClickInterceptedException.phpzGbzhZSFvendor/php-webdriver/webdriver/lib/Exception/NoCollectionException.phpnGbn3Gvendor/php-webdriver/webdriver/lib/Exception/NoSuchElementException.phpoGboHA\Jvendor/php-webdriver/webdriver/lib/Exception/NoSuchCollectionException.phprGbr_gHvendor/php-webdriver/webdriver/lib/Exception/UnknownCommandException.phppGbpKvendor/php-webdriver/webdriver/lib/Exception/UnexpectedTagNameException.php2Gb2l̃Mvendor/php-webdriver/webdriver/lib/Exception/InsecureCertificateException.phpvGbv}Nvendor/php-webdriver/webdriver/lib/Exception/UnexpectedJavascriptException.php|Gb|ۤHvendor/php-webdriver/webdriver/lib/Exception/NoScriptResultException.phppGbpGvendor/php-webdriver/webdriver/lib/Exception/WebDriverCurlException.phplGbl'_Hvendor/php-webdriver/webdriver/lib/Exception/NoStringLengthException.phppGbp⬣Kvendor/php-webdriver/webdriver/lib/Exception/SessionNotCreatedException.phpsGbs~jGvendor/php-webdriver/webdriver/lib/Exception/UnknownServerException.phprGbr-Ivendor/php-webdriver/webdriver/lib/Exception/IMENotAvailableException.phpqGbq(OeIvendor/php-webdriver/webdriver/lib/Exception/InvalidArgumentException.phpqGbqZ Jvendor/php-webdriver/webdriver/lib/Exception/DriverServerDiedException.php.Gb.eEvendor/php-webdriver/webdriver/lib/Exception/NoSuchFrameException.phpmGbmΚLvendor/php-webdriver/webdriver/lib/Exception/InvalidCoordinatesException.phptGbt ܤCvendor/php-webdriver/webdriver/lib/Exception/WebDriverException.phpGb#Fvendor/php-webdriver/webdriver/lib/Exception/NoSuchDriverException.phpnGbnOvendor/php-webdriver/webdriver/lib/Exception/MoveTargetOutOfBoundsException.phpwGbw( ~Ivendor/php-webdriver/webdriver/lib/Exception/NoStringWrapperException.phpqGbqdKwKvendor/php-webdriver/webdriver/lib/Exception/UnableToSetCookieException.phpsGbsEvendor/php-webdriver/webdriver/lib/Exception/NullPointerException.phpmGbm&#:vendor/php-webdriver/webdriver/lib/WebDriverCheckboxes.phpGb_EK<vendor/php-webdriver/webdriver/lib/Firefox/FirefoxDriver.phpGbxCCvendor/php-webdriver/webdriver/lib/Firefox/FirefoxDriverService.phpKGbKB+=vendor/php-webdriver/webdriver/lib/Firefox/FirefoxOptions.phpGb yAvendor/php-webdriver/webdriver/lib/Firefox/FirefoxPreferences.phpkGbkBr=vendor/php-webdriver/webdriver/lib/Firefox/FirefoxProfile.phpZGbZ^>Nvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchScreen.phpGbs'fOvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollAction.phpGbA-6BRvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDoubleTapAction.phpGbQLvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTapAction.phpGb+"8Mvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDownAction.phpGbRvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverLongPressAction.phpGbm%jqZvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollFromElementAction.php7Gb7%dlYvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickFromElementAction.php|Gb|`)YNvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickAction.phpGb]cMvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverMoveAction.phpGbFNvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchAction.phpmGbmߤDvendor/php-webdriver/webdriver/lib/Interactions/WebDriverActions.php Gb a_Ivendor/php-webdriver/webdriver/lib/Interactions/WebDriverTouchActions.php? Gb? 7VcQvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyUpAction.phpGbDTvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSendKeysAction.phpGb Xvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAndHoldAction.phpGbpAzQvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAction.php Gb [R_Xvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMoveToOffsetAction.phpGbMWvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverDoubleClickAction.phpGbUvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseMoveAction.phpGbT]-Wvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeysRelatedAction.phpGb8 Xvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverContextClickAction.phpGb1ӌQvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseAction.phpGbT+Svendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyDownAction.phpGb4vYvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverButtonReleaseAction.phpGbeV$Uvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSingleKeyAction.phpxGbx43Qvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverCoordinates.phpaGbaH.HLvendor/php-webdriver/webdriver/lib/Interactions/WebDriverCompositeAction.phpGb[T0vendor/php-webdriver/webdriver/lib/WebDriver.phpGbN5vendor/php-webdriver/webdriver/lib/WebDriverMouse.phpGb{z?vendor/php-webdriver/webdriver/lib/WebDriverCommandExecutor.phpGbHO٬#vendor/predis/predis/src/Client.phpJGbJPJҤ?vendor/predis/predis/src/Replication/MissingMasterException.phpGb<vendor/predis/predis/src/Replication/ReplicationStrategy.phpmGbmK6vendor/predis/predis/src/Replication/RoleException.phpGb״K,vendor/predis/predis/src/Session/Handler.phpGb= ,vendor/predis/predis/src/ClientInterface.phpGb?۾2vendor/predis/predis/src/Transaction/MultiExec.php]Gb]Bvendor/predis/predis/src/Transaction/AbortedMultiExecException.phpGb57vendor/predis/predis/src/Transaction/MultiExecState.phpGbC2vendor/predis/predis/src/Configuration/Options.phplGbl BM:vendor/predis/predis/src/Configuration/OptionInterface.phpGbv 8vendor/predis/predis/src/Configuration/ClusterOption.phpGbf:7vendor/predis/predis/src/Configuration/PrefixOption.phpGbF=;vendor/predis/predis/src/Configuration/OptionsInterface.phpGb6<vendor/predis/predis/src/Configuration/ReplicationOption.phpGbz%;vendor/predis/predis/src/Configuration/ExceptionsOption.php-Gb-g8vendor/predis/predis/src/Configuration/ProfileOption.php}Gb}"Bvendor/predis/predis/src/Configuration/ConnectionFactoryOption.phpGbm"Ƥ:vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php Gb 7,vendor/predis/predis/src/Pipeline/Atomic.php> Gb> vjw3vendor/predis/predis/src/Pipeline/FireAndForget.phpfGbfj6.vendor/predis/predis/src/Pipeline/Pipeline.php Gb |#v-vendor/predis/predis/src/Monitor/Consumer.phpIGbIJ۬,vendor/predis/predis/src/PubSub/Consumer.php Gb XW?"4vendor/predis/predis/src/PubSub/AbstractConsumer.phpm Gbm rsq2vendor/predis/predis/src/PubSub/DispatcherLoop.php;Gb;%4vendor/predis/predis/src/Profile/RedisVersion220.phpGb}$U4vendor/predis/predis/src/Profile/RedisVersion200.php*Gb*$z4vendor/predis/predis/src/Profile/RedisVersion280.phpGb_ۤ4vendor/predis/predis/src/Profile/RedisVersion320.phpFGbFG.C1vendor/predis/predis/src/Profile/RedisProfile.phpGb;,vendor/predis/predis/src/Profile/Factory.phpGb&r5vendor/predis/predis/src/Profile/ProfileInterface.php\Gb\2z4vendor/predis/predis/src/Profile/RedisVersion260.phpGb)\R2vendor/predis/predis/src/Profile/RedisUnstable.phpGb͋4vendor/predis/predis/src/Profile/RedisVersion240.php(Gb(M`D4vendor/predis/predis/src/Profile/RedisVersion300.phpGb֦3vendor/predis/predis/src/ClientContextInterface.phpGb&}2vendor/predis/predis/src/NotSupportedException.phpaGban),vendor/predis/predis/src/PredisException.php^Gb^:vendor/predis/predis/src/Connection/AbstractConnection.phpQ GbQ Avendor/predis/predis/src/Connection/PhpiredisStreamConnection.phpjGbj Dvendor/predis/predis/src/Connection/AggregateConnectionInterface.phpGblAuAvendor/predis/predis/src/Connection/CompositeStreamConnection.phpGba8vendor/predis/predis/src/Connection/WebdisConnection.phplGblV<;vendor/predis/predis/src/Connection/ConnectionException.phpGb-2vendor/predis/predis/src/Connection/Parameters.phpGGbG¿Dvendor/predis/predis/src/Connection/CompositeConnectionInterface.phpGb];vendor/predis/predis/src/Connection/ConnectionInterface.phpGb^ M/vendor/predis/predis/src/Connection/Factory.php" Gb" ^Ť8vendor/predis/predis/src/Connection/StreamConnection.phpGbr.?vendor/predis/predis/src/Connection/NodeConnectionInterface.phpmGbmi8vendor/predis/predis/src/Connection/FactoryInterface.php;Gb;6.5Avendor/predis/predis/src/Connection/PhpiredisSocketConnection.phpvGbvZK4;vendor/predis/predis/src/Connection/ParametersInterface.phpGb Y֤Hvendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.phpWGbW.%Fvendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.phpGb!Bvendor/predis/predis/src/Connection/Aggregate/ClusterInterface.phpGbN?vendor/predis/predis/src/Connection/Aggregate/PredisCluster.phpB GbB gEvendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php'Gb'<>vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php;#Gb;# i6vendor/predis/predis/src/Cluster/StrategyInterface.php?Gb?"3vendor/predis/predis/src/Cluster/PredisStrategy.phpGbs@vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.phpGb!)/vendor/predis/predis/src/Cluster/Hash/CRC16.php Gb C 2vendor/predis/predis/src/Cluster/RedisStrategy.phpGb m4vendor/predis/predis/src/Cluster/ClusterStrategy.php#Gb# @9vendor/predis/predis/src/Cluster/Distributor/HashRing.php Gb ԐhCvendor/predis/predis/src/Cluster/Distributor/EmptyRingException.phplGblEvendor/predis/predis/src/Cluster/Distributor/DistributorInterface.phpGbj;vendor/predis/predis/src/Cluster/Distributor/KetamaRing.phpvGbvx@vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php_Gb_i"7vendor/predis/predis/src/Protocol/ProtocolException.phpGb5Evendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.phpGb,$<vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php Gb {9vendor/predis/predis/src/Protocol/Text/ResponseReader.phpGb0pDvendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php Gb :dKvendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.phpGb wQ?vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.phpGbW0@vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php;Gb;LIBvendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php`Gb`LNvendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.phpGbA-(Avendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php@Gb@%s|<vendor/predis/predis/src/Protocol/Text/RequestSerializer.phpQGbQo@vendor/predis/predis/src/Protocol/RequestSerializerInterface.phpGbn=vendor/predis/predis/src/Protocol/ResponseReaderInterface.phpGb7'vendor/predis/predis/src/Autoloader.phpGb윝5vendor/predis/predis/src/Response/ServerException.phpbGbb#7vendor/predis/predis/src/Response/ResponseInterface.phpQGbQyZ=vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.phpVGbV@vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.phpGb8vendor/predis/predis/src/Response/Iterator/MultiBulk.phpGb#4vendor/predis/predis/src/Response/ErrorInterface.phpGb`w,vendor/predis/predis/src/Response/Status.phpRGbRp|+vendor/predis/predis/src/Response/Error.phpGb&ˤ3vendor/predis/predis/src/Command/ConnectionEcho.phpGbBg5vendor/predis/predis/src/Command/ZSetRangeByScore.phpyGby`®/vendor/predis/predis/src/Command/HashLength.phpGby4vendor/predis/predis/src/Command/ZSetCardinality.phpGbUR٤1vendor/predis/predis/src/Command/StringBitPos.phpGb4vendor/predis/predis/src/Command/ZSetReverseRank.phpGb,1vendor/predis/predis/src/Command/ListPushHead.phpGb99vendor/predis/predis/src/Command/HashIncrementByFloat.phpGbN`+-vendor/predis/predis/src/Command/HashKeys.phpGbd<3vendor/predis/predis/src/Command/ZSetUnionStore.phpGbc/4vendor/predis/predis/src/Command/StringSetExpire.phpGbL,1vendor/predis/predis/src/Command/ListPushTail.phpGb0vendor/predis/predis/src/Command/KeyExpireAt.phpGb^4vendor/predis/predis/src/Command/HashIncrementBy.phpGbGHo,vendor/predis/predis/src/Command/SetMove.phpGbRAS9vendor/predis/predis/src/Command/ListPopFirstBlocking.phpfGbfɝդ5vendor/predis/predis/src/Command/ConnectionSelect.phpGb=3d-vendor/predis/predis/src/Command/HashScan.phpGb741vendor/predis/predis/src/Command/PubSubPubsub.php Gb =vΤ.vendor/predis/predis/src/Command/KeyRandom.phpGb}3)1vendor/predis/predis/src/Command/ServerObject.phpGb9V3vendor/predis/predis/src/Command/StringBitCount.phpGbl 8vendor/predis/predis/src/Command/ListPopLastPushHead.phpGbŲ2vendor/predis/predis/src/Command/ScriptCommand.phpGbϤ>vendor/predis/predis/src/Command/StringSetMultiplePreserve.phpGb ɤ3vendor/predis/predis/src/Command/ServerFlushAll.phpGbː,vendor/predis/predis/src/Command/KeyDump.phpGbjߤ?vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php Gb j_.vendor/predis/predis/src/Command/ListIndex.phpGb۬ä/vendor/predis/predis/src/Command/ServerSave.phpGb{̤8vendor/predis/predis/src/Command/ServerFlushDatabase.phpGbG3vendor/predis/predis/src/Command/ConnectionAuth.phpGbOn,vendor/predis/predis/src/Command/HashSet.phpGbHKޤ4vendor/predis/predis/src/Command/PubSubSubscribe.phpGbQv.vendor/predis/predis/src/Command/KeyRename.phpGbE߇Τ1vendor/predis/predis/src/Command/ServerScript.phpGbAV^/vendor/predis/predis/src/Command/ListRemove.phpGb'/vendor/predis/predis/src/Command/SetMembers.phpGb@1vendor/predis/predis/src/Command/StringStrlen.phpGb?3vendor/predis/predis/src/Command/ServerLastSave.phpGbeWW1vendor/predis/predis/src/Command/StringGetBit.phpGbB/vendor/predis/predis/src/Command/HashExists.phpGb9vendor/predis/predis/src/Command/KeyPreciseTimeToLive.phpGb0&4vendor/predis/predis/src/Command/StringDecrement.phpGbKԤ9vendor/predis/predis/src/Command/SetIntersectionStore.phpZGbZBW,vendor/predis/predis/src/Command/KeyScan.phpGbp.vendor/predis/predis/src/Command/ZSetCount.phpGb/+4vendor/predis/predis/src/Command/HashSetMultiple.phpGbU+vendor/predis/predis/src/Command/SetPop.phpGb)o¤4vendor/predis/predis/src/Command/HashSetPreserve.phpGbq::,vendor/predis/predis/src/Command/ZSetAdd.php\Gb\R,vendor/predis/predis/src/Command/KeySort.phpYGbY=8vendor/predis/predis/src/Command/ListPopLastBlocking.phpGb{,7vendor/predis/predis/src/Command/SetDifferenceStore.phpGb 2vendor/predis/predis/src/Command/ServerSlaveOf.php4Gb4JgӤ6vendor/predis/predis/src/Command/PubSubUnsubscribe.phpGb|3vendor/predis/predis/src/Command/StringBitField.phpGbٙӤ3vendor/predis/predis/src/Command/ServerInfoV26x.phpcGbcl2vendor/predis/predis/src/Command/ServerCommand.phpGbkӤ2vendor/predis/predis/src/Command/ListPushTailX.phpGbgjܟ0vendor/predis/predis/src/Command/ListPopLast.phpGb:vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.phpGb! T0vendor/predis/predis/src/Command/SetIsMember.phpGb5,2vendor/predis/predis/src/Command/KeyTimeToLive.phpGb<:5vendor/predis/predis/src/Command/TransactionMulti.phpGb U5vendor/predis/predis/src/Command/TransactionWatch.php*Gb*3vendor/predis/predis/src/Command/ZSetRangeByLex.phpGb". 1vendor/predis/predis/src/Command/ListPopFirst.phpGbL4vendor/predis/predis/src/Command/HashGetMultiple.phpGbП6vendor/predis/predis/src/Command/StringDecrementBy.phpGb;/vendor/predis/predis/src/Command/HashDelete.phpGb 4vendor/predis/predis/src/Command/StringIncrement.phpGb);wĤ3vendor/predis/predis/src/Command/SetCardinality.phpGbͯ!-vendor/predis/predis/src/Command/ZSetScan.phpGb7j5vendor/predis/predis/src/Command/KeyPreciseExpire.phpGb|-vendor/predis/predis/src/Command/ListTrim.phpGb Q;vendor/predis/predis/src/Command/StringIncrementByFloat.phpGbq놤/vendor/predis/predis/src/Command/ServerInfo.phpGb4}ޤ6vendor/predis/predis/src/Command/GeospatialGeoDist.phpGbNv@5vendor/predis/predis/src/Command/HashStringLength.phpGb:|r5vendor/predis/predis/src/Command/HyperLogLogCount.phpGb.=^4vendor/predis/predis/src/Command/SetRandomMember.phpGb`/7vendor/predis/predis/src/Command/KeyPreciseExpireAt.phpGb!5vendor/predis/predis/src/Command/HyperLogLogMerge.phpGbhs,vendor/predis/predis/src/Command/KeyKeys.phpGb3vendor/predis/predis/src/Command/ConnectionPing.phpGb<5vendor/predis/predis/src/Command/CommandInterface.phpGb'i/vendor/predis/predis/src/Command/ServerEval.phpGbe9/vendor/predis/predis/src/Command/ListLength.phpGbc92vendor/predis/predis/src/Command/SetDifference.phpGbp9vendor/predis/predis/src/Command/ServerBackgroundSave.phpGbB1vendor/predis/predis/src/Command/StringSetBit.phpGb:їB2vendor/predis/predis/src/Command/ServerSlowlog.phpGbt5vendor/predis/predis/src/Command/GeospatialGeoAdd.phprGbr퓭5vendor/predis/predis/src/Command/GeospatialGeoPos.phpjGbjYä:vendor/predis/predis/src/Command/ZSetReverseRangeByLex.phpGbcQ5vendor/predis/predis/src/Command/ZSetReverseRange.phpGbzh54vendor/predis/predis/src/Command/TransactionExec.phpGbZ-25,vendor/predis/predis/src/Command/KeyMove.phpGbX4vendor/predis/predis/src/Command/SetIntersection.phpGb6vendor/predis/predis/src/Command/KeyRenamePreserve.phpGb.LT?vendor/predis/predis/src/Command/PrefixableCommandInterface.phpGbū-vendor/predis/predis/src/Command/ZSetRank.phpGbh3vendor/predis/predis/src/Command/ServerShutdown.phpGb0vendor/predis/predis/src/Command/StringBitOp.phpGb .vendor/predis/predis/src/Command/KeyExists.phpGbh@vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.phpGb 1vendor/predis/predis/src/Command/ZSetLexCount.phpGbJ-٤.vendor/predis/predis/src/Command/KeyDelete.phpGb}aФ.vendor/predis/predis/src/Command/SetRemove.phpGbWq.vendor/predis/predis/src/Command/ZSetScore.phpGbŤ/vendor/predis/predis/src/Command/HashGetAll.php$Gb$Q1vendor/predis/predis/src/Command/ServerConfig.phpPGbPr3vendor/predis/predis/src/Command/ServerSentinel.phpGbm@2vendor/predis/predis/src/Command/ServerEvalSHA.phpGbJn7vendor/predis/predis/src/Command/TransactionUnwatch.phpGb1vendor/predis/predis/src/Command/ServerClient.phpGb/vendor/predis/predis/src/Command/ServerTime.phpGb쒤2vendor/predis/predis/src/Command/ServerMonitor.phpGb&#$/vendor/predis/predis/src/Command/KeyPersist.phpGbɽ3vendor/predis/predis/src/Command/ConnectionQuit.phpGbI/vendor/predis/predis/src/Command/HashValues.phpGbU F[/vendor/predis/predis/src/Command/ZSetRemove.phpGbz}.vendor/predis/predis/src/Command/ListRange.phpGbϤ6vendor/predis/predis/src/Command/GeospatialGeoHash.phplGblqa;vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.phpGbF/vendor/predis/predis/src/Command/RawCommand.phpGb6eA6vendor/predis/predis/src/Command/StringSetPreserve.phpGbF-vendor/predis/predis/src/Command/SetUnion.phpGbX7vendor/predis/predis/src/Command/TransactionDiscard.phpGbt1vendor/predis/predis/src/Command/StringSubstr.phpGb!/vendor/predis/predis/src/Command/KeyMigrate.phpGb[V.vendor/predis/predis/src/Command/ZSetRange.phpGb>3,vendor/predis/predis/src/Command/KeyType.phpGb,vendor/predis/predis/src/Command/Command.phpGbo[;vendor/predis/predis/src/Command/StringPreciseSetExpire.phpGbhv@vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.phpGb=,vendor/predis/predis/src/Command/SetScan.phpGbE/vendor/predis/predis/src/Command/ListInsert.phpGbWtK/vendor/predis/predis/src/Command/KeyRestore.phpGbfI?ޤ3vendor/predis/predis/src/Command/HyperLogLogAdd.phpGb9vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.phpGbfb6vendor/predis/predis/src/Command/StringIncrementBy.phpGbuS<vendor/predis/predis/src/Command/ZSetReverseRangeByScore.phpGb-Ť2vendor/predis/predis/src/Command/ListPushHeadX.phpGb_f|?vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.phpGba @3vendor/predis/predis/src/Command/StringGetRange.phpGb=vendor/predis/predis/src/Command/PubSubSubscribeByPattern.phpGbt|2vendor/predis/predis/src/Command/SetUnionStore.phpGbS-7vendor/predis/predis/src/Command/ServerDatabaseSize.phpGb82vendor/predis/predis/src/Command/PubSubPublish.phpGb6vendor/predis/predis/src/Command/StringGetMultiple.phpGb)ͤ,vendor/predis/predis/src/Command/ListSet.phpGb}1vendor/predis/predis/src/Command/StringGetSet.phpGbm1vendor/predis/predis/src/Command/StringAppend.phpGbپ:vendor/predis/predis/src/Command/ZSetIntersectionStore.phpGb,_.vendor/predis/predis/src/Command/KeyExpire.phpGb4 .vendor/predis/predis/src/Command/StringGet.phpGb.vendor/predis/predis/src/Command/StringSet.phpGbp6vendor/predis/predis/src/Command/StringSetMultiple.phpGbd8vendor/predis/predis/src/Command/GeospatialGeoRadius.phpGb͐3vendor/predis/predis/src/Command/StringSetRange.phpGbe^+vendor/predis/predis/src/Command/SetAdd.phpGbÄ4vendor/predis/predis/src/Command/ZSetIncrementBy.phpGbK/,vendor/predis/predis/src/Command/HashGet.phpGb@ؤAvendor/predis/predis/src/Command/Processor/ProcessorInterface.phpGbƐcAvendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.phpGbFdo=vendor/predis/predis/src/Command/Processor/ProcessorChain.phpeGbeL|9vendor/predis/predis/src/Collection/Iterator/Keyspace.phpGbRs8vendor/predis/predis/src/Collection/Iterator/HashKey.phpGbGl8vendor/predis/predis/src/Collection/Iterator/ListKey.phpGbH=vendor/predis/predis/src/Collection/Iterator/SortedSetKey.phpGbߚ)Dvendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php?Gb?J~7vendor/predis/predis/src/Collection/Iterator/SetKey.phpGbt3vendor/predis/predis/src/CommunicationException.phpCGbC,vendor/predis/predis/src/ClientException.phpZGbZl<!vendor/predis/predis/autoload.phpWGbWf4C5vendor/predis/predis/examples/replication_complex.phpGbSQ</vendor/predis/predis/examples/key_prefixing.php Gb F0<vendor/predis/predis/examples/custom_cluster_distributor.phpGbm)Ӥ7vendor/predis/predis/examples/transaction_using_cas.php Gb /T0(vendor/predis/predis/examples/shared.phpGb'I7vendor/predis/predis/examples/debuggable_connection.phpGbɤ=vendor/predis/predis/examples/redis_collections_iterators.phpRGbR3oy"5vendor/predis/predis/examples/pipelining_commands.phpjGbjҤ2vendor/predis/predis/examples/monitor_consumer.phpGbI06vendor/predis/predis/examples/replication_sentinel.phpLGbL 1vendor/predis/predis/examples/dispatcher_loop.phpGb!14vendor/predis/predis/examples/replication_simple.php-Gb-ǿ;vendor/predis/predis/examples/lua_scripting_abstraction.phpWGbWmcPg1vendor/predis/predis/examples/pubsub_consumer.phpaGbaϕ:vendor/predis/predis/examples/executing_redis_commands.php(Gb(NY1vendor/predis/predis/examples/session_handler.phpGb IAvendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.phpGb463vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.phpGbBOvendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpGbSӤEvendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.phpGbkAvendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.phpjGbjc$C9vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.phpmGbmaˤEvendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.phpKGbKV~zEvendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.phpGb2vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php+Gb+O?vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.phpfGbfZ.)ȤLvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php[Gb[%MQFvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.phpGbgBvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.phpGb›qLvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.phpnGbnRvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.phpGb&)y;vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.phpGbg{5Mvendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.phpGbs()Rvendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.phpCGbC AWvendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.phpGbh>vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php_Gb_|Gؤ>vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php"Gb"G7vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php|Gb|a]ˤBvendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.phpGbLtEvendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.phpxGbx4&vendor/myclabs/deep-copy/doc/clone.png\0Gb\0&vendor/myclabs/deep-copy/doc/graph.png$Gb$PϤ*vendor/myclabs/deep-copy/doc/deep-copy.png*Gb*i9+vendor/myclabs/deep-copy/doc/deep-clone.png6Gb6JŨ9vendor/myclabs/deep-copy/fixtures/f004/UnclonableItem.phpGbBUy.vendor/myclabs/deep-copy/fixtures/f003/Foo.phpGb:vendor/myclabs/deep-copy/fixtures/f007/FooDateTimeZone.phpGb :vendor/myclabs/deep-copy/fixtures/f007/FooDateInterval.phpGbG/,vendor/myclabs/deep-copy/fixtures/f002/A.phpKGbK.vendor/myclabs/deep-copy/fixtures/f005/Foo.php|Gb| 3Zq,vendor/myclabs/deep-copy/fixtures/f008/B.php7Gb72E,vendor/myclabs/deep-copy/fixtures/f008/A.phpGbGb>NچGGvendor/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.phpGb8tAvendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed.php8Gb8M:PMvendor/symfony/polyfill-intl-idn/Resources/unidata/disallowed_STD3_mapped.phpGb;qf=vendor/symfony/polyfill-intl-idn/Resources/unidata/virama.phpGb/ɤ=vendor/symfony/polyfill-intl-idn/Resources/unidata/mapped.php/fGb/f-<vendor/symfony/polyfill-intl-idn/Resources/unidata/Regex.phṗGḃ q]R>vendor/symfony/polyfill-intl-idn/Resources/unidata/ignored.phpGbd@vendor/symfony/polyfill-intl-idn/Resources/unidata/deviation.phpKGbKë`Ҥ6vendor/symfony/css-selector/Parser/ParserInterface.phpGby9:vendor/symfony/css-selector/Parser/Shortcut/HashParser.phplGblw;vendor/symfony/css-selector/Parser/Shortcut/ClassParser.phppGbpH;Avendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.phpGb X=vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php Gb V!2vendor/symfony/css-selector/Parser/TokenStream.phpGbI:vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.phpGbJeVBvendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.phpGbT'Bvendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.phpGbJ-vendor/symfony/css-selector/Parser/Reader.phpEGbECN<vendor/symfony/css-selector/Parser/Handler/StringHandler.php]Gb] <vendor/symfony/css-selector/Parser/Handler/NumberHandler.phpGbu`Ӥ?vendor/symfony/css-selector/Parser/Handler/HandlerInterface.phpGbKi@vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.phpGbms@vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.phpOGbOah=vendor/symfony/css-selector/Parser/Handler/CommentHandler.phpGb!:vendor/symfony/css-selector/Parser/Handler/HashHandler.phpGb-Z,vendor/symfony/css-selector/Parser/Token.phpGbrL-vendor/symfony/css-selector/Parser/Parser.php!Gb!4vendor/symfony/css-selector/CssSelectorConverter.phpGbG1vendor/symfony/css-selector/Node/FunctionNode.phpGb7"Wy1vendor/symfony/css-selector/Node/SelectorNode.phpGbѨ.vendor/symfony/css-selector/Node/ClassNode.phpgGbgdӤ0vendor/symfony/css-selector/Node/ElementNode.phpGb}C2vendor/symfony/css-selector/Node/AttributeNode.phpGb!g 2vendor/symfony/css-selector/Node/NodeInterface.phpGb.y0vendor/symfony/css-selector/Node/Specificity.phpsGbsW:O1vendor/symfony/css-selector/Node/NegationNode.phpGb)1vendor/symfony/css-selector/Node/AbstractNode.phpIGbI>AƤ9vendor/symfony/css-selector/Node/CombinedSelectorNode.phpGbDY|/vendor/symfony/css-selector/Node/PseudoNode.phpGb;#7-vendor/symfony/css-selector/Node/HashNode.phpXGbXX8vendor/symfony/css-selector/Exception/ParseException.phpGb/X@vendor/symfony/css-selector/Exception/InternalErrorException.phpGb&O>vendor/symfony/css-selector/Exception/SyntaxErrorException.phpnGbnBvendor/symfony/css-selector/Exception/ExpressionErrorException.phpGb6;<vendor/symfony/css-selector/Exception/ExceptionInterface.phpmGbmY~0vendor/symfony/css-selector/XPath/Translator.php5Gb5%9vendor/symfony/css-selector/XPath/TranslatorInterface.phpUGbUi뜤Jvendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php Gb %(Bvendor/symfony/css-selector/XPath/Extension/ExtensionInterface.phpGb#.ɤAvendor/symfony/css-selector/XPath/Extension/FunctionExtension.php Gb ByAvendor/symfony/css-selector/XPath/Extension/AbstractExtension.phpGb/=vendor/symfony/css-selector/XPath/Extension/HtmlExtension.php9Gb9-^!=vendor/symfony/css-selector/XPath/Extension/NodeExtension.phpGbXDvendor/symfony/css-selector/XPath/Extension/CombinationExtension.phpTGbT-ФDvendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.phpGb>/vendor/symfony/css-selector/XPath/XPathExpr.phpkGbkd0}+vendor/symfony/polyfill-ctype/bootstrap.phpGb|p:'vendor/symfony/polyfill-ctype/Ctype.phpH GbH ݤ%vendor/symfony/browser-kit/Client.php%Gb%9(vendor/symfony/browser-kit/CookieJar.php Gb tᇤ%vendor/symfony/browser-kit/Cookie.phpGbڧȤ&vendor/symfony/browser-kit/Request.phpGb(D&vendor/symfony/browser-kit/History.phpGb}0'vendor/symfony/browser-kit/Response.php$Gb$|.vendor/symfony/polyfill-mbstring/bootstrap.phpGbJ-vendor/symfony/polyfill-mbstring/Mbstring.phpGGbGֽ̤@vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpTGbT2?@vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.phpUGbU`8QFvendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.phpGby_+vendor/symfony/process/ExecutableFinder.phpGb>ϸp)vendor/symfony/process/ProcessBuilder.php Gb VqѤ"vendor/symfony/process/Process.php lGb lF~&vendor/symfony/process/InputStream.php'Gb'X>'vendor/symfony/process/ProcessUtils.php:Gb:;vendor/symfony/process/Exception/ProcessFailedException.phpxGbxzy=vendor/symfony/process/Exception/ProcessTimedOutException.phpGb3vendor/symfony/process/Exception/LogicException.phpGb 5vendor/symfony/process/Exception/RuntimeException.phpGb:=vendor/symfony/process/Exception/InvalidArgumentException.phpGb+_7vendor/symfony/process/Exception/ExceptionInterface.phpfGbf]>T.vendor/symfony/process/PhpExecutableFinder.phpGbH-vendor/symfony/process/Pipes/WindowsPipes.php0 Gb0 A*vendor/symfony/process/Pipes/UnixPipes.phpGb/vendor/symfony/process/Pipes/PipesInterface.phpmGbmu.vendor/symfony/process/Pipes/AbstractPipes.php Gb ym%vendor/symfony/process/PhpProcess.phpGbx%vendor/symfony/finder/SplFileInfo.phpGb3vendor/symfony/finder/Comparator/DateComparator.php#Gb#Τ/vendor/symfony/finder/Comparator/Comparator.phpGb5vendor/symfony/finder/Comparator/NumberComparator.php|Gb|ITͤ vendor/symfony/finder/Finder.php%"Gb%"uHvendor/symfony/finder/Glob.phpGbt':vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php`Gb`!ky=vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php= Gb= Y9vendor/symfony/finder/Iterator/FileTypeFilterIterator.phpZGbZ(&<vendor/symfony/finder/Iterator/FilecontentFilterIterator.php5Gb5Avendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.phpGbf_:vendor/symfony/finder/Iterator/DateRangeFilterIterator.phpsGbs=x9vendor/symfony/finder/Iterator/FilenameFilterIterator.phprGbrtu;vendor/symfony/finder/Iterator/DepthRangeFilterIterator.phpGbۤ7vendor/symfony/finder/Iterator/CustomFilterIterator.phpXGbXDVSB=vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php Gb ƺ1vendor/symfony/finder/Iterator/FilterIterator.phpGb 5vendor/symfony/finder/Iterator/PathFilterIterator.phpGbc3vendor/symfony/finder/Iterator/SortableIterator.phpNGbN%9vendor/symfony/finder/Exception/AccessDeniedException.phpGbs6vendor/symfony/finder/Exception/ExceptionInterface.phpGbL~#vendor/symfony/dom-crawler/Form.php*Gb*Z6vendor/symfony/dom-crawler/Field/TextareaFormField.phpGb4^4vendor/symfony/dom-crawler/Field/ChoiceFormField.phpGb.vendor/symfony/dom-crawler/Field/FormField.phpGbYB3vendor/symfony/dom-crawler/Field/InputFormField.phpGb!O2vendor/symfony/dom-crawler/Field/FileFormField.phpGbLJh#vendor/symfony/dom-crawler/Link.phpGbvۮ&vendor/symfony/dom-crawler/Crawler.phpCGbC $vendor/symfony/dom-crawler/Image.phpGbJѓ0vendor/symfony/dom-crawler/FormFieldRegistry.php Gb 1vendor/symfony/dom-crawler/AbstractUriElement.php Gb wz+vendor/symfony/polyfill-php70/bootstrap.phpwGbwb'vendor/symfony/polyfill-php70/Php70.phpGb=+@vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php.Gb.&8y<vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php*Gb*ᤤXvendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.phpGb~Avendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php/Gb/|mͤ;vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php)Gb)O_Evendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php3Gb3h;7vendor/symfony/polyfill-php70/Resources/stubs/Error.php)Gb)[k vendor/symfony/debug/Debug.php.Gb.B;)vendor/symfony/debug/ExceptionHandler.phpzMGbzME<)vendor/symfony/debug/DebugClassLoader.phpJ+GbJ+N>%vendor/symfony/debug/ErrorHandler.phpCGbCU2Kvendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.phpOGbOhѤEvendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.phpGbĹBVIvendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php Gb FMvendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.phpGbFd-(vendor/symfony/debug/BufferingLogger.php`Gb`Dmۤ8vendor/symfony/debug/Exception/ContextErrorException.phpGb6;vendor/symfony/debug/Exception/UndefinedMethodException.phpGbnؤ7vendor/symfony/debug/Exception/SilencedErrorContext.php(Gb(+l6vendor/symfony/debug/Exception/FatalErrorException.php.Gb.J06vendor/symfony/debug/Exception/FatalThrowableError.phpZGbZ;3vendor/symfony/debug/Exception/FlattenException.phpnGbnw =vendor/symfony/debug/Exception/UndefinedFunctionException.phpGbJ7vendor/symfony/debug/Exception/OutOfMemoryException.php~Gb~o9vendor/symfony/debug/Exception/ClassNotFoundException.phpGbi2vendor/symfony/console/Event/ConsoleErrorEvent.phpbGbbMh4vendor/symfony/console/Event/ConsoleCommandEvent.phpGb!Ȥ6vendor/symfony/console/Event/ConsoleExceptionEvent.phpGb(6vendor/symfony/console/Event/ConsoleTerminateEvent.phpzGbz,L-vendor/symfony/console/Event/ConsoleEvent.phpGbӤ,vendor/symfony/console/Output/NullOutput.phpGbZ1vendor/symfony/console/Output/OutputInterface.phpGbn /vendor/symfony/console/Output/ConsoleOutput.phpGbSc0vendor/symfony/console/Output/BufferedOutput.php`Gb`蟗8vendor/symfony/console/Output/ConsoleOutputInterface.phpGbʤ(vendor/symfony/console/Output/Output.php Gb 0p.vendor/symfony/console/Output/StreamOutput.phpGb$W#vendor/symfony/console/Terminal.php Gb Ѥ(vendor/symfony/console/ConsoleEvents.php Gb  Nդ2vendor/symfony/console/Helper/InputAwareHelper.phpcGbc'vendor/symfony/console/Helper/Table.php-Gb-kE,0vendor/symfony/console/Helper/TableSeparator.phpGb2vendor/symfony/console/Helper/DescriptorHelper.phpeGbe2_-vendor/symfony/console/Helper/ProgressBar.php.%Gb.%(B+vendor/symfony/console/Helper/HelperSet.phpfGbfx(vendor/symfony/console/Helper/Helper.phpkGbk'颤1vendor/symfony/console/Helper/FormatterHelper.phpDGbD+L6vendor/symfony/console/Helper/DebugFormatterHelper.phpjGbjOG 3vendor/symfony/console/Helper/ProgressIndicator.phpDGbDJxF+vendor/symfony/console/Helper/TableCell.phpvGbv+ ,vendor/symfony/console/Helper/TableStyle.php Gb Q0vendor/symfony/console/Helper/QuestionHelper.php^%Gb^%3Ph1vendor/symfony/console/Helper/HelperInterface.phpGb/vendor/symfony/console/Helper/ProcessHelper.php Gb O7vendor/symfony/console/Helper/SymfonyQuestionHelper.php Gb 2vendor/symfony/console/Question/ChoiceQuestion.phpB GbB Ф8vendor/symfony/console/Question/ConfirmationQuestion.phpGbd?,vendor/symfony/console/Question/Question.php Gb N6vendor/symfony/console/EventListener/ErrorListener.phpGb0\D&vendor/symfony/console/Application.phpQhGbQh-vendor/symfony/console/Style/SymfonyStyle.php\Gb\豞,vendor/symfony/console/Style/OutputStyle.phpXGbX/vendor/symfony/console/Style/StyleInterface.phpGb/vendor/symfony/console/Tester/CommandTester.phpGb' 3vendor/symfony/console/Tester/ApplicationTester.php) Gb) X/vendor/symfony/console/Logger/ConsoleLogger.php Gb  Z.vendor/symfony/console/Command/ListCommand.phpGb5-0vendor/symfony/console/Command/LockableTrait.php2Gb2Nߤ.vendor/symfony/console/Command/HelpCommand.phpGb<*vendor/symfony/console/Command/Command.php:!Gb:!o%=vendor/symfony/console/CommandLoader/FactoryCommandLoader.phpGbm?vendor/symfony/console/CommandLoader/ContainerCommandLoader.phpHGbH-ߤ?vendor/symfony/console/CommandLoader/CommandLoaderInterface.phpGGbG?Dvendor/symfony/console/DependencyInjection/AddConsoleCommandPass.phpb Gbb 蒜;vendor/symfony/console/Exception/InvalidOptionException.phpGbH3vendor/symfony/console/Exception/LogicException.phpGbO\e5vendor/symfony/console/Exception/RuntimeException.phpGb,6=vendor/symfony/console/Exception/CommandNotFoundException.phpGbK*5=vendor/symfony/console/Exception/InvalidArgumentException.phpGb̽Z7vendor/symfony/console/Exception/ExceptionInterface.phpfGbfAB4vendor/symfony/console/Formatter/OutputFormatter.phpGb:n=vendor/symfony/console/Formatter/OutputFormatterInterface.phpGbBvendor/symfony/console/Formatter/OutputFormatterStyleInterface.phpGbG9vendor/symfony/console/Formatter/OutputFormatterStyle.phpGb`*>vendor/symfony/console/Formatter/OutputFormatterStyleStack.php;Gb;%4vendor/symfony/console/Descriptor/TextDescriptor.php"Gb"O굤3vendor/symfony/console/Descriptor/XmlDescriptor.php Gb 4 Τ0vendor/symfony/console/Descriptor/Descriptor.phpsGbscBM8vendor/symfony/console/Descriptor/MarkdownDescriptor.phpGb>49vendor/symfony/console/Descriptor/DescriptorInterface.phpGbP~<vendor/symfony/console/Descriptor/ApplicationDescription.php Gb @?,פ4vendor/symfony/console/Descriptor/JsonDescriptor.phpGbS]R,vendor/symfony/console/Input/StringInput.phpWGbW}j.vendor/symfony/console/Input/InputArgument.php Gb ;+vendor/symfony/console/Input/ArrayInput.php+ Gb+ '٤9vendor/symfony/console/Input/StreamableInputInterface.phpGbB*vendor/symfony/console/Input/ArgvInput.phpGbN ɤ&vendor/symfony/console/Input/Input.phpG GbG /vendor/symfony/console/Input/InputInterface.phpGbrrg0vendor/symfony/console/Input/InputDefinition.phpGbqR,vendor/symfony/console/Input/InputOption.php Gb 4vendor/symfony/console/Input/InputAwareInterface.phpGbOvendor/symfony/yaml/Inline.php\Gb\ C'vendor/symfony/yaml/Tag/TaggedValue.php>Gb>?vendor/symfony/yaml/Dumper.php%Gb%n,vendor/symfony/yaml/Escaper.phpfGbfj㎤+vendor/symfony/yaml/Command/LintCommand.phpGbE@(!vendor/symfony/yaml/Unescaper.phpGb&%0vendor/symfony/yaml/Exception/ParseException.phpDGbD@ 2vendor/symfony/yaml/Exception/RuntimeException.phpGb$/vendor/symfony/yaml/Exception/DumpException.phpsGbsƤ4vendor/symfony/yaml/Exception/ExceptionInterface.phpcGbcۤvendor/symfony/yaml/Parser.php1lGb1l6vendor/symfony/yaml/Yaml.php Gb (CФ+vendor/symfony/polyfill-php72/bootstrap.phpuGbu&'vendor/symfony/polyfill-php72/Php72.phpGbbqKvendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php5Gb5fVBvendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.phpGbO '9vendor/symfony/event-dispatcher/Debug/WrappedListener.php Gb !<vendor/symfony/event-dispatcher/ImmutableEventDispatcher.phpqGbq3vendor/symfony/event-dispatcher/EventDispatcher.phpGb<vendor/symfony/event-dispatcher/EventSubscriberInterface.phpGb!jcKMvendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.phpGb\]0vendor/symfony/event-dispatcher/GenericEvent.phplGbl@F^Ĥ<vendor/symfony/event-dispatcher/EventDispatcherInterface.phpGbpXAvendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.phpGbBޤ)vendor/symfony/event-dispatcher/Event.php)Gb)@Bvendor/autoload.phpGbՎPvendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.phpGb,A¤Gvendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.phpGbyG]vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.phpGbxi]vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.phpGbpWvendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.phpjGbjΤAvendor/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php Gb fOJvendor/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.phpGbo+}Hvendor/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.phpGbJIvendor/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.phpGbeFvendor/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.phpGb{NDvendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.phpFGbFIaDCvendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php Gb )Tvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/AbstractRetriever.phpGb[ARvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php%Gb%5"Gvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.phpGbԧLbRvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/PredefinedArray.php,Gb,15Xvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.phpGb;vendor/justinrainbow/json-schema/src/JsonSchema/Rfc3339.phpGbf4Kvendor/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.phpGbMXvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.phpaGba qäYvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.phpGb4~\vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.phpGb+jSvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.phpS"GbS":Nvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php Gb wlNvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php]Gb]VU<Pvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.phpGbżxGvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php Gb 0;Svendor/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.phpGb QPvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php Gb (Pvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php0 Gb0 jPvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.phpGbTڤTvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php Gb ӯNvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php1Gb1{Jvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php Gb PFFPvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.phpGbw]vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.phpuGbu=hRvendor/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.phpjGbj>Tvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaException.phplGbl2Qvendor/justinrainbow/json-schema/src/JsonSchema/Exception/ValidationException.phpfGbfTvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidConfigException.phplGblA!LפWvendor/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.phpoGbop^vendor/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.phpGbu-#1Nvendor/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.phpGb%Svendor/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.phpGb\ Wvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSourceUriException.phpwGbwN-[Vvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.phpGbGPvendor/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.phpIGbI%|=vendor/justinrainbow/json-schema/src/JsonSchema/Validator.phpvGbvgGvendor/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.phpGb'BBvendor/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.php<Gb<B́Fvendor/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.phpPGbP0Evendor/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.phpvGbv"dpGvendor/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.phpGb;Evendor/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.phpGba;vendor/phpspec/prophecy/src/Prophecy/Comparator/Factory.phpGbLFvendor/phpspec/prophecy/src/Prophecy/Comparator/ProphecyComparator.php?Gb?gD8vendor/phpspec/prophecy/src/Prophecy/Util/StringUtil.php&Gb& H8vendor/phpspec/prophecy/src/Prophecy/Util/ExportUtil.php4 Gb4 HFvendor/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.phpGb\@vendor/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php5Gb56Avendor/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.phpGb/c>vendor/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.phpGb^-5=vendor/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.phpGbжҤ0vendor/phpspec/prophecy/src/Prophecy/Prophet.php8 Gb8 7Hvendor/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php=Gb=ḖLvendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php[Gb[jAvendor/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.php>Gb>mӤFvendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.phpGbaNMvendor/phpspec/prophecy/src/Prophecy/Argument/Token/ApproximateValueToken.php/Gb/{N&Evendor/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.phpGbǤKvendor/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.phpGbA Gvendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.phpRGbRMGvendor/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.phpGbФGvendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.phpGbߖKvendor/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php Gb vѤGvendor/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.phpGb>v[Gvendor/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.phpLGbL8HEvendor/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php Gb %&|Fvendor/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.phpGbV]稤Cvendor/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.phppGbpˤJvendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.phpGbЊ/@vendor/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.phpXGbX=枤@vendor/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.phpGbdCvendor/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.phpzGbz:vendor/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.phpbGbbZ Cvendor/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.phprGbr+hڤRvendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php"Gb"!]Nvendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.phpGbӥqHvendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassTagRetriever.phpmGbmqLuTvendor/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.phpGbiM:c1vendor/phpspec/prophecy/src/Prophecy/Argument.phppGbpm?+ 8vendor/phpspec/prophecy/src/Prophecy/Call/CallCenter.phpGbUW2vendor/phpspec/prophecy/src/Prophecy/Call/Call.phpGb(>>vendor/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.phpGb[KNvendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php_Gb_~Gvendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php8Gb8c1Ivendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.php Gb RmJvendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php= Gb= 8Lvendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.phpGb?ȹLvendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/TypeHintReference.php`Gb`dnMvendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php Gb y#Fvendor/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.phpGbڭ>vendor/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.phptGbtL;vendor/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.phpGblE58vendor/phpspec/prophecy/src/Prophecy/Doubler/Doubler.phpGb2B`@vendor/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.phpQGbQo̊ʤHvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php-Gb-g[vendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phpGbkSSvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php7Gb7L#Nvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.phpGb6ǤJvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.phpSGbSJOvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phpGb Pvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.phpGb*ԤLvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.phpkGbk~Jvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ThrowablePatch.phpGb-;Lvendor/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.phpGb Wvendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.phpGb|DQvendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.phpGb [vendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.phpGbRVvendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.phpGbf~Nvendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.phpGbwi@Pvendor/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.phpGbR3k<vendor/phpspec/prophecy/src/Prophecy/Exception/Exception.phpnGbn*$Svendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.phpGb@Mvendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.phpGb_nSvendor/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.phpGbe Ovendor/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.phpVGbVh0J+Wvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotExtendableException.phpGb Pvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.phpuGbuТyKvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.phpGbmOvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.phpjGbjg&Uvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.phpGbRUvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.phpGb l Jvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.phpGb '}Rvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.phpNGbNK]Qvendor/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php;Gb;I}Kvendor/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.phpGbȘhvendor/composer/installed.phpaGbay"vendor/composer/autoload_files.phpGb~]!vendor/composer/autoload_real.phpGba>%vendor/composer/autoload_classmap.phpIGbI1qvendor/composer/ClassLoader.phpGbd_#vendor/composer/autoload_static.phpGbJIG"vendor/composer/platform_check.php Gb =ˤ!vendor/composer/autoload_psr4.phpGbz"%vendor/composer/InstalledVersions.php`Gb`LJ'vendor/composer/autoload_namespaces.phpGbZsq*vendor/composer/ca-bundle/src/CaBundle.php$Gb$oQE/vendor/softcreatr/jsonpath/src/AccessHelper.php Gb 2d6vendor/softcreatr/jsonpath/src/Filters/IndexFilter.phpGb-!Ƥ6vendor/softcreatr/jsonpath/src/Filters/SliceFilter.phpGbu]a;vendor/softcreatr/jsonpath/src/Filters/QueryMatchFilter.php7 Gb7  9vendor/softcreatr/jsonpath/src/Filters/AbstractFilter.phpGb<Τ<vendor/softcreatr/jsonpath/src/Filters/QueryResultFilter.php|Gb|@T)c8vendor/softcreatr/jsonpath/src/Filters/IndexesFilter.phpGb]ϏĤ:vendor/softcreatr/jsonpath/src/Filters/RecursiveFilter.phpPGbPbiG0vendor/softcreatr/jsonpath/src/JSONPathLexer.phpGbfcm4vendor/softcreatr/jsonpath/src/JSONPathException.phpeGbe=~[V+vendor/softcreatr/jsonpath/src/JSONPath.php Gb 0vendor/softcreatr/jsonpath/src/JSONPathToken.phpGb1vendor/psr/http-message/src/ResponseInterface.phpGb0vendor/psr/http-message/src/RequestInterface.phpGbV6vendor/psr/http-message/src/ServerRequestInterface.phpoGbo $h0vendor/psr/http-message/src/MessageInterface.phpGb)t /vendor/psr/http-message/src/StreamInterface.phpGbh\l5vendor/psr/http-message/src/UploadedFileInterface.phpzGbz9b,vendor/psr/http-message/src/UriInterface.php|Gb|k3vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php Gb $/Ҥ)vendor/psr/log/Psr/Log/Test/DummyTest.phppGbp*vendor/psr/log/Psr/Log/Test/TestLogger.php<Gb<(I+vendor/psr/log/Psr/Log/LoggerAwareTrait.phpGbTB%vendor/psr/log/Psr/Log/NullLogger.phpGbDi)vendor/psr/log/Psr/Log/AbstractLogger.php;Gb;>3[&vendor/psr/log/Psr/Log/LoggerTrait.phpkGbk}*vendor/psr/log/Psr/Log/LoggerInterface.phpGbx3vendor/psr/log/Psr/Log/InvalidArgumentException.php`Gb` X1#vendor/psr/log/Psr/Log/LogLevel.phpGbj8/vendor/psr/log/Psr/Log/LoggerAwareInterface.php|Gb|$.vendor/guzzlehttp/promises/src/EachPromise.php Gb +bz5vendor/guzzlehttp/promises/src/TaskQueueInterface.phpGb]-Ɩ(vendor/guzzlehttp/promises/src/Utils.phpz Gbz *9E*vendor/guzzlehttp/promises/src/Promise.php}Gb}2/4vendor/guzzlehttp/promises/src/functions_include.phphGbhʏ,vendor/guzzlehttp/promises/src/Coroutine.phpGbZ齤5vendor/guzzlehttp/promises/src/RejectionException.phpGbY%vendor/guzzlehttp/promises/src/Is.php:Gb:ĭ-2vendor/guzzlehttp/promises/src/RejectedPromise.php@Gb@ I3vendor/guzzlehttp/promises/src/FulfilledPromise.phpGb擤8vendor/guzzlehttp/promises/src/CancellationException.phpdGbd,/,vendor/guzzlehttp/promises/src/functions.phpGbD6'vendor/guzzlehttp/promises/src/Each.php3Gb3C(+)vendor/guzzlehttp/promises/src/Create.php(Gb(3vendor/guzzlehttp/promises/src/PromiseInterface.php Gb & 4vendor/guzzlehttp/promises/src/PromisorInterface.phpiGbi42x,vendor/guzzlehttp/promises/src/TaskQueue.phpGb\5vendor/guzzlehttp/promises/src/AggregateException.phpGb +vendor/guzzlehttp/psr7/src/MessageTrait.phpGb(*vendor/guzzlehttp/psr7/src/LimitStream.phpGbۯ$vendor/guzzlehttp/psr7/src/Utils.phpLGbL6?+vendor/guzzlehttp/psr7/src/AppendStream.php Gb 3{V*vendor/guzzlehttp/psr7/src/UriResolver.phpGb>R"vendor/guzzlehttp/psr7/src/Uri.phpK*GbK*&vendor/guzzlehttp/psr7/src/Request.phpF GbF %3vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php@Gb@'¤,vendor/guzzlehttp/psr7/src/CachingStream.phpGb|%,vendor/guzzlehttp/psr7/src/InflateStream.phpGbX+vendor/guzzlehttp/psr7/src/BufferStream.php:Gb:)vendor/guzzlehttp/psr7/src/PumpStream.phpuGbugʂ0vendor/guzzlehttp/psr7/src/functions_include.php]Gb]ɤ%vendor/guzzlehttp/psr7/src/Header.phpGb(YDȤ.vendor/guzzlehttp/psr7/src/MultipartStream.phpe Gbe _s($vendor/guzzlehttp/psr7/src/Query.php Gb YO,vendor/guzzlehttp/psr7/src/ServerRequest.php:Gb:V &vendor/guzzlehttp/psr7/src/Rfc7230.phpGbݤ+vendor/guzzlehttp/psr7/src/UploadedFile.phpGb+aC,vendor/guzzlehttp/psr7/src/UriComparator.phpGb̀H1+vendor/guzzlehttp/psr7/src/NoSeekStream.php=Gb=%TH,vendor/guzzlehttp/psr7/src/UriNormalizer.php Gb d%&vendor/guzzlehttp/psr7/src/Message.phpGb3ˑ,vendor/guzzlehttp/psr7/src/StreamWrapper.phpGb2?['vendor/guzzlehttp/psr7/src/MimeType.phpS GbS %vendor/guzzlehttp/psr7/src/Stream.php=Gb=8֤-vendor/guzzlehttp/psr7/src/DroppingStream.php2Gb2´'vendor/guzzlehttp/psr7/src/FnStream.phpGb(vendor/guzzlehttp/psr7/src/functions.php= Gb= AC 9'vendor/guzzlehttp/psr7/src/Response.php Gb ?$A-vendor/guzzlehttp/psr7/src/LazyOpenStream.phpGb:@'vendor/guzzlehttp/guzzle/src/Client.php6#Gb6#.91vendor/guzzlehttp/guzzle/src/MessageFormatter.php Gb J^e0vendor/guzzlehttp/guzzle/src/ClientInterface.phpGb_:,vendor/guzzlehttp/guzzle/src/UriTemplate.php]Gb]&&vendor/guzzlehttp/guzzle/src/Utils.php_Gb_֤-vendor/guzzlehttp/guzzle/src/HandlerStack.php Gb X{'1vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php>Gb>Ӭg1vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.phpVGbVno8vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.phpGb&5vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.phpGbH9:vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.phpHGbH?I+vendor/guzzlehttp/guzzle/src/Middleware.phpGb/vendor/guzzlehttp/guzzle/src/RequestOptions.phpGbW{2vendor/guzzlehttp/guzzle/src/functions_include.phpaGbaj%vendor/guzzlehttp/guzzle/src/Pool.phpGbMܘ0vendor/guzzlehttp/guzzle/src/RetryMiddleware.phpGb$ͤ.vendor/guzzlehttp/guzzle/src/TransferStats.phpGb/c=vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.phpGbu3vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php9Gb9:\4vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.phpvGbv9f4vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.phpi2Gbi2E36vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php^-Gb^-I!4vendor/guzzlehttp/guzzle/src/Handler/MockHandler.phpGb׋9vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.phpt Gbt x.vendor/guzzlehttp/guzzle/src/Handler/Proxy.phpGbˤ6vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.phpGb3vendor/guzzlehttp/guzzle/src/RedirectMiddleware.phpGb^Ǥ;vendor/guzzlehttp/guzzle/src/Exception/RequestException.php Gb <(ĤDvendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.phpdGbdBu:vendor/guzzlehttp/guzzle/src/Exception/ServerException.phpaGbaeby;vendor/guzzlehttp/guzzle/src/Exception/ConnectException.phpGbհ:vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.phpGbZs~o<vendor/guzzlehttp/guzzle/src/Exception/TransferException.phpxGbx'B?vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.phpcGbc)k-Cvendor/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.phpGbco8vendor/guzzlehttp/guzzle/src/Exception/SeekException.phpGbZM%]:vendor/guzzlehttp/guzzle/src/Exception/ClientException.phpaGbacC*vendor/guzzlehttp/guzzle/src/functions.php]Gb]D1vendor/phpunit/php-file-iterator/src/Iterator.phpGbN0vendor/phpunit/php-file-iterator/src/Factory.phpGbIR/vendor/phpunit/php-file-iterator/src/Facade.phpGb?-ɤ1vendor/phpunit/php-text-template/src/Template.phpGbI&vendor/phpunit/php-timer/src/Timer.phpGb{/vendor/phpunit/phpunit/src/Runner/Exception.phphGbh5vendor/phpunit/phpunit/src/Runner/TestSuiteLoader.phpGbVτ4vendor/phpunit/phpunit/src/Runner/BaseTestRunner.php&Gb&W,-vendor/phpunit/phpunit/src/Runner/Version.phpeGbedN =vendor/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.phpGbY4vendor/phpunit/phpunit/src/Runner/Filter/Factory.phpXGbX1vendor/phpunit/phpunit/src/Runner/Filter/Test.php&Gb&Ek:vendor/phpunit/phpunit/src/Runner/Filter/Group/Include.phpGbi4:vendor/phpunit/phpunit/src/Runner/Filter/Group/Exclude.phpGb2vendor/phpunit/phpunit/src/Runner/Filter/Group.php Gb 6f(vendor/phpunit/phpunit/src/Exception.php2Gb2ޝБ7vendor/phpunit/phpunit/src/Framework/RiskyTestError.phpGb!9vendor/phpunit/phpunit/src/Framework/SkippedTestError.phpGb?3vendor/phpunit/phpunit/src/Framework/TestResult.php HGb HHHvendor/phpunit/phpunit/src/Framework/CoveredCodeNotExecutedException.phpxGbxޙ;vendor/phpunit/phpunit/src/Framework/IncompleteTestCase.phpGbpO9vendor/phpunit/phpunit/src/Framework/ExceptionWrapper.phpGb.5vendor/phpunit/phpunit/src/Framework/Error/Notice.phpGbr56vendor/phpunit/phpunit/src/Framework/Error/Warning.phpGb ͤ9vendor/phpunit/phpunit/src/Framework/Error/Deprecated.phpGboHvendor/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.phpxGbxOb>vendor/phpunit/phpunit/src/Framework/SkippedTestSuiteError.phpGbe=vendor/phpunit/phpunit/src/Framework/Constraint/Attribute.phpGb+;vendor/phpunit/phpunit/src/Framework/Constraint/IsFalse.phpGbؒBvendor/phpunit/phpunit/src/Framework/Constraint/StringContains.phpGbdӤ>vendor/phpunit/phpunit/src/Framework/Constraint/FileExists.php\Gb\zDvendor/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.phpmGbmJYKvendor/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.phpjGbj@(<vendor/phpunit/phpunit/src/Framework/Constraint/Callback.phpGbX`l>vendor/phpunit/phpunit/src/Framework/Constraint/IsReadable.php\Gb\IcAvendor/phpunit/phpunit/src/Framework/Constraint/StringMatches.phpOGbOPTvendor/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.phpGb(`==vendor/phpunit/phpunit/src/Framework/Constraint/Exception.phpGb ?vendor/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.phptGbtMM¤Bvendor/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.phpGbV>vendor/phpunit/phpunit/src/Framework/Constraint/IsWritable.php\Gb\S;vendor/phpunit/phpunit/src/Framework/Constraint/IsEmpty.phpGbGF:vendor/phpunit/phpunit/src/Framework/Constraint/IsTrue.phpGb9?vendor/phpunit/phpunit/src/Framework/Constraint/GreaterThan.phpGb Y"9vendor/phpunit/phpunit/src/Framework/Constraint/Count.php@Gb@4Ĥ>vendor/phpunit/phpunit/src/Framework/Constraint/IsAnything.phpUGbU42?vendor/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php|Gb|d6vendor/phpunit/phpunit/src/Framework/Constraint/Or.phpdGbd\7vendor/phpunit/phpunit/src/Framework/Constraint/Xor.phpGb 3:vendor/phpunit/phpunit/src/Framework/Constraint/IsJson.phpGb#7vendor/phpunit/phpunit/src/Framework/Constraint/And.phpGb #=vendor/phpunit/phpunit/src/Framework/Constraint/PCREMatch.phpGb2<uAvendor/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.phpuGbu)1@vendor/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.phpEGbEKvendor/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.phpGbF:vendor/phpunit/phpunit/src/Framework/Constraint/IsType.phpGbrMe:vendor/phpunit/phpunit/src/Framework/Constraint/IsNull.phpGb@~Cvendor/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.phpfGbf)Y <vendor/phpunit/phpunit/src/Framework/Constraint/SameSize.phpGb87vendor/phpunit/phpunit/src/Framework/Constraint/Not.phpGbEX\Jvendor/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php7Gb7oFvendor/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.phpGb C?vendor/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php9Gb9ا'>vendor/phpunit/phpunit/src/Framework/Constraint/IsInfinite.phpGb6޼ߤ9vendor/phpunit/phpunit/src/Framework/Constraint/IsNan.phpGb06[Evendor/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.phpGbԸGvendor/phpunit/phpunit/src/Framework/Constraint/TraversableContains.phpGbD==vendor/phpunit/phpunit/src/Framework/Constraint/Composite.phpGb|+<vendor/phpunit/phpunit/src/Framework/Constraint/LessThan.phpGbekRDvendor/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.phpGbQߤ;vendor/phpunit/phpunit/src/Framework/Constraint/IsEqual.phpGb$?vendor/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php Gb 1<vendor/phpunit/phpunit/src/Framework/Constraint/IsFinite.phpGb#92vendor/phpunit/phpunit/src/Framework/RiskyTest.php=Gb=p}2vendor/phpunit/phpunit/src/Framework/Exception.php.Gb.Ie"0vendor/phpunit/phpunit/src/Framework/Warning.phpGbIk?vendor/phpunit/phpunit/src/Framework/TestSuite/DataProvider.phpGb.)ˤ1vendor/phpunit/phpunit/src/Framework/TestCase.phpGbD2vendor/phpunit/phpunit/src/Framework/TestSuite.phpx9Gbx9 7vendor/phpunit/phpunit/src/Framework/SelfDescribing.phpbGbb8vendor/phpunit/phpunit/src/Framework/WarningTestCase.phpfGbfIvendor/phpunit/phpunit/src/Framework/MissingCoversAnnotationException.phpyGby=T=vendor/phpunit/phpunit/src/Framework/AssertionFailedError.phpGbY4vendor/phpunit/phpunit/src/Framework/TestFailure.php4Gb4pEvendor/phpunit/phpunit/src/Framework/InvalidCoversTargetException.phpxGbxɯƤ8vendor/phpunit/phpunit/src/Framework/SkippedTestCase.phpGb&ͤ<vendor/phpunit/phpunit/src/Framework/IncompleteTestError.phpGb^~7vendor/phpunit/phpunit/src/Framework/IncompleteTest.phpBGbBs04vendor/phpunit/phpunit/src/Framework/SkippedTest.php>Gb>BIl5vendor/phpunit/phpunit/src/Framework/TestListener.php<Gb<r-vendor/phpunit/phpunit/src/Framework/Test.phpGb%r9vendor/phpunit/phpunit/src/Framework/Assert/Functions.phpk`Gbk`ߌ̌4vendor/phpunit/phpunit/src/Framework/OutputError.phpjGbj@6;/vendor/phpunit/phpunit/src/Framework/Assert.phpGbqN 3vendor/phpunit/phpunit/src/Framework/Constraint.phpGbOǤ9vendor/phpunit/phpunit/src/Framework/BaseTestListener.phpGblF7vendor/phpunit/phpunit/src/Framework/SyntheticError.phpGb0.vendor/phpunit/phpunit/src/Framework/Error.phpGbV%>vendor/phpunit/phpunit/src/Framework/CodeCoverageException.phpeGbeX$-5Cvendor/phpunit/phpunit/src/Framework/ExpectationFailedException.phpGbj#<vendor/phpunit/phpunit/src/ForwardCompatibility/TestCase.phpGb eդ=vendor/phpunit/phpunit/src/ForwardCompatibility/TestSuite.phpGbHvendor/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.phpGb}az@vendor/phpunit/phpunit/src/ForwardCompatibility/TestListener.phpGb}8vendor/phpunit/phpunit/src/ForwardCompatibility/Test.php|Gb||n:vendor/phpunit/phpunit/src/ForwardCompatibility/Assert.phpGb b_Dvendor/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.phpGbk7vendor/phpunit/phpunit/src/Extensions/PhptTestSuite.phpGb@Ѥ8vendor/phpunit/phpunit/src/Extensions/GroupTestSuite.php;Gb;ʖ8vendor/phpunit/phpunit/src/Extensions/TicketListener.php Gb ev6vendor/phpunit/phpunit/src/Extensions/PhptTestCase.phpGb7vendor/phpunit/phpunit/src/Extensions/TestDecorator.phpFGbF6vendor/phpunit/phpunit/src/Extensions/RepeatedTest.phpGb7A:vendor/phpunit/phpunit/src/Util/ConfigurationGenerator.phpGb2*vendor/phpunit/phpunit/src/Util/String.phprGbr-vendor/phpunit/phpunit/src/Util/Blacklist.phpGb>9'vendor/phpunit/phpunit/src/Util/PHP.phpGb.vendor/phpunit/phpunit/src/Util/Fileloader.php4Gb4 vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.phpGb<$=vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter/XML.php Gb ̤>vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.phpGb=f9vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter.phpGbai:vendor/phpunit/phpunit/src/Util/TestDox/NamePrettifier.phpGb/n9vendor/phpunit/phpunit/src/Util/InvalidArgumentHelper.phpGb_q0vendor/phpunit/phpunit/src/TextUI/TestRunner.php}Gb} }3vendor/phpunit/phpunit/src/TextUI/ResultPrinter.php(Gb(-vendor/phpunit/phpunit/src/TextUI/Command.phprdGbrds0Cvendor/phpunit/php-token-stream/src/Token/Stream/CachingFactory.phpGb|c4vendor/phpunit/php-token-stream/src/Token/Stream.php"Gb"-?-vendor/phpunit/php-token-stream/src/Token.phpHGbHg+6vendor/phpunit/php-code-coverage/src/Driver/Xdebug.phpGbH(#6vendor/phpunit/php-code-coverage/src/Driver/Driver.phpGb+\4vendor/phpunit/php-code-coverage/src/Driver/HHVM.phpGb e6vendor/phpunit/php-code-coverage/src/Driver/PHPDBG.phpGb%-vendor/phpunit/php-code-coverage/src/Util.phpGbX l5vendor/phpunit/php-code-coverage/src/CodeCoverage.php?Gb?Į7vendor/phpunit/php-code-coverage/src/Node/Directory.phpGbK6vendor/phpunit/php-code-coverage/src/Node/Iterator.phpGb΄Б5vendor/phpunit/php-code-coverage/src/Node/Builder.php Gb N':vendor/phpunit/php-code-coverage/src/Node/AbstractNode.php Gb XA2vendor/phpunit/php-code-coverage/src/Node/File.php)Gb) /vendor/phpunit/php-code-coverage/src/Filter.phpGb<374vendor/phpunit/php-code-coverage/src/Report/Text.phpGb.3vendor/phpunit/php-code-coverage/src/Report/PHP.phpGb4x=vendor/phpunit/php-code-coverage/src/Report/Xml/Directory.phpfGbfk%a:vendor/phpunit/php-code-coverage/src/Report/Xml/Totals.php Gb AzU8vendor/phpunit/php-code-coverage/src/Report/Xml/Node.php(Gb(Ѵ:vendor/phpunit/php-code-coverage/src/Report/Xml/Report.phpGbY:vendor/phpunit/php-code-coverage/src/Report/Xml/Facade.phpGb4 <vendor/phpunit/php-code-coverage/src/Report/Xml/Coverage.phpGbڢ8vendor/phpunit/php-code-coverage/src/Report/Xml/Unit.phpGbqL:vendor/phpunit/php-code-coverage/src/Report/Xml/Method.phpGbE@8vendor/phpunit/php-code-coverage/src/Report/Xml/File.phpGbo9vendor/phpunit/php-code-coverage/src/Report/Xml/Tests.phpGb[잤;vendor/phpunit/php-code-coverage/src/Report/Xml/Project.phpGb }=vendor/phpunit/php-code-coverage/src/Report/Html/Renderer.phpGb%5դ;vendor/phpunit/php-code-coverage/src/Report/Html/Facade.php]Gb]BGvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php2Gb2 ‚Gvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.phpGbFQvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file.html.dist Gb (﷤Vvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/dashboard.html.distGb6 Xvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/method_item.html.distxGbx*Vvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory.html.dist!Gb!!_Vvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item.html.distgGbgV PXvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/bootstrap.min.csspGbpX|FPvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css+Gb+Y`gTvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/nv.d3.min.cssX%GbX%0,ivendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/fonts/glyphicons-halflings-regular.svg¨Gb¨|ɤjvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/fonts/glyphicons-halflings-regular.woff[Gb[{ivendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/fonts/glyphicons-halflings-regular.ttf\Gb\<ivendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/fonts/glyphicons-halflings-regular.eotNGbNXDZkvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/fonts/glyphicons-halflings-regular.woff2lFGblFva[vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_item.html.dist5Gb5Z]Svendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/jquery.min.jsRGbR~Tvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/respond.min.jsGb{Vvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/bootstrap.min.jsGb/jRvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/nv.d3.min.jsRGbR%vendor/hoa/event/Test/Unit/Source.php Gb VbJ'vendor/hoa/event/Test/Unit/Listener.phpGb1(vendor/hoa/event/Test/Unit/Exception.phpCGbCg%L%vendor/hoa/event/Test/Unit/Bucket.phpGb^&vendor/hoa/event/Test/Unit/Listens.phpGb*]o)vendor/hoa/event/Test/Unit/Listenable.php,Gb,'5$vendor/hoa/event/Test/Unit/Event.phpHGbH{._֤vendor/hoa/event/Exception.phpGbx샤vendor/hoa/event/Bucket.phpGbvvendor/hoa/event/Listens.php~Gb~wvendor/hoa/event/Listenable.phpGb vendor/hoa/event/Event.php Gb x`cvendor/hoa/ustring/Search.phpGbOIV&vendor/hoa/ustring/Test/Unit/Issue.php?Gb?h'vendor/hoa/ustring/Test/Unit/Search.phpGbԠݤ(vendor/hoa/ustring/Test/Unit/Ustring.phpDGbD vendor/hoa/ustring/Exception.phpGbH!vendor/hoa/ustring/Bin/Tocode.phpGbxHӤ#vendor/hoa/ustring/Bin/Fromcode.phpGb s vendor/hoa/ustring/Ustring.php.Gb.42[C vendor/hoa/protocol/Protocol.php Gb €o*vendor/hoa/protocol/Test/Unit/Protocol.php{ Gb{ ݹw)vendor/hoa/protocol/Test/Unit/Wrapper.php1AGb1AC +vendor/hoa/protocol/Test/Unit/Exception.phpIGbI A.vendor/hoa/protocol/Test/Unit/Node/Library.phpGbT/5+vendor/hoa/protocol/Test/Unit/Node/Node.phpGbSĤvendor/hoa/protocol/Wrapper.phpGb? "!vendor/hoa/protocol/Exception.phpGbE #vendor/hoa/protocol/Bin/Resolve.phpGbvh$vendor/hoa/protocol/Node/Library.phpGbkO!vendor/hoa/protocol/Node/Node.phpGb:vendor/hoa/stream/Test/Integration/Filter/LateComputed.phpDGbDB04vendor/hoa/stream/Test/Integration/Filter/Filter.phpg Gbg -vendor/hoa/stream/Test/Integration/Stream.phpeGbe;\)vendor/hoa/stream/Test/Unit/Exception.phpbGbb:9vendor/hoa/stream/Test/Unit/Wrapper/IWrapper/IWrapper.phpGb)W7vendor/hoa/stream/Test/Unit/Wrapper/IWrapper/Stream.phpvGbv ڛ5vendor/hoa/stream/Test/Unit/Wrapper/IWrapper/File.phppGbpj_/vendor/hoa/stream/Test/Unit/Wrapper/Wrapper.php Gb W&1vendor/hoa/stream/Test/Unit/Wrapper/Exception.phpgGbgO0vendor/hoa/stream/Test/Unit/IStream/Statable.phpGb=1vendor/hoa/stream/Test/Unit/IStream/Touchable.php#Gb#IL*vendor/hoa/stream/Test/Unit/IStream/In.phpGb-2vendor/hoa/stream/Test/Unit/IStream/Structural.phpGb` ֤+vendor/hoa/stream/Test/Unit/IStream/Out.phpGb 1vendor/hoa/stream/Test/Unit/IStream/Pointable.phpGbZg.vendor/hoa/stream/Test/Unit/IStream/Stream.php[Gb[[¹2vendor/hoa/stream/Test/Unit/IStream/Bufferable.phpGb7פ0vendor/hoa/stream/Test/Unit/IStream/Lockable.php Gb :6,0vendor/hoa/stream/Test/Unit/IStream/Pathable.phpGbε.&vendor/hoa/stream/Test/Unit/Bucket.php Gb 隞o'vendor/hoa/stream/Test/Unit/Context.php Gb 0vendor/hoa/stream/Test/Unit/Filter/Exception.phpaGba]BT-vendor/hoa/stream/Test/Unit/Filter/Filter.php Gb ,vendor/hoa/stream/Test/Unit/Filter/Basic.phpGb;ODn&vendor/hoa/stream/Test/Unit/Stream.php8Gb8>)vendor/hoa/stream/Test/Unit/Composite.phpGb쑤vendor/hoa/stream/Exception.phpGb'vendor/hoa/console/Cursor.php&#Gb&#(WҤ vendor/hoa/iterator/Multiple.phpFGbFg2)vendor/hoa/iterator/CallbackGenerator.phpGb Ԥ!vendor/hoa/iterator/Directory.phpGḃvФ*vendor/hoa/iterator/Test/Unit/Multiple.phpGb$6=3vendor/hoa/iterator/Test/Unit/CallbackGenerator.phpGb{+vendor/hoa/iterator/Test/Unit/Directory.phpGb[-vendor/hoa/iterator/Test/Unit/SplFileInfo.php. Gb. y,vendor/hoa/iterator/Test/Unit/FileSystem.phpGbVc&0vendor/hoa/iterator/Test/Unit/CallbackFilter.php? Gb? K(vendor/hoa/iterator/Test/Unit/Append.phpGbܙפ/vendor/hoa/iterator/Test/Unit/Demultiplexer.phpGbO&vendor/hoa/iterator/Test/Unit/Mock.php<Gb<Ŝ%X%vendor/hoa/iterator/Test/Unit/Map.phpcGbcX'vendor/hoa/iterator/Test/Unit/Limit.phpGbһ)vendor/hoa/iterator/Test/Unit/Counter.phpPGbPĊs2vendor/hoa/iterator/Test/Unit/IteratorIterator.phpGb޹(vendor/hoa/iterator/Test/Unit/Filter.php"Gb"o% *vendor/hoa/iterator/Test/Unit/Repeater.phpGbZK3vendor/hoa/iterator/Test/Unit/RegularExpression.phpGbGä*vendor/hoa/iterator/Test/Unit/Infinite.phpGb;*vendor/hoa/iterator/Test/Unit/NoRewind.phpSGbS +vendor/hoa/iterator/Test/Unit/Lookahead.phpJ GbJ v ,vendor/hoa/iterator/Test/Unit/Lookbehind.phpO GbO 퍤(vendor/hoa/iterator/Test/Unit/Buffer.php Gb #vendor/hoa/iterator/SplFileInfo.phpGb.!vendor/hoa/iterator/Aggregate.php}Gb}u6 vendor/hoa/iterator/Iterator.phpGb`"vendor/hoa/iterator/FileSystem.php~Gb~^&vendor/hoa/iterator/CallbackFilter.phpGblf!vendor/hoa/iterator/Exception.phpGbIsqvendor/hoa/iterator/Append.phpsGbs?3%vendor/hoa/iterator/Demultiplexer.phpGbvvendor/hoa/iterator/Glob.phpoGboS{vendor/hoa/iterator/Mock.phppGbpZW{vendor/hoa/iterator/Map.phpoGbo]դvendor/hoa/iterator/Outer.phpuGbu/vendor/hoa/iterator/Limit.phpqGbqvendor/hoa/iterator/Counter.php4Gb4(vendor/hoa/iterator/IteratorIterator.phpGbtvendor/hoa/iterator/Filter.php|Gb|VX+vendor/hoa/iterator/Recursive/Directory.phpGbel¤+vendor/hoa/iterator/Recursive/Recursive.phpGb*vendor/hoa/iterator/Recursive/Iterator.phpGbҤ0vendor/hoa/iterator/Recursive/CallbackFilter.phpGbŃ&vendor/hoa/iterator/Recursive/Mock.phpGb>j%vendor/hoa/iterator/Recursive/Map.phpGb*\(vendor/hoa/iterator/Recursive/Filter.phpGbo3vendor/hoa/iterator/Recursive/RegularExpression.php/Gb/ e3 vendor/hoa/iterator/Repeater.php`Gb`vendor/codeception/module-sequence/src/Codeception/Util/sq.php4Gb4&Fvendor/codeception/module-sequence/src/Codeception/Module/Sequence.php Gb 'VHvendor/codeception/module-mongodb/src/Codeception/Lib/Driver/MongoDb.phpGbu*Dvendor/codeception/module-mongodb/src/Codeception/Module/MongoDb.php:Gb:Dvendor/codeception/module-asserts/src/Codeception/Module/Asserts.phpiGbiaLvendor/codeception/module-asserts/src/Codeception/Module/AbstractAsserts.phpGbh?vendor/codeception/phpunit-wrapper/src/ResultPrinter/Report.php%Gb%nVPvendor/codeception/phpunit-wrapper/src/ResultPrinter/template/scenario.html.distGbS&Qvendor/codeception/phpunit-wrapper/src/ResultPrinter/template/scenarios.html.distGb/ZMvendor/codeception/phpunit-wrapper/src/ResultPrinter/template/suite.html.dist3Gb3|Pvendor/codeception/phpunit-wrapper/src/ResultPrinter/template/substeps.html.dist#Gb#͠wWvendor/codeception/phpunit-wrapper/src/ResultPrinter/template/scenario_header.html.dist3Gb3ʤLvendor/codeception/phpunit-wrapper/src/ResultPrinter/template/step.html.distbGbbdLvendor/codeception/phpunit-wrapper/src/ResultPrinter/template/fail.html.dist;Gb;˟=vendor/codeception/phpunit-wrapper/src/ResultPrinter/HTML.php$Gb$X&ؤ;vendor/codeception/phpunit-wrapper/src/ResultPrinter/UI.php Gb pn3vendor/codeception/phpunit-wrapper/src/Listener.phpGb ܤ<vendor/codeception/phpunit-wrapper/src/DispatcherWrapper.phpGb,Bvendor/codeception/phpunit-wrapper/src/Constraint/WebDriverNot.phpGb*Bvendor/codeception/phpunit-wrapper/src/Constraint/JsonContains.phpGbeMӆ:vendor/codeception/phpunit-wrapper/src/Constraint/Page.php&Gb&ؤ@vendor/codeception/phpunit-wrapper/src/Constraint/CrawlerNot.php4Gb4P#'>vendor/codeception/phpunit-wrapper/src/Constraint/JsonType.phpGb!{=vendor/codeception/phpunit-wrapper/src/Constraint/Crawler.phpGb2t٤?vendor/codeception/phpunit-wrapper/src/Constraint/WebDriver.phpGb԰̤3vendor/codeception/phpunit-wrapper/src/TestCase.phpGb8fߤ9vendor/codeception/phpunit-wrapper/src/ConsolePrinter.phpoGbo܈d$6vendor/codeception/phpunit-wrapper/src/Log/PhpUnit.php}Gb}zʤ4vendor/codeception/phpunit-wrapper/src/Log/JUnit.php Gb 9 6/8vendor/codeception/phpunit-wrapper/src/ResultPrinter.phpX GbX ɞG٤;vendor/codeception/phpunit-wrapper/src/phpunit5-loggers.phpjJGbjJ1vendor/codeception/phpunit-wrapper/src/Runner.phpGb^qk5vendor/codeception/phpunit-wrapper/src/FilterTest.phpGb#/vendor/codeception/phpunit-wrapper/src/shim.phpGb}ڤ/vendor/codeception/phpunit-wrapper/src/Init.phpGb!;vendor/codeception/phpunit-wrapper/src/Overrides/Filter.phpN GbN X6 /vendor/codeception/phpunit-wrapper/RoboFile.php8 Gb8 B-Vvendor/codeception/module-webdriver/src/Codeception/Lib/Interfaces/SessionSnapshot.phpGb_yVvendor/codeception/module-webdriver/src/Codeception/Lib/Interfaces/ScreenshotSaver.phphGbhhHvendor/codeception/module-webdriver/src/Codeception/Module/WebDriver.phpGb$/Uvendor/codeception/module-webdriver/src/Codeception/Exception/ConnectionException.php`Gb`LA@vendor/codeception/module-rest/src/Codeception/Util/JsonType.php"Gb"eNAvendor/codeception/module-rest/src/Codeception/Util/JsonArray.php:Gb:.>vendor/codeception/module-rest/src/Codeception/Module/REST.phpXGbXnHvendor/codeception/module-queue/src/Codeception/Lib/Interfaces/Queue.php^Gb^^=Cvendor/codeception/module-queue/src/Codeception/Lib/Driver/Iron.php Gb T cHvendor/codeception/module-queue/src/Codeception/Lib/Driver/AmazonSQS.php[Gb[>dJvendor/codeception/module-queue/src/Codeception/Lib/Driver/Pheanstalk4.phpGbLxHvendor/codeception/module-queue/src/Codeception/Lib/Driver/Beanstalk.php:Gb:Ѥ@vendor/codeception/module-queue/src/Codeception/Module/Queue.php,Gb,Ń@vendor/codeception/module-redis/src/Codeception/Module/Redis.phpvRGbvRxN61vendor/codeception/stub/src/Test/Feature/Stub.php#Gb#p$vendor/codeception/stub/src/Stub.php SGb SK0-vendor/codeception/stub/src/Stub/Expected.phpGb`(3vendor/codeception/stub/src/Stub/ConsecutiveMap.phpUGbU桤2vendor/codeception/stub/src/Stub/StubMarshaler.php!Gb!]/yĤ$vendor/codeception/stub/src/shim.php Gb [o@$vendor/codeception/stub/RoboFile.php4Gb4(<vendor/codeception/module-ftp/src/Codeception/Module/FTP.phpM^GbM^] Fvendor/codeception/module-memcache/src/Codeception/Module/Memcache.phpGb <vendor/codeception/module-cli/src/Codeception/Module/Cli.php Gb b>vendor/codeception/module-amqp/src/Codeception/Module/AMQP.php/Gb/UpMvendor/codeception/module-phpbrowser/src/Codeception/Lib/Connector/Guzzle.phpk.Gbk.Jvendor/codeception/module-phpbrowser/src/Codeception/Module/PhpBrowser.phpGb5/u:Fvendor/codeception/lib-asserts/src/Codeception/Util/Shared/Asserts.php Gb +ĤOvendor/codeception/lib-asserts/src/Codeception/Util/Shared/InheritedAsserts.phpGbڙBvendor/codeception/module-db/src/Codeception/Lib/Interfaces/Db.php< Gb< Δ/פ>vendor/codeception/module-db/src/Codeception/Lib/Driver/Db.php"Gb"ҳAvendor/codeception/module-db/src/Codeception/Lib/Driver/MySql.phpGb_d(?vendor/codeception/module-db/src/Codeception/Lib/Driver/Oci.phpAGbAsMBvendor/codeception/module-db/src/Codeception/Lib/Driver/SqlSrv.phpi Gbi G8Fvendor/codeception/module-db/src/Codeception/Lib/Driver/PostgreSql.phphGbh99Bvendor/codeception/module-db/src/Codeception/Lib/Driver/Sqlite.php Gb @ v@vendor/codeception/module-db/src/Codeception/Lib/DbPopulator.phpGbjC:vendor/codeception/module-db/src/Codeception/Module/Db.phpGb{1/vendor/codeception/codeception/ext/Recorder.php?TGb?TH}H0vendor/codeception/codeception/ext/RunBefore.phpGGbG0?5vendor/codeception/codeception/ext/SimpleReporter.phpGbmA[2vendor/codeception/codeception/ext/DotReporter.php Gb |hq0vendor/codeception/codeception/ext/RunFailed.php Gb K-vendor/codeception/codeception/ext/Logger.phpGbt91vendor/codeception/codeception/ext/RunProcess.phpGb'*Evendor/codeception/codeception/src/Codeception/Lib/Interfaces/API.phpbGbbD^ǤRvendor/codeception/codeception/src/Codeception/Lib/Interfaces/DoctrineProvider.phpuGbu'Pvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ElementLocator.phpGbNHvendor/codeception/codeception/src/Codeception/Lib/Interfaces/Remote.phpGbR\Qvendor/codeception/codeception/src/Codeception/Lib/Interfaces/DependsOnModule.php_Gb_ĪUvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ConflictsWithModule.phpGbhГLvendor/codeception/codeception/src/Codeception/Lib/Interfaces/DataMapper.phpfGbf?jNvendor/codeception/codeception/src/Codeception/Lib/Interfaces/PartedModule.phpGbTQvendor/codeception/codeception/src/Codeception/Lib/Interfaces/PageSourceSaver.php_Gb_蹉Qvendor/codeception/codeception/src/Codeception/Lib/Interfaces/RequiresPackage.phpGbQNvendor/codeception/codeception/src/Codeception/Lib/Interfaces/MultiSession.phpGb)wbEvendor/codeception/codeception/src/Codeception/Lib/Interfaces/Web.phpRoGbRo _Nvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ActiveRecord.phpAGbANŤEvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ORM.php?Gb?ΤFvendor/codeception/codeception/src/Codeception/Lib/ModuleContainer.phpCGbC~9vendor/codeception/codeception/src/Codeception/Lib/Di.phpAGbA@q=vendor/codeception/codeception/src/Codeception/Lib/Friend.phpGb[Hvendor/codeception/codeception/src/Codeception/Lib/Generator/Actions.php6$Gb6$ Hvendor/codeception/codeception/src/Codeception/Lib/Generator/Feature.phpGbL<Fvendor/codeception/codeception/src/Codeception/Lib/Generator/Actor.phpA GbA g-Kvendor/codeception/codeception/src/Codeception/Lib/Generator/StepObject.phpGb`Pvendor/codeception/codeception/src/Codeception/Lib/Generator/GherkinSnippets.phpGb;Qvendor/codeception/codeception/src/Codeception/Lib/Generator/Shared/Classname.phpGb/!Kvendor/codeception/codeception/src/Codeception/Lib/Generator/PageObject.phpGbGvendor/codeception/codeception/src/Codeception/Lib/Generator/Helper.phpGbyi6Evendor/codeception/codeception/src/Codeception/Lib/Generator/Cept.phpGbuAEvendor/codeception/codeception/src/Codeception/Lib/Generator/Test.phpJGbJA:Evendor/codeception/codeception/src/Codeception/Lib/Generator/Cest.phpGb9Fvendor/codeception/codeception/src/Codeception/Lib/Generator/Group.phpBGbB+bIvendor/codeception/codeception/src/Codeception/Lib/Generator/Snapshot.phpGb ۤKvendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Comment.phpGb`Ivendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Retry.phpGb^Jvendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Friend.phpGbtGIvendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Pause.phpGb^=*PHvendor/codeception/codeception/src/Codeception/Lib/Console/Colorizer.phpQGbQz Jvendor/codeception/codeception/src/Codeception/Lib/Console/DiffFactory.phpBGbBjMvendor/codeception/codeception/src/Codeception/Lib/Console/MessageFactory.phpGb߻WJvendor/codeception/codeception/src/Codeception/Lib/Console/ReplHistory.phpGba Fvendor/codeception/codeception/src/Codeception/Lib/Console/Message.php& Gb& )XEvendor/codeception/codeception/src/Codeception/Lib/Console/Output.php Gb cJ¤Cvendor/codeception/codeception/src/Codeception/Lib/ParamsLoader.php&Gb&"XCvendor/codeception/codeception/src/Codeception/Lib/GroupManager.phpfGbf `vendor/codeception/codeception/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.phpGb.Cvendor/codeception/codeception/src/Codeception/Lib/Notification.phpGb<=vendor/codeception/codeception/src/Codeception/Lib/Parser.phpHGbHIvendor/codeception/codeception/src/Codeception/Event/PrintResultEvent.phpGbOk0äJvendor/codeception/codeception/src/Codeception/Event/DispatcherWrapper.phpGbrBvendor/codeception/codeception/src/Codeception/Event/StepEvent.phpbGbb6TBvendor/codeception/codeception/src/Codeception/Event/FailEvent.phpGb7wBvendor/codeception/codeception/src/Codeception/Event/TestEvent.php~Gb~ Cvendor/codeception/codeception/src/Codeception/Event/SuiteEvent.phpGb o8vendor/codeception/codeception/src/Codeception/Actor.phpGbg iLvendor/codeception/codeception/src/Codeception/Test/Interfaces/Dependent.phpoGboNxNvendor/codeception/codeception/src/Codeception/Test/Interfaces/Descriptive.phpGb֙Hvendor/codeception/codeception/src/Codeception/Test/Interfaces/Plain.phpnGbnEKvendor/codeception/codeception/src/Codeception/Test/Interfaces/Reported.phpGbG"+Qvendor/codeception/codeception/src/Codeception/Test/Interfaces/StrictCoverage.phpGbQvendor/codeception/codeception/src/Codeception/Test/Interfaces/ScenarioDriven.phpDGbD&<@vendor/codeception/codeception/src/Codeception/Test/Metadata.phpGb* >vendor/codeception/codeception/src/Codeception/Test/Loader.php^Gb^Bvendor/codeception/codeception/src/Codeception/Test/Descriptor.php2Gb2rNvendor/codeception/codeception/src/Codeception/Test/Loader/LoaderInterface.phpGbFFvendor/codeception/codeception/src/Codeception/Test/Loader/Gherkin.phpGbhWCvendor/codeception/codeception/src/Codeception/Test/Loader/Cept.phpGbNގCvendor/codeception/codeception/src/Codeception/Test/Loader/Unit.php Gb vCvendor/codeception/codeception/src/Codeception/Test/Loader/Cest.phpGbh""n?vendor/codeception/codeception/src/Codeception/Test/Gherkin.phpGb்<vendor/codeception/codeception/src/Codeception/Test/Cept.php2Gb2}aiNvendor/codeception/codeception/src/Codeception/Test/Feature/ScenarioLoader.phpCGbCN<Pvendor/codeception/codeception/src/Codeception/Test/Feature/AssertionCounter.phpGb/ yQvendor/codeception/codeception/src/Codeception/Test/Feature/MetadataCollector.phpGbYKvendor/codeception/codeception/src/Codeception/Test/Feature/ErrorLogger.phpGb ҥLvendor/codeception/codeception/src/Codeception/Test/Feature/CodeCoverage.phpGbWeWvendor/codeception/codeception/src/Codeception/Test/Feature/IgnoreIfMetadataBlocked.phpGb B<vendor/codeception/codeception/src/Codeception/Test/Test.phpGb!<vendor/codeception/codeception/src/Codeception/Test/Unit.phpGbͿZ=<vendor/codeception/codeception/src/Codeception/Test/Cest.phpGb=9vendor/codeception/codeception/src/Codeception/Events.phpGbS>vendor/codeception/codeception/src/Codeception/GroupObject.phpGbDjf;vendor/codeception/codeception/src/Codeception/Codecept.php< Gb< "@vendor/codeception/codeception/src/Codeception/Configuration.phpbGbb3Ovendor/codeception/codeception/src/Codeception/Util/ArrayContainsComparator.phpGbIgDvendor/codeception/codeception/src/Codeception/Util/PathResolver.phpGbe]\@vendor/codeception/codeception/src/Codeception/Util/Fixtures.phpGb`դ=vendor/codeception/codeception/src/Codeception/Util/Debug.phpGbۼrBvendor/codeception/codeception/src/Codeception/Util/FileSystem.phpW GbW -2p@vendor/codeception/codeception/src/Codeception/Util/Autoload.phpGb;vendor/codeception/codeception/src/Codeception/Util/Uri.php,Gb,<Dvendor/codeception/codeception/src/Codeception/Util/XmlStructure.phpZ GbZ KPTIvendor/codeception/codeception/src/Codeception/Util/Shared/Namespaces.phpGb1u@vendor/codeception/codeception/src/Codeception/Util/Template.php5Gb5qn=vendor/codeception/codeception/src/Codeception/Util/Maybe.phpLGbL2w<vendor/codeception/codeception/src/Codeception/Util/Soap.phpnGbn4uK<vendor/codeception/codeception/src/Codeception/Util/Stub.phpGbx55Fvendor/codeception/codeception/src/Codeception/Util/ActionSequence.php Gb 2Hvendor/codeception/codeception/src/Codeception/Util/ReflectionHelper.phpGbѕ;vendor/codeception/codeception/src/Codeception/Util/Xml.phpzGbz~HERvendor/codeception/codeception/src/Codeception/Util/ReflectionPropertyAccessor.php Gb ըBvendor/codeception/codeception/src/Codeception/Util/Annotation.php=Gb= ?vendor/codeception/codeception/src/Codeception/Util/Locator.php3(Gb3(۪Bvendor/codeception/codeception/src/Codeception/Util/XmlBuilder.phpEGbEOȤ?vendor/codeception/codeception/src/Codeception/Step/Comment.php Gb m=vendor/codeception/codeception/src/Codeception/Step/Retry.phpM GbM n>vendor/codeception/codeception/src/Codeception/Step/Action.phpXGbXyM<vendor/codeception/codeception/src/Codeception/Step/Skip.php|Gb|k@=vendor/codeception/codeception/src/Codeception/Step/TryTo.phpGbDcvAvendor/codeception/codeception/src/Codeception/Step/Assertion.phpyGby֝?@vendor/codeception/codeception/src/Codeception/Step/Executor.phpGb8 OBvendor/codeception/codeception/src/Codeception/Step/Incomplete.phpGb}ȮEvendor/codeception/codeception/src/Codeception/Step/GeneratedStep.phpGb@Qvendor/codeception/codeception/src/Codeception/Step/Argument/PasswordArgument.phpGbѡǤPvendor/codeception/codeception/src/Codeception/Step/Argument/FormattedOutput.phpGb5lfAvendor/codeception/codeception/src/Codeception/Step/Condition.phpyGbykLvendor/codeception/codeception/src/Codeception/Step/ConditionalAssertion.phpGb~<vendor/codeception/codeception/src/Codeception/Step/Meta.phpGbPf>vendor/codeception/codeception/src/Codeception/Application.phpGbƈdL@vendor/codeception/codeception/src/Codeception/TestInterface.phpGb8vendor/codeception/codeception/src/Codeception/Suite.phpGb-pKvendor/codeception/codeception/src/Codeception/Coverage/SuiteSubscriber.php Gb l҃Bvendor/codeception/codeception/src/Codeception/Coverage/Filter.phpGbͱlRvendor/codeception/codeception/src/Codeception/Coverage/PhpCodeCoverageFactory.phpGbUC_Svendor/codeception/codeception/src/Codeception/Coverage/Subscriber/RemoteServer.php Gb yLvendor/codeception/codeception/src/Codeception/Coverage/Subscriber/Local.phprGbreNvendor/codeception/codeception/src/Codeception/Coverage/Subscriber/Printer.php5Gb5o^!Rvendor/codeception/codeception/src/Codeception/Coverage/Subscriber/LocalServer.php&Gb&58;vendor/codeception/codeception/src/Codeception/Scenario.php#Gb#?<vendor/codeception/codeception/src/Codeception/Extension.php Gb .Gvendor/codeception/codeception/src/Codeception/Command/GenerateCept.phpGbzfɤBvendor/codeception/codeception/src/Codeception/Command/Console.phpGbpMvendor/codeception/codeception/src/Codeception/Command/GeneratePageObject.phpGb.Jvendor/codeception/codeception/src/Codeception/Command/GherkinSnippets.php Gb T֤@vendor/codeception/codeception/src/Codeception/Command/Clean.php0Gb0)ŤDvendor/codeception/codeception/src/Codeception/Command/Bootstrap.phpGbAAvendor/codeception/codeception/src/Codeception/Command/DryRun.php!Gb!a=Mvendor/codeception/codeception/src/Codeception/Command/CompletionFallback.phpGb S>vendor/codeception/codeception/src/Codeception/Command/Run.phpjsGbjs?-ͤHvendor/codeception/codeception/src/Codeception/Command/Shared/Config.phpGbhBDTLvendor/codeception/codeception/src/Codeception/Command/Shared/FileSystem.phpGbȀGvendor/codeception/codeception/src/Codeception/Command/Shared/Style.phpGbeAOMvendor/codeception/codeception/src/Codeception/Command/GenerateStepObject.php= Gb= [Evendor/codeception/codeception/src/Codeception/Command/Completion.php Gb K|Gvendor/codeception/codeception/src/Codeception/Command/GherkinSteps.phpZGbZGvendor/codeception/codeception/src/Codeception/Command/GenerateCest.phpGb0fKvendor/codeception/codeception/src/Codeception/Command/GenerateSnapshot.phpGbIvendor/codeception/codeception/src/Codeception/Command/GenerateHelper.phpGbXJvendor/codeception/codeception/src/Codeception/Command/GenerateFeature.phpGbб@vendor/codeception/codeception/src/Codeception/Command/Build.php Gb ΤGvendor/codeception/codeception/src/Codeception/Command/GenerateTest.phpGbeNvendor/codeception/codeception/src/Codeception/Command/GenerateEnvironment.php=Gb=UIvendor/codeception/codeception/src/Codeception/Command/ConfigValidate.phpGbREvendor/codeception/codeception/src/Codeception/Command/SelfUpdate.php& Gb& w{Hvendor/codeception/codeception/src/Codeception/Command/GenerateGroup.phpGb4gE?vendor/codeception/codeception/src/Codeception/Command/Init.phpGbg~ Lvendor/codeception/codeception/src/Codeception/Command/GenerateScenarios.phpGb4rHvendor/codeception/codeception/src/Codeception/Command/GenerateSuite.phpGb&x9vendor/codeception/codeception/src/Codeception/Module.phpg"Gbg"vϤ7vendor/codeception/codeception/src/Codeception/Step.phpP*GbP*qfΤ?vendor/codeception/codeception/src/Codeception/SuiteManager.phpGbS;w"Fvendor/codeception/codeception/src/Codeception/Template/Acceptance.phpWGbW+\ԤEvendor/codeception/codeception/src/Codeception/Template/Bootstrap.phpCGbCլDvendor/codeception/codeception/src/Codeception/Template/Upgrade4.phpI GbI p?vendor/codeception/codeception/src/Codeception/Template/Api.php Gb ]82@vendor/codeception/codeception/src/Codeception/Template/Unit.php? Gb? C;vendor/codeception/codeception/src/Codeception/Snapshot.phpMGbMT~Rvendor/codeception/codeception/src/Codeception/Exception/ModuleConfigException.phpGb4Kvendor/codeception/codeception/src/Codeception/Exception/ParseException.phpTGbTg¤Lvendor/codeception/codeception/src/Codeception/Exception/ElementNotFound.phpGbg&ݤLvendor/codeception/codeception/src/Codeception/Exception/RemoteException.phpGbI~Qvendor/codeception/codeception/src/Codeception/Exception/TestRuntimeException.phpaGbaݜLvendor/codeception/codeception/src/Codeception/Exception/ContentNotFound.phprGbr.秤Svendor/codeception/codeception/src/Codeception/Exception/ConfigurationException.php\Gb\LOvendor/codeception/codeception/src/Codeception/Exception/TestParseException.phpGbSűSvendor/codeception/codeception/src/Codeception/Exception/ModuleRequireException.phpGb1Lvendor/codeception/codeception/src/Codeception/Exception/ModuleException.phpGbJVvendor/codeception/codeception/src/Codeception/Exception/MalformedLocatorException.phpGb-mWvendor/codeception/codeception/src/Codeception/Exception/ConditionalAssertionFailed.php}Gb}vTvendor/codeception/codeception/src/Codeception/Exception/ModuleConflictException.phpGb7Ovendor/codeception/codeception/src/Codeception/Exception/ExtensionException.phpGb\+VOvendor/codeception/codeception/src/Codeception/Exception/InjectionException.phpXGbXBh?vendor/codeception/codeception/src/Codeception/InitTemplate.php(Gb(V$aIvendor/codeception/codeception/src/Codeception/Subscriber/AutoRebuild.phpGbzcMvendor/codeception/codeception/src/Codeception/Subscriber/ExtensionLoader.phpGbPEvendor/codeception/codeception/src/Codeception/Subscriber/Console.phpfWGbfWP¤Jvendor/codeception/codeception/src/Codeception/Subscriber/Dependencies.phpGbuL&Ivendor/codeception/codeception/src/Codeception/Subscriber/PrepareTest.phpMGbMxrcGvendor/codeception/codeception/src/Codeception/Subscriber/Bootstrap.phpGbZQvendor/codeception/codeception/src/Codeception/Subscriber/Shared/StaticEvents.phpGbSMvendor/codeception/codeception/src/Codeception/Subscriber/BeforeAfterTest.php Gb bJvendor/codeception/codeception/src/Codeception/Subscriber/ErrorHandler.phpGbRQvendor/codeception/codeception/src/Codeception/Subscriber/GracefulTermination.phpGb2VDvendor/codeception/codeception/src/Codeception/Subscriber/Module.phpl Gbl pFvendor/codeception/codeception/src/Codeception/Subscriber/FailFast.phpGbt8Ivendor/codeception/codeception/src/Codeception/CustomCommandInterface.phpGbgvendor/codeception/module-soap/src/Codeception/Module/SOAP.php8Gb86oHvendor/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.phpGbHEvendor/codeception/lib-innerbrowser/src/Codeception/Lib/Framework.phpGbFEvendor/codeception/lib-innerbrowser/src/Codeception/Util/HttpCode.phpGbnr)Vvendor/codeception/lib-innerbrowser/src/Codeception/Exception/ExternalUrlException.php[Gb[@Ť9vendor/phpdocumentor/reflection-docblock/src/DocBlock.phpGbo@<Ivendor/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.phpGb]F Dvendor/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php Gb JlEvendor/phpdocumentor/reflection-docblock/src/DocBlock/Description.phpiGbi9Gvendor/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.phpLGbL*dLvendor/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php Gb ^ Evendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.phpmGbmWEvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.phpGbFvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.php8Gb8ѤFvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.phpGbeDvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.phpGb"ѤDvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.phpAGbAP4GHvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.phpGb1DEvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.phpGbCvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.phpGbrmLvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.phpuGbuxOvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Strategy.phpGbjoSvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.phpGbF Fvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php+Gb+G9Bvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.phpGbCvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php>Gb>JFvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.phpDGbDܙZEvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.phpGb`Ivendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.phpGbCvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.phpGbGvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.phpjGbj{Evendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.phpGbNvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.phpPGbPLvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php9Gb9tLRvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Reference.phpGbuKvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.phpsGbsF`%CFvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php Gb <*Wvendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.phpGb]vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.phpIGbI߽䞤=vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tag.phpGbKxLvendor/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.phpGb)ŚDvendor/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.phpGbfd@vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php!Gb!h~o 7vendor/phpdocumentor/type-resolver/src/TypeResolver.php%Gb%~di8vendor/phpdocumentor/type-resolver/src/FqsenResolver.php3Gb3ʩ¤/vendor/phpdocumentor/type-resolver/src/Type.phpgGbgGF::vendor/phpdocumentor/type-resolver/src/Types/Callable_.phpGb%G{6vendor/phpdocumentor/type-resolver/src/Types/Self_.phpGb{Ĥ7vendor/phpdocumentor/type-resolver/src/Types/Mixed_.phpGb":vendor/phpdocumentor/type-resolver/src/Types/Iterable_.phpGb<9vendor/phpdocumentor/type-resolver/src/Types/Nullable.phpGb`6vendor/phpdocumentor/type-resolver/src/Types/Null_.phpGbJ7vendor/phpdocumentor/type-resolver/src/Types/Float_.phpGbW6vendor/phpdocumentor/type-resolver/src/Types/Void_.phpGb%2ۤ:vendor/phpdocumentor/type-resolver/src/Types/Resource_.phpGb-8vendor/phpdocumentor/type-resolver/src/Types/Boolean.phpGb7?vendor/phpdocumentor/type-resolver/src/Types/ContextFactory.php; Gb; `5vendor/phpdocumentor/type-resolver/src/Types/This.phpGbV58vendor/phpdocumentor/type-resolver/src/Types/Context.phpGbK7vendor/phpdocumentor/type-resolver/src/Types/Array_.phpGb8vendor/phpdocumentor/type-resolver/src/Types/Parent_.phpGbe8vendor/phpdocumentor/type-resolver/src/Types/String_.phpGbw.8vendor/phpdocumentor/type-resolver/src/Types/Static_.phpGbA58vendor/phpdocumentor/type-resolver/src/Types/Integer.phpGbt`98vendor/phpdocumentor/type-resolver/src/Types/Object_.phpGb 9vendor/phpdocumentor/type-resolver/src/Types/Compound.phpGb֟7vendor/phpdocumentor/type-resolver/src/Types/Scalar.phpGbD,64vendor/phpdocumentor/reflection-common/src/Fqsen.phpGbՙ7vendor/phpdocumentor/reflection-common/src/Location.phpGb@=vendor/phpdocumentor/reflection-common/src/ProjectFactory.phpGb{6vendor/phpdocumentor/reflection-common/src/Element.phpGb#3vendor/phpdocumentor/reflection-common/src/File.phpGb6ޤ6vendor/phpdocumentor/reflection-common/src/Project.phpnGbnj+vendor/padraic/phar-updater/src/Updater.php Gb vu>vendor/padraic/phar-updater/src/Strategy/StrategyInterface.php.Gb. c:8vendor/padraic/phar-updater/src/Strategy/ShaStrategy.phpcGbc;vendor/padraic/phar-updater/src/Strategy/GithubStrategy.php Gb 731vendor/padraic/phar-updater/src/VersionParser.php& Gb& 'pBvendor/padraic/phar-updater/src/Exception/HttpRequestException.phpoGboDAvendor/padraic/phar-updater/src/Exception/FilesystemException.phpnGbnBvendor/padraic/phar-updater/src/Exception/JsonParsingException.phpoGboZ?>vendor/padraic/phar-updater/src/Exception/RuntimeException.phpGbЋBvendor/padraic/phar-updater/src/Exception/NoSignatureException.phpoGbo"}<Fvendor/padraic/phar-updater/src/Exception/InvalidArgumentException.phpGbb"1@vendor/padraic/phar-updater/src/Exception/ExceptionInterface.phpXGbXcu,:vendor/padraic/humbug_get_contents/src/FileGetContents.php"Gb"_a4vendor/padraic/humbug_get_contents/src/functions.phpGbKg@3vendor/padraic/humbug_get_contents/src/function.phpGbn<vendor/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php.Gb.&cm?vendor/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php Gb E:vendor/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.phpGGbG¤Avendor/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.phpGbEZb@vendor/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.phpGbS o=vendor/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php`Gb`5lMCvendor/behat/gherkin/src/Behat/Gherkin/Loader/GherkinFileLoader.phpGbۤDvendor/behat/gherkin/src/Behat/Gherkin/Loader/AbstractFileLoader.phpGbRIvendor/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.phpeGbe7rREvendor/behat/gherkin/src/Behat/Gherkin/Loader/FileLoaderInterface.phpGb=Avendor/behat/gherkin/src/Behat/Gherkin/Loader/DirectoryLoader.phpGbܯG@vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.phpGb38vendor/behat/gherkin/src/Behat/Gherkin/Node/StepNode.phpGbIiAvendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioInterface.phpGbA9p;vendor/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.phpGb <vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.phpGb B<vendor/behat/gherkin/src/Behat/Gherkin/Node/PyStringNode.php?Gb?OFvendor/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.phpGb' ~=vendor/behat/gherkin/src/Behat/Gherkin/Node/NodeInterface.phpGbq!z>vendor/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.phpGb1Cvendor/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.phpGb%,Avendor/behat/gherkin/src/Behat/Gherkin/Node/ArgumentInterface.phpiGbi\Evendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioLikeInterface.phpGbxDvendor/behat/gherkin/src/Behat/Gherkin/Node/KeywordNodeInterface.phpGbn9vendor/behat/gherkin/src/Behat/Gherkin/Node/TableNode.phpGb";vendor/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.phpeGbe?;vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php Gb c|2vendor/behat/gherkin/src/Behat/Gherkin/Gherkin.php Gb  w<vendor/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.phpSGbSFAvendor/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.phpGb[ߤ>vendor/behat/gherkin/src/Behat/Gherkin/Filter/SimpleFilter.phpGb<Avendor/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php: Gb: ;vendor/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.phpt Gbt x<vendor/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.phpYGbYî?Hvendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php$Gb$Q=Ϥ=vendor/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.phpGb{6ݤ<vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.phpGbsHvendor/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.phpGbAvendor/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.phpGbsH?vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilter.phpeGbe Bvendor/behat/gherkin/src/Behat/Gherkin/Exception/NodeException.phpGbt>vendor/behat/gherkin/src/Behat/Gherkin/Exception/Exception.phpKGbKDvendor/behat/gherkin/src/Behat/Gherkin/Exception/ParserException.phpGbdKCvendor/behat/gherkin/src/Behat/Gherkin/Exception/LexerException.phpGbUCvendor/behat/gherkin/src/Behat/Gherkin/Exception/CacheException.phpGbJ񼩤0vendor/behat/gherkin/src/Behat/Gherkin/Lexer.php #Gb #R 1vendor/behat/gherkin/src/Behat/Gherkin/Parser.phpS1GbS1Dvendor/behat/gherkin/src/Behat/Gherkin/Keywords/CucumberKeywords.phpGbFAvendor/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.phpGbeGvendor/behat/gherkin/src/Behat/Gherkin/Keywords/CachedArrayKeywords.phpGbKPEvendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsInterface.phpZGbZ[DBvendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.phpGb*ä vendor/behat/gherkin/libpath.phpGbtrnǤvendor/behat/gherkin/i18n.phpGb|0Z*vendor/sebastian/exporter/src/Exporter.phpGbD /vendor/sebastian/global-state/src/Blacklist.phpGbn|2vendor/sebastian/global-state/src/CodeExporter.phpGb4R=/vendor/sebastian/global-state/src/Exception.phpSGbSC)6vendor/sebastian/global-state/src/RuntimeException.phpGb5~.vendor/sebastian/global-state/src/Restorer.php? Gb? n̦.vendor/sebastian/global-state/src/Snapshot.phpUGbU X$vendor/sebastian/diff/src/Differ.phpqGbqj#vendor/sebastian/diff/src/Chunk.php"Gb"0"vendor/sebastian/diff/src/Line.phpGbM9֤"vendor/sebastian/diff/src/Diff.phpGb}GUvendor/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.phpGbm:vendor/sebastian/diff/src/LCS/LongestCommonSubsequence.phpGb(UWvendor/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php Gb cC$vendor/sebastian/diff/src/Parser.phpGb6a.(vendor/sebastian/version/src/Version.phpuGbu*>4vendor/sebastian/recursion-context/src/Exception.phpXGbXN32vendor/sebastian/recursion-context/src/Context.phpRGbR^k_Cvendor/sebastian/recursion-context/src/InvalidArgumentException.phpGb>_F?vendor/sebastian/resource-operations/src/ResourceOperations.php6Gb6 7vendor/sebastian/resource-operations/build/generate.phpGb{K(4vendor/sebastian/object-enumerator/src/Exception.phpVGbVc5vendor/sebastian/object-enumerator/src/Enumerator.phpGbKCvendor/sebastian/object-enumerator/src/InvalidArgumentException.phpGb,vendor/sebastian/environment/src/Console.php*Gb*_e,vendor/sebastian/environment/src/Runtime.php1 Gb1 o;T+vendor/sebastian/comparator/src/Factory.php'Gb'KW3vendor/sebastian/comparator/src/ArrayComparator.php Gb kT.vendor/sebastian/comparator/src/Comparator.phpGb]9:6vendor/sebastian/comparator/src/DateTimeComparator.phpGbJ5vendor/sebastian/comparator/src/NumericComparator.phpLGbLCO2vendor/sebastian/comparator/src/TypeComparator.php Gb S5vendor/sebastian/comparator/src/ComparisonFailure.phpGbe8vendor/sebastian/comparator/src/MockObjectComparator.phpGbm44vendor/sebastian/comparator/src/ScalarComparator.phpbGbbں5vendor/sebastian/comparator/src/DOMNodeComparator.phpdGbd q^7vendor/sebastian/comparator/src/ExceptionComparator.phpGb`.cG4vendor/sebastian/comparator/src/DoubleComparator.php'Gb'04vendor/sebastian/comparator/src/ObjectComparator.phpxGbx%9<>vendor/sebastian/comparator/src/SplObjectStorageComparator.phpmGbmX#6vendor/sebastian/comparator/src/ResourceComparator.phpGb]8vendor/sebastian/code-unit-reverse-lookup/src/Wizard.phpZGbZ=vendor/paragonie/random_compat/lib/random_bytes_libsodium.phpGb$1vendor/paragonie/random_compat/lib/random_int.phpGb-vendor/paragonie/random_compat/lib/random.php Gb ]-5vendor/paragonie/random_compat/lib/error_polyfill.phpGba4:vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php(Gb(/Dvendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.phpGb}2vendor/paragonie/random_compat/lib/cast_to_int.phpGb6d>vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.phpGbvA8vendor/paragonie/random_compat/lib/byte_safe_strings.phppGbp36d?vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.phpGb1Gvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exchange/AMQPExchangeType.phpGbo5vendor/php-amqplib/php-amqplib/PhpAmqpLib/Package.phphGbh2 Dvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/SocketConstants.phpGbVĄߤ?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/BigInteger.php\Gb\?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/MiscHelper.phpCGbCB@vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/DebugHelper.php3Gb3r¤Jvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap080.php Gb P6ФEvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait091.phpGbUGEvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait080.php Gb lwIvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol080.phpMGbM~Jvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap091.phpGbvqFIvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol091.php5Gb54פ;vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Assert.php7Gb7qAvendor/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.phpGbn Kvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AbstractConnection.phpFGbFTHMvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPStreamConnection.phpGbY]!OWvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.phpjGbjPy Mvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSocketConnection.phpGbGvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnection.phpfGbfitwQvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySocketConnection.phpGbBJvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSSLConnection.phpGb4 cKvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.phpGb%Avendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AbstractClient.phpjGbjO<vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPArray.phplGblUۤ?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants080.phpq Gbq ԛ=vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPWriter.phpGb*qĤIvendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPAbstractCollection.php Gb (3̤<vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPTable.phpGbm;>vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.phpqGbq~Z<vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants.phpGbLy?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants091.php Gb F=¤@vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/AbstractIO.php Gb aϤ>vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/SocketIO.phpGbl|>>vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/StreamIO.phpGbnV=vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPReader.php Gb CVTvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPHeartbeatMissedException.phpuGbu渖Uvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionClosedException.phppGbph Svendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNotImplementedException.phpkGbkd4аKvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNoDataException.phpfGbf? ,ͤUvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPEmptyDeliveryTagException.phpmGbm֔Vvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php=Gb=_{Gvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOException.phpwGbwf5Mvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPDataReadException.phpeGbew068Rvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelClosedException.phpjGbj읦Tvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolChannelException.phpmGbmN!Pvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.php_Gb_<Qvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidFrameException.phpiGbiMKvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOWaitException.phpcGbcMWvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolConnectionException.phpsGbs*Jvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.phpGbJ_Evendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPException.phpGbQ5>Pvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfBoundsException.phpGbOvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfRangeException.phpGb|#ǤKvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPSocketException.phpcGbcbҤTvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidArgumentException.phpGb,DΤNvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPExceptionInterface.phpMGbM"WܤLvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.phpGbMfMvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolException.php Gb Lvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelException.php`Gb` Ovendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionException.phpcGbcLvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPRuntimeException.phpGbAEvendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php$Gb$Avendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.phpnWGbnWܙ 22)); if (isset($query)) { $path.= '?' . $query; } elseif (preg_match('/(\?|\?#)$/', $orig)) { $path.= '?'; } if (isset($fragment)) { $path.= '#' . $fragment; } elseif ($orig[strlen($orig) - 1] == '#') { $path.= '#'; } if (!isset($host)) { return false; } if (isset($this->context)) { $context = stream_context_get_params($this->context); if (isset($context['notification'])) { $this->notification = $context['notification']; } } if ($host[0] == '$') { $host = substr($host, 1); global ${$host}; if (($$host instanceof SFTP) === false) { return false; } $this->sftp = $$host; } else { if (isset($this->context)) { $context = stream_context_get_options($this->context); } if (isset($context[$scheme]['session'])) { $sftp = $context[$scheme]['session']; } if (isset($context[$scheme]['sftp'])) { $sftp = $context[$scheme]['sftp']; } if (isset($sftp) && $sftp instanceof SFTP) { $this->sftp = $sftp; return $path; } if (isset($context[$scheme]['username'])) { $user = $context[$scheme]['username']; } if (isset($context[$scheme]['password'])) { $pass = $context[$scheme]['password']; } if (isset($context[$scheme]['privkey']) && $context[$scheme]['privkey'] instanceof RSA) { $pass = $context[$scheme]['privkey']; } if (!isset($user) || !isset($pass)) { return false; } if (isset(self::$instances[$host][$port][$user][(string) $pass])) { $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; } else { $this->sftp = new SFTP($host, $port); $this->sftp->disableStatCache(); if (isset($this->notification) && is_callable($this->notification)) { call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); if (!$this->sftp->login($user, $pass)) { call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); return false; } call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); } else { if (!$this->sftp->login($user, $pass)) { return false; } } self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; } } return $path; } function _stream_open($path, $mode, $options, &$opened_path) { $path = $this->_parse_path($path); if ($path === false) { return false; } $this->path = $path; $this->size = $this->sftp->size($path); $this->mode = preg_replace('#[bt]$#', '', $mode); $this->eof = false; if ($this->size === false) { if ($this->mode[0] == 'r') { return false; } else { $this->sftp->touch($path); $this->size = 0; } } else { switch ($this->mode[0]) { case 'x': return false; case 'w': $this->sftp->truncate($path, 0); $this->size = 0; } } $this->pos = $this->mode[0] != 'a' ? 0 : $this->size; return true; } function _stream_read($count) { switch ($this->mode) { case 'w': case 'a': case 'x': case 'c': return false; } $result = $this->sftp->get($this->path, false, $this->pos, $count); if (isset($this->notification) && is_callable($this->notification)) { if ($result === false) { call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); return 0; } call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $this->size); } if (empty($result)) { $this->eof = true; return false; } $this->pos+= strlen($result); return $result; } function _stream_write($data) { switch ($this->mode) { case 'r': return false; } $result = $this->sftp->put($this->path, $data, SFTP::SOURCE_STRING, $this->pos); if (isset($this->notification) && is_callable($this->notification)) { if (!$result) { call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); return 0; } call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); } if ($result === false) { return false; } $this->pos+= strlen($data); if ($this->pos > $this->size) { $this->size = $this->pos; } $this->eof = false; return strlen($data); } function _stream_tell() { return $this->pos; } function _stream_eof() { return $this->eof; } function _stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: if ($offset < 0) { return false; } break; case SEEK_CUR: $offset+= $this->pos; break; case SEEK_END: $offset+= $this->size; } $this->pos = $offset; $this->eof = false; return true; } function _stream_metadata($path, $option, $var) { $path = $this->_parse_path($path); if ($path === false) { return false; } switch ($option) { case 1: $time = isset($var[0]) ? $var[0] : null; $atime = isset($var[1]) ? $var[1] : null; return $this->sftp->touch($path, $time, $atime); case 2: case 3: return false; case 4: return $this->sftp->chown($path, $var); case 5: return $this->sftp->chgrp($path, $var); case 6: return $this->sftp->chmod($path, $var) !== false; } } function _stream_cast($cast_as) { return $this->sftp->fsock; } function _stream_lock($operation) { return false; } function _rename($path_from, $path_to) { $path1 = parse_url($path_from); $path2 = parse_url($path_to); unset($path1['path'], $path2['path']); if ($path1 != $path2) { return false; } $path_from = $this->_parse_path($path_from); $path_to = parse_url($path_to); if ($path_from === false) { return false; } $path_to = $path_to['path']; if (!$this->sftp->rename($path_from, $path_to)) { if ($this->sftp->stat($path_to)) { return $this->sftp->delete($path_to, true) && $this->sftp->rename($path_from, $path_to); } return false; } return true; } function _dir_opendir($path, $options) { $path = $this->_parse_path($path); if ($path === false) { return false; } $this->pos = 0; $this->entries = $this->sftp->nlist($path); return $this->entries !== false; } function _dir_readdir() { if (isset($this->entries[$this->pos])) { return $this->entries[$this->pos++]; } return false; } function _dir_rewinddir() { $this->pos = 0; return true; } function _dir_closedir() { return true; } function _mkdir($path, $mode, $options) { $path = $this->_parse_path($path); if ($path === false) { return false; } return $this->sftp->mkdir($path, $mode, $options & STREAM_MKDIR_RECURSIVE); } function _rmdir($path, $options) { $path = $this->_parse_path($path); if ($path === false) { return false; } return $this->sftp->rmdir($path); } function _stream_flush() { return true; } function _stream_stat() { $results = $this->sftp->stat($this->path); if ($results === false) { return false; } return $results; } function _unlink($path) { $path = $this->_parse_path($path); if ($path === false) { return false; } return $this->sftp->delete($path, false); } function _url_stat($path, $flags) { $path = $this->_parse_path($path); if ($path === false) { return false; } $results = $flags & STREAM_URL_STAT_LINK ? $this->sftp->lstat($path) : $this->sftp->stat($path); if ($results === false) { return false; } return $results; } function _stream_truncate($new_size) { if (!$this->sftp->truncate($this->path, $new_size)) { return false; } $this->eof = false; $this->size = $new_size; return true; } function _stream_set_option($option, $arg1, $arg2) { return false; } function _stream_close() { } function __call($name, $arguments) { if (defined('NET_SFTP_STREAM_LOGGING')) { echo $name . '('; $last = count($arguments) - 1; foreach ($arguments as $i => $argument) { var_export($argument); if ($i != $last) { echo ','; } } echo ")\r\n"; } $name = '_' . $name; if (!method_exists($this, $name)) { return false; } return call_user_func_array(array($this, $name), $arguments); } } message_numbers = array( 1 => 'NET_SSH2_MSG_DISCONNECT', 2 => 'NET_SSH2_MSG_IGNORE', 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', 4 => 'NET_SSH2_MSG_DEBUG', 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', 20 => 'NET_SSH2_MSG_KEXINIT', 21 => 'NET_SSH2_MSG_NEWKEYS', 30 => 'NET_SSH2_MSG_KEXDH_INIT', 31 => 'NET_SSH2_MSG_KEXDH_REPLY', 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', 94 => 'NET_SSH2_MSG_CHANNEL_DATA', 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', 96 => 'NET_SSH2_MSG_CHANNEL_EOF', 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' ); $this->disconnect_reasons = array( 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', 4 => 'NET_SSH2_DISCONNECT_RESERVED', 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' ); $this->channel_open_failure_reasons = array( 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' ); $this->terminal_modes = array( 0 => 'NET_SSH2_TTY_OP_END' ); $this->channel_extended_data_type_codes = array( 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' ); $this->_define_array( $this->message_numbers, $this->disconnect_reasons, $this->channel_open_failure_reasons, $this->terminal_modes, $this->channel_extended_data_type_codes, array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'), array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'), array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'), array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'), array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY') ); if (is_resource($host)) { $this->fsock = $host; return; } if (is_string($host)) { $this->host = $host; $this->port = $port; $this->timeout = $timeout; } } function setCryptoEngine($engine) { $this->crypto_engine = $engine; } function sendIdentificationStringFirst() { $this->send_id_string_first = true; } function sendIdentificationStringLast() { $this->send_id_string_first = false; } function sendKEXINITFirst() { $this->send_kex_first = true; } function sendKEXINITLast() { $this->send_kex_first = false; } function _connect() { if ($this->bitmap & self::MASK_CONSTRUCTOR) { return false; } $this->bitmap |= self::MASK_CONSTRUCTOR; $this->curTimeout = $this->timeout; $this->last_packet = microtime(true); if (!is_resource($this->fsock)) { $start = microtime(true); $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout); if (!$this->fsock) { $host = $this->host . ':' . $this->port; user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); return false; } $elapsed = microtime(true) - $start; if ($this->curTimeout) { $this->curTimeout-= $elapsed; if ($this->curTimeout < 0) { $this->is_timeout = true; return false; } } } $this->identifier = $this->_generate_identifier(); if ($this->send_id_string_first) { fputs($this->fsock, $this->identifier . "\r\n"); } $data = ''; while (!feof($this->fsock) && !preg_match('#(.*)^(SSH-(\d\.\d+).*)#ms', $data, $matches)) { $line = ''; while (true) { if ($this->curTimeout) { if ($this->curTimeout < 0) { $this->is_timeout = true; return false; } $read = array($this->fsock); $write = $except = null; $start = microtime(true); $sec = (int) floor($this->curTimeout); $usec = (int) (1000000 * ($this->curTimeout - $sec)); if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { $this->is_timeout = true; return false; } $elapsed = microtime(true) - $start; $this->curTimeout-= $elapsed; } $temp = stream_get_line($this->fsock, 255, "\n"); if (strlen($temp) == 255) { continue; } if ($temp === false) { return false; } $line.= "$temp\n"; break; } $data.= $line; } if (feof($this->fsock)) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } $extra = $matches[1]; if (defined('NET_SSH2_LOGGING')) { $this->_append_log('<-', $matches[0]); $this->_append_log('->', $this->identifier . "\r\n"); } $this->server_identifier = trim($temp, "\r\n"); if (strlen($extra)) { $this->errors[] = $data; } if (version_compare($matches[3], '1.99', '<')) { user_error("Cannot connect to SSH $matches[3] servers"); return false; } if (!$this->send_id_string_first) { fputs($this->fsock, $this->identifier . "\r\n"); } if (!$this->send_kex_first) { $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { user_error('Expected SSH_MSG_KEXINIT'); return false; } if (!$this->_key_exchange($response)) { return false; } } if ($this->send_kex_first && !$this->_key_exchange()) { return false; } $this->bitmap|= self::MASK_CONNECTED; return true; } function _generate_identifier() { $identifier = 'SSH-2.0-phpseclib_2.0'; $ext = array(); if (function_exists('sodium_crypto_box_publickey_from_secretkey')) { $ext[] = 'libsodium'; } if (extension_loaded('openssl')) { $ext[] = 'openssl'; } elseif (extension_loaded('mcrypt')) { $ext[] = 'mcrypt'; } if (extension_loaded('gmp')) { $ext[] = 'gmp'; } elseif (extension_loaded('bcmath')) { $ext[] = 'bcmath'; } if (!empty($ext)) { $identifier .= ' (' . implode(', ', $ext) . ')'; } return $identifier; } function _key_exchange($kexinit_payload_server = false) { $preferred = $this->preferred; $send_kex = true; $kex_algorithms = isset($preferred['kex']) ? $preferred['kex'] : $this->getSupportedKEXAlgorithms(); $server_host_key_algorithms = isset($preferred['hostkey']) ? $preferred['hostkey'] : $this->getSupportedHostKeyAlgorithms(); $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ? $preferred['server_to_client']['crypt'] : $this->getSupportedEncryptionAlgorithms(); $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ? $preferred['client_to_server']['crypt'] : $this->getSupportedEncryptionAlgorithms(); $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ? $preferred['server_to_client']['mac'] : $this->getSupportedMACAlgorithms(); $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ? $preferred['client_to_server']['mac'] : $this->getSupportedMACAlgorithms(); $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ? $preferred['server_to_client']['comp'] : $this->getSupportedCompressionAlgorithms(); $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ? $preferred['client_to_server']['comp'] : $this->getSupportedCompressionAlgorithms(); switch (true) { case $this->server_identifier == 'SSH-2.0-SSHD': case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK': if (!isset($preferred['server_to_client']['mac'])) { $s2c_mac_algorithms = array_values(array_diff( $s2c_mac_algorithms, array('hmac-sha1-96', 'hmac-md5-96') )); } if (!isset($preferred['client_to_server']['mac'])) { $c2s_mac_algorithms = array_values(array_diff( $c2s_mac_algorithms, array('hmac-sha1-96', 'hmac-md5-96') )); } } $str_kex_algorithms = implode(',', $kex_algorithms); $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms); $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms); $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms); $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms); $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms); $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms); $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms); $client_cookie = Random::string(16); $kexinit_payload_client = pack( 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms, strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server), $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client, strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client), $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server, strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '', 0, 0 ); if ($kexinit_payload_server === false) { if (!$this->_send_binary_packet($kexinit_payload_client)) { return false; } $kexinit_payload_server = $this->_get_binary_packet(); if ($kexinit_payload_server === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) { user_error('Expected SSH_MSG_KEXINIT'); return false; } $send_kex = false; } $response = $kexinit_payload_server; $this->_string_shift($response, 1); $server_cookie = $this->_string_shift($response, 16); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); if (!strlen($response)) { return false; } extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); $first_kex_packet_follows = $first_kex_packet_follows != 0; if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) { return false; } $decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client); $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); if ($decryptKeyLength === null) { user_error('No compatible server to client encryption algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server); $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); if ($encryptKeyLength === null) { user_error('No compatible client to server encryption algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); if ($kex_algorithm === false) { user_error('No compatible key exchange algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); if ($server_host_key_algorithm === false) { user_error('No compatible server host key algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); if ($mac_algorithm_in === false) { user_error('No compatible server to client message authentication algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $compression_map = array( 'none' => self::NET_SSH2_COMPRESSION_NONE, 'zlib' => self::NET_SSH2_COMPRESSION_ZLIB, 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH ); $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); if ($compression_algorithm_out === false) { user_error('No compatible client to server compression algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $this->compress = $compression_map[$compression_algorithm_out]; $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); if ($compression_algorithm_in === false) { user_error('No compatible server to client compression algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $this->decompress = $compression_map[$compression_algorithm_in]; $exchange_hash_rfc4419 = ''; if ($kex_algorithm === 'curve25519-sha256@libssh.org') { $x = Random::string(32); $eBytes = sodium_crypto_box_publickey_from_secretkey($x); $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT'; $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY'; $kexHash = new Hash('sha256'); } else { if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) { $dh_group_sizes_packed = pack( 'NNN', $this->kex_dh_group_size_min, $this->kex_dh_group_size_preferred, $this->kex_dh_group_size_max ); $packet = pack( 'Ca*', NET_SSH2_MSG_KEXDH_GEX_REQUEST, $dh_group_sizes_packed ); if (!$this->_send_binary_packet($packet)) { return false; } $this->_updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'); $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) { user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP'); return false; } $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP'); if (strlen($response) < 4) { return false; } extract(unpack('NprimeLength', $this->_string_shift($response, 4))); $primeBytes = $this->_string_shift($response, $primeLength); $prime = new BigInteger($primeBytes, -256); if (strlen($response) < 4) { return false; } extract(unpack('NgLength', $this->_string_shift($response, 4))); $gBytes = $this->_string_shift($response, $gLength); $g = new BigInteger($gBytes, -256); $exchange_hash_rfc4419 = pack( 'a*Na*Na*', $dh_group_sizes_packed, $primeLength, $primeBytes, $gLength, $gBytes ); $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT'; $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY'; } else { switch ($kex_algorithm) { case 'diffie-hellman-group1-sha1': $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF'; break; case 'diffie-hellman-group14-sha1': $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF'; break; } $g = new BigInteger(2); $prime = new BigInteger($prime, 16); $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT'; $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY'; } switch ($kex_algorithm) { case 'diffie-hellman-group-exchange-sha256': $kexHash = new Hash('sha256'); break; default: $kexHash = new Hash('sha1'); } $one = new BigInteger(1); $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength)); $max = $one->bitwise_leftShift(16 * $keyLength); $max = $max->subtract($one); $x = $one->random($one, $max); $e = $g->modPow($x, $prime); $eBytes = $e->toBytes(true); } $data = pack('CNa*', constant($clientKexInitMessage), strlen($eBytes), $eBytes); if (!$this->_send_binary_packet($data)) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } switch ($clientKexInitMessage) { case 'NET_SSH2_MSG_KEX_ECDH_INIT': $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT'); break; case 'NET_SSH2_MSG_KEXDH_GEX_INIT': $this->_updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT'); } $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != constant($serverKexReplyMessage)) { user_error("Expected $serverKexReplyMessage"); return false; } switch ($serverKexReplyMessage) { case 'NET_SSH2_MSG_KEX_ECDH_REPLY': $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY'); break; case 'NET_SSH2_MSG_KEXDH_GEX_REPLY': $this->_updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY'); } if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $fBytes = $this->_string_shift($response, $temp['length']); if (strlen($response) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->signature = $this->_string_shift($response, $temp['length']); if (strlen($this->signature) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); $this->signature_format = $this->_string_shift($this->signature, $temp['length']); if ($kex_algorithm === 'curve25519-sha256@libssh.org') { if (strlen($fBytes) !== 32) { user_error('Received curve25519 public key of invalid length.'); return false; } $key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes), 256); if (extension_loaded('sodium') || extension_loaded('libsodium')) { sodium_memzero($x); } } else { $f = new BigInteger($fBytes, -256); $key = $f->modPow($x, $prime); } $keyBytes = $key->toBytes(true); $this->exchange_hash = pack( 'Na*Na*Na*Na*Na*a*Na*Na*Na*', strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier, strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server), $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, $exchange_hash_rfc4419, strlen($eBytes), $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes ); $this->exchange_hash = $kexHash->hash($this->exchange_hash); if ($this->session_id === false) { $this->session_id = $this->exchange_hash; } switch ($server_host_key_algorithm) { case 'ssh-dss': $expected_key_format = 'ssh-dss'; break; default: $expected_key_format = 'ssh-rsa'; } if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { switch (true) { case $this->signature_format == $server_host_key_algorithm: case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': case $this->signature_format != 'ssh-rsa': user_error('Server Host Key Algorithm Mismatch'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } } $packet = pack( 'C', NET_SSH2_MSG_NEWKEYS ); if (!$this->_send_binary_packet($packet)) { return false; } $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_NEWKEYS) { user_error('Expected SSH_MSG_NEWKEYS'); return false; } $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); if ($this->encrypt) { if ($this->crypto_engine) { $this->encrypt->setPreferredEngine($this->crypto_engine); } if ($this->encrypt->block_size) { $this->encrypt_block_size = $this->encrypt->block_size; } $this->encrypt->enableContinuousBuffer(); $this->encrypt->disablePadding(); if ($this->encrypt->getBlockLength()) { $this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3; } $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); while ($this->encrypt_block_size > strlen($iv)) { $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); while ($encryptKeyLength > strlen($key)) { $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); $this->encrypt->name = $decrypt; } $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt); if ($this->decrypt) { if ($this->crypto_engine) { $this->decrypt->setPreferredEngine($this->crypto_engine); } if ($this->decrypt->block_size) { $this->decrypt_block_size = $this->decrypt->block_size; } $this->decrypt->enableContinuousBuffer(); $this->decrypt->disablePadding(); if ($this->decrypt->getBlockLength()) { $this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3; } $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); while ($this->decrypt_block_size > strlen($iv)) { $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); } $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); while ($decryptKeyLength > strlen($key)) { $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); $this->decrypt->name = $decrypt; } if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { $this->encrypt->encrypt(str_repeat("\0", 1536)); } if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { $this->decrypt->decrypt(str_repeat("\0", 1536)); } $mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); if ($mac_algorithm_out === false) { user_error('No compatible client to server message authentication algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $createKeyLength = 0; switch ($mac_algorithm_out) { case 'hmac-sha2-256': $this->hmac_create = new Hash('sha256'); $createKeyLength = 32; break; case 'hmac-sha1': $this->hmac_create = new Hash('sha1'); $createKeyLength = 20; break; case 'hmac-sha1-96': $this->hmac_create = new Hash('sha1-96'); $createKeyLength = 20; break; case 'hmac-md5': $this->hmac_create = new Hash('md5'); $createKeyLength = 16; break; case 'hmac-md5-96': $this->hmac_create = new Hash('md5-96'); $createKeyLength = 16; } $this->hmac_create->name = $mac_algorithm_out; $checkKeyLength = 0; $this->hmac_size = 0; switch ($mac_algorithm_in) { case 'hmac-sha2-256': $this->hmac_check = new Hash('sha256'); $checkKeyLength = 32; $this->hmac_size = 32; break; case 'hmac-sha1': $this->hmac_check = new Hash('sha1'); $checkKeyLength = 20; $this->hmac_size = 20; break; case 'hmac-sha1-96': $this->hmac_check = new Hash('sha1-96'); $checkKeyLength = 20; $this->hmac_size = 12; break; case 'hmac-md5': $this->hmac_check = new Hash('md5'); $checkKeyLength = 16; $this->hmac_size = 16; break; case 'hmac-md5-96': $this->hmac_check = new Hash('md5-96'); $checkKeyLength = 16; $this->hmac_size = 12; } $this->hmac_check->name = $mac_algorithm_in; $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_create->setKey(substr($key, 0, $createKeyLength)); $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id); while ($checkKeyLength > strlen($key)) { $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); $this->regenerate_compression_context = $this->regenerate_decompression_context = true; return true; } function _encryption_algorithm_to_key_size($algorithm) { if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) { return 16; } switch ($algorithm) { case 'none': return 0; case 'aes128-cbc': case 'aes128-ctr': case 'arcfour': case 'arcfour128': case 'blowfish-cbc': case 'blowfish-ctr': case 'twofish128-cbc': case 'twofish128-ctr': return 16; case '3des-cbc': case '3des-ctr': case 'aes192-cbc': case 'aes192-ctr': case 'twofish192-cbc': case 'twofish192-ctr': return 24; case 'aes256-cbc': case 'aes256-ctr': case 'arcfour256': case 'twofish-cbc': case 'twofish256-cbc': case 'twofish256-ctr': return 32; } return null; } function _encryption_algorithm_to_crypt_instance($algorithm) { switch ($algorithm) { case '3des-cbc': return new TripleDES(); case '3des-ctr': return new TripleDES(Base::MODE_CTR); case 'aes256-cbc': case 'aes192-cbc': case 'aes128-cbc': return new Rijndael(); case 'aes256-ctr': case 'aes192-ctr': case 'aes128-ctr': return new Rijndael(Base::MODE_CTR); case 'blowfish-cbc': return new Blowfish(); case 'blowfish-ctr': return new Blowfish(Base::MODE_CTR); case 'twofish128-cbc': case 'twofish192-cbc': case 'twofish256-cbc': case 'twofish-cbc': return new Twofish(); case 'twofish128-ctr': case 'twofish192-ctr': case 'twofish256-ctr': return new Twofish(Base::MODE_CTR); case 'arcfour': case 'arcfour128': case 'arcfour256': return new RC4(); } return null; } function _bad_algorithm_candidate($algorithm) { switch ($algorithm) { case 'arcfour256': case 'aes192-ctr': case 'aes256-ctr': return true; } return false; } function login($username) { $args = func_get_args(); $this->auth[] = $args; if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { if ($this->_login($username)) { return true; } if (count($args) == 1) { return false; } } return call_user_func_array(array(&$this, '_login'), $args); } function _login($username) { if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { if (!$this->_connect()) { return false; } } $args = array_slice(func_get_args(), 1); if (empty($args)) { return $this->_login_helper($username); } while (count($args)) { if (!$this->auth_methods_to_continue || !$this->smartMFA) { $newargs = $args; $args = array(); } else { $newargs = array(); foreach ($this->auth_methods_to_continue as $method) { switch ($method) { case 'publickey': foreach ($args as $key => $arg) { if (is_object($arg)) { $newargs[] = $arg; unset($args[$key]); break; } } break; case 'keyboard-interactive': $hasArray = $hasString = false; foreach ($args as $arg) { if ($hasArray || is_array($arg)) { $hasArray = true; break; } if ($hasString || is_string($arg)) { $hasString = true; break; } } if ($hasArray && $hasString) { foreach ($args as $key => $arg) { if (is_array($arg)) { $newargs[] = $arg; break 2; } } } case 'password': foreach ($args as $key => $arg) { $newargs[] = $arg; unset($args[$key]); break; } } } } if (!count($newargs)) { return false; } foreach ($newargs as $arg) { if ($this->_login_helper($username, $arg)) { return true; } } } return false; } function _login_helper($username, $password = null) { if (!($this->bitmap & self::MASK_CONNECTED)) { return false; } if (!($this->bitmap & self::MASK_LOGIN_REQ)) { $packet = pack( 'CNa*', NET_SSH2_MSG_SERVICE_REQUEST, strlen('ssh-userauth'), 'ssh-userauth' ); if (!$this->_send_binary_packet($packet)) { return false; } $response = $this->_get_binary_packet(); if ($response === false) { if ($this->retry_connect) { $this->retry_connect = false; if (!$this->_connect()) { return false; } return $this->_login_helper($username, $password); } $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { user_error('Expected SSH_MSG_SERVICE_ACCEPT'); return false; } $this->bitmap |= self::MASK_LOGIN_REQ; } if (strlen($this->last_interactive_response)) { return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password); } if ($password instanceof RSA) { return $this->_privatekey_login($username, $password); } elseif ($password instanceof Agent) { return $this->_ssh_agent_login($username, $password); } if (is_array($password)) { if ($this->_keyboard_interactive_login($username, $password)) { $this->bitmap |= self::MASK_LOGIN; return true; } return false; } if (!isset($password)) { $packet = pack( 'CNa*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', strlen('none'), 'none' ); if (!$this->_send_binary_packet($packet)) { return false; } $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; case NET_SSH2_MSG_USERAUTH_FAILURE: extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); default: return false; } } $packet = pack( 'CNa*Na*Na*CNa*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', strlen('password'), 'password', 0, strlen($password), $password ); if (!defined('NET_SSH2_LOGGING')) { $logged = null; } else { $logged = pack( 'CNa*Na*Na*CNa*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen('username'), 'username', strlen('ssh-connection'), 'ssh-connection', strlen('password'), 'password', 0, strlen('password'), 'password' ); } if (!$this->_send_binary_packet($packet, $logged)) { return false; } $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'); if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length); return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); case NET_SSH2_MSG_USERAUTH_FAILURE: if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $auth_methods = explode(',', $this->_string_shift($response, $length)); $this->auth_methods_to_continue = $auth_methods; if (!strlen($response)) { return false; } extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); $partial_success = $partial_success != 0; if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { if ($this->_keyboard_interactive_login($username, $password)) { $this->bitmap |= self::MASK_LOGIN; return true; } return false; } return false; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; } return false; } function _keyboard_interactive_login($username, $password) { $packet = pack( 'CNa*Na*Na*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, '' ); if (!$this->_send_binary_packet($packet)) { return false; } return $this->_keyboard_interactive_process($password); } function _keyboard_interactive_process() { $responses = func_get_args(); if (strlen($this->last_interactive_response)) { $response = $this->last_interactive_response; } else { $orig = $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); if (strlen($response) < 4) { return false; } extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); for ($i = 0; $i < count($responses); $i++) { if (is_array($responses[$i])) { foreach ($responses[$i] as $key => $value) { $this->keyboard_requests_responses[$key] = $value; } unset($responses[$i]); } } $responses = array_values($responses); if (isset($this->keyboard_requests_responses)) { for ($i = 0; $i < $num_prompts; $i++) { if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $prompt = $this->_string_shift($response, $length); foreach ($this->keyboard_requests_responses as $key => $value) { if (substr($prompt, 0, strlen($key)) == $key) { $responses[] = $value; break; } } } } if (strlen($this->last_interactive_response)) { $this->last_interactive_response = ''; } else { $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST'); } if (!count($responses) && $num_prompts) { $this->last_interactive_response = $orig; return false; } $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses)); for ($i = 0; $i < count($responses); $i++) { $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]); $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer'); } if (!$this->_send_binary_packet($packet, $logged)) { return false; } $this->_updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'); return $this->_keyboard_interactive_process(); case NET_SSH2_MSG_USERAUTH_SUCCESS: return true; case NET_SSH2_MSG_USERAUTH_FAILURE: extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; } return false; } function _ssh_agent_login($username, $agent) { $this->agent = $agent; $keys = $agent->requestIdentities(); foreach ($keys as $key) { if ($this->_privatekey_login($username, $key)) { return true; } } return false; } function _privatekey_login($username, $privatekey) { $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW); if ($publickey === false) { return false; } $publickey = array( 'e' => $publickey['e']->toBytes(true), 'n' => $publickey['n']->toBytes(true) ); $publickey = pack( 'Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey['e']), $publickey['e'], strlen($publickey['n']), $publickey['n'] ); switch ($this->signature_format) { case 'rsa-sha2-512': $hash = 'sha512'; $signatureType = 'rsa-sha2-512'; break; case 'rsa-sha2-256': $hash = 'sha256'; $signatureType = 'rsa-sha2-256'; break; default: $hash = 'sha1'; $signatureType = 'ssh-rsa'; } $part1 = pack( 'CNa*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection', strlen('publickey'), 'publickey' ); $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey); $packet = $part1 . chr(0) . $part2; if (!$this->_send_binary_packet($packet)) { return false; } $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: if (strlen($response) < 4) { return false; } extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; return false; case NET_SSH2_MSG_USERAUTH_PK_OK: $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); break; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; default: user_error('Unexpected response to publickey authentication pt 1'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $packet = $part1 . chr(1) . $part2; $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1); $privatekey->setHash($hash); $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet)); $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature); $packet.= pack('Na*', strlen($signature), $signature); if (!$this->_send_binary_packet($packet)) { return false; } $response = $this->_get_binary_packet(); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; } user_error('Unexpected response to publickey authentication pt 2'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } function setTimeout($timeout) { $this->timeout = $this->curTimeout = $timeout; } function setKeepAlive($interval) { $this->keepAlive = $interval; } function getStdError() { return $this->stdErrorLog; } function exec($command, $callback = null) { $this->curTimeout = $this->timeout; $this->is_timeout = false; $this->stdErrorLog = ''; if (!$this->isAuthenticated()) { return false; } if ($this->in_request_pty_exec) { user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); return false; } $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size; $packet_size = 0x4000; $packet = pack( 'CNa*N3', NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', self::CHANNEL_EXEC, $this->window_size_server_to_client[self::CHANNEL_EXEC], $packet_size ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; $response = $this->_get_channel_packet(self::CHANNEL_EXEC); if ($response === false) { return false; } if ($this->request_pty === true) { $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); $packet = pack( 'CNNa*CNa*N5a*', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_EXEC], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100', $this->windowColumns, $this->windowRows, 0, 0, strlen($terminal_modes), $terminal_modes ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) { user_error('Unable to request pseudo-terminal'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $this->in_request_pty_exec = true; } $packet = pack( 'CNNa*CNa*', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_EXEC], strlen('exec'), 'exec', 1, strlen($command), $command ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; $response = $this->_get_channel_packet(self::CHANNEL_EXEC); if ($response === false) { return false; } $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; if ($callback === false || $this->in_request_pty_exec) { return true; } $output = ''; while (true) { $temp = $this->_get_channel_packet(self::CHANNEL_EXEC); switch (true) { case $temp === true: return is_callable($callback) ? true : $output; case $temp === false: return false; default: if (is_callable($callback)) { if (call_user_func($callback, $temp) === true) { $this->_close_channel(self::CHANNEL_EXEC); return true; } } else { $output.= $temp; } } } } function _initShell() { if ($this->in_request_pty_exec === true) { return true; } $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; $packet_size = 0x4000; $packet = pack( 'CNa*N3', NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', self::CHANNEL_SHELL, $this->window_size_server_to_client[self::CHANNEL_SHELL], $packet_size ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; $response = $this->_get_channel_packet(self::CHANNEL_SHELL); if ($response === false) { return false; } $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); $packet = pack( 'CNNa*CNa*N5a*', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100', $this->windowColumns, $this->windowRows, 0, 0, strlen($terminal_modes), $terminal_modes ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) { user_error('Unable to request pseudo-terminal'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $packet = pack( 'CNNa*C', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SHELL], strlen('shell'), 'shell', 1 ); if (!$this->_send_binary_packet($packet)) { return false; } $response = $this->_get_channel_packet(self::CHANNEL_SHELL); if ($response === false) { return false; } $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; $this->bitmap |= self::MASK_SHELL; return true; } function _get_interactive_channel() { switch (true) { case $this->in_subsystem: return self::CHANNEL_SUBSYSTEM; case $this->in_request_pty_exec: return self::CHANNEL_EXEC; default: return self::CHANNEL_SHELL; } } function _get_open_channel() { $channel = self::CHANNEL_EXEC; do { if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) { return $channel; } } while ($channel++ < self::CHANNEL_SUBSYSTEM); return false; } function read($expect = '', $mode = self::READ_SIMPLE) { $this->curTimeout = $this->timeout; $this->is_timeout = false; if (!$this->isAuthenticated()) { user_error('Operation disallowed prior to login()'); return false; } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { user_error('Unable to initiate an interactive shell session'); return false; } $channel = $this->_get_interactive_channel(); if ($mode == self::READ_NEXT) { return $this->_get_channel_packet($channel); } $match = $expect; while (true) { if ($mode == self::READ_REGEX) { preg_match($expect, substr($this->interactiveBuffer, -1024), $matches); $match = isset($matches[0]) ? $matches[0] : ''; } $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; if ($pos !== false) { return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); } $response = $this->_get_channel_packet($channel); if (is_bool($response)) { $this->in_request_pty_exec = false; return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false; } $this->interactiveBuffer.= $response; } } function write($cmd) { if (!$this->isAuthenticated()) { user_error('Operation disallowed prior to login()'); return false; } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { user_error('Unable to initiate an interactive shell session'); return false; } return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); } function startSubsystem($subsystem) { $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; $packet = pack( 'CNa*N3', NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', self::CHANNEL_SUBSYSTEM, $this->window_size, 0x4000 ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); if ($response === false) { return false; } $packet = pack( 'CNNa*CNa*', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL_SUBSYSTEM], strlen('subsystem'), 'subsystem', 1, strlen($subsystem), $subsystem ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM); if ($response === false) { return false; } $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; $this->bitmap |= self::MASK_SHELL; $this->in_subsystem = true; return true; } function stopSubsystem() { $this->in_subsystem = false; $this->_close_channel(self::CHANNEL_SUBSYSTEM); return true; } function reset() { $this->_close_channel($this->_get_interactive_channel()); } function isTimeout() { return $this->is_timeout; } function disconnect() { $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { fclose($this->realtime_log_file); } } function __destruct() { $this->disconnect(); } function isConnected() { return (bool) ($this->bitmap & self::MASK_CONNECTED); } function isAuthenticated() { return (bool) ($this->bitmap & self::MASK_LOGIN); } function ping() { if (!$this->isAuthenticated()) { if (!empty($this->auth)) { return $this->_reconnect(); } return false; } $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size; $packet_size = 0x4000; $packet = pack( 'CNa*N3', NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', self::CHANNEL_KEEP_ALIVE, $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE], $packet_size ); if (!@$this->_send_binary_packet($packet)) { return $this->_reconnect(); } $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN; $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE); if ($response !== false) { $this->_close_channel(self::CHANNEL_KEEP_ALIVE); return true; } return $this->_reconnect(); } function _reconnect() { $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); $this->retry_connect = true; if (!$this->_connect()) { return false; } foreach ($this->auth as $auth) { $result = call_user_func_array(array(&$this, 'login'), $auth); } return $result; } function _reset_connection($reason) { $this->_disconnect($reason); $this->decrypt = $this->encrypt = false; $this->decrypt_block_size = $this->encrypt_block_size = 8; $this->hmac_check = $this->hmac_create = false; $this->hmac_size = false; $this->session_id = false; $this->retry_connect = true; $this->get_seq_no = $this->send_seq_no = 0; } function _get_binary_packet($skip_channel_filter = false) { if ($skip_channel_filter) { $read = array($this->fsock); $write = $except = null; if (!$this->curTimeout) { if ($this->keepAlive <= 0) { @stream_select($read, $write, $except, null); } else { if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); return $this->_get_binary_packet(true); } } } else { if ($this->curTimeout < 0) { $this->is_timeout = true; return true; } $read = array($this->fsock); $write = $except = null; $start = microtime(true); if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); $elapsed = microtime(true) - $start; $this->curTimeout-= $elapsed; return $this->_get_binary_packet(true); } $elapsed = microtime(true) - $start; $this->curTimeout-= $elapsed; } $sec = (int)floor($this->curTimeout); $usec = (int)(1000000 * ($this->curTimeout - $sec)); if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { $this->is_timeout = true; return true; } $elapsed = microtime(true) - $start; $this->curTimeout-= $elapsed; } } if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; user_error('Connection closed (by server) prematurely ' . $elapsed . 's'); return false; } $start = microtime(true); $raw = stream_get_contents($this->fsock, $this->decrypt_block_size); if (!strlen($raw)) { user_error('No data received from server'); return false; } if ($this->decrypt !== false) { $raw = $this->decrypt->decrypt($raw); } if ($raw === false) { user_error('Unable to decrypt content'); return false; } if (strlen($raw) < 5) { return false; } extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); $remaining_length = $packet_length + 4 - $this->decrypt_block_size; if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & SSH2::MASK_LOGIN)) { $this->bad_key_size_fix = true; $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); return false; } user_error('Invalid size'); return false; } $buffer = ''; while ($remaining_length > 0) { $temp = stream_get_contents($this->fsock, $remaining_length); if ($temp === false || feof($this->fsock)) { $this->bitmap = 0; user_error('Error reading from socket'); return false; } $buffer.= $temp; $remaining_length-= strlen($temp); } $stop = microtime(true); if (strlen($buffer)) { $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer; } $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1); $padding = $this->_string_shift($raw, $padding_length); if ($this->hmac_check !== false) { $hmac = stream_get_contents($this->fsock, $this->hmac_size); if ($hmac === false || strlen($hmac) != $this->hmac_size) { $this->bitmap = 0; user_error('Error reading socket'); return false; } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { user_error('Invalid HMAC'); return false; } } switch ($this->decompress) { case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: if (!$this->isAuthenticated()) { break; } case self::NET_SSH2_COMPRESSION_ZLIB: if ($this->regenerate_decompression_context) { $this->regenerate_decompression_context = false; $cmf = ord($payload[0]); $cm = $cmf & 0x0F; if ($cm != 8) { user_error("Only CM = 8 ('deflate') is supported ($cm)"); } $cinfo = ($cmf & 0xF0) >> 4; if ($cinfo > 7) { user_error("CINFO above 7 is not allowed ($cinfo)"); } $windowSize = 1 << ($cinfo + 8); $flg = ord($payload[1]); if ((($cmf << 8) | $flg) % 31) { user_error('fcheck failed'); } $fdict = boolval($flg & 0x20); $flevel = ($flg & 0xC0) >> 6; $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8)); $payload = substr($payload, 2); } if ($this->decompress_context) { $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH); } } $this->get_seq_no++; if (defined('NET_SSH2_LOGGING')) { $current = microtime(true); $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; $message_number = '<- ' . $message_number . ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; $this->_append_log($message_number, $payload); $this->last_packet = $current; } return $this->_filter($payload, $skip_channel_filter); } function _filter($payload, $skip_channel_filter) { switch (ord($payload[0])) { case NET_SSH2_MSG_DISCONNECT: $this->_string_shift($payload, 1); if (strlen($payload) < 8) { return false; } extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length); $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_DEBUG: $this->_string_shift($payload, 2); if (strlen($payload) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length); $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_UNIMPLEMENTED: return false; case NET_SSH2_MSG_KEXINIT: if ($this->session_id !== false) { $this->send_kex_first = false; if (!$this->_key_exchange($payload)) { $this->bitmap = 0; return false; } $payload = $this->_get_binary_packet($skip_channel_filter); } } if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { $this->_string_shift($payload, 1); if (strlen($payload) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->banner_message = $this->_string_shift($payload, $length); $payload = $this->_get_binary_packet(); } if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { if (is_bool($payload)) { return $payload; } switch (ord($payload[0])) { case NET_SSH2_MSG_CHANNEL_REQUEST: if (strlen($payload) == 31) { extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { if (ord(substr($payload, 9 + $length))) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); } $payload = $this->_get_binary_packet($skip_channel_filter); } } break; case NET_SSH2_MSG_CHANNEL_DATA: case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: case NET_SSH2_MSG_CHANNEL_CLOSE: case NET_SSH2_MSG_CHANNEL_EOF: if (!$skip_channel_filter && !empty($this->server_channels)) { $this->binary_packet_buffer = $payload; $this->_get_channel_packet(true); $payload = $this->_get_binary_packet(); } break; case NET_SSH2_MSG_GLOBAL_REQUEST: if (strlen($payload) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length); if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) { return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_CHANNEL_OPEN: $this->_string_shift($payload, 1); if (strlen($payload) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $data = $this->_string_shift($payload, $length); if (strlen($payload) < 4) { return false; } extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); switch ($data) { case 'auth-agent': case 'auth-agent@openssh.com': if (isset($this->agent)) { $new_channel = self::CHANNEL_AGENT_FORWARD; if (strlen($payload) < 8) { return false; } extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4))); extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4))); $this->packet_size_client_to_server[$new_channel] = $remote_window_size; $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size; $this->window_size_client_to_server[$new_channel] = $this->window_size; $packet_size = 0x4000; $packet = pack( 'CN4', NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, $server_channel, $new_channel, $packet_size, $packet_size ); $this->server_channels[$new_channel] = $server_channel; $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION; if (!$this->_send_binary_packet($packet)) { return false; } } break; default: $packet = pack( 'CN3a*Na*', NET_SSH2_MSG_REQUEST_FAILURE, $server_channel, NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, 0, '', 0, '' ); if (!$this->_send_binary_packet($packet)) { return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } } $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: $this->_string_shift($payload, 1); if (strlen($payload) < 8) { return false; } extract(unpack('Nchannel', $this->_string_shift($payload, 4))); extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); $this->window_size_client_to_server[$channel]+= $window_size; $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter); } } return $payload; } function enableQuietMode() { $this->quiet_mode = true; } function disableQuietMode() { $this->quiet_mode = false; } function isQuietModeEnabled() { return $this->quiet_mode; } function enablePTY() { $this->request_pty = true; } function disablePTY() { if ($this->in_request_pty_exec) { $this->_close_channel(self::CHANNEL_EXEC); $this->in_request_pty_exec = false; } $this->request_pty = false; } function isPTYEnabled() { return $this->request_pty; } function _get_channel_packet($client_channel, $skip_extended = false) { if (!empty($this->channel_buffers[$client_channel])) { switch ($this->channel_status[$client_channel]) { case NET_SSH2_MSG_CHANNEL_REQUEST: foreach ($this->channel_buffers[$client_channel] as $i => $packet) { switch (ord($packet[0])) { case NET_SSH2_MSG_CHANNEL_SUCCESS: case NET_SSH2_MSG_CHANNEL_FAILURE: unset($this->channel_buffers[$client_channel][$i]); return substr($packet, 1); } } break; default: return substr(array_shift($this->channel_buffers[$client_channel]), 1); } } while (true) { if ($this->binary_packet_buffer !== false) { $response = $this->binary_packet_buffer; $this->binary_packet_buffer = false; } else { $response = $this->_get_binary_packet(true); if ($response === true && $this->is_timeout) { if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { $this->_close_channel($client_channel); } return true; } if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); return false; } } if ($client_channel == -1 && $response === true) { return true; } if (!strlen($response)) { return false; } extract(unpack('Ctype', $this->_string_shift($response, 1))); if (strlen($response) < 4) { return false; } if ($type == NET_SSH2_MSG_CHANNEL_OPEN) { extract(unpack('Nlength', $this->_string_shift($response, 4))); } else { extract(unpack('Nchannel', $this->_string_shift($response, 4))); } if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) { $this->window_size_server_to_client[$channel]-= strlen($response); if ($this->window_size_server_to_client[$channel] < 0) { $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize); if (!$this->_send_binary_packet($packet)) { return false; } $this->window_size_server_to_client[$channel]+= $this->window_resize; } switch ($type) { case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: if (strlen($response) < 8) { return false; } extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); $data = $this->_string_shift($response, $length); $this->stdErrorLog.= $data; if ($skip_extended || $this->quiet_mode) { continue 2; } if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { return $data; } $this->channel_buffers[$channel][] = chr($type) . $data; continue 2; case NET_SSH2_MSG_CHANNEL_REQUEST: if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) { continue 2; } if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $value = $this->_string_shift($response, $length); switch ($value) { case 'exit-signal': $this->_string_shift($response, 1); if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); $this->_string_shift($response, 1); if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); if ($length) { $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); } $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; continue 3; case 'exit-status': if (strlen($response) < 5) { return false; } extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); $this->exit_status = $exit_status; continue 3; default: continue 3; } } switch ($this->channel_status[$channel]) { case NET_SSH2_MSG_CHANNEL_OPEN: switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: if (strlen($response) < 4) { return false; } extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); $this->server_channels[$channel] = $server_channel; if (strlen($response) < 4) { return false; } extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); if ($window_size < 0) { $window_size&= 0x7FFFFFFF; $window_size+= 0x80000000; } $this->window_size_client_to_server[$channel] = $window_size; if (strlen($response) < 4) { return false; } $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); $this->_on_channel_open(); return $result; case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: user_error('Unable to open channel'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); default: if ($client_channel == $channel) { user_error('Unexpected response to open request'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } return $this->_get_channel_packet($client_channel, $skip_extended); } break; case NET_SSH2_MSG_CHANNEL_REQUEST: switch ($type) { case NET_SSH2_MSG_CHANNEL_SUCCESS: return true; case NET_SSH2_MSG_CHANNEL_FAILURE: return false; case NET_SSH2_MSG_CHANNEL_DATA: if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $data = $this->_string_shift($response, $length); $this->channel_buffers[$channel][] = chr($type) . $data; return $this->_get_channel_packet($client_channel, $skip_extended); default: user_error('Unable to fulfill channel request'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } case NET_SSH2_MSG_CHANNEL_CLOSE: return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); } } switch ($type) { case NET_SSH2_MSG_CHANNEL_DATA: if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $data = $this->_string_shift($response, $length); if ($channel == self::CHANNEL_AGENT_FORWARD) { $agent_response = $this->agent->_forward_data($data); if (!is_bool($agent_response)) { $this->_send_channel_packet($channel, $agent_response); } break; } if ($client_channel == $channel) { return $data; } $this->channel_buffers[$channel][] = chr($type) . $data; break; case NET_SSH2_MSG_CHANNEL_CLOSE: $this->curTimeout = 5; if ($this->bitmap & self::MASK_SHELL) { $this->bitmap&= ~self::MASK_SHELL; } if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); } $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; if ($client_channel == $channel) { return true; } case NET_SSH2_MSG_CHANNEL_EOF: break; default: user_error("Error reading channel data ($type)"); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } } } function _send_binary_packet($data, $logged = null) { if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; user_error('Connection closed prematurely'); return false; } if (!isset($logged)) { $logged = $data; } switch ($this->compress) { case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: if (!$this->isAuthenticated()) { break; } case self::NET_SSH2_COMPRESSION_ZLIB: if (!$this->regenerate_compression_context) { $header = ''; } else { $this->regenerate_compression_context = false; $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15)); $header = "\x78\x9C"; } if ($this->compress_context) { $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH); } } $packet_length = strlen($data) + 9; $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size; $padding_length = $packet_length - strlen($data) - 5; $padding = Random::string($padding_length); $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding); $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : ''; $this->send_seq_no++; if ($this->encrypt !== false) { $packet = $this->encrypt->encrypt($packet); } $packet.= $hmac; $start = microtime(true); $result = strlen($packet) == @fputs($this->fsock, $packet); $stop = microtime(true); if (defined('NET_SSH2_LOGGING')) { $current = microtime(true); $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; $message_number = '-> ' . $message_number . ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; $this->_append_log($message_number, $logged); $this->last_packet = $current; } return $result; } function _append_log($message_number, $message) { if (strlen($message_number) > 2) { $this->_string_shift($message); } switch (NET_SSH2_LOGGING) { case self::LOG_SIMPLE: $this->message_number_log[] = $message_number; break; case self::LOG_COMPLEX: $this->message_number_log[] = $message_number; $this->log_size+= strlen($message); $this->message_log[] = $message; while ($this->log_size > self::LOG_MAX_SIZE) { $this->log_size-= strlen(array_shift($this->message_log)); array_shift($this->message_number_log); } break; case self::LOG_REALTIME: switch (PHP_SAPI) { case 'cli': $start = $stop = "\r\n"; break; default: $start = '
';
$stop = '
'; } echo $start . $this->_format_log(array($message), array($message_number)) . $stop; @flush(); @ob_flush(); break; case self::LOG_REALTIME_FILE: if (!isset($this->realtime_log_file)) { $filename = self::LOG_REALTIME_FILENAME; $fp = fopen($filename, 'w'); $this->realtime_log_file = $fp; } if (!is_resource($this->realtime_log_file)) { break; } $entry = $this->_format_log(array($message), array($message_number)); if ($this->realtime_log_wrap) { $temp = "<<< START >>>\r\n"; $entry.= $temp; fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); } $this->realtime_log_size+= strlen($entry); if ($this->realtime_log_size > self::LOG_MAX_SIZE) { fseek($this->realtime_log_file, 0); $this->realtime_log_size = strlen($entry); $this->realtime_log_wrap = true; } fputs($this->realtime_log_file, $entry); } } function _send_channel_packet($client_channel, $data) { while (strlen($data)) { if (!$this->window_size_client_to_server[$client_channel]) { $this->bitmap^= self::MASK_WINDOW_ADJUST; $this->_get_channel_packet(-1); $this->bitmap^= self::MASK_WINDOW_ADJUST; } $max_size = min( $this->packet_size_client_to_server[$client_channel], $this->window_size_client_to_server[$client_channel] ); $temp = $this->_string_shift($data, $max_size); $packet = pack( 'CN2a*', NET_SSH2_MSG_CHANNEL_DATA, $this->server_channels[$client_channel], strlen($temp), $temp ); $this->window_size_client_to_server[$client_channel]-= strlen($temp); if (!$this->_send_binary_packet($packet)) { return false; } } return true; } function _close_channel($client_channel, $want_reply = false) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); if (!$want_reply) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); } $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; $this->curTimeout = 5; while (!is_bool($this->_get_channel_packet($client_channel))) { } if ($this->is_timeout) { $this->disconnect(); } if ($want_reply) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); } if ($this->bitmap & self::MASK_SHELL) { $this->bitmap&= ~self::MASK_SHELL; } } function _disconnect($reason) { if ($this->bitmap & self::MASK_CONNECTED) { $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, ''); $this->_send_binary_packet($data); } $this->bitmap = 0; if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') { fclose($this->fsock); } return false; } function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } function _define_array() { $args = func_get_args(); foreach ($args as $arg) { foreach ($arg as $key => $value) { if (!defined($value)) { define($value, $key); } else { break 2; } } } } function getLog() { if (!defined('NET_SSH2_LOGGING')) { return false; } switch (NET_SSH2_LOGGING) { case self::LOG_SIMPLE: return $this->message_number_log; case self::LOG_COMPLEX: $log = $this->_format_log($this->message_log, $this->message_number_log); return PHP_SAPI == 'cli' ? $log : '
' . $log . '
'; default: return false; } } function _format_log($message_log, $message_number_log) { $output = ''; for ($i = 0; $i < count($message_log); $i++) { $output.= $message_number_log[$i] . "\r\n"; $current_log = $message_log[$i]; $j = 0; do { if (strlen($current_log)) { $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; } $fragment = $this->_string_shift($current_log, $this->log_short_width); $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; $j++; } while (strlen($current_log)); $output.= "\r\n"; } return $output; } function _format_log_helper($matches) { return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); } function _on_channel_open() { if (isset($this->agent)) { $this->agent->_on_channel_open($this); } } function _array_intersect_first($array1, $array2) { foreach ($array1 as $value) { if (in_array($value, $array2)) { return $value; } } return false; } function getErrors() { return $this->errors; } function getLastError() { $count = count($this->errors); if ($count > 0) { return $this->errors[$count - 1]; } } function getServerIdentification() { $this->_connect(); return $this->server_identifier; } function getKexAlgorithms() { $this->_connect(); return $this->kex_algorithms; } function getServerHostKeyAlgorithms() { $this->_connect(); return $this->server_host_key_algorithms; } function getEncryptionAlgorithmsClient2Server() { $this->_connect(); return $this->encryption_algorithms_client_to_server; } function getEncryptionAlgorithmsServer2Client() { $this->_connect(); return $this->encryption_algorithms_server_to_client; } function getMACAlgorithmsClient2Server() { $this->_connect(); return $this->mac_algorithms_client_to_server; } function getMACAlgorithmsServer2Client() { $this->_connect(); return $this->mac_algorithms_server_to_client; } function getCompressionAlgorithmsClient2Server() { $this->_connect(); return $this->compression_algorithms_client_to_server; } function getCompressionAlgorithmsServer2Client() { $this->_connect(); return $this->compression_algorithms_server_to_client; } function getLanguagesServer2Client() { $this->_connect(); return $this->languages_server_to_client; } function getLanguagesClient2Server() { $this->_connect(); return $this->languages_client_to_server; } function getServerAlgorithms() { $this->_connect(); return array( 'kex' => $this->kex_algorithms, 'hostkey' => $this->server_host_key_algorithms, 'client_to_server' => array( 'crypt' => $this->encryption_algorithms_client_to_server, 'mac' => $this->mac_algorithms_client_to_server, 'comp' => $this->compression_algorithms_client_to_server, 'lang' => $this->languages_client_to_server ), 'server_to_client' => array( 'crypt' => $this->encryption_algorithms_server_to_client, 'mac' => $this->mac_algorithms_server_to_client, 'comp' => $this->compression_algorithms_server_to_client, 'lang' => $this->languages_server_to_client ) ); } function getSupportedKEXAlgorithms() { $kex_algorithms = array( 'curve25519-sha256@libssh.org', 'diffie-hellman-group-exchange-sha256', 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group14-sha1', 'diffie-hellman-group1-sha1', ); if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) { $kex_algorithms = array_diff( $kex_algorithms, array('curve25519-sha256@libssh.org') ); } return $kex_algorithms; } function getSupportedHostKeyAlgorithms() { return array( 'rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa', 'ssh-dss' ); } function getSupportedEncryptionAlgorithms() { $algos = array( 'arcfour256', 'arcfour128', 'aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc', 'blowfish-ctr', 'blowfish-cbc', '3des-ctr', '3des-cbc', ); if ($this->crypto_engine) { $engines = array($this->crypto_engine); } else { $engines = array( Base::ENGINE_OPENSSL, Base::ENGINE_MCRYPT, Base::ENGINE_INTERNAL ); } $ciphers = array(); foreach ($engines as $engine) { foreach ($algos as $algo) { $obj = $this->_encryption_algorithm_to_crypt_instance($algo); if ($obj instanceof Rijndael) { $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo)); } switch ($algo) { case 'arcfour128': case 'arcfour256': if ($engine != Base::ENGINE_INTERNAL) { continue 2; } } if ($obj->isValidEngine($engine)) { $algos = array_diff($algos, array($algo)); $ciphers[] = $algo; } } } return $ciphers; } function getSupportedMACAlgorithms() { return array( 'hmac-sha2-256', 'hmac-sha1-96', 'hmac-sha1', 'hmac-md5-96', 'hmac-md5', ); } function getSupportedCompressionAlgorithms() { $algos = array('none'); if (function_exists('deflate_init')) { $algos[] = 'zlib@openssh.com'; $algos[] = 'zlib'; } return $algos; } function getAlgorithmsNegotiated() { $this->_connect(); $compression_map = array( self::NET_SSH2_COMPRESSION_NONE => 'none', self::NET_SSH2_COMPRESSION_ZLIB => 'zlib', self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com' ); return array( 'kex' => $this->kex_algorithm, 'hostkey' => $this->signature_format, 'client_to_server' => array( 'crypt' => $this->encrypt->name, 'mac' => $this->hmac_create->name, 'comp' => $compression_map[$this->compress], ), 'server_to_client' => array( 'crypt' => $this->decrypt->name, 'mac' => $this->hmac_check->name, 'comp' => $compression_map[$this->decompress], ) ); } function setPreferredAlgorithms($methods) { $preferred = $methods; if (isset($preferred['kex'])) { $preferred['kex'] = array_intersect( $preferred['kex'], $this->getSupportedKEXAlgorithms() ); } if (isset($preferred['hostkey'])) { $preferred['hostkey'] = array_intersect( $preferred['hostkey'], $this->getSupportedHostKeyAlgorithms() ); } $keys = array('client_to_server', 'server_to_client'); foreach ($keys as $key) { if (isset($preferred[$key])) { $a = &$preferred[$key]; if (isset($a['crypt'])) { $a['crypt'] = array_intersect( $a['crypt'], $this->getSupportedEncryptionAlgorithms() ); } if (isset($a['comp'])) { $a['comp'] = array_intersect( $a['comp'], $this->getSupportedCompressionAlgorithms() ); } if (isset($a['mac'])) { $a['mac'] = array_intersect( $a['mac'], $this->getSupportedMACAlgorithms() ); } } } $keys = array( 'kex', 'hostkey', 'client_to_server/crypt', 'client_to_server/comp', 'client_to_server/mac', 'server_to_client/crypt', 'server_to_client/comp', 'server_to_client/mac', ); foreach ($keys as $key) { $p = $preferred; $m = $methods; $subkeys = explode('/', $key); foreach ($subkeys as $subkey) { if (!isset($p[$subkey])) { continue 2; } $p = $p[$subkey]; $m = $m[$subkey]; } if (count($p) != count($m)) { $diff = array_diff($m, $p); $msg = count($diff) == 1 ? ' is not a supported algorithm' : ' are not supported algorithms'; user_error(implode(', ', $diff) . $msg); return false; } } $this->preferred = $preferred; } function getBannerMessage() { return $this->banner_message; } function getServerPublicHostKey() { if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { if (!$this->_connect()) { return false; } } $signature = $this->signature; $server_public_host_key = $this->server_public_host_key; if (strlen($server_public_host_key) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4))); $this->_string_shift($server_public_host_key, $length); if ($this->signature_validated) { return $this->bitmap ? $this->signature_format . ' ' . base64_encode($this->server_public_host_key) : false; } $this->signature_validated = true; switch ($this->signature_format) { case 'ssh-dss': $zero = new BigInteger(); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); $temp = unpack('Nlength', $this->_string_shift($signature, 4)); if ($temp['length'] != 40) { user_error('Invalid signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $r = new BigInteger($this->_string_shift($signature, 20), 256); $s = new BigInteger($this->_string_shift($signature, 20), 256); switch (true) { case $r->equals($zero): case $r->compare($q) >= 0: case $s->equals($zero): case $s->compare($q) >= 0: user_error('Invalid signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $w = $s->modInverse($q); $u1 = $w->multiply(new BigInteger(sha1($this->exchange_hash), 16)); list(, $u1) = $u1->divide($q); $u2 = $w->multiply($r); list(, $u2) = $u2->divide($q); $g = $g->modPow($u1, $p); $y = $y->modPow($u2, $p); $v = $g->multiply($y); list(, $v) = $v->divide($p); list(, $v) = $v->divide($q); if (!$v->equals($r)) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; case 'ssh-rsa': case 'rsa-sha2-256': case 'rsa-sha2-512': if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); if (strlen($server_public_host_key) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $rawN = $this->_string_shift($server_public_host_key, $temp['length']); $n = new BigInteger($rawN, -256); $nLength = strlen(ltrim($rawN, "\0")); if (strlen($signature) < 4) { return false; } $temp = unpack('Nlength', $this->_string_shift($signature, 4)); $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256); if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) { user_error('Invalid signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $s = $s->modPow($e, $n); $s = $s->toBytes(); switch ($this->signature_format) { case 'rsa-sha2-512': $hash = 'sha512'; break; case 'rsa-sha2-256': $hash = 'sha256'; break; default: $hash = 'sha1'; } $hashObj = new Hash($hash); switch ($this->signature_format) { case 'rsa-sha2-512': $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash)); break; case 'rsa-sha2-256': $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash)); break; default: $hash = 'sha1'; $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash)); } $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h; if ($s != $h) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; default: user_error('Unsupported signature format'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } return $this->signature_format . ' ' . base64_encode($this->server_public_host_key); } function getExitStatus() { if (is_null($this->exit_status)) { return false; } return $this->exit_status; } function getWindowColumns() { return $this->windowColumns; } function getWindowRows() { return $this->windowRows; } function setWindowColumns($value) { $this->windowColumns = $value; } function setWindowRows($value) { $this->windowRows = $value; } function setWindowSize($columns = 80, $rows = 24) { $this->windowColumns = $columns; $this->windowRows = $rows; } function _updateLogHistory($old, $new) { if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { $this->message_number_log[count($this->message_number_log) - 1] = str_replace( $old, $new, $this->message_number_log[count($this->message_number_log) - 1] ); } } function getAuthMethodsToContinue() { return $this->auth_methods_to_continue; } function enableSmartMFA() { $this->smartMFA = true; } function disableSmartMFA() { $this->smartMFA = false; } } max_sftp_packet = 1 << 15; $this->packet_types = array( 1 => 'NET_SFTP_INIT', 2 => 'NET_SFTP_VERSION', 3 => 'NET_SFTP_OPEN', 4 => 'NET_SFTP_CLOSE', 5 => 'NET_SFTP_READ', 6 => 'NET_SFTP_WRITE', 7 => 'NET_SFTP_LSTAT', 9 => 'NET_SFTP_SETSTAT', 10 => 'NET_SFTP_FSETSTAT', 11 => 'NET_SFTP_OPENDIR', 12 => 'NET_SFTP_READDIR', 13 => 'NET_SFTP_REMOVE', 14 => 'NET_SFTP_MKDIR', 15 => 'NET_SFTP_RMDIR', 16 => 'NET_SFTP_REALPATH', 17 => 'NET_SFTP_STAT', 18 => 'NET_SFTP_RENAME', 19 => 'NET_SFTP_READLINK', 20 => 'NET_SFTP_SYMLINK', 21 => 'NET_SFTP_LINK', 101=> 'NET_SFTP_STATUS', 102=> 'NET_SFTP_HANDLE', 103=> 'NET_SFTP_DATA', 104=> 'NET_SFTP_NAME', 105=> 'NET_SFTP_ATTRS', 200=> 'NET_SFTP_EXTENDED' ); $this->status_codes = array( 0 => 'NET_SFTP_STATUS_OK', 1 => 'NET_SFTP_STATUS_EOF', 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', 4 => 'NET_SFTP_STATUS_FAILURE', 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', 13 => 'NET_SFTP_STATUS_NO_MEDIA', 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', 21 => 'NET_SFTP_STATUS_LINK_LOOP', 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', 27 => 'NET_SFTP_STATUS_DELETE_PENDING', 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', 29 => 'NET_SFTP_STATUS_OWNER_INVALID', 30 => 'NET_SFTP_STATUS_GROUP_INVALID', 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' ); $this->attributes = array( 0x00000001 => 'NET_SFTP_ATTR_SIZE', 0x00000002 => 'NET_SFTP_ATTR_UIDGID', 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', 0x00000040 => 'NET_SFTP_ATTR_ACL', 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', 0x00000200 => 'NET_SFTP_ATTR_BITS', 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', 0x00008000 => 'NET_SFTP_ATTR_CTIME', (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED' ); $this->open_flags = array( 0x00000001 => 'NET_SFTP_OPEN_READ', 0x00000002 => 'NET_SFTP_OPEN_WRITE', 0x00000004 => 'NET_SFTP_OPEN_APPEND', 0x00000008 => 'NET_SFTP_OPEN_CREATE', 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', 0x00000020 => 'NET_SFTP_OPEN_EXCL', 0x00000040 => 'NET_SFTP_OPEN_TEXT' ); $this->open_flags5 = array( 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', ); $this->file_types = array( 1 => 'NET_SFTP_TYPE_REGULAR', 2 => 'NET_SFTP_TYPE_DIRECTORY', 3 => 'NET_SFTP_TYPE_SYMLINK', 4 => 'NET_SFTP_TYPE_SPECIAL', 5 => 'NET_SFTP_TYPE_UNKNOWN', 6 => 'NET_SFTP_TYPE_SOCKET', 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', 9 => 'NET_SFTP_TYPE_FIFO' ); $this->_define_array( $this->packet_types, $this->status_codes, $this->attributes, $this->open_flags, $this->open_flags5, $this->file_types ); if (!defined('NET_SFTP_QUEUE_SIZE')) { define('NET_SFTP_QUEUE_SIZE', 32); } if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) { define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024); } } function _precheck() { if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; } if ($this->pwd === false) { return $this->_init_sftp_connection(); } return true; } function _partial_init_sftp_connection() { $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; $packet = pack( 'CNa*N3', NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', self::CHANNEL, $this->window_size, 0x4000 ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; $response = $this->_get_channel_packet(self::CHANNEL, true); if ($response === false) { return false; } elseif ($response === true && $this->isTimeout()) { return false; } $packet = pack( 'CNNa*CNa*', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL], strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp' ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; $response = $this->_get_channel_packet(self::CHANNEL, true); if ($response === false) { $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . "exec sftp-server"; $packet = pack( 'CNNa*CNa*', NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[self::CHANNEL], strlen('exec'), 'exec', 1, strlen($command), $command ); if (!$this->_send_binary_packet($packet)) { return false; } $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; $response = $this->_get_channel_packet(self::CHANNEL, true); if ($response === false) { return false; } } elseif ($response === true && $this->isTimeout()) { return false; } $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_VERSION) { user_error('Expected SSH_FXP_VERSION'); return false; } $this->use_request_id = true; if (strlen($response) < 4) { return false; } extract(unpack('Nversion', $this->_string_shift($response, 4))); $this->defaultVersion = $version; while (!empty($response)) { if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $value = $this->_string_shift($response, $length); $this->extensions[$key] = $value; } $this->partial_init = true; return true; } function _init_sftp_connection() { if (!$this->partial_init && !$this->_partial_init_sftp_connection()) { return false; } $this->version = $this->defaultVersion; if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) { $versions = explode(',', $this->extensions['versions']); $supported = array(6, 5, 4); if ($this->preferredVersion) { $supported = array_diff($supported, array($this->preferredVersion)); array_unshift($supported, $this->preferredVersion); } foreach ($supported as $ver) { if (in_array($ver, $versions)) { if ($ver === $this->version) { break; } $this->version = (int) $ver; $packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver); if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); return false; } break; } } } if ($this->version < 2 || $this->version > 6) { return false; } $this->pwd = $this->_realpath('.'); $this->_update_stat_cache($this->pwd, array()); return true; } function disableStatCache() { $this->use_stat_cache = false; } function enableStatCache() { $this->use_stat_cache = true; } function clearStatCache() { $this->stat_cache = array(); } function enablePathCanonicalization() { $this->canonicalize_paths = true; } function disablePathCanonicalization() { $this->canonicalize_paths = false; } function enableArbitraryLengthPackets() { $this->allow_arbitrary_length_packets = true; } function disableArbitraryLengthPackets() { $this->allow_arbitrary_length_packets = false; } function pwd() { if (!$this->_precheck()) { return false; } return $this->pwd; } function _logError($response, $status = -1) { if ($status == -1) { if (strlen($response) < 4) { return; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); } $error = $this->status_codes[$status]; if ($this->version > 2 || strlen($response) < 4) { extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); } else { $this->sftp_errors[] = $error; } } function realpath($path) { if (!$this->_precheck()) { return false; } return $this->_realpath($path); } function _realpath($path) { if (!$this->canonicalize_paths) { return $path; } if ($this->pwd === false) { if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: $this->_string_shift($response, 4); if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); return $this->_string_shift($response, $length); case NET_SFTP_STATUS: $this->_logError($response); return false; default: user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); return false; } } if (!strlen($path) || $path[0] != '/') { $path = $this->pwd . '/' . $path; } $path = explode('/', $path); $new = array(); foreach ($path as $dir) { if (!strlen($dir)) { continue; } switch ($dir) { case '..': array_pop($new); case '.': break; default: $new[] = $dir; } } return '/' . implode('/', $new); } function chdir($dir) { if (!$this->_precheck()) { return false; } if ($dir === '') { $dir = './'; } elseif ($dir[strlen($dir) - 1] != '/') { $dir.= '/'; } $dir = $this->_realpath($dir); if ($this->use_stat_cache && is_array($this->_query_stat_cache($dir))) { $this->pwd = $dir; return true; } if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: $this->_logError($response); return false; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); return false; } if (!$this->_close_handle($handle)) { return false; } $this->_update_stat_cache($dir, array()); $this->pwd = $dir; return true; } function nlist($dir = '.', $recursive = false) { return $this->_nlist_helper($dir, $recursive, ''); } function _nlist_helper($dir, $recursive, $relativeDir) { $files = $this->_list($dir, false); if (!$recursive || $files === false) { return $files; } $result = array(); foreach ($files as $value) { if ($value == '.' || $value == '..') { if ($relativeDir == '') { $result[] = $value; } continue; } if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) { $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); $temp = is_array($temp) ? $temp : array(); $result = array_merge($result, $temp); } else { $result[] = $relativeDir . $value; } } return $result; } function rawlist($dir = '.', $recursive = false) { $files = $this->_list($dir, true); if (!$recursive || $files === false) { return $files; } static $depth = 0; foreach ($files as $key => $value) { if ($depth != 0 && $key == '..') { unset($files[$key]); continue; } $is_directory = false; if ($key != '.' && $key != '..') { if ($this->use_stat_cache) { $is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key))); } else { $stat = $this->lstat($dir . '/' . $key); $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY; } } if ($is_directory) { $depth++; $files[$key] = $this->rawlist($dir . '/' . $key, true); $depth--; } else { $files[$key] = (object) $value; } } return $files; } function _list($dir, $raw = true) { if (!$this->_precheck()) { return false; } $dir = $this->_realpath($dir . '/'); if ($dir === false) { return false; } if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: $this->_logError($response); return false; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); return false; } $this->_update_stat_cache($dir, array()); $contents = array(); while (true) { if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: if (strlen($response) < 4) { return false; } extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $shortname = $this->_string_shift($response, $length); if ($this->version < 4) { if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $longname = $this->_string_shift($response, $length); } $attributes = $this->_parseAttributes($response); if (!isset($attributes['type']) && $this->version < 4) { $fileType = $this->_parseLongname($longname); if ($fileType) { $attributes['type'] = $fileType; } } $contents[$shortname] = $attributes + array('filename' => $shortname); if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { $this->_update_stat_cache($dir . '/' . $shortname, array()); } else { if ($shortname == '..') { $temp = $this->_realpath($dir . '/..') . '/.'; } else { $temp = $dir . '/' . $shortname; } $this->_update_stat_cache($temp, (object) array('lstat' => $attributes)); } } break; case NET_SFTP_STATUS: if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_EOF) { $this->_logError($response, $status); return false; } break 2; default: user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); return false; } } if (!$this->_close_handle($handle)) { return false; } if (count($this->sortOptions)) { uasort($contents, array(&$this, '_comparator')); } return $raw ? $contents : array_map('strval', array_keys($contents)); } function _comparator($a, $b) { switch (true) { case $a['filename'] === '.' || $b['filename'] === '.': if ($a['filename'] === $b['filename']) { return 0; } return $a['filename'] === '.' ? -1 : 1; case $a['filename'] === '..' || $b['filename'] === '..': if ($a['filename'] === $b['filename']) { return 0; } return $a['filename'] === '..' ? -1 : 1; case isset($a['type']) && $a['type'] === NET_SFTP_TYPE_DIRECTORY: if (!isset($b['type'])) { return 1; } if ($b['type'] !== $a['type']) { return -1; } break; case isset($b['type']) && $b['type'] === NET_SFTP_TYPE_DIRECTORY: return 1; } foreach ($this->sortOptions as $sort => $order) { if (!isset($a[$sort]) || !isset($b[$sort])) { if (isset($a[$sort])) { return -1; } if (isset($b[$sort])) { return 1; } return 0; } switch ($sort) { case 'filename': $result = strcasecmp($a['filename'], $b['filename']); if ($result) { return $order === SORT_DESC ? -$result : $result; } break; case 'permissions': case 'mode': $a[$sort]&= 07777; $b[$sort]&= 07777; default: if ($a[$sort] === $b[$sort]) { break; } return $order === SORT_ASC ? $a[$sort] - $b[$sort] : $b[$sort] - $a[$sort]; } } } function setListOrder() { $this->sortOptions = array(); $args = func_get_args(); if (empty($args)) { return; } $len = count($args) & 0x7FFFFFFE; for ($i = 0; $i < $len; $i+=2) { $this->sortOptions[$args[$i]] = $args[$i + 1]; } if (!count($this->sortOptions)) { $this->sortOptions = array('bogus' => true); } } function size($filename) { $result = $this->stat($filename); if ($result === false) { return false; } return isset($result['size']) ? $result['size'] : -1; } function _update_stat_cache($path, $value) { if ($this->use_stat_cache === false) { return; } $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); $temp = &$this->stat_cache; $max = count($dirs) - 1; foreach ($dirs as $i => $dir) { if (is_object($temp)) { $temp = array(); } if (!isset($temp[$dir])) { $temp[$dir] = array(); } if ($i === $max) { if (is_object($temp[$dir]) && is_object($value)) { if (!isset($value->stat) && isset($temp[$dir]->stat)) { $value->stat = $temp[$dir]->stat; } if (!isset($value->lstat) && isset($temp[$dir]->lstat)) { $value->lstat = $temp[$dir]->lstat; } } $temp[$dir] = $value; break; } $temp = &$temp[$dir]; } } function _remove_from_stat_cache($path) { $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); $temp = &$this->stat_cache; $max = count($dirs) - 1; foreach ($dirs as $i => $dir) { if (!is_array($temp)) { return false; } if ($i === $max) { unset($temp[$dir]); return true; } if (!isset($temp[$dir])) { return false; } $temp = &$temp[$dir]; } } function _query_stat_cache($path) { $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $path)); $temp = &$this->stat_cache; foreach ($dirs as $dir) { if (!is_array($temp)) { return null; } if (!isset($temp[$dir])) { return null; } $temp = &$temp[$dir]; } return $temp; } function stat($filename) { if (!$this->_precheck()) { return false; } $filename = $this->_realpath($filename); if ($filename === false) { return false; } if ($this->use_stat_cache) { $result = $this->_query_stat_cache($filename); if (is_array($result) && isset($result['.']) && isset($result['.']->stat)) { return $result['.']->stat; } if (is_object($result) && isset($result->stat)) { return $result->stat; } } $stat = $this->_stat($filename, NET_SFTP_STAT); if ($stat === false) { $this->_remove_from_stat_cache($filename); return false; } if (isset($stat['type'])) { if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename.= '/.'; } $this->_update_stat_cache($filename, (object) array('stat' => $stat)); return $stat; } $pwd = $this->pwd; $stat['type'] = $this->chdir($filename) ? NET_SFTP_TYPE_DIRECTORY : NET_SFTP_TYPE_REGULAR; $this->pwd = $pwd; if ($stat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename.= '/.'; } $this->_update_stat_cache($filename, (object) array('stat' => $stat)); return $stat; } function lstat($filename) { if (!$this->_precheck()) { return false; } $filename = $this->_realpath($filename); if ($filename === false) { return false; } if ($this->use_stat_cache) { $result = $this->_query_stat_cache($filename); if (is_array($result) && isset($result['.']) && isset($result['.']->lstat)) { return $result['.']->lstat; } if (is_object($result) && isset($result->lstat)) { return $result->lstat; } } $lstat = $this->_stat($filename, NET_SFTP_LSTAT); if ($lstat === false) { $this->_remove_from_stat_cache($filename); return false; } if (isset($lstat['type'])) { if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename.= '/.'; } $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); return $lstat; } $stat = $this->_stat($filename, NET_SFTP_STAT); if ($lstat != $stat) { $lstat = array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); return $stat; } $pwd = $this->pwd; $lstat['type'] = $this->chdir($filename) ? NET_SFTP_TYPE_DIRECTORY : NET_SFTP_TYPE_REGULAR; $this->pwd = $pwd; if ($lstat['type'] == NET_SFTP_TYPE_DIRECTORY) { $filename.= '/.'; } $this->_update_stat_cache($filename, (object) array('lstat' => $lstat)); return $lstat; } function _stat($filename, $type) { $packet = pack('Na*', strlen($filename), $filename); if (!$this->_send_sftp_packet($type, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_ATTRS: return $this->_parseAttributes($response); case NET_SFTP_STATUS: $this->_logError($response); return false; } user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); return false; } function truncate($filename, $new_size) { $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); return $this->_setstat($filename, $attr, false); } function touch($filename, $time = null, $atime = null) { if (!$this->_precheck()) { return false; } $filename = $this->_realpath($filename); if ($filename === false) { return false; } if (!isset($time)) { $time = time(); } if (!isset($atime)) { $atime = $time; } if ($this->version < 4) { $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time); } else { $attr = pack( 'N5', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $atime / 4294967296, $atime, $time / 4294967296, $time ); } $packet = pack('Na*', strlen($filename), $filename); $packet.= $this->version >= 5 ? pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) : pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL); $packet.= $attr; if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: return $this->_close_handle(substr($response, 4)); case NET_SFTP_STATUS: $this->_logError($response); break; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); return false; } return $this->_setstat($filename, $attr, false); } function chown($filename, $uid, $recursive = false) { $attr = $this->version < 4 ? pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) : pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, ''); return $this->_setstat($filename, $attr, $recursive); } function chgrp($filename, $gid, $recursive = false) { $attr = $this->version < 4 ? pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) : pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid); return $this->_setstat($filename, $attr, $recursive); } function chmod($mode, $filename, $recursive = false) { if (is_string($mode) && is_int($filename)) { $temp = $mode; $mode = $filename; $filename = $temp; } $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); if (!$this->_setstat($filename, $attr, $recursive)) { return false; } if ($recursive) { return true; } $filename = $this->realpath($filename); $packet = pack('Na*', strlen($filename), $filename); if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_ATTRS: $attrs = $this->_parseAttributes($response); return $attrs['permissions']; case NET_SFTP_STATUS: $this->_logError($response); return false; } user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); return false; } function _setstat($filename, $attr, $recursive) { if (!$this->_precheck()) { return false; } $filename = $this->_realpath($filename); if ($filename === false) { return false; } $this->_remove_from_stat_cache($filename); if ($recursive) { $i = 0; $result = $this->_setstat_recursive($filename, $attr, $i); $this->_read_put_responses($i); return $result; } $packet = $this->version >= 4 ? pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) : pack('Na*a*', strlen($filename), $filename, $attr); if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); return false; } return true; } function _setstat_recursive($path, $attr, &$i) { if (!$this->_read_put_responses($i)) { return false; } $i = 0; $entries = $this->_list($path, true); if ($entries === false) { return $this->_setstat($path, $attr, false); } if (empty($entries)) { return false; } unset($entries['.'], $entries['..']); foreach ($entries as $filename => $props) { if (!isset($props['type'])) { return false; } $temp = $path . '/' . $filename; if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { if (!$this->_setstat_recursive($temp, $attr, $i)) { return false; } } else { $packet = $this->version >= 4 ? pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) : pack('Na*a*', strlen($temp), $temp, $attr); if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { return false; } $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } $i = 0; } } } $packet = $this->version >= 4 ? pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) : pack('Na*a*', strlen($temp), $temp, $attr); if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) { return false; } $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } $i = 0; } return true; } function readlink($link) { if (!$this->_precheck()) { return false; } $link = $this->_realpath($link); if (!$this->_send_sftp_packet(NET_SFTP_READLINK, pack('Na*', strlen($link), $link))) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: break; case NET_SFTP_STATUS: $this->_logError($response); return false; default: user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Ncount', $this->_string_shift($response, 4))); if (!$count) { return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); return $this->_string_shift($response, $length); } function symlink($target, $link) { if (!$this->_precheck()) { return false; } $link = $this->_realpath($link); if ($this->version == 6) { $type = NET_SFTP_LINK; $packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1); } else { $type = NET_SFTP_SYMLINK; $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ? pack('Na*Na*', strlen($target), $target, strlen($link), $link) : pack('Na*Na*', strlen($link), $link, strlen($target), $target); } if (!$this->_send_sftp_packet($type, $packet)) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); return false; } return true; } function mkdir($dir, $mode = -1, $recursive = false) { if (!$this->_precheck()) { return false; } $dir = $this->_realpath($dir); if ($recursive) { $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); if (empty($dirs[0])) { array_shift($dirs); $dirs[0] = '/' . $dirs[0]; } for ($i = 0; $i < count($dirs); $i++) { $temp = array_slice($dirs, 0, $i + 1); $temp = implode('/', $temp); $result = $this->_mkdir_helper($temp, $mode); } return $result; } return $this->_mkdir_helper($dir, $mode); } function _mkdir_helper($dir, $mode) { if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, "\0\0\0\0"))) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); return false; } if ($mode !== -1) { $this->chmod($mode, $dir); } return true; } function rmdir($dir) { if (!$this->_precheck()) { return false; } $dir = $this->_realpath($dir); if ($dir === false) { return false; } if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); return false; } $this->_remove_from_stat_cache($dir); return true; } function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null) { if (!$this->_precheck()) { return false; } $remote_file = $this->_realpath($remote_file); if ($remote_file === false) { return false; } $this->_remove_from_stat_cache($remote_file); if ($this->version >= 5) { $flags = NET_SFTP_OPEN_OPEN_OR_CREATE; } else { $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; } if ($start >= 0) { $offset = $start; } elseif ($mode & self::RESUME) { $size = $this->size($remote_file); $offset = $size !== false ? $size : 0; } else { $offset = 0; if ($this->version >= 5) { $flags = NET_SFTP_OPEN_CREATE_TRUNCATE; } else { $flags|= NET_SFTP_OPEN_TRUNCATE; } } $packet = pack('Na*', strlen($remote_file), $remote_file); $packet.= $this->version >= 5 ? pack('N3', 0, $flags, 0) : pack('N2', $flags, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: $this->_logError($response); return false; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); return false; } $dataCallback = false; switch (true) { case $mode & self::SOURCE_CALLBACK: if (!is_callable($data)) { user_error("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); } $dataCallback = $data; break; case is_resource($data): $mode = $mode & ~self::SOURCE_LOCAL_FILE; $info = stream_get_meta_data($data); if ($info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') { $fp = fopen('php://memory', 'w+'); stream_copy_to_stream($data, $fp); rewind($fp); } else { $fp = $data; } break; case $mode & self::SOURCE_LOCAL_FILE: if (!is_file($data)) { user_error("$data is not a valid file"); return false; } $fp = @fopen($data, 'rb'); if (!$fp) { return false; } } if (isset($fp)) { $stat = fstat($fp); $size = !empty($stat) ? $stat['size'] : 0; if ($local_start >= 0) { fseek($fp, $local_start); $size-= $local_start; } } elseif ($dataCallback) { $size = 0; } else { $size = strlen($data); } $sent = 0; $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; $sftp_packet_size = $this->max_sftp_packet; $sftp_packet_size-= strlen($handle) + 25; $i = $j = 0; while ($dataCallback || ($size === 0 || $sent < $size)) { if ($dataCallback) { $temp = call_user_func($dataCallback, $sftp_packet_size); if (is_null($temp)) { break; } } else { $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); if ($temp === false || $temp === '') { break; } } $subtemp = $offset + $sent; $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet, $j)) { if ($mode & self::SOURCE_LOCAL_FILE) { fclose($fp); } return false; } $sent+= strlen($temp); if (is_callable($progressCallback)) { call_user_func($progressCallback, $sent); } $i++; $j++; if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { $i = 0; break; } $i = 0; } } $result = $this->_close_handle($handle); if (!$this->_read_put_responses($i)) { if ($mode & self::SOURCE_LOCAL_FILE) { fclose($fp); } $this->_close_handle($handle); return false; } if ($mode & SFTP::SOURCE_LOCAL_FILE) { if (isset($fp) && is_resource($fp)) { fclose($fp); } if ($this->preserveTime) { $stat = stat($data); if ($this->version < 4) { $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']); } else { $attr = pack( 'N5', NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME, $stat['atime'] / 4294967296, $stat['atime'], $stat['mtime'] / 4294967296, $stat['mtime'] ); } if (!$this->_setstat($remote_file, $attr, false)) { user_error('Error setting file time'); } } } return $result; } function _read_put_responses($i) { while ($i--) { $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); break; } } return $i < 0; } function _close_handle($handle) { if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); return false; } return true; } function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null) { if (!$this->_precheck()) { return false; } $remote_file = $this->_realpath($remote_file); if ($remote_file === false) { return false; } $packet = pack('Na*', strlen($remote_file), $remote_file); $packet.= $this->version >= 5 ? pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) : pack('N2', NET_SFTP_OPEN_READ, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: $handle = substr($response, 4); break; case NET_SFTP_STATUS: $this->_logError($response); return false; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); return false; } if (is_resource($local_file)) { $fp = $local_file; $stat = fstat($fp); $res_offset = $stat['size']; } else { $res_offset = 0; if ($local_file !== false && !is_callable($local_file)) { $fp = fopen($local_file, 'wb'); if (!$fp) { return false; } } else { $content = ''; } } $fclose_check = $local_file !== false && !is_callable($local_file) && !is_resource($local_file); $start = $offset; $read = 0; while (true) { $i = 0; while ($i < NET_SFTP_QUEUE_SIZE && ($length < 0 || $read < $length)) { $tempoffset = $start + $read; $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) { if ($fclose_check) { fclose($fp); } return false; } $packet = null; $read+= $packet_size; $i++; } if (!$i) { break; } $packets_sent = $i - 1; $clear_responses = false; while ($i > 0) { $i--; if ($clear_responses) { $this->_get_sftp_packet($packets_sent - $i); continue; } else { $response = $this->_get_sftp_packet($packets_sent - $i); } switch ($this->packet_type) { case NET_SFTP_DATA: $temp = substr($response, 4); $offset+= strlen($temp); if ($local_file === false) { $content.= $temp; } elseif (is_callable($local_file)) { $local_file($temp); } else { fputs($fp, $temp); } if (is_callable($progressCallback)) { call_user_func($progressCallback, $offset); } $temp = null; break; case NET_SFTP_STATUS: $this->_logError($response); $clear_responses = true; break; default: if ($fclose_check) { fclose($fp); } if ($this->channel_close) { $this->partial_init = false; $this->_init_sftp_connection(); return false; } else { user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS'); } } $response = null; } if ($clear_responses) { break; } } if ($length > 0 && $length <= $offset - $start) { if ($local_file === false) { $content = substr($content, 0, $length); } else { ftruncate($fp, $length + $res_offset); } } if ($fclose_check) { fclose($fp); if ($this->preserveTime) { $stat = $this->stat($remote_file); touch($local_file, $stat['mtime'], $stat['atime']); } } if (!$this->_close_handle($handle)) { return false; } return isset($content) ? $content : true; } function delete($path, $recursive = true) { if (!$this->_precheck()) { return false; } if (is_object($path)) { $path = (string) $path; } if (!is_string($path) || $path == '') { return false; } $path = $this->_realpath($path); if ($path === false) { return false; } if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); if (!$recursive) { return false; } $i = 0; $result = $this->_delete_recursive($path, $i); $this->_read_put_responses($i); return $result; } $this->_remove_from_stat_cache($path); return true; } function _delete_recursive($path, &$i) { if (!$this->_read_put_responses($i)) { return false; } $i = 0; $entries = $this->_list($path, true); if (empty($entries)) { return false; } unset($entries['.'], $entries['..']); foreach ($entries as $filename => $props) { if (!isset($props['type'])) { return false; } $temp = $path . '/' . $filename; if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { if (!$this->_delete_recursive($temp, $i)) { return false; } } else { if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) { return false; } $this->_remove_from_stat_cache($temp); $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } $i = 0; } } } if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) { return false; } $this->_remove_from_stat_cache($path); $i++; if ($i >= NET_SFTP_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { return false; } $i = 0; } return true; } function file_exists($path) { if ($this->use_stat_cache) { if (!$this->_precheck()) { return false; } $path = $this->_realpath($path); $result = $this->_query_stat_cache($path); if (isset($result)) { return $result !== false; } } return $this->stat($path) !== false; } function is_dir($path) { $result = $this->_get_stat_cache_prop($path, 'type'); if ($result === false) { return false; } return $result === NET_SFTP_TYPE_DIRECTORY; } function is_file($path) { $result = $this->_get_stat_cache_prop($path, 'type'); if ($result === false) { return false; } return $result === NET_SFTP_TYPE_REGULAR; } function is_link($path) { $result = $this->_get_lstat_cache_prop($path, 'type'); if ($result === false) { return false; } return $result === NET_SFTP_TYPE_SYMLINK; } function is_readable($path) { if (!$this->_precheck()) { return false; } $path = $this->_realpath($path); $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: return true; case NET_SFTP_STATUS: return false; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); return false; } } function is_writable($path) { if (!$this->_precheck()) { return false; } $path = $this->_realpath($path); $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0); if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { return false; } $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_HANDLE: return true; case NET_SFTP_STATUS: return false; default: user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); return false; } } function is_writeable($path) { return $this->is_writable($path); } function fileatime($path) { return $this->_get_stat_cache_prop($path, 'atime'); } function filemtime($path) { return $this->_get_stat_cache_prop($path, 'mtime'); } function fileperms($path) { return $this->_get_stat_cache_prop($path, 'permissions'); } function fileowner($path) { return $this->_get_stat_cache_prop($path, 'uid'); } function filegroup($path) { return $this->_get_stat_cache_prop($path, 'gid'); } function filesize($path) { return $this->_get_stat_cache_prop($path, 'size'); } function filetype($path) { $type = $this->_get_stat_cache_prop($path, 'type'); if ($type === false) { return false; } switch ($type) { case NET_SFTP_TYPE_BLOCK_DEVICE: return 'block'; case NET_SFTP_TYPE_CHAR_DEVICE: return 'char'; case NET_SFTP_TYPE_DIRECTORY: return 'dir'; case NET_SFTP_TYPE_FIFO: return 'fifo'; case NET_SFTP_TYPE_REGULAR: return 'file'; case NET_SFTP_TYPE_SYMLINK: return 'link'; default: return false; } } function _get_stat_cache_prop($path, $prop) { return $this->_get_xstat_cache_prop($path, $prop, 'stat'); } function _get_lstat_cache_prop($path, $prop) { return $this->_get_xstat_cache_prop($path, $prop, 'lstat'); } function _get_xstat_cache_prop($path, $prop, $type) { if (!$this->_precheck()) { return false; } if ($this->use_stat_cache) { $path = $this->_realpath($path); $result = $this->_query_stat_cache($path); if (is_object($result) && isset($result->$type)) { return $result->{$type}[$prop]; } } $result = $this->$type($path); if ($result === false || !isset($result[$prop])) { return false; } return $result[$prop]; } function rename($oldname, $newname) { if (!$this->_precheck()) { return false; } $oldname = $this->_realpath($oldname); $newname = $this->_realpath($newname); if ($oldname === false || $newname === false) { return false; } $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname); if ($this->version >= 5) { $packet.= "\0\0\0\0"; } if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) { return false; } $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { user_error('Expected SSH_FXP_STATUS'); return false; } if (strlen($response) < 4) { return false; } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); return false; } $this->_remove_from_stat_cache($oldname); $this->_remove_from_stat_cache($newname); return true; } function _parseTime($key, $flags, &$response) { if (strlen($response) < 8) { user_error('Malformed file attributes'); return array(); } $attr = array(); $attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8))); if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) { $attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4))); } return $attr; } function _parseAttributes(&$response) { if ($this->version >= 4) { $length = 5; $format = 'Nflags/Ctype'; } else { $length = 4; $format = 'Nflags'; } $attr = array(); if (strlen($response) < $length) { user_error('Malformed file attributes'); return array(); } extract(unpack($format, $this->_string_shift($response, $length))); if (isset($type)) { $attr['type'] = $type; } foreach ($this->attributes as $key => $value) { switch ($flags & $key) { case NET_SFTP_ATTR_UIDGID: if ($this->version > 3) { continue 2; } break; case NET_SFTP_ATTR_CREATETIME: case NET_SFTP_ATTR_MODIFYTIME: case NET_SFTP_ATTR_ACL: case NET_SFTP_ATTR_OWNERGROUP: case NET_SFTP_ATTR_SUBSECOND_TIMES: if ($this->version < 4) { continue 2; } break; case NET_SFTP_ATTR_BITS: if ($this->version < 5) { continue 2; } break; case NET_SFTP_ATTR_ALLOCATION_SIZE: case NET_SFTP_ATTR_TEXT_HINT: case NET_SFTP_ATTR_MIME_TYPE: case NET_SFTP_ATTR_LINK_COUNT: case NET_SFTP_ATTR_UNTRANSLATED_NAME: case NET_SFTP_ATTR_CTIME: if ($this->version < 6) { continue 2; } } switch ($flags & $key) { case NET_SFTP_ATTR_SIZE: $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8))); break; case NET_SFTP_ATTR_UIDGID: if (strlen($response) < 8) { user_error('Malformed file attributes'); return $attr; } $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); break; case NET_SFTP_ATTR_PERMISSIONS: if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); $attr+= array('mode' => $attr['permissions']); $fileType = $this->_parseMode($attr['permissions']); if ($fileType !== false) { $attr+= array('type' => $fileType); } break; case NET_SFTP_ATTR_ACCESSTIME: if ($this->version >= 4) { $attr+= $this->_parseTime('atime', $flags, $response); break; } if (strlen($response) < 8) { user_error('Malformed file attributes'); return $attr; } $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); break; case NET_SFTP_ATTR_CREATETIME: $attr+= $this->_parseTime('createtime', $flags, $response); break; case NET_SFTP_ATTR_MODIFYTIME: $attr+= $this->_parseTime('mtime', $flags, $response); break; case NET_SFTP_ATTR_ACL: if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { if (strlen($response) < 16) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16))); if (strlen($response) < $length) { user_error('Malformed file attributes'); return $attr; } $this->_string_shift($response, $length); } break; case NET_SFTP_ATTR_OWNERGROUP: if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Nlength', $this->_string_shift($response, 4))); if (strlen($response) < $length) { user_error('Malformed file attributes'); return $attr; } $attr['owner'] = $this->_string_shift($response, $length); if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Nlength', $this->_string_shift($response, 4))); if (strlen($response) < $length) { user_error('Malformed file attributes'); return $attr; } $attr['group'] = $this->_string_shift($response, $length); break; case NET_SFTP_ATTR_SUBSECOND_TIMES: break; case NET_SFTP_ATTR_BITS: if (strlen($response) < 8) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8))); break; case NET_SFTP_ATTR_ALLOCATION_SIZE: $attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8))); break; case NET_SFTP_ATTR_TEXT_HINT: extract(unpack('Ctext-hint', $this->_string_shift($response))); break; case NET_SFTP_ATTR_MIME_TYPE: if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Nlength', $this->_string_shift($response, 4))); if (strlen($response) < $length) { user_error('Malformed file attributes'); return $attr; } $attr['mime-type'] = $this->_string_shift($response, $length); break; case NET_SFTP_ATTR_LINK_COUNT: if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } $attr+= unpack('Nlink-count', $this->_string_shift($response, 4)); break; case NET_SFTP_ATTR_UNTRANSLATED_NAME: if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Nlength', $this->_string_shift($response, 4))); if (strlen($response) < $length) { user_error('Malformed file attributes'); return $attr; } $attr['untranslated-name'] = $this->_string_shift($response, $length); break; case NET_SFTP_ATTR_CTIME: $attr+= $this->_parseTime('ctime', $flags, $response); break; case NET_SFTP_ATTR_EXTENDED: if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); if (strlen($response) < 4) { user_error('Malformed file attributes'); return $attr; } extract(unpack('Nlength', $this->_string_shift($response, 4))); $attr[$key] = $this->_string_shift($response, $length); } } } return $attr; } function _parseMode($mode) { switch ($mode & 0170000) { case 0000000: return false; case 0040000: return NET_SFTP_TYPE_DIRECTORY; case 0100000: return NET_SFTP_TYPE_REGULAR; case 0120000: return NET_SFTP_TYPE_SYMLINK; case 0010000: return NET_SFTP_TYPE_FIFO; case 0020000: return NET_SFTP_TYPE_CHAR_DEVICE; case 0060000: return NET_SFTP_TYPE_BLOCK_DEVICE; case 0140000: return NET_SFTP_TYPE_SOCKET; case 0160000: return NET_SFTP_TYPE_SPECIAL; default: return NET_SFTP_TYPE_UNKNOWN; } } function _parseLongname($longname) { if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { switch ($longname[0]) { case '-': return NET_SFTP_TYPE_REGULAR; case 'd': return NET_SFTP_TYPE_DIRECTORY; case 'l': return NET_SFTP_TYPE_SYMLINK; default: return NET_SFTP_TYPE_SPECIAL; } } return false; } function _send_sftp_packet($type, $data, $request_id = 1) { $this->curTimeout = $this->timeout; $packet = $this->use_request_id ? pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : pack('NCa*', strlen($data) + 1, $type, $data); $start = strtok(microtime(), ' ') + strtok(''); $result = $this->_send_channel_packet(self::CHANNEL, $packet); $stop = strtok(microtime(), ' ') + strtok(''); if (defined('NET_SFTP_LOGGING')) { $packet_type = '-> ' . $this->packet_types[$type] . ' (' . round($stop - $start, 4) . 's)'; if (NET_SFTP_LOGGING == self::LOG_REALTIME) { switch (PHP_SAPI) { case 'cli': $start = $stop = "\r\n"; break; default: $start = '
';
$stop = '
'; } echo $start . $this->_format_log(array($data), array($packet_type)) . $stop; @flush(); @ob_flush(); } else { $this->packet_type_log[] = $packet_type; if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { $this->packet_log[] = $data; } } } return $result; } function _reset_connection($reason) { parent::_reset_connection($reason); $this->use_request_id = false; $this->pwd = false; $this->requestBuffer = array(); } function _get_sftp_packet($request_id = null) { $this->channel_close = false; if (isset($request_id) && isset($this->requestBuffer[$request_id])) { $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; $temp = $this->requestBuffer[$request_id]['packet']; unset($this->requestBuffer[$request_id]); return $temp; } $this->curTimeout = $this->timeout; $start = strtok(microtime(), ' ') + strtok(''); while (strlen($this->packet_buffer) < 4) { $temp = $this->_get_channel_packet(self::CHANNEL, true); if ($temp === true) { if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { $this->channel_close = true; } $this->packet_type = false; $this->packet_buffer = ''; return false; } if ($temp === false) { return false; } $this->packet_buffer.= $temp; } if (strlen($this->packet_buffer) < 4) { return false; } extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); $tempLength = $length; $tempLength-= strlen($this->packet_buffer); if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) { user_error('Invalid SFTP packet size'); return false; } while ($tempLength > 0) { $temp = $this->_get_channel_packet(self::CHANNEL, true); if (is_bool($temp)) { $this->packet_type = false; $this->packet_buffer = ''; return false; } $this->packet_buffer.= $temp; $tempLength-= strlen($temp); } $stop = strtok(microtime(), ' ') + strtok(''); $this->packet_type = ord($this->_string_shift($this->packet_buffer)); if ($this->use_request_id) { extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); $length-= 5; } else { $length-= 1; } $packet = $this->_string_shift($this->packet_buffer, $length); if (defined('NET_SFTP_LOGGING')) { $packet_type = '<- ' . $this->packet_types[$this->packet_type] . ' (' . round($stop - $start, 4) . 's)'; if (NET_SFTP_LOGGING == self::LOG_REALTIME) { switch (PHP_SAPI) { case 'cli': $start = $stop = "\r\n"; break; default: $start = '
';
$stop = '
'; } echo $start . $this->_format_log(array($packet), array($packet_type)) . $stop; @flush(); @ob_flush(); } else { $this->packet_type_log[] = $packet_type; if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { $this->packet_log[] = $packet; } } } if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) { $this->requestBuffer[$packet_id] = array( 'packet_type' => $this->packet_type, 'packet' => $packet ); return $this->_get_sftp_packet($request_id); } return $packet; } function getSFTPLog() { if (!defined('NET_SFTP_LOGGING')) { return false; } switch (NET_SFTP_LOGGING) { case self::LOG_COMPLEX: return $this->_format_log($this->packet_log, $this->packet_type_log); break; default: return $this->packet_type_log; } } function getSFTPErrors() { return $this->sftp_errors; } function getLastSFTPError() { return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : ''; } function getSupportedVersions() { if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; } if (!$this->partial_init) { $this->_partial_init_sftp_connection(); } $temp = array('version' => $this->defaultVersion); if (isset($this->extensions['versions'])) { $temp['extensions'] = $this->extensions['versions']; } return $temp; } function getNegotiatedVersion() { if (!$this->_precheck()) { return false; } return $this->version; } function setPreferredVersion($version) { $this->preferredVersion = $version; } function _disconnect($reason) { $this->pwd = false; parent::_disconnect($reason); } function enableDatePreservation() { $this->preserveTime = true; } function disableDatePreservation() { $this->preserveTime = false; } } mode = self::MODE_SSH2; } elseif ($ssh instanceof SSH1) { $this->packet_size = 50000; $this->mode = self::MODE_SSH1; } else { return; } $this->ssh = $ssh; } function put($remote_file, $data, $mode = self::SOURCE_STRING, $callback = null) { if (!isset($this->ssh)) { return false; } if (empty($remote_file)) { user_error('remote_file cannot be blank', E_USER_NOTICE); return false; } if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { return false; } $temp = $this->_receive(); if ($temp !== chr(0)) { return false; } if ($this->mode == self::MODE_SSH2) { $this->packet_size = $this->ssh->packet_size_client_to_server[SSH2::CHANNEL_EXEC] - 4; } $remote_file = basename($remote_file); if ($mode == self::SOURCE_STRING) { $size = strlen($data); } else { if (!is_file($data)) { user_error("$data is not a valid file", E_USER_NOTICE); return false; } $fp = @fopen($data, 'rb'); if (!$fp) { return false; } $size = filesize($data); } $this->_send('C0644 ' . $size . ' ' . $remote_file . "\n"); $temp = $this->_receive(); if ($temp !== chr(0)) { return false; } $sent = 0; while ($sent < $size) { $temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $this->packet_size) : fread($fp, $this->packet_size); $this->_send($temp); $sent+= strlen($temp); if (is_callable($callback)) { call_user_func($callback, $sent); } } $this->_close(); if ($mode != self::SOURCE_STRING) { fclose($fp); } return true; } function get($remote_file, $local_file = false) { if (!isset($this->ssh)) { return false; } if (!$this->ssh->exec('scp -f ' . escapeshellarg($remote_file), false)) { return false; } $this->_send("\0"); if (!preg_match('#(?[^ ]+) (?\d+) (?.+)#', rtrim($this->_receive()), $info)) { return false; } $this->_send("\0"); $size = 0; if ($local_file !== false) { $fp = @fopen($local_file, 'wb'); if (!$fp) { return false; } } $content = ''; while ($size < $info['size']) { $data = $this->_receive(); $size+= strlen($data); if ($local_file === false) { $content.= $data; } else { fputs($fp, $data); } } $this->_close(); if ($local_file !== false) { fclose($fp); return true; } return $content; } function _send($data) { switch ($this->mode) { case self::MODE_SSH2: $this->ssh->_send_channel_packet(SSH2::CHANNEL_EXEC, $data); break; case self::MODE_SSH1: $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data); $this->ssh->_send_binary_packet($data); } } function _receive() { switch ($this->mode) { case self::MODE_SSH2: return $this->ssh->_get_channel_packet(SSH2::CHANNEL_EXEC, true); case self::MODE_SSH1: if (!$this->ssh->bitmap) { return false; } while (true) { $response = $this->ssh->_get_binary_packet(); switch ($response[SSH1::RESPONSE_TYPE]) { case NET_SSH1_SMSG_STDOUT_DATA: if (strlen($response[SSH1::RESPONSE_DATA]) < 4) { return false; } extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); case NET_SSH1_SMSG_STDERR_DATA: break; case NET_SSH1_SMSG_EXITSTATUS: $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION)); fclose($this->ssh->fsock); $this->ssh->bitmap = 0; return false; default: user_error('Unknown packet received', E_USER_NOTICE); return false; } } } } function _close() { switch ($this->mode) { case self::MODE_SSH2: $this->ssh->_close_channel(SSH2::CHANNEL_EXEC, true); break; case self::MODE_SSH1: $this->ssh->disconnect(); } } } 'No encryption', self::CIPHER_IDEA => 'IDEA in CFB mode', self::CIPHER_DES => 'DES in CBC mode', self::CIPHER_3DES => 'Triple-DES in CBC mode', self::CIPHER_BROKEN_TSS => 'TRI\'s Simple Stream encryption CBC', self::CIPHER_RC4 => 'RC4', self::CIPHER_BLOWFISH => 'Blowfish' ); var $supported_authentications = array( self::AUTH_RHOSTS => '.rhosts or /etc/hosts.equiv', self::AUTH_RSA => 'pure RSA authentication', self::AUTH_PASSWORD => 'password authentication', self::AUTH_RHOSTS_RSA => '.rhosts with RSA host authentication' ); var $server_identification = ''; var $protocol_flags = array(); var $protocol_flag_log = array(); var $message_log = array(); var $realtime_log_file; var $realtime_log_size; var $realtime_log_wrap; var $interactiveBuffer = ''; var $timeout; var $curTimeout; var $log_boundary = ':'; var $log_long_width = 65; var $log_short_width = 16; var $host; var $port; var $connectionTimeout; var $cipher; function __construct($host, $port = 22, $timeout = 10, $cipher = self::CIPHER_3DES) { $this->protocol_flags = array( 1 => 'NET_SSH1_MSG_DISCONNECT', 2 => 'NET_SSH1_SMSG_PUBLIC_KEY', 3 => 'NET_SSH1_CMSG_SESSION_KEY', 4 => 'NET_SSH1_CMSG_USER', 9 => 'NET_SSH1_CMSG_AUTH_PASSWORD', 10 => 'NET_SSH1_CMSG_REQUEST_PTY', 12 => 'NET_SSH1_CMSG_EXEC_SHELL', 13 => 'NET_SSH1_CMSG_EXEC_CMD', 14 => 'NET_SSH1_SMSG_SUCCESS', 15 => 'NET_SSH1_SMSG_FAILURE', 16 => 'NET_SSH1_CMSG_STDIN_DATA', 17 => 'NET_SSH1_SMSG_STDOUT_DATA', 18 => 'NET_SSH1_SMSG_STDERR_DATA', 19 => 'NET_SSH1_CMSG_EOF', 20 => 'NET_SSH1_SMSG_EXITSTATUS', 33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION' ); $this->_define_array($this->protocol_flags); $this->host = $host; $this->port = $port; $this->connectionTimeout = $timeout; $this->cipher = $cipher; } function _connect() { $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); if (!$this->fsock) { user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr")); return false; } $this->server_identification = $init_line = fgets($this->fsock, 255); if (defined('NET_SSH1_LOGGING')) { $this->_append_log('<-', $this->server_identification); $this->_append_log('->', $this->identifier . "\r\n"); } if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { user_error('Can only connect to SSH servers'); return false; } if ($parts[1][0] != 1) { user_error("Cannot connect to SSH $parts[1] servers"); return false; } fputs($this->fsock, $this->identifier."\r\n"); $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { user_error('Expected SSH_SMSG_PUBLIC_KEY'); return false; } $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8); $this->_string_shift($response[self::RESPONSE_DATA], 4); if (strlen($response[self::RESPONSE_DATA]) < 2) { return false; } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->server_key_public_exponent = $server_key_public_exponent; if (strlen($response[self::RESPONSE_DATA]) < 2) { return false; } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->server_key_public_modulus = $server_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); if (strlen($response[self::RESPONSE_DATA]) < 2) { return false; } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->host_key_public_exponent = $host_key_public_exponent; if (strlen($response[self::RESPONSE_DATA]) < 2) { return false; } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->host_key_public_modulus = $host_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); if (strlen($response[self::RESPONSE_DATA]) < 4) { return false; } extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); foreach ($this->supported_ciphers as $mask => $name) { if (($supported_ciphers_mask & (1 << $mask)) == 0) { unset($this->supported_ciphers[$mask]); } } if (strlen($response[self::RESPONSE_DATA]) < 4) { return false; } extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); foreach ($this->supported_authentications as $mask => $name) { if (($supported_authentications_mask & (1 << $mask)) == 0) { unset($this->supported_authentications[$mask]); } } $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie)); $session_key = Random::string(32); $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0)); if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) { $double_encrypted_session_key = $this->_rsa_crypt( $double_encrypted_session_key, array( $server_key_public_exponent, $server_key_public_modulus ) ); $double_encrypted_session_key = $this->_rsa_crypt( $double_encrypted_session_key, array( $host_key_public_exponent, $host_key_public_modulus ) ); } else { $double_encrypted_session_key = $this->_rsa_crypt( $double_encrypted_session_key, array( $host_key_public_exponent, $host_key_public_modulus ) ); $double_encrypted_session_key = $this->_rsa_crypt( $double_encrypted_session_key, array( $server_key_public_exponent, $server_key_public_modulus ) ); } $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES; $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_SESSION_KEY'); return false; } switch ($cipher) { case self::CIPHER_DES: $this->crypto = new DES(); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 8)); break; case self::CIPHER_3DES: $this->crypto = new TripleDES(TripleDES::MODE_3CBC); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 24)); break; } $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { user_error('Expected SSH_SMSG_SUCCESS'); return false; } $this->bitmap = self::MASK_CONNECTED; return true; } function login($username, $password = '') { if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { $this->bitmap |= self::MASK_CONSTRUCTOR; if (!$this->_connect()) { return false; } } if (!($this->bitmap & self::MASK_CONNECTED)) { return false; } $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_USER'); return false; } $response = $this->_get_binary_packet(); if ($response === true) { return false; } if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { $this->bitmap |= self::MASK_LOGIN; return true; } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); return false; } $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_AUTH_PASSWORD'); return false; } if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == self::LOG_COMPLEX) { $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password'); $this->message_log[count($this->message_log) - 1] = $data; } $response = $this->_get_binary_packet(); if ($response === true) { return false; } if ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { $this->bitmap |= self::MASK_LOGIN; return true; } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) { return false; } else { user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); return false; } } function setTimeout($timeout) { $this->timeout = $this->curTimeout = $timeout; } function exec($cmd, $block = true) { if (!($this->bitmap & self::MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); return false; } $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_EXEC_CMD'); return false; } if (!$block) { return true; } $output = ''; $response = $this->_get_binary_packet(); if ($response !== false) { do { $output.= substr($response[self::RESPONSE_DATA], 4); $response = $this->_get_binary_packet(); } while (is_array($response) && $response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_EXITSTATUS); } $data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION); $this->_send_binary_packet($data); fclose($this->fsock); $this->bitmap = 0; return $output; } function _initShell() { $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_REQUEST_PTY'); return false; } $response = $this->_get_binary_packet(); if ($response === true) { return false; } if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { user_error('Expected SSH_SMSG_SUCCESS'); return false; } $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_EXEC_SHELL'); return false; } $this->bitmap |= self::MASK_SHELL; return true; } function write($cmd) { return $this->interactiveWrite($cmd); } function read($expect, $mode = self::READ_SIMPLE) { if (!($this->bitmap & self::MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); return false; } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { user_error('Unable to initiate an interactive shell session'); return false; } $match = $expect; while (true) { if ($mode == self::READ_REGEX) { preg_match($expect, $this->interactiveBuffer, $matches); $match = isset($matches[0]) ? $matches[0] : ''; } $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false; if ($pos !== false) { return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match)); } $response = $this->_get_binary_packet(); if ($response === true) { return $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); } $this->interactiveBuffer.= substr($response[self::RESPONSE_DATA], 4); } } function interactiveWrite($cmd) { if (!($this->bitmap & self::MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); return false; } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { user_error('Unable to initiate an interactive shell session'); return false; } $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); if (!$this->_send_binary_packet($data)) { user_error('Error sending SSH_CMSG_STDIN'); return false; } return true; } function interactiveRead() { if (!($this->bitmap & self::MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); return false; } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { user_error('Unable to initiate an interactive shell session'); return false; } $read = array($this->fsock); $write = $except = null; if (stream_select($read, $write, $except, 0)) { $response = $this->_get_binary_packet(); return substr($response[self::RESPONSE_DATA], 4); } else { return ''; } } function disconnect() { $this->_disconnect(); } function __destruct() { $this->_disconnect(); } function _disconnect($msg = 'Client Quit') { if ($this->bitmap) { $data = pack('C', NET_SSH1_CMSG_EOF); $this->_send_binary_packet($data); $data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); $this->_send_binary_packet($data); fclose($this->fsock); $this->bitmap = 0; } } function _get_binary_packet() { if (feof($this->fsock)) { return false; } if ($this->curTimeout) { $read = array($this->fsock); $write = $except = null; $start = strtok(microtime(), ' ') + strtok(''); $sec = floor($this->curTimeout); $usec = 1000000 * ($this->curTimeout - $sec); if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { return true; } $elapsed = strtok(microtime(), ' ') + strtok('') - $start; $this->curTimeout-= $elapsed; } $start = strtok(microtime(), ' ') + strtok(''); $data = fread($this->fsock, 4); if (strlen($data) < 4) { return false; } $temp = unpack('Nlength', $data); $padding_length = 8 - ($temp['length'] & 7); $length = $temp['length'] + $padding_length; $raw = ''; while ($length > 0) { $temp = fread($this->fsock, $length); if (strlen($temp) != $length) { return false; } $raw.= $temp; $length-= strlen($temp); } $stop = strtok(microtime(), ' ') + strtok(''); if (strlen($raw) && $this->crypto !== false) { $raw = $this->crypto->decrypt($raw); } $padding = substr($raw, 0, $padding_length); $type = $raw[$padding_length]; $data = substr($raw, $padding_length + 1, -4); if (strlen($raw) < 4) { return false; } $temp = unpack('Ncrc', substr($raw, -4)); $type = ord($type); if (defined('NET_SSH1_LOGGING')) { $temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN'; $temp = '<- ' . $temp . ' (' . round($stop - $start, 4) . 's)'; $this->_append_log($temp, $data); } return array( self::RESPONSE_TYPE => $type, self::RESPONSE_DATA => $data ); } function _send_binary_packet($data) { if (feof($this->fsock)) { return false; } $length = strlen($data) + 4; $padding = Random::string(8 - ($length & 7)); $orig = $data; $data = $padding . $data; $data.= pack('N', $this->_crc($data)); if ($this->crypto !== false) { $data = $this->crypto->encrypt($data); } $packet = pack('Na*', $length, $data); $start = strtok(microtime(), ' ') + strtok(''); $result = strlen($packet) == fputs($this->fsock, $packet); $stop = strtok(microtime(), ' ') + strtok(''); if (defined('NET_SSH1_LOGGING')) { $temp = isset($this->protocol_flags[ord($orig[0])]) ? $this->protocol_flags[ord($orig[0])] : 'UNKNOWN'; $temp = '-> ' . $temp . ' (' . round($stop - $start, 4) . 's)'; $this->_append_log($temp, $orig); } return $result; } function _crc($data) { static $crc_lookup_table = array( 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D ); $crc = 0x00000000; $length = strlen($data); for ($i=0; $i<$length; $i++) { $crc = (($crc >> 8) & 0x00FFFFFF) ^ $crc_lookup_table[($crc & 0xFF) ^ ord($data[$i])]; } return $crc; } function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } function _rsa_crypt($m, $key) { $modulus = $key[1]->toBytes(); $length = strlen($modulus) - strlen($m) - 3; $random = ''; while (strlen($random) != $length) { $block = Random::string($length - strlen($random)); $block = str_replace("\x00", '', $block); $random.= $block; } $temp = chr(0) . chr(2) . $random . chr(0) . $m; $m = new BigInteger($temp, 256); $m = $m->modPow($key[0], $key[1]); return $m->toBytes(); } function _define_array() { $args = func_get_args(); foreach ($args as $arg) { foreach ($arg as $key => $value) { if (!defined($value)) { define($value, $key); } else { break 2; } } } } function getLog() { if (!defined('NET_SSH1_LOGGING')) { return false; } switch (NET_SSH1_LOGGING) { case self::LOG_SIMPLE: return $this->message_number_log; break; case self::LOG_COMPLEX: return $this->_format_log($this->message_log, $this->protocol_flags_log); break; default: return false; } } function _format_log($message_log, $message_number_log) { $output = ''; for ($i = 0; $i < count($message_log); $i++) { $output.= $message_number_log[$i] . "\r\n"; $current_log = $message_log[$i]; $j = 0; do { if (strlen($current_log)) { $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 '; } $fragment = $this->_string_shift($current_log, $this->log_short_width); $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary)); $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment); $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n"; $j++; } while (strlen($current_log)); $output.= "\r\n"; } return $output; } function _format_log_helper($matches) { return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT); } function getServerKeyPublicExponent($raw_output = false) { return $raw_output ? $this->server_key_public_exponent->toBytes() : $this->server_key_public_exponent->toString(); } function getServerKeyPublicModulus($raw_output = false) { return $raw_output ? $this->server_key_public_modulus->toBytes() : $this->server_key_public_modulus->toString(); } function getHostKeyPublicExponent($raw_output = false) { return $raw_output ? $this->host_key_public_exponent->toBytes() : $this->host_key_public_exponent->toString(); } function getHostKeyPublicModulus($raw_output = false) { return $raw_output ? $this->host_key_public_modulus->toBytes() : $this->host_key_public_modulus->toString(); } function getSupportedCiphers($raw_output = false) { return $raw_output ? array_keys($this->supported_ciphers) : array_values($this->supported_ciphers); } function getSupportedAuthentications($raw_output = false) { return $raw_output ? array_keys($this->supported_authentications) : array_values($this->supported_authentications); } function getServerIdentification() { return rtrim($this->server_identification); } function _append_log($protocol_flags, $message) { switch (NET_SSH1_LOGGING) { case self::LOG_SIMPLE: $this->protocol_flags_log[] = $protocol_flags; break; case self::LOG_COMPLEX: $this->protocol_flags_log[] = $protocol_flags; $this->_string_shift($message); $this->log_size+= strlen($message); $this->message_log[] = $message; while ($this->log_size > self::LOG_MAX_SIZE) { $this->log_size-= strlen(array_shift($this->message_log)); array_shift($this->protocol_flags_log); } break; case self::LOG_REALTIME: echo "
\r\n" . $this->_format_log(array($message), array($protocol_flags)) . "\r\n
\r\n"; @flush(); @ob_flush(); break; case self::LOG_REALTIME_FILE: if (!isset($this->realtime_log_file)) { $filename = self::LOG_REALTIME_FILE; $fp = fopen($filename, 'w'); $this->realtime_log_file = $fp; } if (!is_resource($this->realtime_log_file)) { break; } $entry = $this->_format_log(array($message), array($protocol_flags)); if ($this->realtime_log_wrap) { $temp = "<<< START >>>\r\n"; $entry.= $temp; fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp)); } $this->realtime_log_size+= strlen($entry); if ($this->realtime_log_size > self::LOG_MAX_SIZE) { fseek($this->realtime_log_file, 0); $this->realtime_log_size = strlen($entry); $this->realtime_log_wrap = true; } fputs($this->realtime_log_file, $entry); } } } ', '', strip_tags($matches[2][$i]))); if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { $versions[$matches[1][$i]] = $fullVersion; } else { $versions[$matches[1][$i]] = $m[0]; } } } } switch (true) { case !isset($versions['Header']): case !isset($versions['Library']): case $versions['Header'] == $versions['Library']: case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); break; default: define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); } } if (!defined('PHP_INT_SIZE')) { define('PHP_INT_SIZE', 4); } if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) { switch (PHP_INT_SIZE) { case 8: self::$base = 31; self::$baseFull = 0x80000000; self::$maxDigit = 0x7FFFFFFF; self::$msb = 0x40000000; self::$max10 = 1000000000; self::$max10Len = 9; self::$maxDigit2 = pow(2, 62); break; default: self::$base = 26; self::$baseFull = 0x4000000; self::$maxDigit = 0x3FFFFFF; self::$msb = 0x2000000; self::$max10 = 10000000; self::$max10Len = 7; self::$maxDigit2 = pow(2, 52); } } switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: switch (true) { case is_resource($x) && get_resource_type($x) == 'GMP integer': case $x instanceof \GMP: $this->value = $x; return; } $this->value = gmp_init(0); break; case self::MODE_BCMATH: $this->value = '0'; break; default: $this->value = array(); } if (empty($x) && (abs($base) != 256 || $x !== '0')) { return; } switch ($base) { case -256: if (ord($x[0]) & 0x80) { $x = ~$x; $this->is_negative = true; } case 256: switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $this->value = function_exists('gmp_import') ? gmp_import($x) : gmp_init('0x' . bin2hex($x)); if ($this->is_negative) { $this->value = gmp_neg($this->value); } break; case self::MODE_BCMATH: $len = (strlen($x) + 3) & 0xFFFFFFFC; $x = str_pad($x, $len, chr(0), STR_PAD_LEFT); for ($i = 0; $i < $len; $i+= 4) { $this->value = bcmul($this->value, '4294967296', 0); $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0); } if ($this->is_negative) { $this->value = '-' . $this->value; } break; default: while (strlen($x)) { $this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base)); } } if ($this->is_negative) { if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { $this->is_negative = false; } $temp = $this->add(new static('-1')); $this->value = $temp->value; } break; case 16: case -16: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x); $is_negative = false; if ($base < 0 && hexdec($x[0]) >= 8) { $this->is_negative = $is_negative = true; $x = bin2hex(~pack('H*', $x)); } switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = $this->is_negative ? '-0x' . $x : '0x' . $x; $this->value = gmp_init($temp); $this->is_negative = false; break; case self::MODE_BCMATH: $x = (strlen($x) & 1) ? '0' . $x : $x; $temp = new static(pack('H*', $x), 256); $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; $this->is_negative = false; break; default: $x = (strlen($x) & 1) ? '0' . $x : $x; $temp = new static(pack('H*', $x), 256); $this->value = $temp->value; } if ($is_negative) { $temp = $this->add(new static('-1')); $this->value = $temp->value; } break; case 10: case -10: $x = preg_replace('#(?value = gmp_init($x); break; case self::MODE_BCMATH: $this->value = $x === '-' ? '0' : (string) $x; break; default: $temp = new static(); $multiplier = new static(); $multiplier->value = array(self::$max10); if ($x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } $x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT); while (strlen($x)) { $temp = $temp->multiply($multiplier); $temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256)); $x = substr($x, self::$max10Len); } $this->value = $temp->value; } break; case 2: case -2: if ($base > 0 && $x[0] == '-') { $this->is_negative = true; $x = substr($x, 1); } $x = preg_replace('#^([01]*).*#', '$1', $x); $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT); $str = '0x'; while (strlen($x)) { $part = substr($x, 0, 4); $str.= dechex(bindec($part)); $x = substr($x, 4); } if ($this->is_negative) { $str = '-' . $str; } $temp = new static($str, 8 * $base); $this->value = $temp->value; $this->is_negative = $temp->is_negative; break; default: } } function toBytes($twos_compliment = false) { if ($twos_compliment) { $comparison = $this->compare(new static()); if ($comparison == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy(); $bytes = $temp->toBytes(); if (!strlen($bytes)) { $bytes = chr(0); } if ($this->precision <= 0 && (ord($bytes[0]) & 0x80)) { $bytes = chr(0) . $bytes; } return $comparison < 0 ? ~$bytes : $bytes; } switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: if (gmp_cmp($this->value, gmp_init(0)) == 0) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } if (function_exists('gmp_export')) { $temp = gmp_export($this->value); } else { $temp = gmp_strval(gmp_abs($this->value), 16); $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; $temp = pack('H*', $temp); } return $this->precision > 0 ? substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : ltrim($temp, chr(0)); case self::MODE_BCMATH: if ($this->value === '0') { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $value = ''; $current = $this->value; if ($current[0] == '-') { $current = substr($current, 1); } while (bccomp($current, '0', 0) > 0) { $temp = bcmod($current, '16777216'); $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value; $current = bcdiv($current, '16777216', 0); } return $this->precision > 0 ? substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : ltrim($value, chr(0)); } if (!count($this->value)) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } $result = $this->_int2bytes($this->value[count($this->value) - 1]); $temp = $this->copy(); for ($i = count($temp->value) - 2; $i >= 0; --$i) { $temp->_base256_lshift($result, self::$base); $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); } return $this->precision > 0 ? str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) : $result; } function toHex($twos_compliment = false) { return bin2hex($this->toBytes($twos_compliment)); } function toBits($twos_compliment = false) { $hex = $this->toHex($twos_compliment); $bits = ''; for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) { $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits; } if ($start) { $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits; } $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) { return '0' . $result; } return $result; } function toString() { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: return gmp_strval($this->value); case self::MODE_BCMATH: if ($this->value === '0') { return '0'; } return ltrim($this->value, '0'); } if (!count($this->value)) { return '0'; } $temp = $this->copy(); $temp->bitmask = false; $temp->is_negative = false; $divisor = new static(); $divisor->value = array(self::$max10); $result = ''; while (count($temp->value)) { list($temp, $mod) = $temp->divide($divisor); $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result; } $result = ltrim($result, '0'); if (empty($result)) { $result = '0'; } if ($this->is_negative) { $result = '-' . $result; } return $result; } function copy() { $temp = new static(); $temp->value = $this->value; $temp->is_negative = $this->is_negative; $temp->precision = $this->precision; $temp->bitmask = $this->bitmask; return $temp; } function __toString() { return $this->toString(); } function __clone() { return $this->copy(); } function __sleep() { $this->hex = $this->toHex(true); $vars = array('hex'); if ($this->precision > 0) { $vars[] = 'precision'; } return $vars; } function __wakeup() { $temp = new static($this->hex, -16); $this->value = $temp->value; $this->is_negative = $temp->is_negative; if ($this->precision > 0) { $this->setPrecision($this->precision); } } function __debugInfo() { $opts = array(); switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $engine = 'gmp'; break; case self::MODE_BCMATH: $engine = 'bcmath'; break; case self::MODE_INTERNAL: $engine = 'internal'; $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit'; } if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { $opts[] = 'OpenSSL'; } if (!empty($opts)) { $engine.= ' (' . implode('.', $opts) . ')'; } return array( 'value' => '0x' . $this->toHex(true), 'engine' => $engine ); } function add($y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_add($this->value, $y->value); return $this->_normalize($temp); case self::MODE_BCMATH: $temp = new static(); $temp->value = bcadd($this->value, $y->value, 0); return $this->_normalize($temp); } $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative); $result = new static(); $result->value = $temp[self::VALUE]; $result->is_negative = $temp[self::SIGN]; return $this->_normalize($result); } function _add($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return array( self::VALUE => $y_value, self::SIGN => $y_negative ); } elseif ($y_size == 0) { return array( self::VALUE => $x_value, self::SIGN => $x_negative ); } if ($x_negative != $y_negative) { if ($x_value == $y_value) { return array( self::VALUE => array(), self::SIGN => false ); } $temp = $this->_subtract($x_value, false, $y_value, false); $temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? $x_negative : $y_negative; return $temp; } if ($x_size < $y_size) { $size = $x_size; $value = $y_value; } else { $size = $y_size; $value = $x_value; } $value[count($value)] = 0; $carry = 0; for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) { $sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry; $carry = $sum >= self::$maxDigit2; $sum = $carry ? $sum - self::$maxDigit2 : $sum; $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); $value[$i] = (int) ($sum - self::$baseFull * $temp); $value[$j] = $temp; } if ($j == $size) { $sum = $x_value[$i] + $y_value[$i] + $carry; $carry = $sum >= self::$baseFull; $value[$i] = $carry ? $sum - self::$baseFull : $sum; ++$i; } if ($carry) { for (; $value[$i] == self::$maxDigit; ++$i) { $value[$i] = 0; } ++$value[$i]; } return array( self::VALUE => $this->_trim($value), self::SIGN => $x_negative ); } function subtract($y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_sub($this->value, $y->value); return $this->_normalize($temp); case self::MODE_BCMATH: $temp = new static(); $temp->value = bcsub($this->value, $y->value, 0); return $this->_normalize($temp); } $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); $result = new static(); $result->value = $temp[self::VALUE]; $result->is_negative = $temp[self::SIGN]; return $this->_normalize($result); } function _subtract($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); if ($x_size == 0) { return array( self::VALUE => $y_value, self::SIGN => !$y_negative ); } elseif ($y_size == 0) { return array( self::VALUE => $x_value, self::SIGN => $x_negative ); } if ($x_negative != $y_negative) { $temp = $this->_add($x_value, false, $y_value, false); $temp[self::SIGN] = $x_negative; return $temp; } $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative); if (!$diff) { return array( self::VALUE => array(), self::SIGN => false ); } if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; $x_negative = !$x_negative; $x_size = count($x_value); $y_size = count($y_value); } $carry = 0; for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) { $sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry; $carry = $sum < 0; $sum = $carry ? $sum + self::$maxDigit2 : $sum; $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31); $x_value[$i] = (int) ($sum - self::$baseFull * $temp); $x_value[$j] = $temp; } if ($j == $y_size) { $sum = $x_value[$i] - $y_value[$i] - $carry; $carry = $sum < 0; $x_value[$i] = $carry ? $sum + self::$baseFull : $sum; ++$i; } if ($carry) { for (; !$x_value[$i]; ++$i) { $x_value[$i] = self::$maxDigit; } --$x_value[$i]; } return array( self::VALUE => $this->_trim($x_value), self::SIGN => $x_negative ); } function multiply($x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_mul($this->value, $x->value); return $this->_normalize($temp); case self::MODE_BCMATH: $temp = new static(); $temp->value = bcmul($this->value, $x->value, 0); return $this->_normalize($temp); } $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); $product = new static(); $product->value = $temp[self::VALUE]; $product->is_negative = $temp[self::SIGN]; return $this->_normalize($product); } function _multiply($x_value, $x_negative, $y_value, $y_negative) { $x_length = count($x_value); $y_length = count($y_value); if (!$x_length || !$y_length) { return array( self::VALUE => array(), self::SIGN => false ); } return array( self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? $this->_trim($this->_regularMultiply($x_value, $y_value)) : $this->_trim($this->_karatsuba($x_value, $y_value)), self::SIGN => $x_negative != $y_negative ); } function _regularMultiply($x_value, $y_value) { $x_length = count($x_value); $y_length = count($y_value); if (!$x_length || !$y_length) { return array(); } if ($x_length < $y_length) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; $x_length = count($x_value); $y_length = count($y_value); } $product_value = $this->_array_repeat(0, $x_length + $y_length); $carry = 0; for ($j = 0; $j < $x_length; ++$j) { $temp = $x_value[$j] * $y_value[0] + $carry; $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$j] = (int) ($temp - self::$baseFull * $carry); } $product_value[$j] = $carry; for ($i = 1; $i < $y_length; ++$i) { $carry = 0; for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$k] = (int) ($temp - self::$baseFull * $carry); } $product_value[$k] = $carry; } return $product_value; } function _karatsuba($x_value, $y_value) { $m = min(count($x_value) >> 1, count($y_value) >> 1); if ($m < self::KARATSUBA_CUTOFF) { return $this->_regularMultiply($x_value, $y_value); } $x1 = array_slice($x_value, $m); $x0 = array_slice($x_value, 0, $m); $y1 = array_slice($y_value, $m); $y0 = array_slice($y_value, 0, $m); $z2 = $this->_karatsuba($x1, $y1); $z0 = $this->_karatsuba($x0, $y0); $z1 = $this->_add($x1, false, $x0, false); $temp = $this->_add($y1, false, $y0, false); $z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]); $temp = $this->_add($z2, false, $z0, false); $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); $xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); $xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); return $xy[self::VALUE]; } function _square($x = false) { return count($x) < 2 * self::KARATSUBA_CUTOFF ? $this->_trim($this->_baseSquare($x)) : $this->_trim($this->_karatsubaSquare($x)); } function _baseSquare($value) { if (empty($value)) { return array(); } $square_value = $this->_array_repeat(0, 2 * count($value)); for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { $i2 = $i << 1; $temp = $square_value[$i2] + $value[$i] * $value[$i]; $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $square_value[$i2] = (int) ($temp - self::$baseFull * $carry); for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) { $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry; $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $square_value[$k] = (int) ($temp - self::$baseFull * $carry); } $square_value[$i + $max_index + 1] = $carry; } return $square_value; } function _karatsubaSquare($value) { $m = count($value) >> 1; if ($m < self::KARATSUBA_CUTOFF) { return $this->_baseSquare($value); } $x1 = array_slice($value, $m); $x0 = array_slice($value, 0, $m); $z2 = $this->_karatsubaSquare($x1); $z0 = $this->_karatsubaSquare($x0); $z1 = $this->_add($x1, false, $x0, false); $z1 = $this->_karatsubaSquare($z1[self::VALUE]); $temp = $this->_add($z2, false, $z0, false); $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); $xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); $xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); return $xx[self::VALUE]; } function divide($y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $quotient = new static(); $remainder = new static(); list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); if (gmp_sign($remainder->value) < 0) { $remainder->value = gmp_add($remainder->value, gmp_abs($y->value)); } return array($this->_normalize($quotient), $this->_normalize($remainder)); case self::MODE_BCMATH: $quotient = new static(); $remainder = new static(); $quotient->value = bcdiv($this->value, $y->value, 0); $remainder->value = bcmod($this->value, $y->value); if ($remainder->value[0] == '-') { $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0); } return array($this->_normalize($quotient), $this->_normalize($remainder)); } if (count($y->value) == 1) { list($q, $r) = $this->_divide_digit($this->value, $y->value[0]); $quotient = new static(); $remainder = new static(); $quotient->value = $q; $remainder->value = array($r); $quotient->is_negative = $this->is_negative != $y->is_negative; return array($this->_normalize($quotient), $this->_normalize($remainder)); } static $zero; if (!isset($zero)) { $zero = new static(); } $x = $this->copy(); $y = $y->copy(); $x_sign = $x->is_negative; $y_sign = $y->is_negative; $x->is_negative = $y->is_negative = false; $diff = $x->compare($y); if (!$diff) { $temp = new static(); $temp->value = array(1); $temp->is_negative = $x_sign != $y_sign; return array($this->_normalize($temp), $this->_normalize(new static())); } if ($diff < 0) { if ($x_sign) { $x = $y->subtract($x); } return array($this->_normalize(new static()), $this->_normalize($x)); } $msb = $y->value[count($y->value) - 1]; for ($shift = 0; !($msb & self::$msb); ++$shift) { $msb <<= 1; } $x->_lshift($shift); $y->_lshift($shift); $y_value = &$y->value; $x_max = count($x->value) - 1; $y_max = count($y->value) - 1; $quotient = new static(); $quotient_value = &$quotient->value; $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1); static $temp, $lhs, $rhs; if (!isset($temp)) { $temp = new static(); $lhs = new static(); $rhs = new static(); } $temp_value = &$temp->value; $rhs_value = &$rhs->value; $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value); while ($x->compare($temp) >= 0) { ++$quotient_value[$x_max - $y_max]; $x = $x->subtract($temp); $x_max = count($x->value) - 1; } for ($i = $x_max; $i >= $y_max + 1; --$i) { $x_value = &$x->value; $x_window = array( isset($x_value[$i]) ? $x_value[$i] : 0, isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0, isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0 ); $y_window = array( $y_value[$y_max], ($y_max > 0) ? $y_value[$y_max - 1] : 0 ); $q_index = $i - $y_max - 1; if ($x_window[0] == $y_window[0]) { $quotient_value[$q_index] = self::$maxDigit; } else { $quotient_value[$q_index] = $this->_safe_divide( $x_window[0] * self::$baseFull + $x_window[1], $y_window[0] ); } $temp_value = array($y_window[1], $y_window[0]); $lhs->value = array($quotient_value[$q_index]); $lhs = $lhs->multiply($temp); $rhs_value = array($x_window[2], $x_window[1], $x_window[0]); while ($lhs->compare($rhs) > 0) { --$quotient_value[$q_index]; $lhs->value = array($quotient_value[$q_index]); $lhs = $lhs->multiply($temp); } $adjust = $this->_array_repeat(0, $q_index); $temp_value = array($quotient_value[$q_index]); $temp = $temp->multiply($y); $temp_value = &$temp->value; if (count($temp_value)) { $temp_value = array_merge($adjust, $temp_value); } $x = $x->subtract($temp); if ($x->compare($zero) < 0) { $temp_value = array_merge($adjust, $y_value); $x = $x->add($temp); --$quotient_value[$q_index]; } $x_max = count($x_value) - 1; } $x->_rshift($shift); $quotient->is_negative = $x_sign != $y_sign; if ($x_sign) { $y->_rshift($shift); $x = $y->subtract($x); } return array($this->_normalize($quotient), $this->_normalize($x)); } function _divide_digit($dividend, $divisor) { $carry = 0; $result = array(); for ($i = count($dividend) - 1; $i >= 0; --$i) { $temp = self::$baseFull * $carry + $dividend[$i]; $result[$i] = $this->_safe_divide($temp, $divisor); $carry = (int) ($temp - $divisor * $result[$i]); } return array($result, $carry); } function modPow($e, $n) { $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); if ($e->compare(new static()) < 0) { $e = $e->abs(); $temp = $this->modInverse($n); if ($temp === false) { return false; } return $this->_normalize($temp->modPow($e, $n)); } if (MATH_BIGINTEGER_MODE == self::MODE_GMP) { $temp = new static(); $temp->value = gmp_powm($this->value, $e->value, $n->value); return $this->_normalize($temp); } if ($this->compare(new static()) < 0 || $this->compare($n) > 0) { list(, $temp) = $this->divide($n); return $temp->modPow($e, $n); } if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { $components = array( 'modulus' => $n->toBytes(true), 'publicExponent' => $e->toBytes(true) ); $components = array( 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) ); $RSAPublicKey = pack( 'Ca*a*a*', 48, $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent'] ); $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); $RSAPublicKey = chr(0) . $RSAPublicKey; $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; $encapsulated = pack( 'Ca*a*', 48, $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey ); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(base64_encode($encapsulated)) . '-----END PUBLIC KEY-----'; $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) { return new static($result, 256); } } if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { $temp = new static(); $temp->value = bcpowmod($this->value, $e->value, $n->value, 0); return $this->_normalize($temp); } if (empty($e->value)) { $temp = new static(); $temp->value = array(1); return $this->_normalize($temp); } if ($e->value == array(1)) { list(, $temp) = $this->divide($n); return $this->_normalize($temp); } if ($e->value == array(2)) { $temp = new static(); $temp->value = $this->_square($this->value); list(, $temp) = $temp->divide($n); return $this->_normalize($temp); } return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT)); if ($n->value[0] & 1) { return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY)); } for ($i = 0; $i < count($n->value); ++$i) { if ($n->value[$i]) { $temp = decbin($n->value[$i]); $j = strlen($temp) - strrpos($temp, '1') - 1; $j+= 26 * $i; break; } } $mod1 = $n->copy(); $mod1->_rshift($j); $mod2 = new static(); $mod2->value = array(1); $mod2->_lshift($j); $part1 = ($mod1->value != array(1)) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static(); $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2); $y1 = $mod2->modInverse($mod1); $y2 = $mod1->modInverse($mod2); $result = $part1->multiply($mod2); $result = $result->multiply($y1); $temp = $part2->multiply($mod1); $temp = $temp->multiply($y2); $result = $result->add($temp); list(, $result) = $result->divide($n); return $this->_normalize($result); } function powMod($e, $n) { return $this->modPow($e, $n); } function _slidingWindow($e, $n, $mode) { static $window_ranges = array(7, 25, 81, 241, 673, 1793); $e_value = $e->value; $e_length = count($e_value) - 1; $e_bits = decbin($e_value[$e_length]); for ($i = $e_length - 1; $i >= 0; --$i) { $e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT); } $e_length = strlen($e_bits); for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) { } $n_value = $n->value; $powers = array(); $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode); $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode); $temp = 1 << ($window_size - 1); for ($i = 1; $i < $temp; ++$i) { $i2 = $i << 1; $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); } $result = array(1); $result = $this->_prepareReduce($result, $n_value, $mode); for ($i = 0; $i < $e_length;) { if (!$e_bits[$i]) { $result = $this->_squareReduce($result, $n_value, $mode); ++$i; } else { for ($j = $window_size - 1; $j > 0; --$j) { if (!empty($e_bits[$i + $j])) { break; } } for ($k = 0; $k <= $j; ++$k) { $result = $this->_squareReduce($result, $n_value, $mode); } $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); $i += $j + 1; } } $temp = new static(); $temp->value = $this->_reduce($result, $n_value, $mode); return $temp; } function _reduce($x, $n, $mode) { switch ($mode) { case self::MONTGOMERY: return $this->_montgomery($x, $n); case self::BARRETT: return $this->_barrett($x, $n); case self::POWEROF2: $lhs = new static(); $lhs->value = $x; $rhs = new static(); $rhs->value = $n; return $x->_mod2($n); case self::CLASSIC: $lhs = new static(); $lhs->value = $x; $rhs = new static(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; case self::NONE: return $x; default: } } function _prepareReduce($x, $n, $mode) { if ($mode == self::MONTGOMERY) { return $this->_prepMontgomery($x, $n); } return $this->_reduce($x, $n, $mode); } function _multiplyReduce($x, $y, $n, $mode) { if ($mode == self::MONTGOMERY) { return $this->_montgomeryMultiply($x, $y, $n); } $temp = $this->_multiply($x, false, $y, false); return $this->_reduce($temp[self::VALUE], $n, $mode); } function _squareReduce($x, $n, $mode) { if ($mode == self::MONTGOMERY) { return $this->_montgomeryMultiply($x, $x, $n); } return $this->_reduce($this->_square($x), $n, $mode); } function _mod2($n) { $temp = new static(); $temp->value = array(1); return $this->bitwise_and($n->subtract($temp)); } function _barrett($n, $m) { static $cache = array( self::VARIABLE => array(), self::DATA => array() ); $m_length = count($m); if (count($n) > 2 * $m_length) { $lhs = new static(); $rhs = new static(); $lhs->value = $n; $rhs->value = $m; list(, $temp) = $lhs->divide($rhs); return $temp->value; } if ($m_length < 5) { return $this->_regularBarrett($n, $m); } if (($key = array_search($m, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $m; $lhs = new static(); $lhs_value = &$lhs->value; $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1)); $lhs_value[] = 1; $rhs = new static(); $rhs->value = $m; list($u, $m1) = $lhs->divide($rhs); $u = $u->value; $m1 = $m1->value; $cache[self::DATA][] = array( 'u' => $u, 'm1'=> $m1 ); } else { extract($cache[self::DATA][$key]); } $cutoff = $m_length + ($m_length >> 1); $lsd = array_slice($n, 0, $cutoff); $msd = array_slice($n, $cutoff); $lsd = $this->_trim($lsd); $temp = $this->_multiply($msd, false, $m1, false); $n = $this->_add($lsd, false, $temp[self::VALUE], false); if ($m_length & 1) { return $this->_regularBarrett($n[self::VALUE], $m); } $temp = array_slice($n[self::VALUE], $m_length - 1); $temp = $this->_multiply($temp, false, $u, false); $temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1); $temp = $this->_multiply($temp, false, $m, false); $result = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false); while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false); } return $result[self::VALUE]; } function _regularBarrett($x, $n) { static $cache = array( self::VARIABLE => array(), self::DATA => array() ); $n_length = count($n); if (count($x) > 2 * $n_length) { $lhs = new static(); $rhs = new static(); $lhs->value = $x; $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; } if (($key = array_search($n, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $n; $lhs = new static(); $lhs_value = &$lhs->value; $lhs_value = $this->_array_repeat(0, 2 * $n_length); $lhs_value[] = 1; $rhs = new static(); $rhs->value = $n; list($temp, ) = $lhs->divide($rhs); $cache[self::DATA][] = $temp->value; } $temp = array_slice($x, $n_length - 1); $temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false); $temp = array_slice($temp[self::VALUE], $n_length + 1); $result = array_slice($x, 0, $n_length + 1); $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1); if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { $corrector_value = $this->_array_repeat(0, $n_length + 1); $corrector_value[count($corrector_value)] = 1; $result = $this->_add($result, false, $corrector_value, false); $result = $result[self::VALUE]; } $result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false); } return $result[self::VALUE]; } function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) { $x_length = count($x_value); $y_length = count($y_value); if (!$x_length || !$y_length) { return array( self::VALUE => array(), self::SIGN => false ); } if ($x_length < $y_length) { $temp = $x_value; $x_value = $y_value; $y_value = $temp; $x_length = count($x_value); $y_length = count($y_value); } $product_value = $this->_array_repeat(0, $x_length + $y_length); $carry = 0; for ($j = 0; $j < $x_length; ++$j) { $temp = $x_value[$j] * $y_value[0] + $carry; $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$j] = (int) ($temp - self::$baseFull * $carry); } if ($j < $stop) { $product_value[$j] = $carry; } for ($i = 1; $i < $y_length; ++$i) { $carry = 0; for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) { $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry; $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $product_value[$k] = (int) ($temp - self::$baseFull * $carry); } if ($k < $stop) { $product_value[$k] = $carry; } } return array( self::VALUE => $this->_trim($product_value), self::SIGN => $x_negative != $y_negative ); } function _montgomery($x, $n) { static $cache = array( self::VARIABLE => array(), self::DATA => array() ); if (($key = array_search($n, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $x; $cache[self::DATA][] = $this->_modInverse67108864($n); } $k = count($n); $result = array(self::VALUE => $x); for ($i = 0; $i < $k; ++$i) { $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); $temp = $this->_regularMultiply(array($temp), $n); $temp = array_merge($this->_array_repeat(0, $i), $temp); $result = $this->_add($result[self::VALUE], false, $temp, false); } $result[self::VALUE] = array_slice($result[self::VALUE], $k); if ($this->_compare($result, false, $n, false) >= 0) { $result = $this->_subtract($result[self::VALUE], false, $n, false); } return $result[self::VALUE]; } function _montgomeryMultiply($x, $y, $m) { $temp = $this->_multiply($x, false, $y, false); return $this->_montgomery($temp[self::VALUE], $m); static $cache = array( self::VARIABLE => array(), self::DATA => array() ); if (($key = array_search($m, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $m; $cache[self::DATA][] = $this->_modInverse67108864($m); } $n = max(count($x), count($y), count($m)); $x = array_pad($x, $n, 0); $y = array_pad($y, $n, 0); $m = array_pad($m, $n, 0); $a = array(self::VALUE => $this->_array_repeat(0, $n + 1)); for ($i = 0; $i < $n; ++$i) { $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); $temp = $temp * $cache[self::DATA][$key]; $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); $a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false); $a[self::VALUE] = array_slice($a[self::VALUE], 1); } if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) { $a = $this->_subtract($a[self::VALUE], false, $m, false); } return $a[self::VALUE]; } function _prepMontgomery($x, $n) { $lhs = new static(); $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x); $rhs = new static(); $rhs->value = $n; list(, $temp) = $lhs->divide($rhs); return $temp->value; } function _modInverse67108864($x) { $x = -$x[0]; $result = $x & 0x3; $result = ($result * (2 - $x * $result)) & 0xF; $result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; $result = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); return $result & self::$maxDigit; } function modInverse($n) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_invert($this->value, $n->value); return ($temp->value === false) ? false : $this->_normalize($temp); } static $zero, $one; if (!isset($zero)) { $zero = new static(); $one = new static(1); } $n = $n->abs(); if ($this->compare($zero) < 0) { $temp = $this->abs(); $temp = $temp->modInverse($n); return $this->_normalize($n->subtract($temp)); } extract($this->extendedGCD($n)); if (!$gcd->equals($one)) { return false; } $x = $x->compare($zero) < 0 ? $x->add($n) : $x; return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x); } function extendedGCD($n) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: extract(gmp_gcdext($this->value, $n->value)); return array( 'gcd' => $this->_normalize(new static($g)), 'x' => $this->_normalize(new static($s)), 'y' => $this->_normalize(new static($t)) ); case self::MODE_BCMATH: $u = $this->value; $v = $n->value; $a = '1'; $b = '0'; $c = '0'; $d = '1'; while (bccomp($v, '0', 0) != 0) { $q = bcdiv($u, $v, 0); $temp = $u; $u = $v; $v = bcsub($temp, bcmul($v, $q, 0), 0); $temp = $a; $a = $c; $c = bcsub($temp, bcmul($a, $q, 0), 0); $temp = $b; $b = $d; $d = bcsub($temp, bcmul($b, $q, 0), 0); } return array( 'gcd' => $this->_normalize(new static($u)), 'x' => $this->_normalize(new static($a)), 'y' => $this->_normalize(new static($b)) ); } $y = $n->copy(); $x = $this->copy(); $g = new static(); $g->value = array(1); while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) { $x->_rshift(1); $y->_rshift(1); $g->_lshift(1); } $u = $x->copy(); $v = $y->copy(); $a = new static(); $b = new static(); $c = new static(); $d = new static(); $a->value = $d->value = $g->value = array(1); $b->value = $c->value = array(); while (!empty($u->value)) { while (!($u->value[0] & 1)) { $u->_rshift(1); if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) { $a = $a->add($y); $b = $b->subtract($x); } $a->_rshift(1); $b->_rshift(1); } while (!($v->value[0] & 1)) { $v->_rshift(1); if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) { $c = $c->add($y); $d = $d->subtract($x); } $c->_rshift(1); $d->_rshift(1); } if ($u->compare($v) >= 0) { $u = $u->subtract($v); $a = $a->subtract($c); $b = $b->subtract($d); } else { $v = $v->subtract($u); $c = $c->subtract($a); $d = $d->subtract($b); } } return array( 'gcd' => $this->_normalize($g->multiply($v)), 'x' => $this->_normalize($c), 'y' => $this->_normalize($d) ); } function gcd($n) { extract($this->extendedGCD($n)); return $gcd; } function abs() { $temp = new static(); switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp->value = gmp_abs($this->value); break; case self::MODE_BCMATH: $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value; break; default: $temp->value = $this->value; } return $temp; } function compare($y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $r = gmp_cmp($this->value, $y->value); if ($r < -1) { $r = -1; } if ($r > 1) { $r = 1; } return $r; case self::MODE_BCMATH: return bccomp($this->value, $y->value, 0); } return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative); } function _compare($x_value, $x_negative, $y_value, $y_negative) { if ($x_negative != $y_negative) { return (!$x_negative && $y_negative) ? 1 : -1; } $result = $x_negative ? -1 : 1; if (count($x_value) != count($y_value)) { return (count($x_value) > count($y_value)) ? $result : -$result; } $size = max(count($x_value), count($y_value)); $x_value = array_pad($x_value, $size, 0); $y_value = array_pad($y_value, $size, 0); for ($i = count($x_value) - 1; $i >= 0; --$i) { if ($x_value[$i] != $y_value[$i]) { return ($x_value[$i] > $y_value[$i]) ? $result : -$result; } } return 0; } function equals($x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: return gmp_cmp($this->value, $x->value) == 0; default: return $this->value === $x->value && $this->is_negative == $x->is_negative; } } function setPrecision($bits) { $this->precision = $bits; if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) { $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); } else { $this->bitmask = new static(bcpow('2', $bits, 0)); } $temp = $this->_normalize($this); $this->value = $temp->value; } function bitwise_and($x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_and($this->value, $x->value); return $this->_normalize($temp); case self::MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return $this->_normalize(new static($left & $right, 256)); } $result = $this->copy(); $length = min(count($x->value), count($this->value)); $result->value = array_slice($result->value, 0, $length); for ($i = 0; $i < $length; ++$i) { $result->value[$i]&= $x->value[$i]; } return $this->_normalize($result); } function bitwise_or($x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_or($this->value, $x->value); return $this->_normalize($temp); case self::MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return $this->_normalize(new static($left | $right, 256)); } $length = max(count($this->value), count($x->value)); $result = $this->copy(); $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); for ($i = 0; $i < $length; ++$i) { $result->value[$i]|= $x->value[$i]; } return $this->_normalize($result); } function bitwise_xor($x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); $temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value)); return $this->_normalize($temp); case self::MODE_BCMATH: $left = $this->toBytes(); $right = $x->toBytes(); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return $this->_normalize(new static($left ^ $right, 256)); } $length = max(count($this->value), count($x->value)); $result = $this->copy(); $result->is_negative = false; $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); for ($i = 0; $i < $length; ++$i) { $result->value[$i]^= $x->value[$i]; } return $this->_normalize($result); } function bitwise_not() { $temp = $this->toBytes(); if ($temp == '') { return $this->_normalize(new static()); } $pre_msb = decbin(ord($temp[0])); $temp = ~$temp; $msb = decbin(ord($temp[0])); if (strlen($msb) == 8) { $msb = substr($msb, strpos($msb, '0')); } $temp[0] = chr(bindec($msb)); $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8; $new_bits = $this->precision - $current_bits; if ($new_bits <= 0) { return $this->_normalize(new static($temp, 256)); } $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); $this->_base256_lshift($leading_ones, $current_bits); $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); return $this->_normalize(new static($leading_ones | $temp, 256)); } function bitwise_rightShift($shift) { $temp = new static(); switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: static $two; if (!isset($two)) { $two = gmp_init('2'); } $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift)); break; case self::MODE_BCMATH: $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0); break; default: $temp->value = $this->value; $temp->_rshift($shift); } return $this->_normalize($temp); } function bitwise_leftShift($shift) { $temp = new static(); switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: static $two; if (!isset($two)) { $two = gmp_init('2'); } $temp->value = gmp_mul($this->value, gmp_pow($two, $shift)); break; case self::MODE_BCMATH: $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0); break; default: $temp->value = $this->value; $temp->_lshift($shift); } return $this->_normalize($temp); } function bitwise_leftRotate($shift) { $bits = $this->toBytes(); if ($this->precision > 0) { $precision = $this->precision; if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { $mask = $this->bitmask->subtract(new static(1)); $mask = $mask->toBytes(); } else { $mask = $this->bitmask->toBytes(); } } else { $temp = ord($bits[0]); for ($i = 0; $temp >> $i; ++$i) { } $precision = 8 * strlen($bits) - 8 + $i; $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3); } if ($shift < 0) { $shift+= $precision; } $shift%= $precision; if (!$shift) { return $this->copy(); } $left = $this->bitwise_leftShift($shift); $left = $left->bitwise_and(new static($mask, 256)); $right = $this->bitwise_rightShift($precision - $shift); $result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right); return $this->_normalize($result); } function bitwise_rightRotate($shift) { return $this->bitwise_leftRotate(-$shift); } function _random_number_helper($size) { if (class_exists('\phpseclib\Crypt\Random')) { $random = Random::string($size); } else { $random = ''; if ($size & 1) { $random.= chr(mt_rand(0, 255)); } $blocks = $size >> 1; for ($i = 0; $i < $blocks; ++$i) { $random.= pack('n', mt_rand(0, 0xFFFF)); } } return new static($random, 256); } function random($arg1, $arg2 = false) { if ($arg1 === false) { return false; } if ($arg2 === false) { $max = $arg1; $min = $this; } else { $min = $arg1; $max = $arg2; } $compare = $max->compare($min); if (!$compare) { return $this->_normalize($min); } elseif ($compare < 0) { $temp = $max; $max = $min; $min = $temp; } static $one; if (!isset($one)) { $one = new static(1); } $max = $max->subtract($min->subtract($one)); $size = strlen(ltrim($max->toBytes(), chr(0))); $random_max = new static(chr(1) . str_repeat("\0", $size), 256); $random = $this->_random_number_helper($size); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); while ($random->compare($max_multiple) >= 0) { $random = $random->subtract($max_multiple); $random_max = $random_max->subtract($max_multiple); $random = $random->bitwise_leftShift(8); $random = $random->add($this->_random_number_helper(1)); $random_max = $random_max->bitwise_leftShift(8); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); } list(, $random) = $random->divide($max); return $this->_normalize($random->add($min)); } function randomPrime($arg1, $arg2 = false, $timeout = false) { if ($arg1 === false) { return false; } if ($arg2 === false) { $max = $arg1; $min = $this; } else { $min = $arg1; $max = $arg2; } $compare = $max->compare($min); if (!$compare) { return $min->isPrime() ? $min : false; } elseif ($compare < 0) { $temp = $max; $max = $min; $min = $temp; } static $one, $two; if (!isset($one)) { $one = new static(1); $two = new static(2); } $start = time(); $x = $this->random($min, $max); if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { $p = new static(); $p->value = gmp_nextprime($x->value); if ($p->compare($max) <= 0) { return $p; } if (!$min->equals($x)) { $x = $x->subtract($one); } return $x->randomPrime($min, $x); } if ($x->equals($two)) { return $x; } $x->_make_odd(); if ($x->compare($max) > 0) { if ($min->equals($max)) { return false; } $x = $min->copy(); $x->_make_odd(); } $initial_x = $x->copy(); while (true) { if ($timeout !== false && time() - $start > $timeout) { return false; } if ($x->isPrime()) { return $x; } $x = $x->add($two); if ($x->compare($max) > 0) { $x = $min->copy(); if ($x->equals($two)) { return $x; } $x->_make_odd(); } if ($x->equals($initial_x)) { return false; } } } function _make_odd() { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: gmp_setbit($this->value, 0); break; case self::MODE_BCMATH: if ($this->value[strlen($this->value) - 1] % 2 == 0) { $this->value = bcadd($this->value, '1'); } break; default: $this->value[0] |= 1; } } function isPrime($t = false) { $length = strlen($this->toBytes()); if (!$t) { if ($length >= 163) { $t = 2; } else if ($length >= 106) { $t = 3; } else if ($length >= 81 ) { $t = 4; } else if ($length >= 68 ) { $t = 5; } else if ($length >= 56 ) { $t = 6; } else if ($length >= 50 ) { $t = 7; } else if ($length >= 43 ) { $t = 8; } else if ($length >= 37 ) { $t = 9; } else if ($length >= 31 ) { $t = 12; } else if ($length >= 25 ) { $t = 15; } else if ($length >= 18 ) { $t = 18; } else { $t = 27; } } switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: return gmp_prob_prime($this->value, $t) != 0; case self::MODE_BCMATH: if ($this->value === '2') { return true; } if ($this->value[strlen($this->value) - 1] % 2 == 0) { return false; } break; default: if ($this->value == array(2)) { return true; } if (~$this->value[0] & 1) { return false; } } static $primes, $zero, $one, $two; if (!isset($primes)) { $primes = array( 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 ); if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { for ($i = 0; $i < count($primes); ++$i) { $primes[$i] = new static($primes[$i]); } } $zero = new static(); $one = new static(1); $two = new static(2); } if ($this->equals($one)) { return false; } if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) { foreach ($primes as $prime) { list(, $r) = $this->divide($prime); if ($r->equals($zero)) { return $this->equals($prime); } } } else { $value = $this->value; foreach ($primes as $prime) { list(, $r) = $this->_divide_digit($value, $prime); if (!$r) { return count($value) == 1 && $value[0] == $prime; } } } $n = $this->copy(); $n_1 = $n->subtract($one); $n_2 = $n->subtract($two); $r = $n_1->copy(); $r_value = $r->value; if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { $s = 0; while ($r->value[strlen($r->value) - 1] % 2 == 0) { $r->value = bcdiv($r->value, '2', 0); ++$s; } } else { for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) { $temp = ~$r_value[$i] & 0xFFFFFF; for ($j = 1; ($temp >> $j) & 1; ++$j) { } if ($j != 25) { break; } } $s = 26 * $i + $j; $r->_rshift($s); } for ($i = 0; $i < $t; ++$i) { $a = $this->random($two, $n_2); $y = $a->modPow($r, $n); if (!$y->equals($one) && !$y->equals($n_1)) { for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) { $y = $y->modPow($two, $n); if ($y->equals($one)) { return false; } } if (!$y->equals($n_1)) { return false; } } } return true; } function _lshift($shift) { if ($shift == 0) { return; } $num_digits = (int) ($shift / self::$base); $shift %= self::$base; $shift = 1 << $shift; $carry = 0; for ($i = 0; $i < count($this->value); ++$i) { $temp = $this->value[$i] * $shift + $carry; $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31); $this->value[$i] = (int) ($temp - $carry * self::$baseFull); } if ($carry) { $this->value[count($this->value)] = $carry; } while ($num_digits--) { array_unshift($this->value, 0); } } function _rshift($shift) { if ($shift == 0) { return; } $num_digits = (int) ($shift / self::$base); $shift %= self::$base; $carry_shift = self::$base - $shift; $carry_mask = (1 << $shift) - 1; if ($num_digits) { $this->value = array_slice($this->value, $num_digits); } $carry = 0; for ($i = count($this->value) - 1; $i >= 0; --$i) { $temp = $this->value[$i] >> $shift | $carry; $carry = ($this->value[$i] & $carry_mask) << $carry_shift; $this->value[$i] = $temp; } $this->value = $this->_trim($this->value); } function _normalize($result) { $result->precision = $this->precision; $result->bitmask = $this->bitmask; switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: if ($this->bitmask !== false) { $flip = gmp_cmp($result->value, gmp_init(0)) < 0; if ($flip) { $result->value = gmp_neg($result->value); } $result->value = gmp_and($result->value, $result->bitmask->value); if ($flip) { $result->value = gmp_neg($result->value); } } return $result; case self::MODE_BCMATH: if (!empty($result->bitmask->value)) { $result->value = bcmod($result->value, $result->bitmask->value); } return $result; } $value = &$result->value; if (!count($value)) { $result->is_negative = false; return $result; } $value = $this->_trim($value); if (!empty($result->bitmask->value)) { $length = min(count($value), count($this->bitmask->value)); $value = array_slice($value, 0, $length); for ($i = 0; $i < $length; ++$i) { $value[$i] = $value[$i] & $this->bitmask->value[$i]; } } return $result; } function _trim($value) { for ($i = count($value) - 1; $i >= 0; --$i) { if ($value[$i]) { break; } unset($value[$i]); } return $value; } function _array_repeat($input, $multiplier) { return ($multiplier) ? array_fill(0, $multiplier, $input) : array(); } function _base256_lshift(&$x, $shift) { if ($shift == 0) { return; } $num_bytes = $shift >> 3; $shift &= 7; $carry = 0; for ($i = strlen($x) - 1; $i >= 0; --$i) { $temp = ord($x[$i]) << $shift | $carry; $x[$i] = chr($temp); $carry = $temp >> 8; } $carry = ($carry != 0) ? chr($carry) : ''; $x = $carry . $x . str_repeat(chr(0), $num_bytes); } function _base256_rshift(&$x, $shift) { if ($shift == 0) { $x = ltrim($x, chr(0)); return ''; } $num_bytes = $shift >> 3; $shift &= 7; $remainder = ''; if ($num_bytes) { $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes; $remainder = substr($x, $start); $x = substr($x, 0, -$num_bytes); } $carry = 0; $carry_shift = 8 - $shift; for ($i = 0; $i < strlen($x); ++$i) { $temp = (ord($x[$i]) >> $shift) | $carry; $carry = (ord($x[$i]) << $carry_shift) & 0xFF; $x[$i] = chr($temp); } $x = ltrim($x, chr(0)); $remainder = chr($carry >> $carry_shift) . $remainder; return ltrim($remainder, chr(0)); } function _int2bytes($x) { return ltrim(pack('N', $x), chr(0)); } function _bytes2int($x) { $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); return $temp['int']; } function _encodeASN1Length($length) { if ($length <= 0x7F) { return chr($length); } $temp = ltrim(pack('N', $length), chr(0)); return pack('Ca*', 0x80 | strlen($temp), $temp); } function _safe_divide($x, $y) { if (self::$base === 26) { return (int) ($x / $y); } return ($x - ($x % $y)) / $y; } } current_key_length != 128 || strlen($this->orig_key) < 16) { return false; } $this->cipher_name_openssl_ecb = 'rc2-ecb'; $this->cipher_name_openssl = 'rc2-' . $this->_openssl_translate_mode(); } return parent::isValidEngine($engine); } function setKeyLength($length) { if ($length < 8) { $this->default_key_length = 1; } elseif ($length > 1024) { $this->default_key_length = 128; } else { $this->default_key_length = $length; } $this->current_key_length = $this->default_key_length; parent::setKeyLength($length); } function getKeyLength() { return $this->current_key_length; } function setKey($key, $t1 = 0) { $this->orig_key = $key; if ($t1 <= 0) { $t1 = $this->default_key_length; } elseif ($t1 > 1024) { $t1 = 1024; } $this->current_key_length = $t1; $key = strlen($key) ? substr($key, 0, 128) : "\x00"; $t = strlen($key); $l = array_values(unpack('C*', $key)); $t8 = ($t1 + 7) >> 3; $tm = 0xFF >> (8 * $t8 - $t1); $pitable = $this->pitable; for ($i = $t; $i < 128; $i++) { $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]]; } $i = 128 - $t8; $l[$i] = $pitable[$l[$i] & $tm]; while ($i--) { $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]]; } $l[0] = $this->invpitable[$l[0]]; array_unshift($l, 'C*'); parent::setKey(call_user_func_array('pack', $l)); } function encrypt($plaintext) { if ($this->engine == self::ENGINE_OPENSSL) { $temp = $this->key; $this->key = $this->orig_key; $result = parent::encrypt($plaintext); $this->key = $temp; return $result; } return parent::encrypt($plaintext); } function decrypt($ciphertext) { if ($this->engine == self::ENGINE_OPENSSL) { $temp = $this->key; $this->key = $this->orig_key; $result = parent::decrypt($ciphertext); $this->key = $temp; return $result; } return parent::decrypt($ciphertext); } function _encryptBlock($in) { list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); $keys = $this->keys; $limit = 20; $actions = array($limit => 44, 44 => 64); $j = 0; for (;;) { $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; $r0 |= $r0 >> 16; $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; $r1 |= $r1 >> 16; $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; $r2 |= $r2 >> 16; $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; $r3 |= $r3 >> 16; if ($j === $limit) { if ($limit === 64) { break; } $r0 += $keys[$r3 & 0x3F]; $r1 += $keys[$r0 & 0x3F]; $r2 += $keys[$r1 & 0x3F]; $r3 += $keys[$r2 & 0x3F]; $limit = $actions[$limit]; } } return pack('vvvv', $r0, $r1, $r2, $r3); } function _decryptBlock($in) { list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in)); $keys = $this->keys; $limit = 44; $actions = array($limit => 20, 20 => 0); $j = 64; for (;;) { $r3 = ($r3 | ($r3 << 16)) >> 5; $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; $r2 = ($r2 | ($r2 << 16)) >> 3; $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; $r1 = ($r1 | ($r1 << 16)) >> 2; $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; $r0 = ($r0 | ($r0 << 16)) >> 1; $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF; if ($j === $limit) { if ($limit === 0) { break; } $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF; $limit = $actions[$limit]; } } return pack('vvvv', $r0, $r1, $r2, $r3); } function _setupMcrypt() { if (!isset($this->key)) { $this->setKey(''); } parent::_setupMcrypt(); } function _setupKey() { if (!isset($this->key)) { $this->setKey(''); } $l = unpack('Ca/Cb/v*', $this->key); array_unshift($l, $this->pitable[$l['a']] | ($l['b'] << 8)); unset($l['a']); unset($l['b']); $this->keys = $l; } function _setupInlineCrypt() { $lambda_functions =& self::_getLambdaFunctions(); $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); $code_hash = "Crypt_RC2, {$this->mode}"; if ($gen_hi_opt_code) { $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } if (!isset($lambda_functions[$code_hash])) { $init_crypt = '$keys = $self->keys;'; switch (true) { case $gen_hi_opt_code: $keys = $this->keys; default: $keys = array(); foreach ($this->keys as $k => $v) { $keys[$k] = '$keys[' . $k . ']'; } } $encrypt_block = $decrypt_block = ' $in = unpack("v4", $in); $r0 = $in[1]; $r1 = $in[2]; $r2 = $in[3]; $r3 = $in[4]; '; $limit = 20; $actions = array($limit => 44, 44 => 64); $j = 0; for (;;) { $encrypt_block .= ' $r0 = (($r0 + ' . $keys[$j++] . ' + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1; $r0 |= $r0 >> 16; $r1 = (($r1 + ' . $keys[$j++] . ' + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2; $r1 |= $r1 >> 16; $r2 = (($r2 + ' . $keys[$j++] . ' + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3; $r2 |= $r2 >> 16; $r3 = (($r3 + ' . $keys[$j++] . ' + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5; $r3 |= $r3 >> 16;'; if ($j === $limit) { if ($limit === 64) { break; } $encrypt_block .= ' $r0 += $keys[$r3 & 0x3F]; $r1 += $keys[$r0 & 0x3F]; $r2 += $keys[$r1 & 0x3F]; $r3 += $keys[$r2 & 0x3F];'; $limit = $actions[$limit]; } } $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; $limit = 44; $actions = array($limit => 20, 20 => 0); $j = 64; for (;;) { $decrypt_block .= ' $r3 = ($r3 | ($r3 << 16)) >> 5; $r3 = ($r3 - ' . $keys[--$j] . ' - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF; $r2 = ($r2 | ($r2 << 16)) >> 3; $r2 = ($r2 - ' . $keys[--$j] . ' - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF; $r1 = ($r1 | ($r1 << 16)) >> 2; $r1 = ($r1 - ' . $keys[--$j] . ' - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF; $r0 = ($r0 | ($r0 << 16)) >> 1; $r0 = ($r0 - ' . $keys[--$j] . ' - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;'; if ($j === $limit) { if ($limit === 0) { break; } $decrypt_block .= ' $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF; $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF; $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF; $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;'; $limit = $actions[$limit]; } } $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);'; $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( array( 'init_crypt' => $init_crypt, 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ) ); } $this->inline_crypt = $lambda_functions[$code_hash]; } } =')) { try { return \random_bytes($length); } catch (\Throwable $e) { } } if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { if (extension_loaded('mcrypt') && function_exists('class_alias')) { return @mcrypt_create_iv($length); } if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) { return openssl_random_pseudo_bytes($length); } } else { if (extension_loaded('openssl')) { return openssl_random_pseudo_bytes($length); } static $fp = true; if ($fp === true) { $fp = @fopen('/dev/urandom', 'rb'); } if ($fp !== true && $fp !== false) { $temp = fread($fp, $length); if (strlen($temp) == $length) { return $temp; } } if (extension_loaded('mcrypt')) { return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); } } static $crypto = false, $v; if ($crypto === false) { $old_session_id = session_id(); $old_use_cookies = ini_get('session.use_cookies'); $old_session_cache_limiter = session_cache_limiter(); $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false; if ($old_session_id != '') { session_write_close(); } session_id(1); ini_set('session.use_cookies', 0); session_cache_limiter(''); session_start(); $v = $seed = $_SESSION['seed'] = pack('H*', sha1( (isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') . (isset($_POST) ? phpseclib_safe_serialize($_POST) : '') . (isset($_GET) ? phpseclib_safe_serialize($_GET) : '') . (isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') . phpseclib_safe_serialize($GLOBALS) . phpseclib_safe_serialize($_SESSION) . phpseclib_safe_serialize($_OLD_SESSION) )); if (!isset($_SESSION['count'])) { $_SESSION['count'] = 0; } $_SESSION['count']++; session_write_close(); if ($old_session_id != '') { session_id($old_session_id); session_start(); ini_set('session.use_cookies', $old_use_cookies); session_cache_limiter($old_session_cache_limiter); } else { if ($_OLD_SESSION !== false) { $_SESSION = $_OLD_SESSION; unset($_OLD_SESSION); } else { unset($_SESSION); } } $key = pack('H*', sha1($seed . 'A')); $iv = pack('H*', sha1($seed . 'C')); switch (true) { case class_exists('\phpseclib\Crypt\AES'): $crypto = new AES(Base::MODE_CTR); break; case class_exists('\phpseclib\Crypt\Twofish'): $crypto = new Twofish(Base::MODE_CTR); break; case class_exists('\phpseclib\Crypt\Blowfish'): $crypto = new Blowfish(Base::MODE_CTR); break; case class_exists('\phpseclib\Crypt\TripleDES'): $crypto = new TripleDES(Base::MODE_CTR); break; case class_exists('\phpseclib\Crypt\DES'): $crypto = new DES(Base::MODE_CTR); break; case class_exists('\phpseclib\Crypt\RC4'): $crypto = new RC4(); break; default: user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded'); return false; } $crypto->setKey($key); $crypto->setIV($iv); $crypto->enableContinuousBuffer(); } $result = ''; while (strlen($result) < $length) { $i = $crypto->encrypt(microtime()); $r = $crypto->encrypt($i ^ $v); $v = $crypto->encrypt($r ^ $i); $result.= $r; } return substr($result, 0, $length); } } if (!function_exists('phpseclib_safe_serialize')) { function phpseclib_safe_serialize(&$arr) { if (is_object($arr)) { return ''; } if (!is_array($arr)) { return serialize($arr); } if (isset($arr['__phpseclib_marker'])) { return ''; } $safearr = array(); $arr['__phpseclib_marker'] = true; foreach (array_keys($arr) as $key) { if ($key !== '__phpseclib_marker') { $safearr[$key] = phpseclib_safe_serialize($arr[$key]); } } unset($arr['__phpseclib_marker']); return serialize($safearr); } } key_length = 16; break; case $length <= 160: $this->key_length = 20; break; case $length <= 192: $this->key_length = 24; break; case $length <= 224: $this->key_length = 28; break; default: $this->key_length = 32; } parent::setKeyLength($length); } function setBlockLength($length) { $length >>= 5; if ($length > 8) { $length = 8; } elseif ($length < 4) { $length = 4; } $this->Nb = $length; $this->block_size = $length << 2; $this->changed = true; $this->_setEngine(); } function isValidEngine($engine) { switch ($engine) { case self::ENGINE_OPENSSL: if ($this->block_size != 16) { return false; } $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb'; $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->_openssl_translate_mode(); break; case self::ENGINE_MCRYPT: $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3); if ($this->key_length % 8) { return false; } } return parent::isValidEngine($engine); } function _encryptBlock($in) { static $tables; if (empty($tables)) { $tables = &$this->_getTables(); } $t0 = $tables[0]; $t1 = $tables[1]; $t2 = $tables[2]; $t3 = $tables[3]; $sbox = $tables[4]; $state = array(); $words = unpack('N*', $in); $c = $this->c; $w = $this->w; $Nb = $this->Nb; $Nr = $this->Nr; $wc = $Nb - 1; foreach ($words as $word) { $state[] = $word ^ $w[++$wc]; } $temp = array(); for ($round = 1; $round < $Nr; ++$round) { $i = 0; $j = $c[1]; $k = $c[2]; $l = $c[3]; while ($i < $Nb) { $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^ $t1[$state[$j] >> 16 & 0x000000FF] ^ $t2[$state[$k] >> 8 & 0x000000FF] ^ $t3[$state[$l] & 0x000000FF] ^ $w[++$wc]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } $state = $temp; } for ($i = 0; $i < $Nb; ++$i) { $state[$i] = $sbox[$state[$i] & 0x000000FF] | ($sbox[$state[$i] >> 8 & 0x000000FF] << 8) | ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) | ($sbox[$state[$i] >> 24 & 0x000000FF] << 24); } $i = 0; $j = $c[1]; $k = $c[2]; $l = $c[3]; while ($i < $Nb) { $temp[$i] = ($state[$i] & 0xFF000000) ^ ($state[$j] & 0x00FF0000) ^ ($state[$k] & 0x0000FF00) ^ ($state[$l] & 0x000000FF) ^ $w[$i]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } switch ($Nb) { case 8: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]); case 7: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]); case 6: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]); case 5: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]); default: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); } } function _decryptBlock($in) { static $invtables; if (empty($invtables)) { $invtables = &$this->_getInvTables(); } $dt0 = $invtables[0]; $dt1 = $invtables[1]; $dt2 = $invtables[2]; $dt3 = $invtables[3]; $isbox = $invtables[4]; $state = array(); $words = unpack('N*', $in); $c = $this->c; $dw = $this->dw; $Nb = $this->Nb; $Nr = $this->Nr; $wc = $Nb - 1; foreach ($words as $word) { $state[] = $word ^ $dw[++$wc]; } $temp = array(); for ($round = $Nr - 1; $round > 0; --$round) { $i = 0; $j = $Nb - $c[1]; $k = $Nb - $c[2]; $l = $Nb - $c[3]; while ($i < $Nb) { $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^ $dt1[$state[$j] >> 16 & 0x000000FF] ^ $dt2[$state[$k] >> 8 & 0x000000FF] ^ $dt3[$state[$l] & 0x000000FF] ^ $dw[++$wc]; ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } $state = $temp; } $i = 0; $j = $Nb - $c[1]; $k = $Nb - $c[2]; $l = $Nb - $c[3]; while ($i < $Nb) { $word = ($state[$i] & 0xFF000000) | ($state[$j] & 0x00FF0000) | ($state[$k] & 0x0000FF00) | ($state[$l] & 0x000000FF); $temp[$i] = $dw[$i] ^ ($isbox[$word & 0x000000FF] | ($isbox[$word >> 8 & 0x000000FF] << 8) | ($isbox[$word >> 16 & 0x000000FF] << 16) | ($isbox[$word >> 24 & 0x000000FF] << 24)); ++$i; $j = ($j + 1) % $Nb; $k = ($k + 1) % $Nb; $l = ($l + 1) % $Nb; } switch ($Nb) { case 8: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]); case 7: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]); case 6: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]); case 5: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]); default: return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]); } } function _setupKey() { static $rcon = array(0, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000, 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000, 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000, 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000 ); if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) { return; } $this->kl = array('key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size); $this->Nk = $this->key_length >> 2; $this->Nr = max($this->Nk, $this->Nb) + 6; switch ($this->Nb) { case 4: case 5: case 6: $this->c = array(0, 1, 2, 3); break; case 7: $this->c = array(0, 1, 2, 4); break; case 8: $this->c = array(0, 1, 3, 4); } $w = array_values(unpack('N*words', $this->key)); $length = $this->Nb * ($this->Nr + 1); for ($i = $this->Nk; $i < $length; $i++) { $temp = $w[$i - 1]; if ($i % $this->Nk == 0) { $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk]; } elseif ($this->Nk > 6 && $i % $this->Nk == 4) { $temp = $this->_subWord($temp); } $w[$i] = $w[$i - $this->Nk] ^ $temp; } list($dt0, $dt1, $dt2, $dt3) = $this->_getInvTables(); $temp = $this->w = $this->dw = array(); for ($i = $row = $col = 0; $i < $length; $i++, $col++) { if ($col == $this->Nb) { if ($row == 0) { $this->dw[0] = $this->w[0]; } else { $j = 0; while ($j < $this->Nb) { $dw = $this->_subWord($this->w[$row][$j]); $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^ $dt1[$dw >> 16 & 0x000000FF] ^ $dt2[$dw >> 8 & 0x000000FF] ^ $dt3[$dw & 0x000000FF]; $j++; } $this->dw[$row] = $temp; } $col = 0; $row++; } $this->w[$row][$col] = $w[$i]; } $this->dw[$row] = $this->w[$row]; $this->dw = array_reverse($this->dw); $w = array_pop($this->w); $dw = array_pop($this->dw); foreach ($this->w as $r => $wr) { foreach ($wr as $c => $wc) { $w[] = $wc; $dw[] = $this->dw[$r][$c]; } } $this->w = $w; $this->dw = $dw; } function _subWord($word) { static $sbox; if (empty($sbox)) { list(, , , , $sbox) = $this->_getTables(); } return $sbox[$word & 0x000000FF] | ($sbox[$word >> 8 & 0x000000FF] << 8) | ($sbox[$word >> 16 & 0x000000FF] << 16) | ($sbox[$word >> 24 & 0x000000FF] << 24); } function &_getTables() { static $tables; if (empty($tables)) { $t3 = array_map('intval', array( 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B, 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83, 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A, 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F, 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA, 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B, 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713, 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6, 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85, 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411, 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B, 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1, 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF, 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E, 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6, 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B, 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD, 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8, 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2, 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049, 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810, 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197, 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F, 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C, 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927, 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733, 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5, 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0, 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C )); foreach ($t3 as $t3i) { $t0[] = (($t3i << 24) & 0xFF000000) | (($t3i >> 8) & 0x00FFFFFF); $t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i >> 16) & 0x0000FFFF); $t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i >> 24) & 0x000000FF); } $tables = array( $t0, $t1, $t2, $t3, array( 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 ) ); } return $tables; } function &_getInvTables() { static $tables; if (empty($tables)) { $dt3 = array_map('intval', array( 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E, 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D, 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9, 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66, 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED, 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4, 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD, 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60, 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79, 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C, 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24, 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C, 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814, 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B, 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084, 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077, 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22, 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F, 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582, 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB, 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF, 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035, 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17, 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46, 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D, 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A, 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678, 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF, 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0 )); foreach ($dt3 as $dt3i) { $dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >> 8) & 0x00FFFFFF); $dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF); $dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF); }; $tables = array( $dt0, $dt1, $dt2, $dt3, array( 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D ) ); } return $tables; } function _setupInlineCrypt() { $lambda_functions =& self::_getLambdaFunctions(); $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); $code_hash = "Crypt_Rijndael, {$this->mode}, {$this->Nr}, {$this->Nb}"; if ($gen_hi_opt_code) { $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: $w = $this->w; $dw = $this->dw; $init_encrypt = ''; $init_decrypt = ''; break; default: for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) { $w[] = '$w[' . $i . ']'; $dw[] = '$dw[' . $i . ']'; } $init_encrypt = '$w = $self->w;'; $init_decrypt = '$dw = $self->dw;'; } $Nr = $this->Nr; $Nb = $this->Nb; $c = $this->c; $init_encrypt.= ' static $tables; if (empty($tables)) { $tables = &$self->_getTables(); } $t0 = $tables[0]; $t1 = $tables[1]; $t2 = $tables[2]; $t3 = $tables[3]; $sbox = $tables[4]; '; $s = 'e'; $e = 's'; $wc = $Nb - 1; $encrypt_block = '$in = unpack("N*", $in);'."\n"; for ($i = 0; $i < $Nb; ++$i) { $encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n"; } for ($round = 1; $round < $Nr; ++$round) { list($s, $e) = array($e, $s); for ($i = 0; $i < $Nb; ++$i) { $encrypt_block.= '$'.$e.$i.' = $t0[($'.$s.$i .' >> 24) & 0xff] ^ $t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^ $t2[($'.$s.(($i + $c[2]) % $Nb).' >> 8) & 0xff] ^ $t3[ $'.$s.(($i + $c[3]) % $Nb).' & 0xff] ^ '.$w[++$wc].";\n"; } } for ($i = 0; $i < $Nb; ++$i) { $encrypt_block.= '$'.$e.$i.' = $sbox[ $'.$e.$i.' & 0xff] | ($sbox[($'.$e.$i.' >> 8) & 0xff] << 8) | ($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) | ($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n"; } $encrypt_block .= '$in = pack("N*"'."\n"; for ($i = 0; $i < $Nb; ++$i) { $encrypt_block.= ', ($'.$e.$i .' & '.((int)0xFF000000).') ^ ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000 ) ^ ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00 ) ^ ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF ) ^ '.$w[$i]."\n"; } $encrypt_block .= ');'; $init_decrypt.= ' static $invtables; if (empty($invtables)) { $invtables = &$self->_getInvTables(); } $dt0 = $invtables[0]; $dt1 = $invtables[1]; $dt2 = $invtables[2]; $dt3 = $invtables[3]; $isbox = $invtables[4]; '; $s = 'e'; $e = 's'; $wc = $Nb - 1; $decrypt_block = '$in = unpack("N*", $in);'."\n"; for ($i = 0; $i < $Nb; ++$i) { $decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n"; } for ($round = 1; $round < $Nr; ++$round) { list($s, $e) = array($e, $s); for ($i = 0; $i < $Nb; ++$i) { $decrypt_block.= '$'.$e.$i.' = $dt0[($'.$s.$i .' >> 24) & 0xff] ^ $dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^ $dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >> 8) & 0xff] ^ $dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).' & 0xff] ^ '.$dw[++$wc].";\n"; } } for ($i = 0; $i < $Nb; ++$i) { $decrypt_block.= '$'.$e.$i.' = $isbox[ $'.$e.$i.' & 0xff] | ($isbox[($'.$e.$i.' >> 8) & 0xff] << 8) | ($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) | ($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n"; } $decrypt_block .= '$in = pack("N*"'."\n"; for ($i = 0; $i < $Nb; ++$i) { $decrypt_block.= ', ($'.$e.$i. ' & '.((int)0xFF000000).') ^ ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000 ) ^ ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00 ) ^ ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF ) ^ '.$dw[$i]."\n"; } $decrypt_block .= ');'; $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( array( 'init_crypt' => '', 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ) ); } $this->inline_crypt = $lambda_functions[$code_hash]; } } key_length = 4; } elseif ($length > 448) { $this->key_length = 56; } else { $this->key_length = $length >> 3; } parent::setKeyLength($length); } function isValidEngine($engine) { if ($engine == self::ENGINE_OPENSSL) { if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) { return false; } if ($this->key_length < 16) { return false; } $this->cipher_name_openssl_ecb = 'bf-ecb'; $this->cipher_name_openssl = 'bf-' . $this->_openssl_translate_mode(); } return parent::isValidEngine($engine); } function _setupKey() { if (isset($this->kl['key']) && $this->key === $this->kl['key']) { return; } $this->kl = array('key' => $this->key); $this->bctx = array( 'p' => array(), 'sb' => array( $this->sbox0, $this->sbox1, $this->sbox2, $this->sbox3 ) ); $key = array_values(unpack('C*', $this->key)); $keyl = count($key); for ($j = 0, $i = 0; $i < 18; ++$i) { for ($data = 0, $k = 0; $k < 4; ++$k) { $data = ($data << 8) | $key[$j]; if (++$j >= $keyl) { $j = 0; } } $this->bctx['p'][] = $this->parray[$i] ^ $data; } $data = "\0\0\0\0\0\0\0\0"; for ($i = 0; $i < 18; $i += 2) { list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); $this->bctx['p'][$i ] = $l; $this->bctx['p'][$i + 1] = $r; } for ($i = 0; $i < 4; ++$i) { for ($j = 0; $j < 256; $j += 2) { list($l, $r) = array_values(unpack('N*', $data = $this->_encryptBlock($data))); $this->bctx['sb'][$i][$j ] = $l; $this->bctx['sb'][$i][$j + 1] = $r; } } } function _encryptBlock($in) { $p = $this->bctx["p"]; $sb_0 = $this->bctx["sb"][0]; $sb_1 = $this->bctx["sb"][1]; $sb_2 = $this->bctx["sb"][2]; $sb_3 = $this->bctx["sb"][3]; $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; for ($i = 0; $i < 16; $i+= 2) { $l^= $p[$i]; $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ $sb_2[$l >> 8 & 0xff]) + $sb_3[$l & 0xff]); $r^= $p[$i + 1]; $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]); } return pack("N*", $r ^ $p[17], $l ^ $p[16]); } function _decryptBlock($in) { $p = $this->bctx["p"]; $sb_0 = $this->bctx["sb"][0]; $sb_1 = $this->bctx["sb"][1]; $sb_2 = $this->bctx["sb"][2]; $sb_3 = $this->bctx["sb"][3]; $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; for ($i = 17; $i > 2; $i-= 2) { $l^= $p[$i]; $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ $sb_2[$l >> 8 & 0xff]) + $sb_3[$l & 0xff]); $r^= $p[$i - 1]; $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]); } return pack("N*", $r ^ $p[0], $l ^ $p[1]); } function _setupInlineCrypt() { $lambda_functions =& self::_getLambdaFunctions(); $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); $code_hash = "Crypt_Blowfish, {$this->mode}"; if ($gen_hi_opt_code) { $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } $safeint = $this->safe_intval_inline(); if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: $p = $this->bctx['p']; $init_crypt = ' static $sb_0, $sb_1, $sb_2, $sb_3; if (!$sb_0) { $sb_0 = $self->bctx["sb"][0]; $sb_1 = $self->bctx["sb"][1]; $sb_2 = $self->bctx["sb"][2]; $sb_3 = $self->bctx["sb"][3]; } '; break; default: $p = array(); for ($i = 0; $i < 18; ++$i) { $p[] = '$p_' . $i; } $init_crypt = ' list($sb_0, $sb_1, $sb_2, $sb_3) = $self->bctx["sb"]; list(' . implode(',', $p) . ') = $self->bctx["p"]; '; } $encrypt_block = ' $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; '; for ($i = 0; $i < 16; $i+= 2) { $encrypt_block.= ' $l^= ' . $p[$i] . '; $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i + 1] . '; $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]') . '; '; } $encrypt_block.= ' $in = pack("N*", $r ^ ' . $p[17] . ', $l ^ ' . $p[16] . ' ); '; $decrypt_block = ' $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; '; for ($i = 17; $i > 2; $i-= 2) { $decrypt_block.= ' $l^= ' . $p[$i] . '; $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i - 1] . '; $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + $sb_3[$r & 0xff]') . '; '; } $decrypt_block.= ' $in = pack("N*", $r ^ ' . $p[0] . ', $l ^ ' . $p[1] . ' ); '; $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( array( 'init_crypt' => $init_crypt, 'init_encrypt' => '', 'init_decrypt' => '', 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ) ); } $this->inline_crypt = $lambda_functions[$code_hash]; } } key_length = 16; break; case $length <= 192: $this->key_length = 24; break; default: $this->key_length = 32; } parent::setKeyLength($length); } function _setupKey() { if (isset($this->kl['key']) && $this->key === $this->kl['key']) { return; } $this->kl = array('key' => $this->key); $le_longs = unpack('V*', $this->key); $key = unpack('C*', $this->key); $m0 = $this->m0; $m1 = $this->m1; $m2 = $this->m2; $m3 = $this->m3; $q0 = $this->q0; $q1 = $this->q1; $K = $S0 = $S1 = $S2 = $S3 = array(); switch (strlen($this->key)) { case 16: list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[1], $le_longs[2]); list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[3], $le_longs[4]); for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { $A = $m0[$q0[$q0[$i] ^ $key[ 9]] ^ $key[1]] ^ $m1[$q0[$q1[$i] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$i] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$i] ^ $key[12]] ^ $key[4]]; $B = $m0[$q0[$q0[$j] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$j] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); $A = $this->safe_intval($A + $B); $K[] = $A; $A = $this->safe_intval($A + $B); $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; $S1[$i] = $m1[$q0[$q1[$i] ^ $s5] ^ $s1]; $S2[$i] = $m2[$q1[$q0[$i] ^ $s6] ^ $s2]; $S3[$i] = $m3[$q1[$q1[$i] ^ $s7] ^ $s3]; } break; case 24: list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[1], $le_longs[2]); list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[3], $le_longs[4]); list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[5], $le_longs[6]); for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { $A = $m0[$q0[$q0[$q1[$i] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ $m1[$q0[$q1[$q1[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$q0[$i] ^ $key[20]] ^ $key[12]] ^ $key[4]]; $B = $m0[$q0[$q0[$q1[$j] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$q1[$j] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); $A = $this->safe_intval($A + $B); $K[] = $A; $A = $this->safe_intval($A + $B); $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; $S1[$i] = $m1[$q0[$q1[$q1[$i] ^ $s9] ^ $s5] ^ $s1]; $S2[$i] = $m2[$q1[$q0[$q0[$i] ^ $sa] ^ $s6] ^ $s2]; $S3[$i] = $m3[$q1[$q1[$q0[$i] ^ $sb] ^ $s7] ^ $s3]; } break; default: list($sf, $se, $sd, $sc) = $this->_mdsrem($le_longs[1], $le_longs[2]); list($sb, $sa, $s9, $s8) = $this->_mdsrem($le_longs[3], $le_longs[4]); list($s7, $s6, $s5, $s4) = $this->_mdsrem($le_longs[5], $le_longs[6]); list($s3, $s2, $s1, $s0) = $this->_mdsrem($le_longs[7], $le_longs[8]); for ($i = 0, $j = 1; $i < 40; $i+= 2, $j+= 2) { $A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[25]] ^ $key[17]] ^ $key[ 9]] ^ $key[1]] ^ $m1[$q0[$q1[$q1[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^ $m2[$q1[$q0[$q0[$q0[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]] ^ $key[3]] ^ $m3[$q1[$q1[$q0[$q1[$i] ^ $key[28]] ^ $key[20]] ^ $key[12]] ^ $key[4]]; $B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]] ^ $key[5]] ^ $m1[$q0[$q1[$q1[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] ^ $key[6]] ^ $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); $A = $this->safe_intval($A + $B); $K[] = $A; $A = $this->safe_intval($A + $B); $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; $S1[$i] = $m1[$q0[$q1[$q1[$q0[$i] ^ $sd] ^ $s9] ^ $s5] ^ $s1]; $S2[$i] = $m2[$q1[$q0[$q0[$q0[$i] ^ $se] ^ $sa] ^ $s6] ^ $s2]; $S3[$i] = $m3[$q1[$q1[$q0[$q1[$i] ^ $sf] ^ $sb] ^ $s7] ^ $s3]; } } $this->K = $K; $this->S0 = $S0; $this->S1 = $S1; $this->S2 = $S2; $this->S3 = $S3; } function _mdsrem($A, $B) { for ($i = 0; $i < 8; ++$i) { $t = 0xff & ($B >> 24); $B = ($B << 8) | (0xff & ($A >> 24)); $A<<= 8; $u = $t << 1; if ($t & 0x80) { $u^= 0x14d; } $B ^= $t ^ ($u << 16); $u^= 0x7fffffff & ($t >> 1); if ($t & 0x01) { $u^= 0xa6 ; } $B^= ($u << 24) | ($u << 8); } return array( 0xff & $B >> 24, 0xff & $B >> 16, 0xff & $B >> 8, 0xff & $B); } function _encryptBlock($in) { $S0 = $this->S0; $S1 = $this->S1; $S2 = $this->S2; $S3 = $this->S3; $K = $this->K; $in = unpack("V4", $in); $R0 = $K[0] ^ $in[1]; $R1 = $K[1] ^ $in[2]; $R2 = $K[2] ^ $in[3]; $R3 = $K[3] ^ $in[4]; $ki = 7; while ($ki < 39) { $t0 = $S0[ $R0 & 0xff] ^ $S1[($R0 >> 8) & 0xff] ^ $S2[($R0 >> 16) & 0xff] ^ $S3[($R0 >> 24) & 0xff]; $t1 = $S0[($R1 >> 24) & 0xff] ^ $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; $R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]); $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ $S2[($R2 >> 16) & 0xff] ^ $S3[($R2 >> 24) & 0xff]; $t1 = $S0[($R3 >> 24) & 0xff] ^ $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; $R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]); $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); } return pack("V4", $K[4] ^ $R2, $K[5] ^ $R3, $K[6] ^ $R0, $K[7] ^ $R1); } function _decryptBlock($in) { $S0 = $this->S0; $S1 = $this->S1; $S2 = $this->S2; $S3 = $this->S3; $K = $this->K; $in = unpack("V4", $in); $R0 = $K[4] ^ $in[1]; $R1 = $K[5] ^ $in[2]; $R2 = $K[6] ^ $in[3]; $R3 = $K[7] ^ $in[4]; $ki = 40; while ($ki > 8) { $t0 = $S0[$R0 & 0xff] ^ $S1[$R0 >> 8 & 0xff] ^ $S2[$R0 >> 16 & 0xff] ^ $S3[$R0 >> 24 & 0xff]; $t1 = $S0[$R1 >> 24 & 0xff] ^ $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; $R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ $S2[$R2 >> 16 & 0xff] ^ $S3[$R2 >> 24 & 0xff]; $t1 = $S0[$R3 >> 24 & 0xff] ^ $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; $R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); } return pack("V4", $K[0] ^ $R2, $K[1] ^ $R3, $K[2] ^ $R0, $K[3] ^ $R1); } function _setupInlineCrypt() { $lambda_functions =& self::_getLambdaFunctions(); $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); $code_hash = "Crypt_Twofish, {$this->mode}"; if ($gen_hi_opt_code) { $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } $safeint = $this->safe_intval_inline(); if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: $K = $this->K; $init_crypt = ' static $S0, $S1, $S2, $S3; if (!$S0) { for ($i = 0; $i < 256; ++$i) { $S0[] = (int)$self->S0[$i]; $S1[] = (int)$self->S1[$i]; $S2[] = (int)$self->S2[$i]; $S3[] = (int)$self->S3[$i]; } } '; break; default: $K = array(); for ($i = 0; $i < 40; ++$i) { $K[] = '$K_' . $i; } $init_crypt = ' $S0 = $self->S0; $S1 = $self->S1; $S2 = $self->S2; $S3 = $self->S3; list(' . implode(',', $K) . ') = $self->K; '; } $encrypt_block = ' $in = unpack("V4", $in); $R0 = '.$K[0].' ^ $in[1]; $R1 = '.$K[1].' ^ $in[2]; $R2 = '.$K[2].' ^ $in[3]; $R3 = '.$K[3].' ^ $in[4]; '; for ($ki = 7, $i = 0; $i < 8; ++$i) { $encrypt_block.= ' $t0 = $S0[ $R0 & 0xff] ^ $S1[($R0 >> 8) & 0xff] ^ $S2[($R0 >> 16) & 0xff] ^ $S3[($R0 >> 24) & 0xff]; $t1 = $S0[($R1 >> 24) & 0xff] ^ $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . '; $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ $S2[($R2 >> 16) & 0xff] ^ $S3[($R2 >> 24) & 0xff]; $t1 = $S0[($R3 >> 24) & 0xff] ^ $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . '; $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; '; } $encrypt_block.= ' $in = pack("V4", ' . $K[4] . ' ^ $R2, ' . $K[5] . ' ^ $R3, ' . $K[6] . ' ^ $R0, ' . $K[7] . ' ^ $R1); '; $decrypt_block = ' $in = unpack("V4", $in); $R0 = '.$K[4].' ^ $in[1]; $R1 = '.$K[5].' ^ $in[2]; $R2 = '.$K[6].' ^ $in[3]; $R3 = '.$K[7].' ^ $in[4]; '; for ($ki = 40, $i = 0; $i < 8; ++$i) { $decrypt_block.= ' $t0 = $S0[$R0 & 0xff] ^ $S1[$R0 >> 8 & 0xff] ^ $S2[$R0 >> 16 & 0xff] ^ $S3[$R0 >> 24 & 0xff]; $t1 = $S0[$R1 >> 24 & 0xff] ^ $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ $S2[$R2 >> 16 & 0xff] ^ $S3[$R2 >> 24 & 0xff]; $t1 = $S0[$R3 >> 24 & 0xff] ^ $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; '; } $decrypt_block.= ' $in = pack("V4", ' . $K[0] . ' ^ $R2, ' . $K[1] . ' ^ $R3, ' . $K[2] . ' ^ $R0, ' . $K[3] . ' ^ $R1); '; $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( array( 'init_crypt' => $init_crypt, 'init_encrypt' => '', 'init_decrypt' => '', 'encrypt_block' => $encrypt_block, 'decrypt_block' => $decrypt_block ) ); } $this->inline_crypt = $lambda_functions[$code_hash]; } } explicit_key_length) { $length = strlen($key); switch (true) { case $length <= 16: $this->key_length = 16; break; case $length <= 24: $this->key_length = 24; break; default: $this->key_length = 32; } $this->_setEngine(); } } } paddable = true; $this->mode = self::MODE_ECB; break; case self::MODE_CTR: case self::MODE_CFB: case self::MODE_CFB8: case self::MODE_OFB8: case self::MODE_OFB: case self::MODE_STREAM: $this->mode = $mode; break; case self::MODE_CBC: default: $this->paddable = true; $this->mode = self::MODE_CBC; } $this->_setEngine(); } function setIV($iv) { if ($this->mode == self::MODE_ECB) { return; } $this->iv = $iv; $this->changed = true; } function setKeyLength($length) { $this->explicit_key_length = true; $this->changed = true; $this->_setEngine(); } function getKeyLength() { return $this->key_length << 3; } function getBlockLength() { return $this->block_size << 3; } function setKey($key) { if (!$this->explicit_key_length) { $this->setKeyLength(strlen($key) << 3); $this->explicit_key_length = false; } $this->key = $key; $this->changed = true; $this->_setEngine(); } function setPassword($password, $method = 'pbkdf2') { $key = ''; switch ($method) { default: $func_args = func_get_args(); $hash = isset($func_args[2]) ? $func_args[2] : 'sha1'; $salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt; $count = isset($func_args[4]) ? $func_args[4] : 1000; if (isset($func_args[5])) { $dkLen = $func_args[5]; } else { $dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length; } switch (true) { case $method == 'pbkdf1': $hashObj = new Hash(); $hashObj->setHash($hash); if ($dkLen > $hashObj->getLength()) { user_error('Derived key too long'); return false; } $t = $password . $salt; for ($i = 0; $i < $count; ++$i) { $t = $hashObj->hash($t); } $key = substr($t, 0, $dkLen); $this->setKey(substr($key, 0, $dkLen >> 1)); $this->setIV(substr($key, $dkLen >> 1)); return true; case !function_exists('hash_pbkdf2'): case !function_exists('hash_algos'): case !in_array($hash, hash_algos()): $i = 1; $hmac = new Hash(); $hmac->setHash($hash); $hmac->setKey($password); while (strlen($key) < $dkLen) { $f = $u = $hmac->hash($salt . pack('N', $i++)); for ($j = 2; $j <= $count; ++$j) { $u = $hmac->hash($u); $f^= $u; } $key.= $f; } $key = substr($key, 0, $dkLen); break; default: $key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true); } } $this->setKey($key); return true; } function encrypt($plaintext) { if ($this->paddable) { $plaintext = $this->_pad($plaintext); } if ($this->engine === self::ENGINE_OPENSSL) { if ($this->changed) { $this->_clearBuffers(); $this->changed = false; } switch ($this->mode) { case self::MODE_STREAM: return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); case self::MODE_ECB: $result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; case self::MODE_CBC: $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); if (!defined('OPENSSL_RAW_DATA')) { $result = substr($result, 0, -$this->block_size); } if ($this->continuousBuffer) { $this->encryptIV = substr($result, -$this->block_size); } return $result; case self::MODE_CTR: return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer); case self::MODE_CFB: $ciphertext = ''; if ($this->continuousBuffer) { $iv = &$this->encryptIV; $pos = &$this->enbuffer['pos']; } else { $iv = $this->encryptIV; $pos = 0; } $len = strlen($plaintext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $this->block_size - $pos; if ($len >= $max) { $i = $max; $len-= $max; $pos = 0; } else { $i = $len; $pos+= $len; $len = 0; } $ciphertext = substr($iv, $orig_pos) ^ $plaintext; $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); $plaintext = substr($plaintext, $i); } $overflow = $len % $this->block_size; if ($overflow) { $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); $iv = $this->_string_pop($ciphertext, $this->block_size); $size = $len - $overflow; $block = $iv ^ substr($plaintext, -$overflow); $iv = substr_replace($iv, $block, 0, $overflow); $ciphertext.= $block; $pos = $overflow; } elseif ($len) { $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); $iv = substr($ciphertext, -$this->block_size); } return $ciphertext; case self::MODE_CFB8: $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); if ($this->continuousBuffer) { if (($len = strlen($ciphertext)) >= $this->block_size) { $this->encryptIV = substr($ciphertext, -$this->block_size); } else { $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len); } } return $ciphertext; case self::MODE_OFB8: $ciphertext = ''; $len = strlen($plaintext); $iv = $this->encryptIV; for ($i = 0; $i < $len; ++$i) { $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); $ciphertext.= $plaintext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->encryptIV = $iv; } break; case self::MODE_OFB: return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); } } if ($this->engine === self::ENGINE_MCRYPT) { set_error_handler(array($this, 'do_nothing')); if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->enchanged) { mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); $this->enchanged = false; } if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { $block_size = $this->block_size; $iv = &$this->encryptIV; $pos = &$this->enbuffer['pos']; $len = strlen($plaintext); $ciphertext = ''; $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len-= $max; $pos = 0; } else { $i = $len; $pos+= $len; $len = 0; } $ciphertext = substr($iv, $orig_pos) ^ $plaintext; $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); $this->enbuffer['enmcrypt_init'] = true; } if ($len >= $block_size) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === true) { mcrypt_generic_init($this->enmcrypt, $this->key, $iv); $this->enbuffer['enmcrypt_init'] = false; } $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); $iv = substr($ciphertext, -$block_size); $len%= $block_size; } else { while ($len >= $block_size) { $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); $ciphertext.= $iv; $len-= $block_size; $i+= $block_size; } } } if ($len) { $iv = mcrypt_generic($this->ecb, $iv); $block = $iv ^ substr($plaintext, -$len); $iv = substr_replace($iv, $block, 0, $len); $ciphertext.= $block; $pos = $len; } restore_error_handler(); return $ciphertext; } $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); } restore_error_handler(); return $ciphertext; } if ($this->changed) { $this->_setup(); $this->changed = false; } if ($this->use_inline_crypt) { $inline = $this->inline_crypt; return $inline('encrypt', $this, $plaintext); } $buffer = &$this->enbuffer; $block_size = $this->block_size; $ciphertext = ''; switch ($this->mode) { case self::MODE_ECB: for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); } break; case self::MODE_CBC: $xor = $this->encryptIV; for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); $block = $this->_encryptBlock($block ^ $xor); $xor = $block; $ciphertext.= $block; } if ($this->continuousBuffer) { $this->encryptIV = $xor; } break; case self::MODE_CTR: $xor = $this->encryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { $buffer['ciphertext'].= $this->_encryptBlock($xor); $this->_increment_str($xor); } $key = $this->_string_shift($buffer['ciphertext'], $block_size); $ciphertext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); $key = $this->_encryptBlock($xor); $this->_increment_str($xor); $ciphertext.= $block ^ $key; } } if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; } } break; case self::MODE_CFB: if ($this->continuousBuffer) { $iv = &$this->encryptIV; $pos = &$buffer['pos']; } else { $iv = $this->encryptIV; $pos = 0; } $len = strlen($plaintext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len-= $max; $pos = 0; } else { $i = $len; $pos+= $len; $len = 0; } $ciphertext = substr($iv, $orig_pos) ^ $plaintext; $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); } while ($len >= $block_size) { $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size); $ciphertext.= $iv; $len-= $block_size; $i+= $block_size; } if ($len) { $iv = $this->_encryptBlock($iv); $block = $iv ^ substr($plaintext, $i); $iv = substr_replace($iv, $block, 0, $len); $ciphertext.= $block; $pos = $len; } break; case self::MODE_CFB8: $ciphertext = ''; $len = strlen($plaintext); $iv = $this->encryptIV; for ($i = 0; $i < $len; ++$i) { $ciphertext.= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv)); $iv = substr($iv, 1) . $c; } if ($this->continuousBuffer) { if ($len >= $block_size) { $this->encryptIV = substr($ciphertext, -$block_size); } else { $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len); } } break; case self::MODE_OFB8: $ciphertext = ''; $len = strlen($plaintext); $iv = $this->encryptIV; for ($i = 0; $i < $len; ++$i) { $xor = $this->_encryptBlock($iv); $ciphertext.= $plaintext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->encryptIV = $iv; } break; case self::MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['xor'])) { $xor = $this->_encryptBlock($xor); $buffer['xor'].= $xor; } $key = $this->_string_shift($buffer['xor'], $block_size); $ciphertext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $xor = $this->_encryptBlock($xor); $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor; } $key = $xor; } if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; case self::MODE_STREAM: $ciphertext = $this->_encryptBlock($plaintext); break; } return $ciphertext; } function decrypt($ciphertext) { if ($this->paddable) { $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0)); } if ($this->engine === self::ENGINE_OPENSSL) { if ($this->changed) { $this->_clearBuffers(); $this->changed = false; } switch ($this->mode) { case self::MODE_STREAM: $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); break; case self::MODE_ECB: if (!defined('OPENSSL_RAW_DATA')) { $ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); } $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); break; case self::MODE_CBC: if (!defined('OPENSSL_RAW_DATA')) { $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); $ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); $offset = 2 * $this->block_size; } else { $offset = $this->block_size; } $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); if ($this->continuousBuffer) { $this->decryptIV = substr($ciphertext, -$offset, $this->block_size); } break; case self::MODE_CTR: $plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer); break; case self::MODE_CFB: $plaintext = ''; if ($this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$this->buffer['pos']; } else { $iv = $this->decryptIV; $pos = 0; } $len = strlen($ciphertext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $this->block_size - $pos; if ($len >= $max) { $i = $max; $len-= $max; $pos = 0; } else { $i = $len; $pos+= $len; $len = 0; } $plaintext = substr($iv, $orig_pos) ^ $ciphertext; $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); $ciphertext = substr($ciphertext, $i); } $overflow = $len % $this->block_size; if ($overflow) { $plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); if ($len - $overflow) { $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow); } $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); $plaintext.= $iv ^ substr($ciphertext, -$overflow); $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow); $pos = $overflow; } elseif ($len) { $plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv); $iv = substr($ciphertext, -$this->block_size); } break; case self::MODE_CFB8: $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); if ($this->continuousBuffer) { if (($len = strlen($ciphertext)) >= $this->block_size) { $this->decryptIV = substr($ciphertext, -$this->block_size); } else { $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len); } } break; case self::MODE_OFB8: $plaintext = ''; $len = strlen($ciphertext); $iv = $this->decryptIV; for ($i = 0; $i < $len; ++$i) { $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV); $plaintext.= $ciphertext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->decryptIV = $iv; } break; case self::MODE_OFB: $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); } return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } if ($this->engine === self::ENGINE_MCRYPT) { set_error_handler(array($this, 'do_nothing')); $block_size = $this->block_size; if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->dechanged) { mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); $this->dechanged = false; } if ($this->mode == self::MODE_CFB && $this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$this->debuffer['pos']; $len = strlen($ciphertext); $plaintext = ''; $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len-= $max; $pos = 0; } else { $i = $len; $pos+= $len; $len = 0; } $plaintext = substr($iv, $orig_pos) ^ $ciphertext; $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); } if ($len >= $block_size) { $cb = substr($ciphertext, $i, $len - $len % $block_size); $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; $iv = substr($cb, -$block_size); $len%= $block_size; } if ($len) { $iv = mcrypt_generic($this->ecb, $iv); $plaintext.= $iv ^ substr($ciphertext, -$len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $pos = $len; } restore_error_handler(); return $plaintext; } $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); } restore_error_handler(); return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } if ($this->changed) { $this->_setup(); $this->changed = false; } if ($this->use_inline_crypt) { $inline = $this->inline_crypt; return $inline('decrypt', $this, $ciphertext); } $block_size = $this->block_size; $buffer = &$this->debuffer; $plaintext = ''; switch ($this->mode) { case self::MODE_ECB: for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); } break; case self::MODE_CBC: $xor = $this->decryptIV; for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); $plaintext.= $this->_decryptBlock($block) ^ $xor; $xor = $block; } if ($this->continuousBuffer) { $this->decryptIV = $xor; } break; case self::MODE_CTR: $xor = $this->decryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { $buffer['ciphertext'].= $this->_encryptBlock($xor); $this->_increment_str($xor); } $key = $this->_string_shift($buffer['ciphertext'], $block_size); $plaintext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); $key = $this->_encryptBlock($xor); $this->_increment_str($xor); $plaintext.= $block ^ $key; } } if ($this->continuousBuffer) { $this->decryptIV = $xor; if ($start = strlen($ciphertext) % $block_size) { $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; } } break; case self::MODE_CFB: if ($this->continuousBuffer) { $iv = &$this->decryptIV; $pos = &$buffer['pos']; } else { $iv = $this->decryptIV; $pos = 0; } $len = strlen($ciphertext); $i = 0; if ($pos) { $orig_pos = $pos; $max = $block_size - $pos; if ($len >= $max) { $i = $max; $len-= $max; $pos = 0; } else { $i = $len; $pos+= $len; $len = 0; } $plaintext = substr($iv, $orig_pos) ^ $ciphertext; $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); } while ($len >= $block_size) { $iv = $this->_encryptBlock($iv); $cb = substr($ciphertext, $i, $block_size); $plaintext.= $iv ^ $cb; $iv = $cb; $len-= $block_size; $i+= $block_size; } if ($len) { $iv = $this->_encryptBlock($iv); $plaintext.= $iv ^ substr($ciphertext, $i); $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len); $pos = $len; } break; case self::MODE_CFB8: $plaintext = ''; $len = strlen($ciphertext); $iv = $this->decryptIV; for ($i = 0; $i < $len; ++$i) { $plaintext.= $ciphertext[$i] ^ $this->_encryptBlock($iv); $iv = substr($iv, 1) . $ciphertext[$i]; } if ($this->continuousBuffer) { if ($len >= $block_size) { $this->decryptIV = substr($ciphertext, -$block_size); } else { $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len); } } break; case self::MODE_OFB8: $plaintext = ''; $len = strlen($ciphertext); $iv = $this->decryptIV; for ($i = 0; $i < $len; ++$i) { $xor = $this->_encryptBlock($iv); $plaintext.= $ciphertext[$i] ^ $xor; $iv = substr($iv, 1) . $xor[0]; } if ($this->continuousBuffer) { $this->decryptIV = $iv; } break; case self::MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $block = substr($ciphertext, $i, $block_size); if (strlen($block) > strlen($buffer['xor'])) { $xor = $this->_encryptBlock($xor); $buffer['xor'].= $xor; } $key = $this->_string_shift($buffer['xor'], $block_size); $plaintext.= $block ^ $key; } } else { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { $xor = $this->_encryptBlock($xor); $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor; } $key = $xor; } if ($this->continuousBuffer) { $this->decryptIV = $xor; if ($start = strlen($ciphertext) % $block_size) { $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } break; case self::MODE_STREAM: $plaintext = $this->_decryptBlock($ciphertext); break; } return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer) { $ciphertext = ''; $block_size = $this->block_size; $key = $this->key; if ($this->openssl_emulate_ctr) { $xor = $encryptIV; if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { $result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; $buffer['ciphertext'].= $result; } $this->_increment_str($xor); $otp = $this->_string_shift($buffer['ciphertext'], $block_size); $ciphertext.= $block ^ $otp; } } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); $otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp; $this->_increment_str($xor); $ciphertext.= $block ^ $otp; } } if ($this->continuousBuffer) { $encryptIV = $xor; if ($start = strlen($plaintext) % $block_size) { $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext']; } } return $ciphertext; } if (strlen($buffer['ciphertext'])) { $ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext)); $plaintext = substr($plaintext, strlen($ciphertext)); if (!strlen($plaintext)) { return $ciphertext; } } $overflow = strlen($plaintext) % $block_size; if ($overflow) { $plaintext2 = $this->_string_pop($plaintext, $overflow); $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); $temp = $this->_string_pop($encrypted, $block_size); $ciphertext.= $encrypted . ($plaintext2 ^ $temp); if ($this->continuousBuffer) { $buffer['ciphertext'] = substr($temp, $overflow); $encryptIV = $temp; } } elseif (!strlen($buffer['ciphertext'])) { $ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); $temp = $this->_string_pop($ciphertext, $block_size); if ($this->continuousBuffer) { $encryptIV = $temp; } } if ($this->continuousBuffer) { if (!defined('OPENSSL_RAW_DATA')) { $encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); } $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); if ($overflow) { $this->_increment_str($encryptIV); } } return $ciphertext; } function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer) { if (strlen($buffer['xor'])) { $ciphertext = $plaintext ^ $buffer['xor']; $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); $plaintext = substr($plaintext, strlen($ciphertext)); } else { $ciphertext = ''; } $block_size = $this->block_size; $len = strlen($plaintext); $key = $this->key; $overflow = $len % $block_size; if (strlen($plaintext)) { if ($overflow) { $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); $xor = $this->_string_pop($ciphertext, $block_size); if ($this->continuousBuffer) { $encryptIV = $xor; } $ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow); if ($this->continuousBuffer) { $buffer['xor'] = $xor; } } else { $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV); if ($this->continuousBuffer) { $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size); } } } return $ciphertext; } function _openssl_translate_mode() { switch ($this->mode) { case self::MODE_ECB: return 'ecb'; case self::MODE_CBC: return 'cbc'; case self::MODE_CTR: return 'ctr'; case self::MODE_CFB: return 'cfb'; case self::MODE_CFB8: return 'cfb8'; case self::MODE_OFB: return 'ofb'; } } function enablePadding() { $this->padding = true; } function disablePadding() { $this->padding = false; } function enableContinuousBuffer() { if ($this->mode == self::MODE_ECB) { return; } $this->continuousBuffer = true; $this->_setEngine(); } function disableContinuousBuffer() { if ($this->mode == self::MODE_ECB) { return; } if (!$this->continuousBuffer) { return; } $this->continuousBuffer = false; $this->changed = true; $this->_setEngine(); } function isValidEngine($engine) { switch ($engine) { case self::ENGINE_OPENSSL: if ($this->mode == self::MODE_STREAM && $this->continuousBuffer) { return false; } $this->openssl_emulate_ctr = false; $result = $this->cipher_name_openssl && extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.3', '>='); if (!$result) { return false; } if (!defined('OPENSSL_RAW_DATA')) { $this->openssl_options = true; } else { $this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; } $methods = openssl_get_cipher_methods(); if (in_array($this->cipher_name_openssl, $methods)) { return true; } switch ($this->mode) { case self::MODE_CTR: if (in_array($this->cipher_name_openssl_ecb, $methods)) { $this->openssl_emulate_ctr = true; return true; } } return false; case self::ENGINE_MCRYPT: set_error_handler(array($this, 'do_nothing')); $result = $this->cipher_name_mcrypt && extension_loaded('mcrypt') && in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); restore_error_handler(); return $result; case self::ENGINE_INTERNAL: return true; } return false; } function setPreferredEngine($engine) { switch ($engine) { case self::ENGINE_MCRYPT: case self::ENGINE_INTERNAL: $this->preferredEngine = $engine; break; default: $this->preferredEngine = self::ENGINE_OPENSSL; } $this->_setEngine(); } function getEngine() { return $this->engine; } function _setEngine() { $this->engine = null; $candidateEngines = array( $this->preferredEngine, self::ENGINE_OPENSSL, self::ENGINE_MCRYPT ); foreach ($candidateEngines as $engine) { if ($this->isValidEngine($engine)) { $this->engine = $engine; break; } } if (!$this->engine) { $this->engine = self::ENGINE_INTERNAL; } if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { set_error_handler(array($this, 'do_nothing')); mcrypt_module_close($this->enmcrypt); mcrypt_module_close($this->demcrypt); $this->enmcrypt = null; $this->demcrypt = null; if ($this->ecb) { mcrypt_module_close($this->ecb); $this->ecb = null; } restore_error_handler(); } $this->changed = true; } abstract function _encryptBlock($in); abstract function _decryptBlock($in); abstract function _setupKey(); function _setup() { $this->_clearBuffers(); $this->_setupKey(); if ($this->use_inline_crypt) { $this->_setupInlineCrypt(); } } function _setupMcrypt() { $this->_clearBuffers(); $this->enchanged = $this->dechanged = true; if (!isset($this->enmcrypt)) { static $mcrypt_modes = array( self::MODE_CTR => 'ctr', self::MODE_ECB => MCRYPT_MODE_ECB, self::MODE_CBC => MCRYPT_MODE_CBC, self::MODE_CFB => 'ncfb', self::MODE_CFB8 => MCRYPT_MODE_CFB, self::MODE_OFB => MCRYPT_MODE_NOFB, self::MODE_OFB8 => MCRYPT_MODE_OFB, self::MODE_STREAM => MCRYPT_MODE_STREAM, ); $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); if ($this->mode == self::MODE_CFB) { $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } } if ($this->mode == self::MODE_CFB) { mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } } function _pad($text) { $length = strlen($text); if (!$this->padding) { if ($length % $this->block_size == 0) { return $text; } else { user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); $this->padding = true; } } $pad = $this->block_size - ($length % $this->block_size); return str_pad($text, $length + $pad, chr($pad)); } function _unpad($text) { if (!$this->padding) { return $text; } $length = ord($text[strlen($text) - 1]); if (!$length || $length > $this->block_size) { return false; } return substr($text, 0, -$length); } function _clearBuffers() { $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0"); if (!$this->skip_key_adjustment) { $this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0"); } } function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } function _string_pop(&$string, $index = 1) { $substr = substr($string, -$index); $string = substr($string, 0, -$index); return $substr; } function _increment_str(&$var) { if (function_exists('sodium_increment')) { $var = strrev($var); sodium_increment($var); $var = strrev($var); return; } for ($i = 4; $i <= strlen($var); $i+= 4) { $temp = substr($var, -$i, 4); switch ($temp) { case "\xFF\xFF\xFF\xFF": $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4); break; case "\x7F\xFF\xFF\xFF": $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4); return; default: $temp = unpack('Nnum', $temp); $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4); return; } } $remainder = strlen($var) % 4; if ($remainder == 0) { return; } $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT)); $temp = substr(pack('N', $temp['num'] + 1), -$remainder); $var = substr_replace($var, $temp, 0, $remainder); } function _setupInlineCrypt() { $this->use_inline_crypt = false; } function _createInlineCryptFunction($cipher_code) { $block_size = $this->block_size; $init_crypt = isset($cipher_code['init_crypt']) ? $cipher_code['init_crypt'] : ''; $init_encrypt = isset($cipher_code['init_encrypt']) ? $cipher_code['init_encrypt'] : ''; $init_decrypt = isset($cipher_code['init_decrypt']) ? $cipher_code['init_decrypt'] : ''; $encrypt_block = $cipher_code['encrypt_block']; $decrypt_block = $cipher_code['decrypt_block']; switch ($this->mode) { case self::MODE_ECB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $in = substr($_text, $_i, '.$block_size.'); '.$encrypt_block.' $_ciphertext.= $in; } return $_ciphertext; '; $decrypt = $init_decrypt . ' $_plaintext = ""; $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0)); $_ciphertext_len = strlen($_text); for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $in = substr($_text, $_i, '.$block_size.'); '.$decrypt_block.' $_plaintext.= $in; } return $self->_unpad($_plaintext); '; break; case self::MODE_CTR: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $_xor = $self->encryptIV; $_buffer = &$self->enbuffer; if (strlen($_buffer["ciphertext"])) { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); if (strlen($_block) > strlen($_buffer["ciphertext"])) { $in = $_xor; '.$encrypt_block.' $self->_increment_str($_xor); $_buffer["ciphertext"].= $in; } $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); $_ciphertext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); $in = $_xor; '.$encrypt_block.' $self->_increment_str($_xor); $_key = $in; $_ciphertext.= $_block ^ $_key; } } if ($self->continuousBuffer) { $self->encryptIV = $_xor; if ($_start = $_plaintext_len % '.$block_size.') { $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; } } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_ciphertext_len = strlen($_text); $_xor = $self->decryptIV; $_buffer = &$self->debuffer; if (strlen($_buffer["ciphertext"])) { for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); if (strlen($_block) > strlen($_buffer["ciphertext"])) { $in = $_xor; '.$encrypt_block.' $self->_increment_str($_xor); $_buffer["ciphertext"].= $in; } $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.'); $_plaintext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); $in = $_xor; '.$encrypt_block.' $self->_increment_str($_xor); $_key = $in; $_plaintext.= $_block ^ $_key; } } if ($self->continuousBuffer) { $self->decryptIV = $_xor; if ($_start = $_ciphertext_len % '.$block_size.') { $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"]; } } return $_plaintext; '; break; case self::MODE_CFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_buffer = &$self->enbuffer; if ($self->continuousBuffer) { $_iv = &$self->encryptIV; $_pos = &$_buffer["pos"]; } else { $_iv = $self->encryptIV; $_pos = 0; } $_len = strlen($_text); $_i = 0; if ($_pos) { $_orig_pos = $_pos; $_max = '.$block_size.' - $_pos; if ($_len >= $_max) { $_i = $_max; $_len-= $_max; $_pos = 0; } else { $_i = $_len; $_pos+= $_len; $_len = 0; } $_ciphertext = substr($_iv, $_orig_pos) ^ $_text; $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i); } while ($_len >= '.$block_size.') { $in = $_iv; '.$encrypt_block.'; $_iv = $in ^ substr($_text, $_i, '.$block_size.'); $_ciphertext.= $_iv; $_len-= '.$block_size.'; $_i+= '.$block_size.'; } if ($_len) { $in = $_iv; '.$encrypt_block.' $_iv = $in; $_block = $_iv ^ substr($_text, $_i); $_iv = substr_replace($_iv, $_block, 0, $_len); $_ciphertext.= $_block; $_pos = $_len; } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_buffer = &$self->debuffer; if ($self->continuousBuffer) { $_iv = &$self->decryptIV; $_pos = &$_buffer["pos"]; } else { $_iv = $self->decryptIV; $_pos = 0; } $_len = strlen($_text); $_i = 0; if ($_pos) { $_orig_pos = $_pos; $_max = '.$block_size.' - $_pos; if ($_len >= $_max) { $_i = $_max; $_len-= $_max; $_pos = 0; } else { $_i = $_len; $_pos+= $_len; $_len = 0; } $_plaintext = substr($_iv, $_orig_pos) ^ $_text; $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i); } while ($_len >= '.$block_size.') { $in = $_iv; '.$encrypt_block.' $_iv = $in; $cb = substr($_text, $_i, '.$block_size.'); $_plaintext.= $_iv ^ $cb; $_iv = $cb; $_len-= '.$block_size.'; $_i+= '.$block_size.'; } if ($_len) { $in = $_iv; '.$encrypt_block.' $_iv = $in; $_plaintext.= $_iv ^ substr($_text, $_i); $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len); $_pos = $_len; } return $_plaintext; '; break; case self::MODE_CFB8: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_len = strlen($_text); $_iv = $self->encryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; '.$encrypt_block.' $_ciphertext.= ($_c = $_text[$_i] ^ $in); $_iv = substr($_iv, 1) . $_c; } if ($self->continuousBuffer) { if ($_len >= '.$block_size.') { $self->encryptIV = substr($_ciphertext, -'.$block_size.'); } else { $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len); } } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_len = strlen($_text); $_iv = $self->decryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; '.$encrypt_block.' $_plaintext.= $_text[$_i] ^ $in; $_iv = substr($_iv, 1) . $_text[$_i]; } if ($self->continuousBuffer) { if ($_len >= '.$block_size.') { $self->decryptIV = substr($_text, -'.$block_size.'); } else { $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len); } } return $_plaintext; '; break; case self::MODE_OFB8: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_len = strlen($_text); $_iv = $self->encryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; '.$encrypt_block.' $_ciphertext.= $_text[$_i] ^ $in; $_iv = substr($_iv, 1) . $in[0]; } if ($self->continuousBuffer) { $self->encryptIV = $_iv; } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_len = strlen($_text); $_iv = $self->decryptIV; for ($_i = 0; $_i < $_len; ++$_i) { $in = $_iv; '.$encrypt_block.' $_plaintext.= $_text[$_i] ^ $in; $_iv = substr($_iv, 1) . $in[0]; } if ($self->continuousBuffer) { $self->decryptIV = $_iv; } return $_plaintext; '; break; case self::MODE_OFB: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $_xor = $self->encryptIV; $_buffer = &$self->enbuffer; if (strlen($_buffer["xor"])) { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); if (strlen($_block) > strlen($_buffer["xor"])) { $in = $_xor; '.$encrypt_block.' $_xor = $in; $_buffer["xor"].= $_xor; } $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); $_ciphertext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $in = $_xor; '.$encrypt_block.' $_xor = $in; $_ciphertext.= substr($_text, $_i, '.$block_size.') ^ $_xor; } $_key = $_xor; } if ($self->continuousBuffer) { $self->encryptIV = $_xor; if ($_start = $_plaintext_len % '.$block_size.') { $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; } } return $_ciphertext; '; $decrypt = $init_encrypt . ' $_plaintext = ""; $_ciphertext_len = strlen($_text); $_xor = $self->decryptIV; $_buffer = &$self->debuffer; if (strlen($_buffer["xor"])) { for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $_block = substr($_text, $_i, '.$block_size.'); if (strlen($_block) > strlen($_buffer["xor"])) { $in = $_xor; '.$encrypt_block.' $_xor = $in; $_buffer["xor"].= $_xor; } $_key = $self->_string_shift($_buffer["xor"], '.$block_size.'); $_plaintext.= $_block ^ $_key; } } else { for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $in = $_xor; '.$encrypt_block.' $_xor = $in; $_plaintext.= substr($_text, $_i, '.$block_size.') ^ $_xor; } $_key = $_xor; } if ($self->continuousBuffer) { $self->decryptIV = $_xor; if ($_start = $_ciphertext_len % '.$block_size.') { $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"]; } } return $_plaintext; '; break; case self::MODE_STREAM: $encrypt = $init_encrypt . ' $_ciphertext = ""; '.$encrypt_block.' return $_ciphertext; '; $decrypt = $init_decrypt . ' $_plaintext = ""; '.$decrypt_block.' return $_plaintext; '; break; default: $encrypt = $init_encrypt . ' $_ciphertext = ""; $_plaintext_len = strlen($_text); $in = $self->encryptIV; for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') { $in = substr($_text, $_i, '.$block_size.') ^ $in; '.$encrypt_block.' $_ciphertext.= $in; } if ($self->continuousBuffer) { $self->encryptIV = $in; } return $_ciphertext; '; $decrypt = $init_decrypt . ' $_plaintext = ""; $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0)); $_ciphertext_len = strlen($_text); $_iv = $self->decryptIV; for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') { $in = $_block = substr($_text, $_i, '.$block_size.'); '.$decrypt_block.' $_plaintext.= $in ^ $_iv; $_iv = $_block; } if ($self->continuousBuffer) { $self->decryptIV = $_iv; } return $self->_unpad($_plaintext); '; break; } eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };'); return $func; } function &_getLambdaFunctions() { static $functions = array(); return $functions; } function _hashInlineCryptFunction($bytes) { if (!isset(self::$WHIRLPOOL_AVAILABLE)) { self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash') && in_array('whirlpool', hash_algos()); } $result = ''; $hash = $bytes; switch (true) { case self::$WHIRLPOOL_AVAILABLE: foreach (str_split($bytes, 64) as $t) { $hash = hash('whirlpool', $hash, true); $result .= $t ^ $hash; } return $result . hash('whirlpool', $hash, true); default: $len = strlen($bytes); for ($i = 0; $i < $len; $i+=20) { $t = substr($bytes, $i, 20); $hash = pack('H*', sha1($hash)); $result .= $t ^ $hash; } return $result . pack('H*', sha1($hash)); } } function safe_intval($x) { switch (true) { case is_int($x): case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM': return $x; } return (fmod($x, 0x80000000) & 0x7FFFFFFF) | ((fmod(floor($x / 0x80000000), 2) & 1) << 31); } function safe_intval_inline() { switch (true) { case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8: case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM': return '%s'; break; default: $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | '; return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; } } function do_nothing() { } } mode_3cbc = true; $this->des = array( new DES(Base::MODE_CBC), new DES(Base::MODE_CBC), new DES(Base::MODE_CBC), ); $this->des[0]->disablePadding(); $this->des[1]->disablePadding(); $this->des[2]->disablePadding(); break; default: parent::__construct($mode); } } function isValidEngine($engine) { if ($engine == self::ENGINE_OPENSSL) { $this->cipher_name_openssl_ecb = 'des-ede3'; $mode = $this->_openssl_translate_mode(); $this->cipher_name_openssl = $mode == 'ecb' ? 'des-ede3' : 'des-ede3-' . $mode; } return parent::isValidEngine($engine); } function setIV($iv) { parent::setIV($iv); if ($this->mode_3cbc) { $this->des[0]->setIV($iv); $this->des[1]->setIV($iv); $this->des[2]->setIV($iv); } } function setKeyLength($length) { $length >>= 3; switch (true) { case $length <= 8: $this->key_length = 8; break; case $length <= 16: $this->key_length = 16; break; default: $this->key_length = 24; } parent::setKeyLength($length); } function setKey($key) { $length = $this->explicit_key_length ? $this->key_length : strlen($key); if ($length > 8) { $key = str_pad(substr($key, 0, 24), 24, chr(0)); $key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); } else { $key = str_pad($key, 8, chr(0)); } parent::setKey($key); if ($this->mode_3cbc && $length > 8) { $this->des[0]->setKey(substr($key, 0, 8)); $this->des[1]->setKey(substr($key, 8, 8)); $this->des[2]->setKey(substr($key, 16, 8)); } } function encrypt($plaintext) { if ($this->mode_3cbc && strlen($this->key) > 8) { return $this->des[2]->encrypt( $this->des[1]->decrypt( $this->des[0]->encrypt( $this->_pad($plaintext) ) ) ); } return parent::encrypt($plaintext); } function decrypt($ciphertext) { if ($this->mode_3cbc && strlen($this->key) > 8) { return $this->_unpad( $this->des[0]->decrypt( $this->des[1]->encrypt( $this->des[2]->decrypt( str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, "\0") ) ) ) ); } return parent::decrypt($ciphertext); } function enableContinuousBuffer() { parent::enableContinuousBuffer(); if ($this->mode_3cbc) { $this->des[0]->enableContinuousBuffer(); $this->des[1]->enableContinuousBuffer(); $this->des[2]->enableContinuousBuffer(); } } function disableContinuousBuffer() { parent::disableContinuousBuffer(); if ($this->mode_3cbc) { $this->des[0]->disableContinuousBuffer(); $this->des[1]->disableContinuousBuffer(); $this->des[2]->disableContinuousBuffer(); } } function _setupKey() { switch (true) { case strlen($this->key) <= 8: $this->des_rounds = 1; break; default: $this->des_rounds = 3; if ($this->mode_3cbc) { $this->des[0]->_setupKey(); $this->des[1]->_setupKey(); $this->des[2]->_setupKey(); return; } } parent::_setupKey(); } function setPreferredEngine($engine) { if ($this->mode_3cbc) { $this->des[0]->setPreferredEngine($engine); $this->des[1]->setPreferredEngine($engine); $this->des[2]->setPreferredEngine($engine); } return parent::setPreferredEngine($engine); } } 'des-ecb', self::MODE_CBC => 'des-cbc', self::MODE_CFB => 'des-cfb', self::MODE_OFB => 'des-ofb' ); var $cfb_init_len = 500; var $des_rounds = 1; var $key_length_max = 8; var $keys; var $shuffle = array( "\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\xFF", "\x00\x00\x00\x00\x00\x00\xFF\x00", "\x00\x00\x00\x00\x00\x00\xFF\xFF", "\x00\x00\x00\x00\x00\xFF\x00\x00", "\x00\x00\x00\x00\x00\xFF\x00\xFF", "\x00\x00\x00\x00\x00\xFF\xFF\x00", "\x00\x00\x00\x00\x00\xFF\xFF\xFF", "\x00\x00\x00\x00\xFF\x00\x00\x00", "\x00\x00\x00\x00\xFF\x00\x00\xFF", "\x00\x00\x00\x00\xFF\x00\xFF\x00", "\x00\x00\x00\x00\xFF\x00\xFF\xFF", "\x00\x00\x00\x00\xFF\xFF\x00\x00", "\x00\x00\x00\x00\xFF\xFF\x00\xFF", "\x00\x00\x00\x00\xFF\xFF\xFF\x00", "\x00\x00\x00\x00\xFF\xFF\xFF\xFF", "\x00\x00\x00\xFF\x00\x00\x00\x00", "\x00\x00\x00\xFF\x00\x00\x00\xFF", "\x00\x00\x00\xFF\x00\x00\xFF\x00", "\x00\x00\x00\xFF\x00\x00\xFF\xFF", "\x00\x00\x00\xFF\x00\xFF\x00\x00", "\x00\x00\x00\xFF\x00\xFF\x00\xFF", "\x00\x00\x00\xFF\x00\xFF\xFF\x00", "\x00\x00\x00\xFF\x00\xFF\xFF\xFF", "\x00\x00\x00\xFF\xFF\x00\x00\x00", "\x00\x00\x00\xFF\xFF\x00\x00\xFF", "\x00\x00\x00\xFF\xFF\x00\xFF\x00", "\x00\x00\x00\xFF\xFF\x00\xFF\xFF", "\x00\x00\x00\xFF\xFF\xFF\x00\x00", "\x00\x00\x00\xFF\xFF\xFF\x00\xFF", "\x00\x00\x00\xFF\xFF\xFF\xFF\x00", "\x00\x00\x00\xFF\xFF\xFF\xFF\xFF", "\x00\x00\xFF\x00\x00\x00\x00\x00", "\x00\x00\xFF\x00\x00\x00\x00\xFF", "\x00\x00\xFF\x00\x00\x00\xFF\x00", "\x00\x00\xFF\x00\x00\x00\xFF\xFF", "\x00\x00\xFF\x00\x00\xFF\x00\x00", "\x00\x00\xFF\x00\x00\xFF\x00\xFF", "\x00\x00\xFF\x00\x00\xFF\xFF\x00", "\x00\x00\xFF\x00\x00\xFF\xFF\xFF", "\x00\x00\xFF\x00\xFF\x00\x00\x00", "\x00\x00\xFF\x00\xFF\x00\x00\xFF", "\x00\x00\xFF\x00\xFF\x00\xFF\x00", "\x00\x00\xFF\x00\xFF\x00\xFF\xFF", "\x00\x00\xFF\x00\xFF\xFF\x00\x00", "\x00\x00\xFF\x00\xFF\xFF\x00\xFF", "\x00\x00\xFF\x00\xFF\xFF\xFF\x00", "\x00\x00\xFF\x00\xFF\xFF\xFF\xFF", "\x00\x00\xFF\xFF\x00\x00\x00\x00", "\x00\x00\xFF\xFF\x00\x00\x00\xFF", "\x00\x00\xFF\xFF\x00\x00\xFF\x00", "\x00\x00\xFF\xFF\x00\x00\xFF\xFF", "\x00\x00\xFF\xFF\x00\xFF\x00\x00", "\x00\x00\xFF\xFF\x00\xFF\x00\xFF", "\x00\x00\xFF\xFF\x00\xFF\xFF\x00", "\x00\x00\xFF\xFF\x00\xFF\xFF\xFF", "\x00\x00\xFF\xFF\xFF\x00\x00\x00", "\x00\x00\xFF\xFF\xFF\x00\x00\xFF", "\x00\x00\xFF\xFF\xFF\x00\xFF\x00", "\x00\x00\xFF\xFF\xFF\x00\xFF\xFF", "\x00\x00\xFF\xFF\xFF\xFF\x00\x00", "\x00\x00\xFF\xFF\xFF\xFF\x00\xFF", "\x00\x00\xFF\xFF\xFF\xFF\xFF\x00", "\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF", "\x00\xFF\x00\x00\x00\x00\x00\x00", "\x00\xFF\x00\x00\x00\x00\x00\xFF", "\x00\xFF\x00\x00\x00\x00\xFF\x00", "\x00\xFF\x00\x00\x00\x00\xFF\xFF", "\x00\xFF\x00\x00\x00\xFF\x00\x00", "\x00\xFF\x00\x00\x00\xFF\x00\xFF", "\x00\xFF\x00\x00\x00\xFF\xFF\x00", "\x00\xFF\x00\x00\x00\xFF\xFF\xFF", "\x00\xFF\x00\x00\xFF\x00\x00\x00", "\x00\xFF\x00\x00\xFF\x00\x00\xFF", "\x00\xFF\x00\x00\xFF\x00\xFF\x00", "\x00\xFF\x00\x00\xFF\x00\xFF\xFF", "\x00\xFF\x00\x00\xFF\xFF\x00\x00", "\x00\xFF\x00\x00\xFF\xFF\x00\xFF", "\x00\xFF\x00\x00\xFF\xFF\xFF\x00", "\x00\xFF\x00\x00\xFF\xFF\xFF\xFF", "\x00\xFF\x00\xFF\x00\x00\x00\x00", "\x00\xFF\x00\xFF\x00\x00\x00\xFF", "\x00\xFF\x00\xFF\x00\x00\xFF\x00", "\x00\xFF\x00\xFF\x00\x00\xFF\xFF", "\x00\xFF\x00\xFF\x00\xFF\x00\x00", "\x00\xFF\x00\xFF\x00\xFF\x00\xFF", "\x00\xFF\x00\xFF\x00\xFF\xFF\x00", "\x00\xFF\x00\xFF\x00\xFF\xFF\xFF", "\x00\xFF\x00\xFF\xFF\x00\x00\x00", "\x00\xFF\x00\xFF\xFF\x00\x00\xFF", "\x00\xFF\x00\xFF\xFF\x00\xFF\x00", "\x00\xFF\x00\xFF\xFF\x00\xFF\xFF", "\x00\xFF\x00\xFF\xFF\xFF\x00\x00", "\x00\xFF\x00\xFF\xFF\xFF\x00\xFF", "\x00\xFF\x00\xFF\xFF\xFF\xFF\x00", "\x00\xFF\x00\xFF\xFF\xFF\xFF\xFF", "\x00\xFF\xFF\x00\x00\x00\x00\x00", "\x00\xFF\xFF\x00\x00\x00\x00\xFF", "\x00\xFF\xFF\x00\x00\x00\xFF\x00", "\x00\xFF\xFF\x00\x00\x00\xFF\xFF", "\x00\xFF\xFF\x00\x00\xFF\x00\x00", "\x00\xFF\xFF\x00\x00\xFF\x00\xFF", "\x00\xFF\xFF\x00\x00\xFF\xFF\x00", "\x00\xFF\xFF\x00\x00\xFF\xFF\xFF", "\x00\xFF\xFF\x00\xFF\x00\x00\x00", "\x00\xFF\xFF\x00\xFF\x00\x00\xFF", "\x00\xFF\xFF\x00\xFF\x00\xFF\x00", "\x00\xFF\xFF\x00\xFF\x00\xFF\xFF", "\x00\xFF\xFF\x00\xFF\xFF\x00\x00", "\x00\xFF\xFF\x00\xFF\xFF\x00\xFF", "\x00\xFF\xFF\x00\xFF\xFF\xFF\x00", "\x00\xFF\xFF\x00\xFF\xFF\xFF\xFF", "\x00\xFF\xFF\xFF\x00\x00\x00\x00", "\x00\xFF\xFF\xFF\x00\x00\x00\xFF", "\x00\xFF\xFF\xFF\x00\x00\xFF\x00", "\x00\xFF\xFF\xFF\x00\x00\xFF\xFF", "\x00\xFF\xFF\xFF\x00\xFF\x00\x00", "\x00\xFF\xFF\xFF\x00\xFF\x00\xFF", "\x00\xFF\xFF\xFF\x00\xFF\xFF\x00", "\x00\xFF\xFF\xFF\x00\xFF\xFF\xFF", "\x00\xFF\xFF\xFF\xFF\x00\x00\x00", "\x00\xFF\xFF\xFF\xFF\x00\x00\xFF", "\x00\xFF\xFF\xFF\xFF\x00\xFF\x00", "\x00\xFF\xFF\xFF\xFF\x00\xFF\xFF", "\x00\xFF\xFF\xFF\xFF\xFF\x00\x00", "\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF", "\xFF\x00\x00\x00\x00\x00\x00\x00", "\xFF\x00\x00\x00\x00\x00\x00\xFF", "\xFF\x00\x00\x00\x00\x00\xFF\x00", "\xFF\x00\x00\x00\x00\x00\xFF\xFF", "\xFF\x00\x00\x00\x00\xFF\x00\x00", "\xFF\x00\x00\x00\x00\xFF\x00\xFF", "\xFF\x00\x00\x00\x00\xFF\xFF\x00", "\xFF\x00\x00\x00\x00\xFF\xFF\xFF", "\xFF\x00\x00\x00\xFF\x00\x00\x00", "\xFF\x00\x00\x00\xFF\x00\x00\xFF", "\xFF\x00\x00\x00\xFF\x00\xFF\x00", "\xFF\x00\x00\x00\xFF\x00\xFF\xFF", "\xFF\x00\x00\x00\xFF\xFF\x00\x00", "\xFF\x00\x00\x00\xFF\xFF\x00\xFF", "\xFF\x00\x00\x00\xFF\xFF\xFF\x00", "\xFF\x00\x00\x00\xFF\xFF\xFF\xFF", "\xFF\x00\x00\xFF\x00\x00\x00\x00", "\xFF\x00\x00\xFF\x00\x00\x00\xFF", "\xFF\x00\x00\xFF\x00\x00\xFF\x00", "\xFF\x00\x00\xFF\x00\x00\xFF\xFF", "\xFF\x00\x00\xFF\x00\xFF\x00\x00", "\xFF\x00\x00\xFF\x00\xFF\x00\xFF", "\xFF\x00\x00\xFF\x00\xFF\xFF\x00", "\xFF\x00\x00\xFF\x00\xFF\xFF\xFF", "\xFF\x00\x00\xFF\xFF\x00\x00\x00", "\xFF\x00\x00\xFF\xFF\x00\x00\xFF", "\xFF\x00\x00\xFF\xFF\x00\xFF\x00", "\xFF\x00\x00\xFF\xFF\x00\xFF\xFF", "\xFF\x00\x00\xFF\xFF\xFF\x00\x00", "\xFF\x00\x00\xFF\xFF\xFF\x00\xFF", "\xFF\x00\x00\xFF\xFF\xFF\xFF\x00", "\xFF\x00\x00\xFF\xFF\xFF\xFF\xFF", "\xFF\x00\xFF\x00\x00\x00\x00\x00", "\xFF\x00\xFF\x00\x00\x00\x00\xFF", "\xFF\x00\xFF\x00\x00\x00\xFF\x00", "\xFF\x00\xFF\x00\x00\x00\xFF\xFF", "\xFF\x00\xFF\x00\x00\xFF\x00\x00", "\xFF\x00\xFF\x00\x00\xFF\x00\xFF", "\xFF\x00\xFF\x00\x00\xFF\xFF\x00", "\xFF\x00\xFF\x00\x00\xFF\xFF\xFF", "\xFF\x00\xFF\x00\xFF\x00\x00\x00", "\xFF\x00\xFF\x00\xFF\x00\x00\xFF", "\xFF\x00\xFF\x00\xFF\x00\xFF\x00", "\xFF\x00\xFF\x00\xFF\x00\xFF\xFF", "\xFF\x00\xFF\x00\xFF\xFF\x00\x00", "\xFF\x00\xFF\x00\xFF\xFF\x00\xFF", "\xFF\x00\xFF\x00\xFF\xFF\xFF\x00", "\xFF\x00\xFF\x00\xFF\xFF\xFF\xFF", "\xFF\x00\xFF\xFF\x00\x00\x00\x00", "\xFF\x00\xFF\xFF\x00\x00\x00\xFF", "\xFF\x00\xFF\xFF\x00\x00\xFF\x00", "\xFF\x00\xFF\xFF\x00\x00\xFF\xFF", "\xFF\x00\xFF\xFF\x00\xFF\x00\x00", "\xFF\x00\xFF\xFF\x00\xFF\x00\xFF", "\xFF\x00\xFF\xFF\x00\xFF\xFF\x00", "\xFF\x00\xFF\xFF\x00\xFF\xFF\xFF", "\xFF\x00\xFF\xFF\xFF\x00\x00\x00", "\xFF\x00\xFF\xFF\xFF\x00\x00\xFF", "\xFF\x00\xFF\xFF\xFF\x00\xFF\x00", "\xFF\x00\xFF\xFF\xFF\x00\xFF\xFF", "\xFF\x00\xFF\xFF\xFF\xFF\x00\x00", "\xFF\x00\xFF\xFF\xFF\xFF\x00\xFF", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\x00\xFF\xFF\xFF\xFF\xFF\xFF", "\xFF\xFF\x00\x00\x00\x00\x00\x00", "\xFF\xFF\x00\x00\x00\x00\x00\xFF", "\xFF\xFF\x00\x00\x00\x00\xFF\x00", "\xFF\xFF\x00\x00\x00\x00\xFF\xFF", "\xFF\xFF\x00\x00\x00\xFF\x00\x00", "\xFF\xFF\x00\x00\x00\xFF\x00\xFF", "\xFF\xFF\x00\x00\x00\xFF\xFF\x00", "\xFF\xFF\x00\x00\x00\xFF\xFF\xFF", "\xFF\xFF\x00\x00\xFF\x00\x00\x00", "\xFF\xFF\x00\x00\xFF\x00\x00\xFF", "\xFF\xFF\x00\x00\xFF\x00\xFF\x00", "\xFF\xFF\x00\x00\xFF\x00\xFF\xFF", "\xFF\xFF\x00\x00\xFF\xFF\x00\x00", "\xFF\xFF\x00\x00\xFF\xFF\x00\xFF", "\xFF\xFF\x00\x00\xFF\xFF\xFF\x00", "\xFF\xFF\x00\x00\xFF\xFF\xFF\xFF", "\xFF\xFF\x00\xFF\x00\x00\x00\x00", "\xFF\xFF\x00\xFF\x00\x00\x00\xFF", "\xFF\xFF\x00\xFF\x00\x00\xFF\x00", "\xFF\xFF\x00\xFF\x00\x00\xFF\xFF", "\xFF\xFF\x00\xFF\x00\xFF\x00\x00", "\xFF\xFF\x00\xFF\x00\xFF\x00\xFF", "\xFF\xFF\x00\xFF\x00\xFF\xFF\x00", "\xFF\xFF\x00\xFF\x00\xFF\xFF\xFF", "\xFF\xFF\x00\xFF\xFF\x00\x00\x00", "\xFF\xFF\x00\xFF\xFF\x00\x00\xFF", "\xFF\xFF\x00\xFF\xFF\x00\xFF\x00", "\xFF\xFF\x00\xFF\xFF\x00\xFF\xFF", "\xFF\xFF\x00\xFF\xFF\xFF\x00\x00", "\xFF\xFF\x00\xFF\xFF\xFF\x00\xFF", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\x00\xFF\xFF\xFF\xFF\xFF", "\xFF\xFF\xFF\x00\x00\x00\x00\x00", "\xFF\xFF\xFF\x00\x00\x00\x00\xFF", "\xFF\xFF\xFF\x00\x00\x00\xFF\x00", "\xFF\xFF\xFF\x00\x00\x00\xFF\xFF", "\xFF\xFF\xFF\x00\x00\xFF\x00\x00", "\xFF\xFF\xFF\x00\x00\xFF\x00\xFF", "\xFF\xFF\xFF\x00\x00\xFF\xFF\x00", "\xFF\xFF\xFF\x00\x00\xFF\xFF\xFF", "\xFF\xFF\xFF\x00\xFF\x00\x00\x00", "\xFF\xFF\xFF\x00\xFF\x00\x00\xFF", "\xFF\xFF\xFF\x00\xFF\x00\xFF\x00", "\xFF\xFF\xFF\x00\xFF\x00\xFF\xFF", "\xFF\xFF\xFF\x00\xFF\xFF\x00\x00", "\xFF\xFF\xFF\x00\xFF\xFF\x00\xFF", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\x00\xFF\xFF\xFF\xFF", "\xFF\xFF\xFF\xFF\x00\x00\x00\x00", "\xFF\xFF\xFF\xFF\x00\x00\x00\xFF", "\xFF\xFF\xFF\xFF\x00\x00\xFF\x00", "\xFF\xFF\xFF\xFF\x00\x00\xFF\xFF", "\xFF\xFF\xFF\xFF\x00\xFF\x00\x00", "\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\x00\xFF\xFF\xFF", "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", "\xFF\xFF\xFF\xFF\xFF\x00\x00\xFF", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\x00\xFF\xFF", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" ); var $ipmap = array( 0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x21, 0x31, 0x02, 0x12, 0x03, 0x13, 0x22, 0x32, 0x23, 0x33, 0x40, 0x50, 0x41, 0x51, 0x60, 0x70, 0x61, 0x71, 0x42, 0x52, 0x43, 0x53, 0x62, 0x72, 0x63, 0x73, 0x04, 0x14, 0x05, 0x15, 0x24, 0x34, 0x25, 0x35, 0x06, 0x16, 0x07, 0x17, 0x26, 0x36, 0x27, 0x37, 0x44, 0x54, 0x45, 0x55, 0x64, 0x74, 0x65, 0x75, 0x46, 0x56, 0x47, 0x57, 0x66, 0x76, 0x67, 0x77, 0x80, 0x90, 0x81, 0x91, 0xA0, 0xB0, 0xA1, 0xB1, 0x82, 0x92, 0x83, 0x93, 0xA2, 0xB2, 0xA3, 0xB3, 0xC0, 0xD0, 0xC1, 0xD1, 0xE0, 0xF0, 0xE1, 0xF1, 0xC2, 0xD2, 0xC3, 0xD3, 0xE2, 0xF2, 0xE3, 0xF3, 0x84, 0x94, 0x85, 0x95, 0xA4, 0xB4, 0xA5, 0xB5, 0x86, 0x96, 0x87, 0x97, 0xA6, 0xB6, 0xA7, 0xB7, 0xC4, 0xD4, 0xC5, 0xD5, 0xE4, 0xF4, 0xE5, 0xF5, 0xC6, 0xD6, 0xC7, 0xD7, 0xE6, 0xF6, 0xE7, 0xF7, 0x08, 0x18, 0x09, 0x19, 0x28, 0x38, 0x29, 0x39, 0x0A, 0x1A, 0x0B, 0x1B, 0x2A, 0x3A, 0x2B, 0x3B, 0x48, 0x58, 0x49, 0x59, 0x68, 0x78, 0x69, 0x79, 0x4A, 0x5A, 0x4B, 0x5B, 0x6A, 0x7A, 0x6B, 0x7B, 0x0C, 0x1C, 0x0D, 0x1D, 0x2C, 0x3C, 0x2D, 0x3D, 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, 0x4C, 0x5C, 0x4D, 0x5D, 0x6C, 0x7C, 0x6D, 0x7D, 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, 0x88, 0x98, 0x89, 0x99, 0xA8, 0xB8, 0xA9, 0xB9, 0x8A, 0x9A, 0x8B, 0x9B, 0xAA, 0xBA, 0xAB, 0xBB, 0xC8, 0xD8, 0xC9, 0xD9, 0xE8, 0xF8, 0xE9, 0xF9, 0xCA, 0xDA, 0xCB, 0xDB, 0xEA, 0xFA, 0xEB, 0xFB, 0x8C, 0x9C, 0x8D, 0x9D, 0xAC, 0xBC, 0xAD, 0xBD, 0x8E, 0x9E, 0x8F, 0x9F, 0xAE, 0xBE, 0xAF, 0xBF, 0xCC, 0xDC, 0xCD, 0xDD, 0xEC, 0xFC, 0xED, 0xFD, 0xCE, 0xDE, 0xCF, 0xDF, 0xEE, 0xFE, 0xEF, 0xFF ); var $invipmap = array( 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF ); var $sbox1 = array( 0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000, 0x00000200, 0x00808200, 0x00808202, 0x00000200, 0x00800202, 0x00808002, 0x00800000, 0x00000002, 0x00000202, 0x00800200, 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202, 0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202, 0x00008202, 0x00800000, 0x00008000, 0x00808202, 0x00000002, 0x00808000, 0x00808200, 0x00800000, 0x00800000, 0x00000200, 0x00808002, 0x00008000, 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202, 0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202, 0x00008202, 0x00808200, 0x00000202, 0x00800200, 0x00800200, 0x00000000, 0x00008002, 0x00008200, 0x00000000, 0x00808002 ); var $sbox2 = array( 0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010, 0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x00080000, 0x00000010, 0x40080010, 0x00084000, 0x00080010, 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000, 0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000, 0x40080000, 0x00004010, 0x00000000, 0x00084010, 0x40080010, 0x00080000, 0x40004010, 0x40080000, 0x40084000, 0x00004000, 0x40080000, 0x40004000, 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000, 0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010, 0x40000010, 0x00080010, 0x00084000, 0x00000000, 0x40004000, 0x00004010, 0x40000000, 0x40080010, 0x40084010, 0x00084000 ); var $sbox3 = array( 0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100, 0x00010004, 0x04000004, 0x04000004, 0x00010000, 0x04010104, 0x00010004, 0x04010000, 0x00000104, 0x04000000, 0x00000004, 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104, 0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104, 0x00000100, 0x04000000, 0x04010100, 0x04000000, 0x00010004, 0x00000104, 0x00010000, 0x04010100, 0x04000100, 0x00000000, 0x00000100, 0x00010004, 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004, 0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104, 0x00010100, 0x04000004, 0x04010000, 0x04000104, 0x00000104, 0x04010000, 0x00010104, 0x00000004, 0x04010004, 0x00010100 ); var $sbox4 = array( 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000, 0x00000000, 0x00401000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00400040, 0x80400000, 0x80000000, 0x00001000, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040, 0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040, 0x80401040, 0x80000040, 0x00400040, 0x80400000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00000000, 0x00401000, 0x00001040, 0x00400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000, 0x00401040, 0x80400040, 0x80001000, 0x00001040, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x00001000, 0x00401040 ); var $sbox5 = array( 0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000, 0x20040080, 0x00040000, 0x01000080, 0x20040080, 0x21000080, 0x21040000, 0x00040080, 0x20000000, 0x01000000, 0x20040000, 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080, 0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000, 0x21000000, 0x00040080, 0x00040000, 0x21000080, 0x00000080, 0x01000000, 0x20000000, 0x01040000, 0x21000080, 0x20040080, 0x01000080, 0x20000000, 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000, 0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000, 0x20040000, 0x21000000, 0x00040080, 0x01000080, 0x20000080, 0x00040000, 0x00000000, 0x20040000, 0x01040080, 0x20000080 ); var $sbox6 = array( 0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000, 0x10002000, 0x00202008, 0x00200000, 0x10000008, 0x00200008, 0x10002000, 0x10000000, 0x00002008, 0x00000000, 0x00200008, 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008, 0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000, 0x10202000, 0x10000000, 0x10002000, 0x00000008, 0x10200008, 0x00202000, 0x10202008, 0x00200000, 0x00002008, 0x10000008, 0x00200000, 0x10002000, 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000, 0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000, 0x10200000, 0x00202008, 0x00002000, 0x00200008, 0x10002008, 0x00000000, 0x10202000, 0x10000000, 0x00200008, 0x10002008 ); var $sbox7 = array( 0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400, 0x02100401, 0x00100000, 0x00000000, 0x02000001, 0x00000001, 0x02000000, 0x02100001, 0x00000401, 0x02000400, 0x00100401, 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001, 0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001, 0x02000000, 0x00100400, 0x02000000, 0x00100400, 0x00100000, 0x02000401, 0x02000401, 0x02100001, 0x02100001, 0x00000001, 0x00100001, 0x02000000, 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400, 0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000, 0x00000001, 0x02100401, 0x00000000, 0x00100401, 0x02100000, 0x00000400, 0x02000001, 0x02000400, 0x00000400, 0x00100001 ); var $sbox8 = array( 0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000, 0x00020020, 0x08020000, 0x08020820, 0x00020800, 0x08020800, 0x00020820, 0x00000800, 0x00000020, 0x08020000, 0x08000020, 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800, 0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800, 0x00020820, 0x00020000, 0x00020820, 0x00020000, 0x08020800, 0x00000800, 0x00000020, 0x08020020, 0x00000800, 0x00020820, 0x08000800, 0x00000020, 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820, 0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800, 0x08000820, 0x00000000, 0x08020820, 0x00020800, 0x00020800, 0x00000820, 0x00000820, 0x00020020, 0x08000000, 0x08020800 ); function isValidEngine($engine) { if ($this->key_length_max == 8) { if ($engine == self::ENGINE_OPENSSL) { $this->cipher_name_openssl_ecb = 'des-ecb'; $this->cipher_name_openssl = 'des-' . $this->_openssl_translate_mode(); } } return parent::isValidEngine($engine); } function setKey($key) { if (strlen($key) > $this->key_length_max) { $key = substr($key, 0, $this->key_length_max); } parent::setKey($key); } function _encryptBlock($in) { return $this->_processBlock($in, self::ENCRYPT); } function _decryptBlock($in) { return $this->_processBlock($in, self::DECRYPT); } function _processBlock($block, $mode) { static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; if (!$sbox1) { $sbox1 = array_map("intval", $this->sbox1); $sbox2 = array_map("intval", $this->sbox2); $sbox3 = array_map("intval", $this->sbox3); $sbox4 = array_map("intval", $this->sbox4); $sbox5 = array_map("intval", $this->sbox5); $sbox6 = array_map("intval", $this->sbox6); $sbox7 = array_map("intval", $this->sbox7); $sbox8 = array_map("intval", $this->sbox8); for ($i = 0; $i < 256; ++$i) { $shuffleip[] = $this->shuffle[$this->ipmap[$i]]; $shuffleinvip[] = $this->shuffle[$this->invipmap[$i]]; } } $keys = $this->keys[$mode]; $ki = -1; $t = unpack('Nl/Nr', $block); list($l, $r) = array($t['l'], $t['r']); $block = ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); $t = unpack('Nl/Nr', $block); list($l, $r) = array($t['l'], $t['r']); for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { for ($i = 0; $i < 16; $i++) { $b1 = (($r >> 3) & 0x1FFFFFFF) ^ ($r << 29) ^ $keys[++$ki]; $b2 = (($r >> 31) & 0x00000001) ^ ($r << 1) ^ $keys[++$ki]; $t = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ $l; $l = $r; $r = $t; } $t = $l; $l = $r; $r = $t; } return ($shuffleinvip[($r >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleinvip[($l >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleinvip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleinvip[($l >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleinvip[($r >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleinvip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleinvip[ $r & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleinvip[ $l & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); } function _setupKey() { if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->des_rounds === $this->kl['des_rounds']) { return; } $this->kl = array('key' => $this->key, 'des_rounds' => $this->des_rounds); static $shifts = array( 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 ); static $pc1map = array( 0x00, 0x00, 0x08, 0x08, 0x04, 0x04, 0x0C, 0x0C, 0x02, 0x02, 0x0A, 0x0A, 0x06, 0x06, 0x0E, 0x0E, 0x10, 0x10, 0x18, 0x18, 0x14, 0x14, 0x1C, 0x1C, 0x12, 0x12, 0x1A, 0x1A, 0x16, 0x16, 0x1E, 0x1E, 0x20, 0x20, 0x28, 0x28, 0x24, 0x24, 0x2C, 0x2C, 0x22, 0x22, 0x2A, 0x2A, 0x26, 0x26, 0x2E, 0x2E, 0x30, 0x30, 0x38, 0x38, 0x34, 0x34, 0x3C, 0x3C, 0x32, 0x32, 0x3A, 0x3A, 0x36, 0x36, 0x3E, 0x3E, 0x40, 0x40, 0x48, 0x48, 0x44, 0x44, 0x4C, 0x4C, 0x42, 0x42, 0x4A, 0x4A, 0x46, 0x46, 0x4E, 0x4E, 0x50, 0x50, 0x58, 0x58, 0x54, 0x54, 0x5C, 0x5C, 0x52, 0x52, 0x5A, 0x5A, 0x56, 0x56, 0x5E, 0x5E, 0x60, 0x60, 0x68, 0x68, 0x64, 0x64, 0x6C, 0x6C, 0x62, 0x62, 0x6A, 0x6A, 0x66, 0x66, 0x6E, 0x6E, 0x70, 0x70, 0x78, 0x78, 0x74, 0x74, 0x7C, 0x7C, 0x72, 0x72, 0x7A, 0x7A, 0x76, 0x76, 0x7E, 0x7E, 0x80, 0x80, 0x88, 0x88, 0x84, 0x84, 0x8C, 0x8C, 0x82, 0x82, 0x8A, 0x8A, 0x86, 0x86, 0x8E, 0x8E, 0x90, 0x90, 0x98, 0x98, 0x94, 0x94, 0x9C, 0x9C, 0x92, 0x92, 0x9A, 0x9A, 0x96, 0x96, 0x9E, 0x9E, 0xA0, 0xA0, 0xA8, 0xA8, 0xA4, 0xA4, 0xAC, 0xAC, 0xA2, 0xA2, 0xAA, 0xAA, 0xA6, 0xA6, 0xAE, 0xAE, 0xB0, 0xB0, 0xB8, 0xB8, 0xB4, 0xB4, 0xBC, 0xBC, 0xB2, 0xB2, 0xBA, 0xBA, 0xB6, 0xB6, 0xBE, 0xBE, 0xC0, 0xC0, 0xC8, 0xC8, 0xC4, 0xC4, 0xCC, 0xCC, 0xC2, 0xC2, 0xCA, 0xCA, 0xC6, 0xC6, 0xCE, 0xCE, 0xD0, 0xD0, 0xD8, 0xD8, 0xD4, 0xD4, 0xDC, 0xDC, 0xD2, 0xD2, 0xDA, 0xDA, 0xD6, 0xD6, 0xDE, 0xDE, 0xE0, 0xE0, 0xE8, 0xE8, 0xE4, 0xE4, 0xEC, 0xEC, 0xE2, 0xE2, 0xEA, 0xEA, 0xE6, 0xE6, 0xEE, 0xEE, 0xF0, 0xF0, 0xF8, 0xF8, 0xF4, 0xF4, 0xFC, 0xFC, 0xF2, 0xF2, 0xFA, 0xFA, 0xF6, 0xF6, 0xFE, 0xFE ); static $pc2mapc1 = array( 0x00000000, 0x00000400, 0x00200000, 0x00200400, 0x00000001, 0x00000401, 0x00200001, 0x00200401, 0x02000000, 0x02000400, 0x02200000, 0x02200400, 0x02000001, 0x02000401, 0x02200001, 0x02200401 ); static $pc2mapc2 = array( 0x00000000, 0x00000800, 0x08000000, 0x08000800, 0x00010000, 0x00010800, 0x08010000, 0x08010800, 0x00000000, 0x00000800, 0x08000000, 0x08000800, 0x00010000, 0x00010800, 0x08010000, 0x08010800, 0x00000100, 0x00000900, 0x08000100, 0x08000900, 0x00010100, 0x00010900, 0x08010100, 0x08010900, 0x00000100, 0x00000900, 0x08000100, 0x08000900, 0x00010100, 0x00010900, 0x08010100, 0x08010900, 0x00000010, 0x00000810, 0x08000010, 0x08000810, 0x00010010, 0x00010810, 0x08010010, 0x08010810, 0x00000010, 0x00000810, 0x08000010, 0x08000810, 0x00010010, 0x00010810, 0x08010010, 0x08010810, 0x00000110, 0x00000910, 0x08000110, 0x08000910, 0x00010110, 0x00010910, 0x08010110, 0x08010910, 0x00000110, 0x00000910, 0x08000110, 0x08000910, 0x00010110, 0x00010910, 0x08010110, 0x08010910, 0x00040000, 0x00040800, 0x08040000, 0x08040800, 0x00050000, 0x00050800, 0x08050000, 0x08050800, 0x00040000, 0x00040800, 0x08040000, 0x08040800, 0x00050000, 0x00050800, 0x08050000, 0x08050800, 0x00040100, 0x00040900, 0x08040100, 0x08040900, 0x00050100, 0x00050900, 0x08050100, 0x08050900, 0x00040100, 0x00040900, 0x08040100, 0x08040900, 0x00050100, 0x00050900, 0x08050100, 0x08050900, 0x00040010, 0x00040810, 0x08040010, 0x08040810, 0x00050010, 0x00050810, 0x08050010, 0x08050810, 0x00040010, 0x00040810, 0x08040010, 0x08040810, 0x00050010, 0x00050810, 0x08050010, 0x08050810, 0x00040110, 0x00040910, 0x08040110, 0x08040910, 0x00050110, 0x00050910, 0x08050110, 0x08050910, 0x00040110, 0x00040910, 0x08040110, 0x08040910, 0x00050110, 0x00050910, 0x08050110, 0x08050910, 0x01000000, 0x01000800, 0x09000000, 0x09000800, 0x01010000, 0x01010800, 0x09010000, 0x09010800, 0x01000000, 0x01000800, 0x09000000, 0x09000800, 0x01010000, 0x01010800, 0x09010000, 0x09010800, 0x01000100, 0x01000900, 0x09000100, 0x09000900, 0x01010100, 0x01010900, 0x09010100, 0x09010900, 0x01000100, 0x01000900, 0x09000100, 0x09000900, 0x01010100, 0x01010900, 0x09010100, 0x09010900, 0x01000010, 0x01000810, 0x09000010, 0x09000810, 0x01010010, 0x01010810, 0x09010010, 0x09010810, 0x01000010, 0x01000810, 0x09000010, 0x09000810, 0x01010010, 0x01010810, 0x09010010, 0x09010810, 0x01000110, 0x01000910, 0x09000110, 0x09000910, 0x01010110, 0x01010910, 0x09010110, 0x09010910, 0x01000110, 0x01000910, 0x09000110, 0x09000910, 0x01010110, 0x01010910, 0x09010110, 0x09010910, 0x01040000, 0x01040800, 0x09040000, 0x09040800, 0x01050000, 0x01050800, 0x09050000, 0x09050800, 0x01040000, 0x01040800, 0x09040000, 0x09040800, 0x01050000, 0x01050800, 0x09050000, 0x09050800, 0x01040100, 0x01040900, 0x09040100, 0x09040900, 0x01050100, 0x01050900, 0x09050100, 0x09050900, 0x01040100, 0x01040900, 0x09040100, 0x09040900, 0x01050100, 0x01050900, 0x09050100, 0x09050900, 0x01040010, 0x01040810, 0x09040010, 0x09040810, 0x01050010, 0x01050810, 0x09050010, 0x09050810, 0x01040010, 0x01040810, 0x09040010, 0x09040810, 0x01050010, 0x01050810, 0x09050010, 0x09050810, 0x01040110, 0x01040910, 0x09040110, 0x09040910, 0x01050110, 0x01050910, 0x09050110, 0x09050910, 0x01040110, 0x01040910, 0x09040110, 0x09040910, 0x01050110, 0x01050910, 0x09050110, 0x09050910 ); static $pc2mapc3 = array( 0x00000000, 0x00000004, 0x00001000, 0x00001004, 0x00000000, 0x00000004, 0x00001000, 0x00001004, 0x10000000, 0x10000004, 0x10001000, 0x10001004, 0x10000000, 0x10000004, 0x10001000, 0x10001004, 0x00000020, 0x00000024, 0x00001020, 0x00001024, 0x00000020, 0x00000024, 0x00001020, 0x00001024, 0x10000020, 0x10000024, 0x10001020, 0x10001024, 0x10000020, 0x10000024, 0x10001020, 0x10001024, 0x00080000, 0x00080004, 0x00081000, 0x00081004, 0x00080000, 0x00080004, 0x00081000, 0x00081004, 0x10080000, 0x10080004, 0x10081000, 0x10081004, 0x10080000, 0x10080004, 0x10081000, 0x10081004, 0x00080020, 0x00080024, 0x00081020, 0x00081024, 0x00080020, 0x00080024, 0x00081020, 0x00081024, 0x10080020, 0x10080024, 0x10081020, 0x10081024, 0x10080020, 0x10080024, 0x10081020, 0x10081024, 0x20000000, 0x20000004, 0x20001000, 0x20001004, 0x20000000, 0x20000004, 0x20001000, 0x20001004, 0x30000000, 0x30000004, 0x30001000, 0x30001004, 0x30000000, 0x30000004, 0x30001000, 0x30001004, 0x20000020, 0x20000024, 0x20001020, 0x20001024, 0x20000020, 0x20000024, 0x20001020, 0x20001024, 0x30000020, 0x30000024, 0x30001020, 0x30001024, 0x30000020, 0x30000024, 0x30001020, 0x30001024, 0x20080000, 0x20080004, 0x20081000, 0x20081004, 0x20080000, 0x20080004, 0x20081000, 0x20081004, 0x30080000, 0x30080004, 0x30081000, 0x30081004, 0x30080000, 0x30080004, 0x30081000, 0x30081004, 0x20080020, 0x20080024, 0x20081020, 0x20081024, 0x20080020, 0x20080024, 0x20081020, 0x20081024, 0x30080020, 0x30080024, 0x30081020, 0x30081024, 0x30080020, 0x30080024, 0x30081020, 0x30081024, 0x00000002, 0x00000006, 0x00001002, 0x00001006, 0x00000002, 0x00000006, 0x00001002, 0x00001006, 0x10000002, 0x10000006, 0x10001002, 0x10001006, 0x10000002, 0x10000006, 0x10001002, 0x10001006, 0x00000022, 0x00000026, 0x00001022, 0x00001026, 0x00000022, 0x00000026, 0x00001022, 0x00001026, 0x10000022, 0x10000026, 0x10001022, 0x10001026, 0x10000022, 0x10000026, 0x10001022, 0x10001026, 0x00080002, 0x00080006, 0x00081002, 0x00081006, 0x00080002, 0x00080006, 0x00081002, 0x00081006, 0x10080002, 0x10080006, 0x10081002, 0x10081006, 0x10080002, 0x10080006, 0x10081002, 0x10081006, 0x00080022, 0x00080026, 0x00081022, 0x00081026, 0x00080022, 0x00080026, 0x00081022, 0x00081026, 0x10080022, 0x10080026, 0x10081022, 0x10081026, 0x10080022, 0x10080026, 0x10081022, 0x10081026, 0x20000002, 0x20000006, 0x20001002, 0x20001006, 0x20000002, 0x20000006, 0x20001002, 0x20001006, 0x30000002, 0x30000006, 0x30001002, 0x30001006, 0x30000002, 0x30000006, 0x30001002, 0x30001006, 0x20000022, 0x20000026, 0x20001022, 0x20001026, 0x20000022, 0x20000026, 0x20001022, 0x20001026, 0x30000022, 0x30000026, 0x30001022, 0x30001026, 0x30000022, 0x30000026, 0x30001022, 0x30001026, 0x20080002, 0x20080006, 0x20081002, 0x20081006, 0x20080002, 0x20080006, 0x20081002, 0x20081006, 0x30080002, 0x30080006, 0x30081002, 0x30081006, 0x30080002, 0x30080006, 0x30081002, 0x30081006, 0x20080022, 0x20080026, 0x20081022, 0x20081026, 0x20080022, 0x20080026, 0x20081022, 0x20081026, 0x30080022, 0x30080026, 0x30081022, 0x30081026, 0x30080022, 0x30080026, 0x30081022, 0x30081026 ); static $pc2mapc4 = array( 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x00000000, 0x00100000, 0x00000008, 0x00100008, 0x00000200, 0x00100200, 0x00000208, 0x00100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x04000000, 0x04100000, 0x04000008, 0x04100008, 0x04000200, 0x04100200, 0x04000208, 0x04100208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x00002000, 0x00102000, 0x00002008, 0x00102008, 0x00002200, 0x00102200, 0x00002208, 0x00102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x04002000, 0x04102000, 0x04002008, 0x04102008, 0x04002200, 0x04102200, 0x04002208, 0x04102208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x00020000, 0x00120000, 0x00020008, 0x00120008, 0x00020200, 0x00120200, 0x00020208, 0x00120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x04020000, 0x04120000, 0x04020008, 0x04120008, 0x04020200, 0x04120200, 0x04020208, 0x04120208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x00022000, 0x00122000, 0x00022008, 0x00122008, 0x00022200, 0x00122200, 0x00022208, 0x00122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208, 0x04022000, 0x04122000, 0x04022008, 0x04122008, 0x04022200, 0x04122200, 0x04022208, 0x04122208 ); static $pc2mapd1 = array( 0x00000000, 0x00000001, 0x08000000, 0x08000001, 0x00200000, 0x00200001, 0x08200000, 0x08200001, 0x00000002, 0x00000003, 0x08000002, 0x08000003, 0x00200002, 0x00200003, 0x08200002, 0x08200003 ); static $pc2mapd2 = array( 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x00000000, 0x00100000, 0x00000800, 0x00100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x04000000, 0x04100000, 0x04000800, 0x04100800, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x00000004, 0x00100004, 0x00000804, 0x00100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x04000004, 0x04100004, 0x04000804, 0x04100804, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x00000200, 0x00100200, 0x00000A00, 0x00100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x04000200, 0x04100200, 0x04000A00, 0x04100A00, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x00000204, 0x00100204, 0x00000A04, 0x00100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x04000204, 0x04100204, 0x04000A04, 0x04100A04, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x00020000, 0x00120000, 0x00020800, 0x00120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x04020000, 0x04120000, 0x04020800, 0x04120800, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x00020004, 0x00120004, 0x00020804, 0x00120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x04020004, 0x04120004, 0x04020804, 0x04120804, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x00020200, 0x00120200, 0x00020A00, 0x00120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x04020200, 0x04120200, 0x04020A00, 0x04120A00, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x00020204, 0x00120204, 0x00020A04, 0x00120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04, 0x04020204, 0x04120204, 0x04020A04, 0x04120A04 ); static $pc2mapd3 = array( 0x00000000, 0x00010000, 0x02000000, 0x02010000, 0x00000020, 0x00010020, 0x02000020, 0x02010020, 0x00040000, 0x00050000, 0x02040000, 0x02050000, 0x00040020, 0x00050020, 0x02040020, 0x02050020, 0x00002000, 0x00012000, 0x02002000, 0x02012000, 0x00002020, 0x00012020, 0x02002020, 0x02012020, 0x00042000, 0x00052000, 0x02042000, 0x02052000, 0x00042020, 0x00052020, 0x02042020, 0x02052020, 0x00000000, 0x00010000, 0x02000000, 0x02010000, 0x00000020, 0x00010020, 0x02000020, 0x02010020, 0x00040000, 0x00050000, 0x02040000, 0x02050000, 0x00040020, 0x00050020, 0x02040020, 0x02050020, 0x00002000, 0x00012000, 0x02002000, 0x02012000, 0x00002020, 0x00012020, 0x02002020, 0x02012020, 0x00042000, 0x00052000, 0x02042000, 0x02052000, 0x00042020, 0x00052020, 0x02042020, 0x02052020, 0x00000010, 0x00010010, 0x02000010, 0x02010010, 0x00000030, 0x00010030, 0x02000030, 0x02010030, 0x00040010, 0x00050010, 0x02040010, 0x02050010, 0x00040030, 0x00050030, 0x02040030, 0x02050030, 0x00002010, 0x00012010, 0x02002010, 0x02012010, 0x00002030, 0x00012030, 0x02002030, 0x02012030, 0x00042010, 0x00052010, 0x02042010, 0x02052010, 0x00042030, 0x00052030, 0x02042030, 0x02052030, 0x00000010, 0x00010010, 0x02000010, 0x02010010, 0x00000030, 0x00010030, 0x02000030, 0x02010030, 0x00040010, 0x00050010, 0x02040010, 0x02050010, 0x00040030, 0x00050030, 0x02040030, 0x02050030, 0x00002010, 0x00012010, 0x02002010, 0x02012010, 0x00002030, 0x00012030, 0x02002030, 0x02012030, 0x00042010, 0x00052010, 0x02042010, 0x02052010, 0x00042030, 0x00052030, 0x02042030, 0x02052030, 0x20000000, 0x20010000, 0x22000000, 0x22010000, 0x20000020, 0x20010020, 0x22000020, 0x22010020, 0x20040000, 0x20050000, 0x22040000, 0x22050000, 0x20040020, 0x20050020, 0x22040020, 0x22050020, 0x20002000, 0x20012000, 0x22002000, 0x22012000, 0x20002020, 0x20012020, 0x22002020, 0x22012020, 0x20042000, 0x20052000, 0x22042000, 0x22052000, 0x20042020, 0x20052020, 0x22042020, 0x22052020, 0x20000000, 0x20010000, 0x22000000, 0x22010000, 0x20000020, 0x20010020, 0x22000020, 0x22010020, 0x20040000, 0x20050000, 0x22040000, 0x22050000, 0x20040020, 0x20050020, 0x22040020, 0x22050020, 0x20002000, 0x20012000, 0x22002000, 0x22012000, 0x20002020, 0x20012020, 0x22002020, 0x22012020, 0x20042000, 0x20052000, 0x22042000, 0x22052000, 0x20042020, 0x20052020, 0x22042020, 0x22052020, 0x20000010, 0x20010010, 0x22000010, 0x22010010, 0x20000030, 0x20010030, 0x22000030, 0x22010030, 0x20040010, 0x20050010, 0x22040010, 0x22050010, 0x20040030, 0x20050030, 0x22040030, 0x22050030, 0x20002010, 0x20012010, 0x22002010, 0x22012010, 0x20002030, 0x20012030, 0x22002030, 0x22012030, 0x20042010, 0x20052010, 0x22042010, 0x22052010, 0x20042030, 0x20052030, 0x22042030, 0x22052030, 0x20000010, 0x20010010, 0x22000010, 0x22010010, 0x20000030, 0x20010030, 0x22000030, 0x22010030, 0x20040010, 0x20050010, 0x22040010, 0x22050010, 0x20040030, 0x20050030, 0x22040030, 0x22050030, 0x20002010, 0x20012010, 0x22002010, 0x22012010, 0x20002030, 0x20012030, 0x22002030, 0x22012030, 0x20042010, 0x20052010, 0x22042010, 0x22052010, 0x20042030, 0x20052030, 0x22042030, 0x22052030 ); static $pc2mapd4 = array( 0x00000000, 0x00000400, 0x01000000, 0x01000400, 0x00000000, 0x00000400, 0x01000000, 0x01000400, 0x00000100, 0x00000500, 0x01000100, 0x01000500, 0x00000100, 0x00000500, 0x01000100, 0x01000500, 0x10000000, 0x10000400, 0x11000000, 0x11000400, 0x10000000, 0x10000400, 0x11000000, 0x11000400, 0x10000100, 0x10000500, 0x11000100, 0x11000500, 0x10000100, 0x10000500, 0x11000100, 0x11000500, 0x00080000, 0x00080400, 0x01080000, 0x01080400, 0x00080000, 0x00080400, 0x01080000, 0x01080400, 0x00080100, 0x00080500, 0x01080100, 0x01080500, 0x00080100, 0x00080500, 0x01080100, 0x01080500, 0x10080000, 0x10080400, 0x11080000, 0x11080400, 0x10080000, 0x10080400, 0x11080000, 0x11080400, 0x10080100, 0x10080500, 0x11080100, 0x11080500, 0x10080100, 0x10080500, 0x11080100, 0x11080500, 0x00000008, 0x00000408, 0x01000008, 0x01000408, 0x00000008, 0x00000408, 0x01000008, 0x01000408, 0x00000108, 0x00000508, 0x01000108, 0x01000508, 0x00000108, 0x00000508, 0x01000108, 0x01000508, 0x10000008, 0x10000408, 0x11000008, 0x11000408, 0x10000008, 0x10000408, 0x11000008, 0x11000408, 0x10000108, 0x10000508, 0x11000108, 0x11000508, 0x10000108, 0x10000508, 0x11000108, 0x11000508, 0x00080008, 0x00080408, 0x01080008, 0x01080408, 0x00080008, 0x00080408, 0x01080008, 0x01080408, 0x00080108, 0x00080508, 0x01080108, 0x01080508, 0x00080108, 0x00080508, 0x01080108, 0x01080508, 0x10080008, 0x10080408, 0x11080008, 0x11080408, 0x10080008, 0x10080408, 0x11080008, 0x11080408, 0x10080108, 0x10080508, 0x11080108, 0x11080508, 0x10080108, 0x10080508, 0x11080108, 0x11080508, 0x00001000, 0x00001400, 0x01001000, 0x01001400, 0x00001000, 0x00001400, 0x01001000, 0x01001400, 0x00001100, 0x00001500, 0x01001100, 0x01001500, 0x00001100, 0x00001500, 0x01001100, 0x01001500, 0x10001000, 0x10001400, 0x11001000, 0x11001400, 0x10001000, 0x10001400, 0x11001000, 0x11001400, 0x10001100, 0x10001500, 0x11001100, 0x11001500, 0x10001100, 0x10001500, 0x11001100, 0x11001500, 0x00081000, 0x00081400, 0x01081000, 0x01081400, 0x00081000, 0x00081400, 0x01081000, 0x01081400, 0x00081100, 0x00081500, 0x01081100, 0x01081500, 0x00081100, 0x00081500, 0x01081100, 0x01081500, 0x10081000, 0x10081400, 0x11081000, 0x11081400, 0x10081000, 0x10081400, 0x11081000, 0x11081400, 0x10081100, 0x10081500, 0x11081100, 0x11081500, 0x10081100, 0x10081500, 0x11081100, 0x11081500, 0x00001008, 0x00001408, 0x01001008, 0x01001408, 0x00001008, 0x00001408, 0x01001008, 0x01001408, 0x00001108, 0x00001508, 0x01001108, 0x01001508, 0x00001108, 0x00001508, 0x01001108, 0x01001508, 0x10001008, 0x10001408, 0x11001008, 0x11001408, 0x10001008, 0x10001408, 0x11001008, 0x11001408, 0x10001108, 0x10001508, 0x11001108, 0x11001508, 0x10001108, 0x10001508, 0x11001108, 0x11001508, 0x00081008, 0x00081408, 0x01081008, 0x01081408, 0x00081008, 0x00081408, 0x01081008, 0x01081408, 0x00081108, 0x00081508, 0x01081108, 0x01081508, 0x00081108, 0x00081508, 0x01081108, 0x01081508, 0x10081008, 0x10081408, 0x11081008, 0x11081408, 0x10081008, 0x10081408, 0x11081008, 0x11081408, 0x10081108, 0x10081508, 0x11081108, 0x11081508, 0x10081108, 0x10081508, 0x11081108, 0x11081508 ); $keys = array(); for ($des_round = 0; $des_round < $this->des_rounds; ++$des_round) { $key = str_pad(substr($this->key, $des_round * 8, 8), 8, "\0"); $t = unpack('Nl/Nr', $key); list($l, $r) = array($t['l'], $t['r']); $key = ($this->shuffle[$pc1map[ $r & 0xFF]] & "\x80\x80\x80\x80\x80\x80\x80\x00") | ($this->shuffle[$pc1map[($r >> 8) & 0xFF]] & "\x40\x40\x40\x40\x40\x40\x40\x00") | ($this->shuffle[$pc1map[($r >> 16) & 0xFF]] & "\x20\x20\x20\x20\x20\x20\x20\x00") | ($this->shuffle[$pc1map[($r >> 24) & 0xFF]] & "\x10\x10\x10\x10\x10\x10\x10\x00") | ($this->shuffle[$pc1map[ $l & 0xFF]] & "\x08\x08\x08\x08\x08\x08\x08\x00") | ($this->shuffle[$pc1map[($l >> 8) & 0xFF]] & "\x04\x04\x04\x04\x04\x04\x04\x00") | ($this->shuffle[$pc1map[($l >> 16) & 0xFF]] & "\x02\x02\x02\x02\x02\x02\x02\x00") | ($this->shuffle[$pc1map[($l >> 24) & 0xFF]] & "\x01\x01\x01\x01\x01\x01\x01\x00"); $key = unpack('Nc/Nd', $key); $c = ( $key['c'] >> 4) & 0x0FFFFFFF; $d = (($key['d'] >> 4) & 0x0FFFFFF0) | ($key['c'] & 0x0F); $keys[$des_round] = array( self::ENCRYPT => array(), self::DECRYPT => array_fill(0, 32, 0) ); for ($i = 0, $ki = 31; $i < 16; ++$i, $ki-= 2) { $c <<= $shifts[$i]; $c = ($c | ($c >> 28)) & 0x0FFFFFFF; $d <<= $shifts[$i]; $d = ($d | ($d >> 28)) & 0x0FFFFFFF; $cp = $pc2mapc1[ $c >> 24 ] | $pc2mapc2[($c >> 16) & 0xFF] | $pc2mapc3[($c >> 8) & 0xFF] | $pc2mapc4[ $c & 0xFF]; $dp = $pc2mapd1[ $d >> 24 ] | $pc2mapd2[($d >> 16) & 0xFF] | $pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[ $d & 0xFF]; $val1 = ( $cp & 0xFF000000) | (($cp << 8) & 0x00FF0000) | (($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF); $val2 = (($cp << 8) & 0xFF000000) | (($cp << 16) & 0x00FF0000) | (($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF); $keys[$des_round][self::ENCRYPT][ ] = $val1; $keys[$des_round][self::DECRYPT][$ki - 1] = $val1; $keys[$des_round][self::ENCRYPT][ ] = $val2; $keys[$des_round][self::DECRYPT][$ki ] = $val2; } } switch ($this->des_rounds) { case 3: $this->keys = array( self::ENCRYPT => array_merge( $keys[0][self::ENCRYPT], $keys[1][self::DECRYPT], $keys[2][self::ENCRYPT] ), self::DECRYPT => array_merge( $keys[2][self::DECRYPT], $keys[1][self::ENCRYPT], $keys[0][self::DECRYPT] ) ); break; default: $this->keys = array( self::ENCRYPT => $keys[0][self::ENCRYPT], self::DECRYPT => $keys[0][self::DECRYPT] ); } } function _setupInlineCrypt() { $lambda_functions =& self::_getLambdaFunctions(); $des_rounds = $this->des_rounds; $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 ); $code_hash = "Crypt_DES, $des_rounds, {$this->mode}"; if ($gen_hi_opt_code) { $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } if (!isset($lambda_functions[$code_hash])) { $init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip; if (!$sbox1) { $sbox1 = array_map("intval", $self->sbox1); $sbox2 = array_map("intval", $self->sbox2); $sbox3 = array_map("intval", $self->sbox3); $sbox4 = array_map("intval", $self->sbox4); $sbox5 = array_map("intval", $self->sbox5); $sbox6 = array_map("intval", $self->sbox6); $sbox7 = array_map("intval", $self->sbox7); $sbox8 = array_map("intval", $self->sbox8);' . ' for ($i = 0; $i < 256; ++$i) { $shuffleip[] = $self->shuffle[$self->ipmap[$i]]; $shuffleinvip[] = $self->shuffle[$self->invipmap[$i]]; } } '; switch (true) { case $gen_hi_opt_code: $k = array( self::ENCRYPT => $this->keys[self::ENCRYPT], self::DECRYPT => $this->keys[self::DECRYPT] ); $init_encrypt = ''; $init_decrypt = ''; break; default: $k = array( self::ENCRYPT => array(), self::DECRYPT => array() ); for ($i = 0, $c = count($this->keys[self::ENCRYPT]); $i < $c; ++$i) { $k[self::ENCRYPT][$i] = '$ke[' . $i . ']'; $k[self::DECRYPT][$i] = '$kd[' . $i . ']'; } $init_encrypt = '$ke = $self->keys[$self::ENCRYPT];'; $init_decrypt = '$kd = $self->keys[$self::DECRYPT];'; break; } $crypt_block = array(); foreach (array(self::ENCRYPT, self::DECRYPT) as $c) { $crypt_block[$c] = ' $in = unpack("N*", $in); $l = $in[1]; $r = $in[2]; $in = unpack("N*", ($shuffleip[ $r & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleip[($r >> 8) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleip[($r >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleip[($r >> 24) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleip[ $l & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleip[($l >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleip[($l >> 16) & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleip[($l >> 24) & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01") ); ' . ' $l = $in[1]; $r = $in[2]; '; $l = '$l'; $r = '$r'; for ($ki = -1, $des_round = 0; $des_round < $des_rounds; ++$des_round) { for ($i = 0; $i < 16; ++$i) { $crypt_block[$c].= ' $b1 = ((' . $r . ' >> 3) & 0x1FFFFFFF) ^ (' . $r . ' << 29) ^ ' . $k[$c][++$ki] . '; $b2 = ((' . $r . ' >> 31) & 0x00000001) ^ (' . $r . ' << 1) ^ ' . $k[$c][++$ki] . ';' . $l . ' = $sbox1[($b1 >> 24) & 0x3F] ^ $sbox2[($b2 >> 24) & 0x3F] ^ $sbox3[($b1 >> 16) & 0x3F] ^ $sbox4[($b2 >> 16) & 0x3F] ^ $sbox5[($b1 >> 8) & 0x3F] ^ $sbox6[($b2 >> 8) & 0x3F] ^ $sbox7[ $b1 & 0x3F] ^ $sbox8[ $b2 & 0x3F] ^ ' . $l . '; '; list($l, $r) = array($r, $l); } list($l, $r) = array($r, $l); } $crypt_block[$c].= '$in = ($shuffleinvip[($l >> 24) & 0xFF] & "\x80\x80\x80\x80\x80\x80\x80\x80") | ($shuffleinvip[($r >> 24) & 0xFF] & "\x40\x40\x40\x40\x40\x40\x40\x40") | ($shuffleinvip[($l >> 16) & 0xFF] & "\x20\x20\x20\x20\x20\x20\x20\x20") | ($shuffleinvip[($r >> 16) & 0xFF] & "\x10\x10\x10\x10\x10\x10\x10\x10") | ($shuffleinvip[($l >> 8) & 0xFF] & "\x08\x08\x08\x08\x08\x08\x08\x08") | ($shuffleinvip[($r >> 8) & 0xFF] & "\x04\x04\x04\x04\x04\x04\x04\x04") | ($shuffleinvip[ $l & 0xFF] & "\x02\x02\x02\x02\x02\x02\x02\x02") | ($shuffleinvip[ $r & 0xFF] & "\x01\x01\x01\x01\x01\x01\x01\x01"); '; } $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( array( 'init_crypt' => $init_crypt, 'init_encrypt' => $init_encrypt, 'init_decrypt' => $init_decrypt, 'encrypt_block' => $crypt_block[self::ENCRYPT], 'decrypt_block' => $crypt_block[self::DECRYPT] ) ); } $this->inline_crypt = $lambda_functions[$code_hash]; } } configFile = dirname(__FILE__) . '/../openssl.cnf'; if (!defined('CRYPT_RSA_MODE')) { switch (true) { case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): define('CRYPT_RSA_MODE', self::MODE_INTERNAL); break; case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile): $versions = array(); if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { ob_start(); @phpinfo(); $content = ob_get_contents(); ob_end_clean(); preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); if (!empty($matches[1])) { for ($i = 0; $i < count($matches[1]); $i++) { $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { $versions[$matches[1][$i]] = $fullVersion; } else { $versions[$matches[1][$i]] = $m[0]; } } } } switch (true) { case !isset($versions['Header']): case !isset($versions['Library']): case $versions['Header'] == $versions['Library']: case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: define('CRYPT_RSA_MODE', self::MODE_OPENSSL); break; default: define('CRYPT_RSA_MODE', self::MODE_INTERNAL); define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); } break; default: define('CRYPT_RSA_MODE', self::MODE_INTERNAL); } } $this->zero = new BigInteger(); $this->one = new BigInteger(1); $this->hash = new Hash('sha1'); $this->hLen = $this->hash->getLength(); $this->hashName = 'sha1'; $this->mgfHash = new Hash('sha1'); $this->mgfHLen = $this->mgfHash->getLength(); } function createKey($bits = 1024, $timeout = false, $partial = array()) { if (!defined('CRYPT_RSA_EXPONENT')) { define('CRYPT_RSA_EXPONENT', '65537'); } if (!defined('CRYPT_RSA_SMALLEST_PRIME')) { define('CRYPT_RSA_SMALLEST_PRIME', 4096); } if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { $config = array(); if (isset($this->configFile)) { $config['config'] = $this->configFile; } $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); openssl_pkey_export($rsa, $privatekey, null, $config); $publickey = openssl_pkey_get_details($rsa); $publickey = $publickey['key']; $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1))); $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1))); while (openssl_error_string() !== false) { } return array( 'privatekey' => $privatekey, 'publickey' => $publickey, 'partialkey' => false ); } static $e; if (!isset($e)) { $e = new BigInteger(CRYPT_RSA_EXPONENT); } extract($this->_generateMinMax($bits)); $absoluteMin = $min; $temp = $bits >> 1; if ($temp > CRYPT_RSA_SMALLEST_PRIME) { $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME); $temp = CRYPT_RSA_SMALLEST_PRIME; } else { $num_primes = 2; } extract($this->_generateMinMax($temp + $bits % $temp)); $finalMax = $max; extract($this->_generateMinMax($temp)); $generator = new BigInteger(); $n = $this->one->copy(); if (!empty($partial)) { extract(unserialize($partial)); } else { $exponents = $coefficients = $primes = array(); $lcm = array( 'top' => $this->one->copy(), 'bottom' => false ); } $start = time(); $i0 = count($primes) + 1; do { for ($i = $i0; $i <= $num_primes; $i++) { if ($timeout !== false) { $timeout-= time() - $start; $start = time(); if ($timeout <= 0) { return array( 'privatekey' => '', 'publickey' => '', 'partialkey' => serialize(array( 'primes' => $primes, 'coefficients' => $coefficients, 'lcm' => $lcm, 'exponents' => $exponents )) ); } } if ($i == $num_primes) { list($min, $temp) = $absoluteMin->divide($n); if (!$temp->equals($this->zero)) { $min = $min->add($this->one); } $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); } else { $primes[$i] = $generator->randomPrime($min, $max, $timeout); } if ($primes[$i] === false) { if (count($primes) > 1) { $partialkey = ''; } else { array_pop($primes); $partialkey = serialize(array( 'primes' => $primes, 'coefficients' => $coefficients, 'lcm' => $lcm, 'exponents' => $exponents )); } return array( 'privatekey' => '', 'publickey' => '', 'partialkey' => $partialkey ); } if ($i > 2) { $coefficients[$i] = $n->modInverse($primes[$i]); } $n = $n->multiply($primes[$i]); $temp = $primes[$i]->subtract($this->one); $lcm['top'] = $lcm['top']->multiply($temp); $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp); $exponents[$i] = $e->modInverse($temp); } list($temp) = $lcm['top']->divide($lcm['bottom']); $gcd = $temp->gcd($e); $i0 = 1; } while (!$gcd->equals($this->one)); $d = $e->modInverse($temp); $coefficients[2] = $primes[2]->modInverse($primes[1]); return array( 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false ); } function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) { $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML; $num_primes = count($primes); $raw = array( 'version' => $num_primes == 2 ? chr(0) : chr(1), 'modulus' => $n->toBytes($signed), 'publicExponent' => $e->toBytes($signed), 'privateExponent' => $d->toBytes($signed), 'prime1' => $primes[1]->toBytes($signed), 'prime2' => $primes[2]->toBytes($signed), 'exponent1' => $exponents[1]->toBytes($signed), 'exponent2' => $exponents[2]->toBytes($signed), 'coefficient' => $coefficients[2]->toBytes($signed) ); switch ($this->privateKeyFormat) { case self::PRIVATE_FORMAT_XML: if ($num_primes != 2) { return false; } return "\r\n" . ' ' . base64_encode($raw['modulus']) . "\r\n" . ' ' . base64_encode($raw['publicExponent']) . "\r\n" . '

' . base64_encode($raw['prime1']) . "

\r\n" . ' ' . base64_encode($raw['prime2']) . "\r\n" . ' ' . base64_encode($raw['exponent1']) . "\r\n" . ' ' . base64_encode($raw['exponent2']) . "\r\n" . ' ' . base64_encode($raw['coefficient']) . "\r\n" . ' ' . base64_encode($raw['privateExponent']) . "\r\n" . '
'; break; case self::PRIVATE_FORMAT_PUTTY: if ($num_primes != 2) { return false; } $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; $key.= $encryption; $key.= "\r\nComment: " . $this->comment . "\r\n"; $public = pack( 'Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus'] ); $source = pack( 'Na*Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption, strlen($this->comment), $this->comment, strlen($public), $public ); $public = base64_encode($public); $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; $key.= chunk_split($public, 64); $private = pack( 'Na*Na*Na*Na*', strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'], strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient'] ); if (empty($this->password) && !is_string($this->password)) { $source.= pack('Na*', strlen($private), $private); $hashkey = 'putty-private-key-file-mac-key'; } else { $private.= Random::string(16 - (strlen($private) & 15)); $source.= pack('Na*', strlen($private), $private); $sequence = 0; $symkey = ''; while (strlen($symkey) < 32) { $temp = pack('Na*', $sequence++, $this->password); $symkey.= pack('H*', sha1($temp)); } $symkey = substr($symkey, 0, 32); $crypto = new AES(); $crypto->setKey($symkey); $crypto->disablePadding(); $private = $crypto->encrypt($private); $hashkey = 'putty-private-key-file-mac-key' . $this->password; } $private = base64_encode($private); $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; $key.= chunk_split($private, 64); $hash = new Hash('sha1'); $hash->setKey(pack('H*', sha1($hashkey))); $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; return $key; case self::PRIVATE_FORMAT_OPENSSH: if ($num_primes != 2) { return false; } $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']); $privateKey = pack( 'Na*Na*Na*Na*Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['modulus']), $raw['modulus'], strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['coefficient']), $raw['coefficient'], strlen($raw['prime1']), $raw['prime1'], strlen($raw['prime2']), $raw['prime2'] ); $checkint = Random::string(4); $paddedKey = pack( 'a*Na*', $checkint . $checkint . $privateKey, strlen($this->comment), $this->comment ); $paddingLength = (7 * strlen($paddedKey)) % 8; for ($i = 1; $i <= $paddingLength; $i++) { $paddedKey.= chr($i); } $key = pack( 'Na*Na*Na*NNa*Na*', strlen('none'), 'none', strlen('none'), 'none', 0, '', 1, strlen($publicKey), $publicKey, strlen($paddedKey), $paddedKey ); $key = "openssh-key-v1\0$key"; return "-----BEGIN OPENSSH PRIVATE KEY-----\n" . chunk_split(base64_encode($key), 70, "\n") . "-----END OPENSSH PRIVATE KEY-----\n"; default: $components = array(); foreach ($raw as $name => $value) { $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); } $RSAPrivateKey = implode('', $components); if ($num_primes > 2) { $OtherPrimeInfos = ''; for ($i = 3; $i <= $num_primes; $i++) { $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); } $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); } $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) { $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); $RSAPrivateKey = pack( 'Ca*a*Ca*a*', self::ASN1_INTEGER, "\01\00", $rsaOID, 4, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey ); $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); if (!empty($this->password) || is_string($this->password)) { $salt = Random::string(8); $iterationCount = 2048; $crypto = new DES(); $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); $parameters = pack( 'Ca*a*Ca*N', self::ASN1_OCTETSTRING, $this->_encodeLength(strlen($salt)), $salt, self::ASN1_INTEGER, $this->_encodeLength(4), $iterationCount ); $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; $encryptionAlgorithm = pack( 'Ca*a*Ca*a*', self::ASN1_OBJECT, $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), $pbeWithMD5AndDES_CBC, self::ASN1_SEQUENCE, $this->_encodeLength(strlen($parameters)), $parameters ); $RSAPrivateKey = pack( 'Ca*a*Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($encryptionAlgorithm)), $encryptionAlgorithm, self::ASN1_OCTETSTRING, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey ); $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . chunk_split(base64_encode($RSAPrivateKey), 64) . '-----END ENCRYPTED PRIVATE KEY-----'; } else { $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . chunk_split(base64_encode($RSAPrivateKey), 64) . '-----END PRIVATE KEY-----'; } return $RSAPrivateKey; } if (!empty($this->password) || is_string($this->password)) { $iv = Random::string(8); $symkey = pack('H*', md5($this->password . $iv)); $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); $des = new TripleDES(); $des->setKey($symkey); $des->setIV($iv); $iv = strtoupper(bin2hex($iv)); $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . "Proc-Type: 4,ENCRYPTED\r\n" . "DEK-Info: DES-EDE3-CBC,$iv\r\n" . "\r\n" . chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . '-----END RSA PRIVATE KEY-----'; } else { $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . chunk_split(base64_encode($RSAPrivateKey), 64) . '-----END RSA PRIVATE KEY-----'; } return $RSAPrivateKey; } } function _convertPublicKey($n, $e) { $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML; $modulus = $n->toBytes($signed); $publicExponent = $e->toBytes($signed); switch ($this->publicKeyFormat) { case self::PUBLIC_FORMAT_RAW: return array('e' => $e->copy(), 'n' => $n->copy()); case self::PUBLIC_FORMAT_XML: return "\r\n" . ' ' . base64_encode($modulus) . "\r\n" . ' ' . base64_encode($publicExponent) . "\r\n" . ''; break; case self::PUBLIC_FORMAT_OPENSSH: $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment; return $RSAPublicKey; default: $components = array( 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) ); $RSAPublicKey = pack( 'Ca*a*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent'] ); if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) { $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . chunk_split(base64_encode($RSAPublicKey), 64) . '-----END RSA PUBLIC KEY-----'; } else { $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); $RSAPublicKey = chr(0) . $RSAPublicKey; $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; $RSAPublicKey = pack( 'Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey ); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(base64_encode($RSAPublicKey), 64) . '-----END PUBLIC KEY-----'; } return $RSAPublicKey; } } function _parseKey($key, $type) { if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) { return false; } switch ($type) { case self::PUBLIC_FORMAT_RAW: if (!is_array($key)) { return false; } $components = array(); switch (true) { case isset($key['e']): $components['publicExponent'] = $key['e']->copy(); break; case isset($key['exponent']): $components['publicExponent'] = $key['exponent']->copy(); break; case isset($key['publicExponent']): $components['publicExponent'] = $key['publicExponent']->copy(); break; case isset($key[0]): $components['publicExponent'] = $key[0]->copy(); } switch (true) { case isset($key['n']): $components['modulus'] = $key['n']->copy(); break; case isset($key['modulo']): $components['modulus'] = $key['modulo']->copy(); break; case isset($key['modulus']): $components['modulus'] = $key['modulus']->copy(); break; case isset($key[1]): $components['modulus'] = $key[1]->copy(); } return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; case self::PRIVATE_FORMAT_PKCS1: case self::PRIVATE_FORMAT_PKCS8: case self::PUBLIC_FORMAT_PKCS1: if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { $iv = pack('H*', trim($matches[2])); $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); $ciphertext = $this->_extractBER($key); if ($ciphertext === false) { $ciphertext = $key; } switch ($matches[1]) { case 'AES-256-CBC': $crypto = new AES(); break; case 'AES-128-CBC': $symkey = substr($symkey, 0, 16); $crypto = new AES(); break; case 'DES-EDE3-CFB': $crypto = new TripleDES(Base::MODE_CFB); break; case 'DES-EDE3-CBC': $symkey = substr($symkey, 0, 24); $crypto = new TripleDES(); break; case 'DES-CBC': $crypto = new DES(); break; default: return false; } $crypto->setKey($symkey); $crypto->setIV($iv); $decoded = $crypto->decrypt($ciphertext); } else { $decoded = $this->_extractBER($key); } if ($decoded !== false) { $key = $decoded; } $components = array(); if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { return false; } if ($this->_decodeLength($key) != strlen($key)) { return false; } $tag = ord($this->_string_shift($key)); if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { $this->_string_shift($key, 3); $tag = self::ASN1_SEQUENCE; } if ($tag == self::ASN1_SEQUENCE) { $temp = $this->_string_shift($key, $this->_decodeLength($key)); if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) { return false; } $length = $this->_decodeLength($temp); switch ($this->_string_shift($temp, $length)) { case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": break; case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) { return false; } if ($this->_decodeLength($temp) != strlen($temp)) { return false; } $this->_string_shift($temp); $salt = $this->_string_shift($temp, $this->_decodeLength($temp)); if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) { return false; } $this->_decodeLength($temp); list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); $this->_string_shift($key); $length = $this->_decodeLength($key); if (strlen($key) != $length) { return false; } $crypto = new DES(); $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); $key = $crypto->decrypt($key); if ($key === false) { return false; } return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1); default: return false; } $tag = ord($this->_string_shift($key)); $this->_decodeLength($key); if ($tag == self::ASN1_BITSTRING) { $this->_string_shift($key); } if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { return false; } if ($this->_decodeLength($key) != strlen($key)) { return false; } $tag = ord($this->_string_shift($key)); } if ($tag != self::ASN1_INTEGER) { return false; } $length = $this->_decodeLength($key); $temp = $this->_string_shift($key, $length); if (strlen($temp) != 1 || ord($temp) > 2) { $components['modulus'] = new BigInteger($temp, 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); return $components; } if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) { return false; } $length = $this->_decodeLength($key); $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256)); if (!empty($key)) { if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { return false; } $this->_decodeLength($key); while (!empty($key)) { if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { return false; } $this->_decodeLength($key); $key = substr($key, 1); $length = $this->_decodeLength($key); $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); $this->_string_shift($key); $length = $this->_decodeLength($key); $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256); } } return $components; case self::PUBLIC_FORMAT_OPENSSH: $parts = explode(' ', $key, 3); $key = isset($parts[1]) ? base64_decode($parts[1]) : false; if ($key === false) { return false; } $comment = isset($parts[2]) ? $parts[2] : false; $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; if (strlen($key) <= 4) { return false; } extract(unpack('Nlength', $this->_string_shift($key, 4))); $publicExponent = new BigInteger($this->_string_shift($key, $length), -256); if (strlen($key) <= 4) { return false; } extract(unpack('Nlength', $this->_string_shift($key, 4))); $modulus = new BigInteger($this->_string_shift($key, $length), -256); if ($cleanup && strlen($key)) { if (strlen($key) <= 4) { return false; } extract(unpack('Nlength', $this->_string_shift($key, 4))); $realModulus = new BigInteger($this->_string_shift($key, $length), -256); return strlen($key) ? false : array( 'modulus' => $realModulus, 'publicExponent' => $modulus, 'comment' => $comment ); } else { return strlen($key) ? false : array( 'modulus' => $modulus, 'publicExponent' => $publicExponent, 'comment' => $comment ); } case self::PRIVATE_FORMAT_XML: case self::PUBLIC_FORMAT_XML: $this->components = array(); $xml = xml_parser_create('UTF-8'); xml_set_object($xml, $this); xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); xml_set_character_data_handler($xml, '_data_handler'); if (!xml_parse($xml, '' . $key . '')) { xml_parser_free($xml); unset($xml); return false; } xml_parser_free($xml); unset($xml); return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; case self::PRIVATE_FORMAT_PUTTY: $components = array(); $key = preg_split('#\r\n|\r|\n#', $key); if ($this->_string_shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') { return false; } $version = (int) $this->_string_shift($key[0], 3); if ($version != 2 && $version != 3) { return false; } $type = rtrim($key[0]); if ($type != 'ssh-rsa') { return false; } $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); $public = substr($public, 11); extract(unpack('Nlength', $this->_string_shift($public, 4))); $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256); extract(unpack('Nlength', $this->_string_shift($public, 4))); $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256); $offset = $publicLength + 4; switch ($encryption) { case 'aes256-cbc': $crypto = new AES(); switch ($version) { case 3: if (!function_exists('sodium_crypto_pwhash')) { return false; } $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++])); switch ($flavour) { case 'Argon2i': $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13; break; case 'Argon2id': $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13; break; default: return false; } $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++])); $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++])); $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++])); $salt = pack('H*', trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++]))); $length = 80; $temp = sodium_crypto_pwhash($length, $this->password, $salt, $passes, $memory << 10, $flavour); $symkey = substr($temp, 0, 32); $symiv = substr($temp, 32, 16); break; case 2: $symkey = ''; $sequence = 0; while (strlen($symkey) < 32) { $temp = pack('Na*', $sequence++, $this->password); $symkey.= pack('H*', sha1($temp)); } $symkey = substr($symkey, 0, 32); $symiv = str_repeat("\0", 16); } } $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++])); $private = base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength)))); if ($encryption != 'none') { $crypto->setKey($symkey); $crypto->setIV($symiv); $crypto->disablePadding(); $private = $crypto->decrypt($private); if ($private === false) { return false; } } extract(unpack('Nlength', $this->_string_shift($private, 4))); if (strlen($private) < $length) { return false; } $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256); extract(unpack('Nlength', $this->_string_shift($private, 4))); if (strlen($private) < $length) { return false; } $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256)); extract(unpack('Nlength', $this->_string_shift($private, 4))); if (strlen($private) < $length) { return false; } $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256); $temp = $components['primes'][1]->subtract($this->one); $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); $temp = $components['primes'][2]->subtract($this->one); $components['exponents'][] = $components['publicExponent']->modInverse($temp); extract(unpack('Nlength', $this->_string_shift($private, 4))); if (strlen($private) < $length) { return false; } $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); return $components; case self::PRIVATE_FORMAT_OPENSSH: $components = array(); $decoded = $this->_extractBER($key); $magic = $this->_string_shift($decoded, 15); if ($magic !== "openssh-key-v1\0") { return false; } $options = $this->_string_shift($decoded, 24); if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") { return false; } extract(unpack('Nlength', $this->_string_shift($decoded, 4))); if (strlen($decoded) < $length) { return false; } $publicKey = $this->_string_shift($decoded, $length); extract(unpack('Nlength', $this->_string_shift($decoded, 4))); if (strlen($decoded) < $length) { return false; } $paddedKey = $this->_string_shift($decoded, $length); if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") { return false; } $checkint1 = $this->_string_shift($paddedKey, 4); $checkint2 = $this->_string_shift($paddedKey, 4); if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) { return false; } if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") { return false; } $values = array( &$components['modulus'], &$components['publicExponent'], &$components['privateExponent'], &$components['coefficients'][2], &$components['primes'][1], &$components['primes'][2] ); foreach ($values as &$value) { extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); if (strlen($paddedKey) < $length) { return false; } $value = new BigInteger($this->_string_shift($paddedKey, $length), -256); } extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); if (strlen($paddedKey) < $length) { return false; } $components['comment'] = $this->_string_shift($decoded, $length); $temp = $components['primes'][1]->subtract($this->one); $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); $temp = $components['primes'][2]->subtract($this->one); $components['exponents'][] = $components['publicExponent']->modInverse($temp); return $components; } return false; } function getSize() { return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); } function _start_element_handler($parser, $name, $attribs) { switch ($name) { case 'MODULUS': $this->current = &$this->components['modulus']; break; case 'EXPONENT': $this->current = &$this->components['publicExponent']; break; case 'P': $this->current = &$this->components['primes'][1]; break; case 'Q': $this->current = &$this->components['primes'][2]; break; case 'DP': $this->current = &$this->components['exponents'][1]; break; case 'DQ': $this->current = &$this->components['exponents'][2]; break; case 'INVERSEQ': $this->current = &$this->components['coefficients'][2]; break; case 'D': $this->current = &$this->components['privateExponent']; } $this->current = ''; } function _stop_element_handler($parser, $name) { if (isset($this->current)) { $this->current = new BigInteger(base64_decode($this->current), 256); unset($this->current); } } function _data_handler($parser, $data) { if (!isset($this->current) || is_object($this->current)) { return; } $this->current.= trim($data); } function loadKey($key, $type = false) { if ($key instanceof RSA) { $this->privateKeyFormat = $key->privateKeyFormat; $this->publicKeyFormat = $key->publicKeyFormat; $this->k = $key->k; $this->hLen = $key->hLen; $this->sLen = $key->sLen; $this->mgfHLen = $key->mgfHLen; $this->encryptionMode = $key->encryptionMode; $this->signatureMode = $key->signatureMode; $this->password = $key->password; $this->configFile = $key->configFile; $this->comment = $key->comment; if (is_object($key->hash)) { $this->hash = new Hash($key->hash->getHash()); } if (is_object($key->mgfHash)) { $this->mgfHash = new Hash($key->mgfHash->getHash()); } if (is_object($key->modulus)) { $this->modulus = $key->modulus->copy(); } if (is_object($key->exponent)) { $this->exponent = $key->exponent->copy(); } if (is_object($key->publicExponent)) { $this->publicExponent = $key->publicExponent->copy(); } $this->primes = array(); $this->exponents = array(); $this->coefficients = array(); foreach ($this->primes as $prime) { $this->primes[] = $prime->copy(); } foreach ($this->exponents as $exponent) { $this->exponents[] = $exponent->copy(); } foreach ($this->coefficients as $coefficient) { $this->coefficients[] = $coefficient->copy(); } return true; } if ($type === false) { $types = array( self::PUBLIC_FORMAT_RAW, self::PRIVATE_FORMAT_PKCS1, self::PRIVATE_FORMAT_XML, self::PRIVATE_FORMAT_PUTTY, self::PUBLIC_FORMAT_OPENSSH, self::PRIVATE_FORMAT_OPENSSH ); foreach ($types as $type) { $components = $this->_parseKey($key, $type); if ($components !== false) { break; } } } else { $components = $this->_parseKey($key, $type); } if ($components === false) { $this->comment = null; $this->modulus = null; $this->k = null; $this->exponent = null; $this->primes = null; $this->exponents = null; $this->coefficients = null; $this->publicExponent = null; return false; } if (isset($components['comment']) && $components['comment'] !== false) { $this->comment = $components['comment']; } $this->modulus = $components['modulus']; $this->k = strlen($this->modulus->toBytes()); $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; if (isset($components['primes'])) { $this->primes = $components['primes']; $this->exponents = $components['exponents']; $this->coefficients = $components['coefficients']; $this->publicExponent = $components['publicExponent']; } else { $this->primes = array(); $this->exponents = array(); $this->coefficients = array(); $this->publicExponent = false; } switch ($type) { case self::PUBLIC_FORMAT_OPENSSH: case self::PUBLIC_FORMAT_RAW: $this->setPublicKey(); break; case self::PRIVATE_FORMAT_PKCS1: switch (true) { case strpos($key, '-BEGIN PUBLIC KEY-') !== false: case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false: $this->setPublicKey(); } } return true; } function setPassword($password = false) { $this->password = $password; } function setPublicKey($key = false, $type = false) { if (!empty($this->publicExponent)) { return false; } if ($key === false && !empty($this->modulus)) { $this->publicExponent = $this->exponent; return true; } if ($type === false) { $types = array( self::PUBLIC_FORMAT_RAW, self::PUBLIC_FORMAT_PKCS1, self::PUBLIC_FORMAT_XML, self::PUBLIC_FORMAT_OPENSSH ); foreach ($types as $type) { $components = $this->_parseKey($key, $type); if ($components !== false) { break; } } } else { $components = $this->_parseKey($key, $type); } if ($components === false) { return false; } if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { $this->modulus = $components['modulus']; $this->exponent = $this->publicExponent = $components['publicExponent']; return true; } $this->publicExponent = $components['publicExponent']; return true; } function setPrivateKey($key = false, $type = false) { if ($key === false && !empty($this->publicExponent)) { $this->publicExponent = false; return true; } $rsa = new RSA(); if (!$rsa->loadKey($key, $type)) { return false; } $rsa->publicExponent = false; $this->loadKey($rsa); return true; } function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) { if (empty($this->modulus) || empty($this->publicExponent)) { return false; } $oldFormat = $this->publicKeyFormat; $this->publicKeyFormat = $type; $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); $this->publicKeyFormat = $oldFormat; return $temp; } function getPublicKeyFingerprint($algorithm = 'md5') { if (empty($this->modulus) || empty($this->publicExponent)) { return false; } $modulus = $this->modulus->toBytes(true); $publicExponent = $this->publicExponent->toBytes(true); $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); switch ($algorithm) { case 'sha256': $hash = new Hash('sha256'); $base = base64_encode($hash->hash($RSAPublicKey)); return substr($base, 0, strlen($base) - 1); case 'md5': return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1); default: return false; } } function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1) { if (empty($this->primes)) { return false; } $oldFormat = $this->privateKeyFormat; $this->privateKeyFormat = $type; $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); $this->privateKeyFormat = $oldFormat; return $temp; } function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) { if (empty($this->modulus) || empty($this->exponent)) { return false; } $oldFormat = $this->publicKeyFormat; $this->publicKeyFormat = $mode; $temp = $this->_convertPublicKey($this->modulus, $this->exponent); $this->publicKeyFormat = $oldFormat; return $temp; } function __toString() { $key = $this->getPrivateKey($this->privateKeyFormat); if ($key !== false) { return $key; } $key = $this->_getPrivatePublicKey($this->publicKeyFormat); return $key !== false ? $key : ''; } function __clone() { $key = new RSA(); $key->loadKey($this); return $key; } function _generateMinMax($bits) { $bytes = $bits >> 3; $min = str_repeat(chr(0), $bytes); $max = str_repeat(chr(0xFF), $bytes); $msb = $bits & 7; if ($msb) { $min = chr(1 << ($msb - 1)) . $min; $max = chr((1 << $msb) - 1) . $max; } else { $min[0] = chr(0x80); } return array( 'min' => new BigInteger($min, 256), 'max' => new BigInteger($max, 256) ); } function _decodeLength(&$string) { $length = ord($this->_string_shift($string)); if ($length & 0x80) { $length&= 0x7F; $temp = $this->_string_shift($string, $length); list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); } return $length; } function _encodeLength($length) { if ($length <= 0x7F) { return chr($length); } $temp = ltrim(pack('N', $length), chr(0)); return pack('Ca*', 0x80 | strlen($temp), $temp); } function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } function setPrivateKeyFormat($format) { $this->privateKeyFormat = $format; } function setPublicKeyFormat($format) { $this->publicKeyFormat = $format; } function setHash($hash) { switch ($hash) { case 'md2': case 'md5': case 'sha1': case 'sha256': case 'sha384': case 'sha512': $this->hash = new Hash($hash); $this->hashName = $hash; break; default: $this->hash = new Hash('sha1'); $this->hashName = 'sha1'; } $this->hLen = $this->hash->getLength(); } function setMGFHash($hash) { switch ($hash) { case 'md2': case 'md5': case 'sha1': case 'sha256': case 'sha384': case 'sha512': $this->mgfHash = new Hash($hash); break; default: $this->mgfHash = new Hash('sha1'); } $this->mgfHLen = $this->mgfHash->getLength(); } function setSaltLength($sLen) { $this->sLen = $sLen; } function _i2osp($x, $xLen) { $x = $x->toBytes(); if (strlen($x) > $xLen) { user_error('Integer too large'); return false; } return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); } function _os2ip($x) { return new BigInteger($x, 256); } function _exponentiate($x) { switch (true) { case empty($this->primes): case $this->primes[1]->equals($this->zero): case empty($this->coefficients): case $this->coefficients[2]->equals($this->zero): case empty($this->exponents): case $this->exponents[1]->equals($this->zero): return $x->modPow($this->exponent, $this->modulus); } $num_primes = count($this->primes); if (defined('CRYPT_RSA_DISABLE_BLINDING')) { $m_i = array( 1 => $x->modPow($this->exponents[1], $this->primes[1]), 2 => $x->modPow($this->exponents[2], $this->primes[2]) ); $h = $m_i[1]->subtract($m_i[2]); $h = $h->multiply($this->coefficients[2]); list(, $h) = $h->divide($this->primes[1]); $m = $m_i[2]->add($h->multiply($this->primes[2])); $r = $this->primes[1]; for ($i = 3; $i <= $num_primes; $i++) { $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]); $r = $r->multiply($this->primes[$i - 1]); $h = $m_i->subtract($m); $h = $h->multiply($this->coefficients[$i]); list(, $h) = $h->divide($this->primes[$i]); $m = $m->add($r->multiply($h)); } } else { $smallest = $this->primes[1]; for ($i = 2; $i <= $num_primes; $i++) { if ($smallest->compare($this->primes[$i]) > 0) { $smallest = $this->primes[$i]; } } $one = new BigInteger(1); $r = $one->random($one, $smallest->subtract($one)); $m_i = array( 1 => $this->_blind($x, $r, 1), 2 => $this->_blind($x, $r, 2) ); $h = $m_i[1]->subtract($m_i[2]); $h = $h->multiply($this->coefficients[2]); list(, $h) = $h->divide($this->primes[1]); $m = $m_i[2]->add($h->multiply($this->primes[2])); $r = $this->primes[1]; for ($i = 3; $i <= $num_primes; $i++) { $m_i = $this->_blind($x, $r, $i); $r = $r->multiply($this->primes[$i - 1]); $h = $m_i->subtract($m); $h = $h->multiply($this->coefficients[$i]); list(, $h) = $h->divide($this->primes[$i]); $m = $m->add($r->multiply($h)); } } return $m; } function _blind($x, $r, $i) { $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); $x = $x->modPow($this->exponents[$i], $this->primes[$i]); $r = $r->modInverse($this->primes[$i]); $x = $x->multiply($r); list(, $x) = $x->divide($this->primes[$i]); return $x; } function _equals($x, $y) { if (function_exists('hash_equals')) { return hash_equals($x, $y); } if (strlen($x) != strlen($y)) { return false; } $result = "\0"; $x^= $y; for ($i = 0; $i < strlen($x); $i++) { $result|= $x[$i]; } return $result === "\0"; } function _rsaep($m) { if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { user_error('Message representative out of range'); return false; } return $this->_exponentiate($m); } function _rsadp($c) { if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { user_error('Ciphertext representative out of range'); return false; } return $this->_exponentiate($c); } function _rsasp1($m) { if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { user_error('Message representative out of range'); return false; } return $this->_exponentiate($m); } function _rsavp1($s) { if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { user_error('Signature representative out of range'); return false; } return $this->_exponentiate($s); } function _mgf1($mgfSeed, $maskLen) { $t = ''; $count = ceil($maskLen / $this->mgfHLen); for ($i = 0; $i < $count; $i++) { $c = pack('N', $i); $t.= $this->mgfHash->hash($mgfSeed . $c); } return substr($t, 0, $maskLen); } function _rsaes_oaep_encrypt($m, $l = '') { $mLen = strlen($m); if ($mLen > $this->k - 2 * $this->hLen - 2) { user_error('Message too long'); return false; } $lHash = $this->hash->hash($l); $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2); $db = $lHash . $ps . chr(1) . $m; $seed = Random::string($this->hLen); $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); $maskedDB = $db ^ $dbMask; $seedMask = $this->_mgf1($maskedDB, $this->hLen); $maskedSeed = $seed ^ $seedMask; $em = chr(0) . $maskedSeed . $maskedDB; $m = $this->_os2ip($em); $c = $this->_rsaep($m); $c = $this->_i2osp($c, $this->k); return $c; } function _rsaes_oaep_decrypt($c, $l = '') { if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { user_error('Decryption error'); return false; } $c = $this->_os2ip($c); $m = $this->_rsadp($c); if ($m === false) { user_error('Decryption error'); return false; } $em = $this->_i2osp($m, $this->k); $lHash = $this->hash->hash($l); $y = ord($em[0]); $maskedSeed = substr($em, 1, $this->hLen); $maskedDB = substr($em, $this->hLen + 1); $seedMask = $this->_mgf1($maskedDB, $this->hLen); $seed = $maskedSeed ^ $seedMask; $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1); $db = $maskedDB ^ $dbMask; $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); $hashesMatch = $this->_equals($lHash, $lHash2); $leadingZeros = 1; $patternMatch = 0; $offset = 0; for ($i = 0; $i < strlen($m); $i++) { $patternMatch|= $leadingZeros & ($m[$i] === "\1"); $leadingZeros&= $m[$i] === "\0"; $offset+= $patternMatch ? 0 : 1; } if (!$hashesMatch | !$patternMatch) { user_error('Decryption error'); return false; } return substr($m, $offset + 1); } function _raw_encrypt($m) { $temp = $this->_os2ip($m); $temp = $this->_rsaep($temp); return $this->_i2osp($temp, $this->k); } function _rsaes_pkcs1_v1_5_encrypt($m) { $mLen = strlen($m); if ($mLen > $this->k - 11) { user_error('Message too long'); return false; } $psLen = $this->k - $mLen - 3; $ps = ''; while (strlen($ps) != $psLen) { $temp = Random::string($psLen - strlen($ps)); $temp = str_replace("\x00", '', $temp); $ps.= $temp; } $type = 2; if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { $type = 1; $ps = str_repeat("\xFF", $psLen); } $em = chr(0) . chr($type) . $ps . chr(0) . $m; $m = $this->_os2ip($em); $c = $this->_rsaep($m); $c = $this->_i2osp($c, $this->k); return $c; } function _rsaes_pkcs1_v1_5_decrypt($c) { if (strlen($c) != $this->k) { user_error('Decryption error'); return false; } $c = $this->_os2ip($c); $m = $this->_rsadp($c); if ($m === false) { user_error('Decryption error'); return false; } $em = $this->_i2osp($m, $this->k); if (ord($em[0]) != 0 || ord($em[1]) > 2) { user_error('Decryption error'); return false; } $ps = substr($em, 2, strpos($em, chr(0), 2) - 2); $m = substr($em, strlen($ps) + 3); if (strlen($ps) < 8) { user_error('Decryption error'); return false; } return $m; } function _emsa_pss_encode($m, $emBits) { $emLen = ($emBits + 1) >> 3; $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { user_error('Encoding error'); return false; } $salt = Random::string($sLen); $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; $h = $this->hash->hash($m2); $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2); $db = $ps . chr(1) . $salt; $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); $maskedDB = $db ^ $dbMask; $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0]; $em = $maskedDB . $h . chr(0xBC); return $em; } function _emsa_pss_verify($m, $em, $emBits) { $emLen = ($emBits + 7) >> 3; $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { return false; } if ($em[strlen($em) - 1] != chr(0xBC)) { return false; } $maskedDB = substr($em, 0, -$this->hLen - 1); $h = substr($em, -$this->hLen - 1, $this->hLen); $temp = chr(0xFF << ($emBits & 7)); if ((~$maskedDB[0] & $temp) != $temp) { return false; } $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1); $db = $maskedDB ^ $dbMask; $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0]; $temp = $emLen - $this->hLen - $sLen - 2; if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) { return false; } $salt = substr($db, $temp + 1); $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt; $h2 = $this->hash->hash($m2); return $this->_equals($h, $h2); } function _rsassa_pss_sign($m) { $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1); $m = $this->_os2ip($em); $s = $this->_rsasp1($m); $s = $this->_i2osp($s, $this->k); return $s; } function _rsassa_pss_verify($m, $s) { if (strlen($s) != $this->k) { user_error('Invalid signature'); return false; } $modBits = strlen($this->modulus->toBits()); $s2 = $this->_os2ip($s); $m2 = $this->_rsavp1($s2); if ($m2 === false) { user_error('Invalid signature'); return false; } $em = $this->_i2osp($m2, $this->k); if ($em === false) { user_error('Invalid signature'); return false; } return $this->_emsa_pss_verify($m, $em, $modBits - 1); } function _emsa_pkcs1_v1_5_encode($m, $emLen) { $h = $this->hash->hash($m); if ($h === false) { return false; } switch ($this->hashName) { case 'md2': $t = pack('H*', '3020300c06082a864886f70d020205000410'); break; case 'md5': $t = pack('H*', '3020300c06082a864886f70d020505000410'); break; case 'sha1': $t = pack('H*', '3021300906052b0e03021a05000414'); break; case 'sha256': $t = pack('H*', '3031300d060960864801650304020105000420'); break; case 'sha384': $t = pack('H*', '3041300d060960864801650304020205000430'); break; case 'sha512': $t = pack('H*', '3051300d060960864801650304020305000440'); } $t.= $h; $tLen = strlen($t); if ($emLen < $tLen + 11) { user_error('Intended encoded message length too short'); return false; } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); $em = "\0\1$ps\0$t"; return $em; } function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen) { $h = $this->hash->hash($m); if ($h === false) { return false; } switch ($this->hashName) { case 'sha1': $t = pack('H*', '301f300706052b0e03021a0414'); break; case 'sha256': $t = pack('H*', '302f300b06096086480165030402010420'); break; case 'sha384': $t = pack('H*', '303f300b06096086480165030402020430'); break; case 'sha512': $t = pack('H*', '304f300b06096086480165030402030440'); break; default: return false; } $t.= $h; $tLen = strlen($t); if ($emLen < $tLen + 11) { user_error('Intended encoded message length too short'); return false; } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); $em = "\0\1$ps\0$t"; return $em; } function _rsassa_pkcs1_v1_5_sign($m) { $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); if ($em === false) { user_error('RSA modulus too short'); return false; } $m = $this->_os2ip($em); $s = $this->_rsasp1($m); $s = $this->_i2osp($s, $this->k); return $s; } function _rsassa_pkcs1_v1_5_verify($m, $s) { if (strlen($s) != $this->k) { user_error('Invalid signature'); return false; } $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); if ($m2 === false) { user_error('Invalid signature'); return false; } $em = $this->_i2osp($m2, $this->k); if ($em === false) { user_error('Invalid signature'); return false; } $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k); if ($em2 === false && $em3 === false) { user_error('RSA modulus too short'); return false; } return ($em2 !== false && $this->_equals($em, $em2)) || ($em3 !== false && $this->_equals($em, $em3)); } function setEncryptionMode($mode) { $this->encryptionMode = $mode; } function setSignatureMode($mode) { $this->signatureMode = $mode; } function setComment($comment) { $this->comment = $comment; } function getComment() { return $this->comment; } function encrypt($plaintext) { switch ($this->encryptionMode) { case self::ENCRYPTION_NONE: $plaintext = str_split($plaintext, $this->k); $ciphertext = ''; foreach ($plaintext as $m) { $ciphertext.= $this->_raw_encrypt($m); } return $ciphertext; case self::ENCRYPTION_PKCS1: $length = $this->k - 11; if ($length <= 0) { return false; } $plaintext = str_split($plaintext, $length); $ciphertext = ''; foreach ($plaintext as $m) { $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); } return $ciphertext; default: $length = $this->k - 2 * $this->hLen - 2; if ($length <= 0) { return false; } $plaintext = str_split($plaintext, $length); $ciphertext = ''; foreach ($plaintext as $m) { $ciphertext.= $this->_rsaes_oaep_encrypt($m); } return $ciphertext; } } function decrypt($ciphertext) { if ($this->k <= 0) { return false; } $ciphertext = str_split($ciphertext, $this->k); $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); $plaintext = ''; switch ($this->encryptionMode) { case self::ENCRYPTION_NONE: $decrypt = '_raw_encrypt'; break; case self::ENCRYPTION_PKCS1: $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; break; default: $decrypt = '_rsaes_oaep_decrypt'; } foreach ($ciphertext as $c) { $temp = $this->$decrypt($c); if ($temp === false) { return false; } $plaintext.= $temp; } return $plaintext; } function sign($message) { if (empty($this->modulus) || empty($this->exponent)) { return false; } switch ($this->signatureMode) { case self::SIGNATURE_PKCS1: return $this->_rsassa_pkcs1_v1_5_sign($message); default: return $this->_rsassa_pss_sign($message); } } function verify($message, $signature) { if (empty($this->modulus) || empty($this->exponent)) { return false; } switch ($this->signatureMode) { case self::SIGNATURE_PKCS1: return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); default: return $this->_rsassa_pss_verify($message, $signature); } } function _extractBER($str) { $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); $temp = preg_replace('#-+[^-]+-+#', '', $temp); $temp = str_replace(array("\r", "\n", ' '), '', $temp); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; return $temp != false ? $temp : $str; } } = 0) { $this->cipher_name_openssl = 'rc4-40'; } else { switch (strlen($this->key)) { case 5: $this->cipher_name_openssl = 'rc4-40'; break; case 8: $this->cipher_name_openssl = 'rc4-64'; break; case 16: $this->cipher_name_openssl = 'rc4'; break; default: return false; } } } return parent::isValidEngine($engine); } function setIV($iv) { } function setKeyLength($length) { if ($length < 8) { $this->key_length = 1; } elseif ($length > 2048) { $this->key_length = 256; } else { $this->key_length = $length >> 3; } parent::setKeyLength($length); } function encrypt($plaintext) { if ($this->engine != Base::ENGINE_INTERNAL) { return parent::encrypt($plaintext); } return $this->_crypt($plaintext, self::ENCRYPT); } function decrypt($ciphertext) { if ($this->engine != Base::ENGINE_INTERNAL) { return parent::decrypt($ciphertext); } return $this->_crypt($ciphertext, self::DECRYPT); } function _encryptBlock($in) { } function _decryptBlock($in) { } function _setupKey() { $key = $this->key; $keyLength = strlen($key); $keyStream = range(0, 255); $j = 0; for ($i = 0; $i < 256; $i++) { $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; $temp = $keyStream[$i]; $keyStream[$i] = $keyStream[$j]; $keyStream[$j] = $temp; } $this->stream = array(); $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = array( 0, 0, $keyStream ); } function _crypt($text, $mode) { if ($this->changed) { $this->_setup(); $this->changed = false; } $stream = &$this->stream[$mode]; if ($this->continuousBuffer) { $i = &$stream[0]; $j = &$stream[1]; $keyStream = &$stream[2]; } else { $i = $stream[0]; $j = $stream[1]; $keyStream = $stream[2]; } $len = strlen($text); for ($k = 0; $k < $len; ++$k) { $i = ($i + 1) & 255; $ksi = $keyStream[$i]; $j = ($j + $ksi) & 255; $ksj = $keyStream[$j]; $keyStream[$i] = $ksj; $keyStream[$j] = $ksi; $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]); } return $text; } } setHash($hash); } function setKey($key = false) { $this->key = $key; $this->_computeKey(); } function _computeKey() { if ($this->key === false) { $this->computedKey = false; return; } if (strlen($this->key) <= $this->b) { $this->computedKey = $this->key; return; } switch ($this->engine) { case self::MODE_MHASH: $this->computedKey = mhash($this->hash, $this->key); break; case self::MODE_HASH: $this->computedKey = hash($this->hash, $this->key, true); break; case self::MODE_INTERNAL: $this->computedKey = call_user_func($this->hash, $this->key); } } function getHash() { return $this->hashParam; } function setHash($hash) { $this->hashParam = $hash = strtolower($hash); switch ($hash) { case 'md5-96': case 'sha1-96': case 'sha256-96': case 'sha512-96': $hash = substr($hash, 0, -3); $this->l = 12; break; case 'md2': case 'md5': $this->l = 16; break; case 'sha1': $this->l = 20; break; case 'sha256': $this->l = 32; break; case 'sha384': $this->l = 48; break; case 'sha512': $this->l = 64; } switch ($hash) { case 'md2-96': case 'md2': $this->b = 16; case 'md5-96': case 'sha1-96': case 'sha224-96': case 'sha256-96': case 'md2': case 'md5': case 'sha1': case 'sha224': case 'sha256': $this->b = 64; break; default: $this->b = 128; } switch ($hash) { case 'md2': $this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? self::MODE_HASH : self::MODE_INTERNAL; break; case 'sha384': case 'sha512': $this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; break; default: $this->engine = CRYPT_HASH_MODE; } switch ($this->engine) { case self::MODE_MHASH: switch ($hash) { case 'md5': $this->hash = MHASH_MD5; break; case 'sha256': $this->hash = MHASH_SHA256; break; case 'sha1': default: $this->hash = MHASH_SHA1; } $this->_computeKey(self::MODE_MHASH); return; case self::MODE_HASH: switch ($hash) { case 'md5': $this->hash = 'md5'; return; case 'md2': case 'sha256': case 'sha384': case 'sha512': $this->hash = $hash; return; case 'sha1': default: $this->hash = 'sha1'; } $this->_computeKey(self::MODE_HASH); return; } switch ($hash) { case 'md2': $this->hash = array($this, '_md2'); break; case 'md5': $this->hash = array($this, '_md5'); break; case 'sha256': $this->hash = array($this, '_sha256'); break; case 'sha384': case 'sha512': $this->hash = array($this, '_sha512'); break; case 'sha1': default: $this->hash = array($this, '_sha1'); } $this->ipad = str_repeat(chr(0x36), $this->b); $this->opad = str_repeat(chr(0x5C), $this->b); $this->_computeKey(self::MODE_INTERNAL); } function hash($text) { if (!empty($this->key) || is_string($this->key)) { switch ($this->engine) { case self::MODE_MHASH: $output = mhash($this->hash, $text, $this->computedKey); break; case self::MODE_HASH: $output = hash_hmac($this->hash, $text, $this->computedKey, true); break; case self::MODE_INTERNAL: $key = str_pad($this->computedKey, $this->b, chr(0)); $temp = $this->ipad ^ $key; $temp .= $text; $temp = call_user_func($this->hash, $temp); $output = $this->opad ^ $key; $output.= $temp; $output = call_user_func($this->hash, $output); } } else { switch ($this->engine) { case self::MODE_MHASH: $output = mhash($this->hash, $text); break; case self::MODE_HASH: $output = hash($this->hash, $text, true); break; case self::MODE_INTERNAL: $output = call_user_func($this->hash, $text); } } return substr($output, 0, $this->l); } function getLength() { return $this->l; } function _md5($m) { return pack('H*', md5($m)); } function _sha1($m) { return pack('H*', sha1($m)); } function _md2($m) { static $s = array( 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 ); $pad = 16 - (strlen($m) & 0xF); $m.= str_repeat(chr($pad), $pad); $length = strlen($m); $c = str_repeat(chr(0), 16); $l = chr(0); for ($i = 0; $i < $length; $i+= 16) { for ($j = 0; $j < 16; $j++) { $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j])); $l = $c[$j]; } } $m.= $c; $length+= 16; $x = str_repeat(chr(0), 48); for ($i = 0; $i < $length; $i+= 16) { for ($j = 0; $j < 16; $j++) { $x[$j + 16] = $m[$i + $j]; $x[$j + 32] = $x[$j + 16] ^ $x[$j]; } $t = chr(0); for ($j = 0; $j < 18; $j++) { for ($k = 0; $k < 48; $k++) { $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]); } $t = chr(ord($t) + $j); } } return substr($x, 0, 16); } function _sha256($m) { if (extension_loaded('suhosin')) { return pack('H*', sha256($m)); } $hash = array( 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ); static $k = array( 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ); $length = strlen($m); $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F)); $m[$length] = chr(0x80); $m.= pack('N2', 0, $length << 3); $chunks = str_split($m, 64); foreach ($chunks as $chunk) { $w = array(); for ($i = 0; $i < 16; $i++) { extract(unpack('Ntemp', $this->_string_shift($chunk, 4))); $w[] = $temp; } for ($i = 16; $i < 64; $i++) { $s0 = $this->_rightRotate($w[$i - 15], 7) ^ $this->_rightRotate($w[$i - 15], 18) ^ $this->_rightShift( $w[$i - 15], 3); $s1 = $this->_rightRotate($w[$i - 2], 17) ^ $this->_rightRotate($w[$i - 2], 19) ^ $this->_rightShift( $w[$i - 2], 10); $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1); } list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; for ($i = 0; $i < 64; $i++) { $s0 = $this->_rightRotate($a, 2) ^ $this->_rightRotate($a, 13) ^ $this->_rightRotate($a, 22); $maj = ($a & $b) ^ ($a & $c) ^ ($b & $c); $t2 = $this->_add($s0, $maj); $s1 = $this->_rightRotate($e, 6) ^ $this->_rightRotate($e, 11) ^ $this->_rightRotate($e, 25); $ch = ($e & $f) ^ ($this->_not($e) & $g); $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]); $h = $g; $g = $f; $f = $e; $e = $this->_add($d, $t1); $d = $c; $c = $b; $b = $a; $a = $this->_add($t1, $t2); } $hash = array( $this->_add($hash[0], $a), $this->_add($hash[1], $b), $this->_add($hash[2], $c), $this->_add($hash[3], $d), $this->_add($hash[4], $e), $this->_add($hash[5], $f), $this->_add($hash[6], $g), $this->_add($hash[7], $h) ); } return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]); } function _sha512($m) { static $init384, $init512, $k; if (!isset($k)) { $init384 = array( 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4' ); $init512 = array( '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1', '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179' ); for ($i = 0; $i < 8; $i++) { $init384[$i] = new BigInteger($init384[$i], 16); $init384[$i]->setPrecision(64); $init512[$i] = new BigInteger($init512[$i], 16); $init512[$i]->setPrecision(64); } $k = array( '428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc', '3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118', 'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2', '72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694', 'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65', '2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5', '983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4', 'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70', '27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df', '650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b', 'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30', 'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8', '19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8', '391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3', '748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec', '90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b', 'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178', '06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b', '28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c', '4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817' ); for ($i = 0; $i < 80; $i++) { $k[$i] = new BigInteger($k[$i], 16); } } $hash = $this->l == 48 ? $init384 : $init512; $length = strlen($m); $m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F)); $m[$length] = chr(0x80); $m.= pack('N4', 0, 0, 0, $length << 3); $chunks = str_split($m, 128); foreach ($chunks as $chunk) { $w = array(); for ($i = 0; $i < 16; $i++) { $temp = new BigInteger($this->_string_shift($chunk, 8), 256); $temp->setPrecision(64); $w[] = $temp; } for ($i = 16; $i < 80; $i++) { $temp = array( $w[$i - 15]->bitwise_rightRotate(1), $w[$i - 15]->bitwise_rightRotate(8), $w[$i - 15]->bitwise_rightShift(7) ); $s0 = $temp[0]->bitwise_xor($temp[1]); $s0 = $s0->bitwise_xor($temp[2]); $temp = array( $w[$i - 2]->bitwise_rightRotate(19), $w[$i - 2]->bitwise_rightRotate(61), $w[$i - 2]->bitwise_rightShift(6) ); $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); $w[$i] = $w[$i - 16]->copy(); $w[$i] = $w[$i]->add($s0); $w[$i] = $w[$i]->add($w[$i - 7]); $w[$i] = $w[$i]->add($s1); } $a = $hash[0]->copy(); $b = $hash[1]->copy(); $c = $hash[2]->copy(); $d = $hash[3]->copy(); $e = $hash[4]->copy(); $f = $hash[5]->copy(); $g = $hash[6]->copy(); $h = $hash[7]->copy(); for ($i = 0; $i < 80; $i++) { $temp = array( $a->bitwise_rightRotate(28), $a->bitwise_rightRotate(34), $a->bitwise_rightRotate(39) ); $s0 = $temp[0]->bitwise_xor($temp[1]); $s0 = $s0->bitwise_xor($temp[2]); $temp = array( $a->bitwise_and($b), $a->bitwise_and($c), $b->bitwise_and($c) ); $maj = $temp[0]->bitwise_xor($temp[1]); $maj = $maj->bitwise_xor($temp[2]); $t2 = $s0->add($maj); $temp = array( $e->bitwise_rightRotate(14), $e->bitwise_rightRotate(18), $e->bitwise_rightRotate(41) ); $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); $temp = array( $e->bitwise_and($f), $g->bitwise_and($e->bitwise_not()) ); $ch = $temp[0]->bitwise_xor($temp[1]); $t1 = $h->add($s1); $t1 = $t1->add($ch); $t1 = $t1->add($k[$i]); $t1 = $t1->add($w[$i]); $h = $g->copy(); $g = $f->copy(); $f = $e->copy(); $e = $d->add($t1); $d = $c->copy(); $c = $b->copy(); $b = $a->copy(); $a = $t1->add($t2); } $hash = array( $hash[0]->add($a), $hash[1]->add($b), $hash[2]->add($c), $hash[3]->add($d), $hash[4]->add($e), $hash[5]->add($f), $hash[6]->add($g), $hash[7]->add($h) ); } $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . $hash[4]->toBytes() . $hash[5]->toBytes(); if ($this->l != 48) { $temp.= $hash[6]->toBytes() . $hash[7]->toBytes(); } return $temp; } function _rightRotate($int, $amt) { $invamt = 32 - $amt; $mask = (1 << $invamt) - 1; return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask); } function _rightShift($int, $amt) { $mask = (1 << (32 - $amt)) - 1; return ($int >> $amt) & $mask; } function _not($int) { return ~$int & 0xFFFFFFFF; } function _add() { static $mod; if (!isset($mod)) { $mod = pow(2, 32); } $result = 0; $arguments = func_get_args(); foreach ($arguments as $argument) { $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; } if ((php_uname('m') & "\xDF\xDF\xDF") != 'ARM') { return fmod($result, $mod); } return (fmod($result, 0x80000000) & 0x7FFFFFFF) | ((fmod(floor($result / 0x80000000), 2) & 1) << 31); } function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } } DirectoryString = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'teletexString' => array('type' => ASN1::TYPE_TELETEX_STRING), 'printableString' => array('type' => ASN1::TYPE_PRINTABLE_STRING), 'universalString' => array('type' => ASN1::TYPE_UNIVERSAL_STRING), 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING), 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING) ) ); $this->PKCS9String = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), 'directoryString' => $this->DirectoryString ) ); $this->AttributeValue = array('type' => ASN1::TYPE_ANY); $AttributeType = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $AttributeTypeAndValue = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'type' => $AttributeType, 'value'=> $this->AttributeValue ) ); $this->RelativeDistinguishedName = array( 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $AttributeTypeAndValue ); $RDNSequence = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 0, 'max' => -1, 'children' => $this->RelativeDistinguishedName ); $this->Name = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'rdnSequence' => $RDNSequence ) ); $AlgorithmIdentifier = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'parameters' => array( 'type' => ASN1::TYPE_ANY, 'optional' => true ) ) ); $Extension = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'extnId' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'critical' => array( 'type' => ASN1::TYPE_BOOLEAN, 'optional' => true, 'default' => false ), 'extnValue' => array('type' => ASN1::TYPE_OCTET_STRING) ) ); $this->Extensions = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $Extension ); $SubjectPublicKeyInfo = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'algorithm' => $AlgorithmIdentifier, 'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING) ) ); $UniqueIdentifier = array('type' => ASN1::TYPE_BIT_STRING); $Time = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'utcTime' => array('type' => ASN1::TYPE_UTC_TIME), 'generalTime' => array('type' => ASN1::TYPE_GENERALIZED_TIME) ) ); $Validity = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'notBefore' => $Time, 'notAfter' => $Time ) ); $CertificateSerialNumber = array('type' => ASN1::TYPE_INTEGER); $Version = array( 'type' => ASN1::TYPE_INTEGER, 'mapping' => array('v1', 'v2', 'v3') ); $TBSCertificate = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'version' => array( 'constant' => 0, 'optional' => true, 'explicit' => true, 'default' => 'v1' ) + $Version, 'serialNumber' => $CertificateSerialNumber, 'signature' => $AlgorithmIdentifier, 'issuer' => $this->Name, 'validity' => $Validity, 'subject' => $this->Name, 'subjectPublicKeyInfo' => $SubjectPublicKeyInfo, 'issuerUniqueID' => array( 'constant' => 1, 'optional' => true, 'implicit' => true ) + $UniqueIdentifier, 'subjectUniqueID' => array( 'constant' => 2, 'optional' => true, 'implicit' => true ) + $UniqueIdentifier, 'extensions' => array( 'constant' => 3, 'optional' => true, 'explicit' => true ) + $this->Extensions ) ); $this->Certificate = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'tbsCertificate' => $TBSCertificate, 'signatureAlgorithm' => $AlgorithmIdentifier, 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); $this->KeyUsage = array( 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => array( 'digitalSignature', 'nonRepudiation', 'keyEncipherment', 'dataEncipherment', 'keyAgreement', 'keyCertSign', 'cRLSign', 'encipherOnly', 'decipherOnly' ) ); $this->BasicConstraints = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'cA' => array( 'type' => ASN1::TYPE_BOOLEAN, 'optional' => true, 'default' => false ), 'pathLenConstraint' => array( 'type' => ASN1::TYPE_INTEGER, 'optional' => true ) ) ); $this->KeyIdentifier = array('type' => ASN1::TYPE_OCTET_STRING); $OrganizationalUnitNames = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 4, 'children' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ); $PersonalName = array( 'type' => ASN1::TYPE_SET, 'children' => array( 'surname' => array( 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ), 'given-name' => array( 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ), 'initials' => array( 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true ), 'generation-qualifier' => array( 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 3, 'optional' => true, 'implicit' => true ) ) ); $NumericUserIdentifier = array('type' => ASN1::TYPE_NUMERIC_STRING); $OrganizationName = array('type' => ASN1::TYPE_PRINTABLE_STRING); $PrivateDomainName = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); $TerminalIdentifier = array('type' => ASN1::TYPE_PRINTABLE_STRING); $NetworkAddress = array('type' => ASN1::TYPE_NUMERIC_STRING); $AdministrationDomainName = array( 'type' => ASN1::TYPE_CHOICE, 'class' => ASN1::CLASS_APPLICATION, 'cast' => 2, 'children' => array( 'numeric' => array('type' => ASN1::TYPE_NUMERIC_STRING), 'printable' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); $CountryName = array( 'type' => ASN1::TYPE_CHOICE, 'class' => ASN1::CLASS_APPLICATION, 'cast' => 1, 'children' => array( 'x121-dcc-code' => array('type' => ASN1::TYPE_NUMERIC_STRING), 'iso-3166-alpha2-code' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); $AnotherName = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'type-id' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'value' => array( 'type' => ASN1::TYPE_ANY, 'constant' => 0, 'optional' => true, 'explicit' => true ) ) ); $ExtensionAttribute = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'extension-attribute-type' => array( 'type' => ASN1::TYPE_PRINTABLE_STRING, 'constant' => 0, 'optional' => true, 'implicit' => true ), 'extension-attribute-value' => array( 'type' => ASN1::TYPE_ANY, 'constant' => 1, 'optional' => true, 'explicit' => true ) ) ); $ExtensionAttributes = array( 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => 256, 'children' => $ExtensionAttribute ); $BuiltInDomainDefinedAttribute = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'type' => array('type' => ASN1::TYPE_PRINTABLE_STRING), 'value' => array('type' => ASN1::TYPE_PRINTABLE_STRING) ) ); $BuiltInDomainDefinedAttributes = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 4, 'children' => $BuiltInDomainDefinedAttribute ); $BuiltInStandardAttributes = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'country-name' => array('optional' => true) + $CountryName, 'administration-domain-name' => array('optional' => true) + $AdministrationDomainName, 'network-address' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $NetworkAddress, 'terminal-identifier' => array( 'constant' => 1, 'optional' => true, 'implicit' => true ) + $TerminalIdentifier, 'private-domain-name' => array( 'constant' => 2, 'optional' => true, 'explicit' => true ) + $PrivateDomainName, 'organization-name' => array( 'constant' => 3, 'optional' => true, 'implicit' => true ) + $OrganizationName, 'numeric-user-identifier' => array( 'constant' => 4, 'optional' => true, 'implicit' => true ) + $NumericUserIdentifier, 'personal-name' => array( 'constant' => 5, 'optional' => true, 'implicit' => true ) + $PersonalName, 'organizational-unit-names' => array( 'constant' => 6, 'optional' => true, 'implicit' => true ) + $OrganizationalUnitNames ) ); $ORAddress = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'built-in-standard-attributes' => $BuiltInStandardAttributes, 'built-in-domain-defined-attributes' => array('optional' => true) + $BuiltInDomainDefinedAttributes, 'extension-attributes' => array('optional' => true) + $ExtensionAttributes ) ); $EDIPartyName = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'nameAssigner' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $this->DirectoryString, 'partyName' => array( 'constant' => 1, 'optional' => true, 'implicit' => true ) + $this->DirectoryString ) ); $GeneralName = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'otherName' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $AnotherName, 'rfc822Name' => array( 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 1, 'optional' => true, 'implicit' => true ), 'dNSName' => array( 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 2, 'optional' => true, 'implicit' => true ), 'x400Address' => array( 'constant' => 3, 'optional' => true, 'implicit' => true ) + $ORAddress, 'directoryName' => array( 'constant' => 4, 'optional' => true, 'explicit' => true ) + $this->Name, 'ediPartyName' => array( 'constant' => 5, 'optional' => true, 'implicit' => true ) + $EDIPartyName, 'uniformResourceIdentifier' => array( 'type' => ASN1::TYPE_IA5_STRING, 'constant' => 6, 'optional' => true, 'implicit' => true ), 'iPAddress' => array( 'type' => ASN1::TYPE_OCTET_STRING, 'constant' => 7, 'optional' => true, 'implicit' => true ), 'registeredID' => array( 'type' => ASN1::TYPE_OBJECT_IDENTIFIER, 'constant' => 8, 'optional' => true, 'implicit' => true ) ) ); $GeneralNames = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $GeneralName ); $this->IssuerAltName = $GeneralNames; $ReasonFlags = array( 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => array( 'unused', 'keyCompromise', 'cACompromise', 'affiliationChanged', 'superseded', 'cessationOfOperation', 'certificateHold', 'privilegeWithdrawn', 'aACompromise' ) ); $DistributionPointName = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'fullName' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $GeneralNames, 'nameRelativeToCRLIssuer' => array( 'constant' => 1, 'optional' => true, 'implicit' => true ) + $this->RelativeDistinguishedName ) ); $DistributionPoint = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'distributionPoint' => array( 'constant' => 0, 'optional' => true, 'explicit' => true ) + $DistributionPointName, 'reasons' => array( 'constant' => 1, 'optional' => true, 'implicit' => true ) + $ReasonFlags, 'cRLIssuer' => array( 'constant' => 2, 'optional' => true, 'implicit' => true ) + $GeneralNames ) ); $this->CRLDistributionPoints = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $DistributionPoint ); $this->AuthorityKeyIdentifier = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'keyIdentifier' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $this->KeyIdentifier, 'authorityCertIssuer' => array( 'constant' => 1, 'optional' => true, 'implicit' => true ) + $GeneralNames, 'authorityCertSerialNumber' => array( 'constant' => 2, 'optional' => true, 'implicit' => true ) + $CertificateSerialNumber ) ); $PolicyQualifierId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $PolicyQualifierInfo = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'policyQualifierId' => $PolicyQualifierId, 'qualifier' => array('type' => ASN1::TYPE_ANY) ) ); $CertPolicyId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $PolicyInformation = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'policyIdentifier' => $CertPolicyId, 'policyQualifiers' => array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 0, 'max' => -1, 'optional' => true, 'children' => $PolicyQualifierInfo ) ) ); $this->CertificatePolicies = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $PolicyInformation ); $this->PolicyMappings = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'issuerDomainPolicy' => $CertPolicyId, 'subjectDomainPolicy' => $CertPolicyId ) ) ); $KeyPurposeId = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $this->ExtKeyUsageSyntax = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $KeyPurposeId ); $AccessDescription = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'accessMethod' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'accessLocation' => $GeneralName ) ); $this->AuthorityInfoAccessSyntax = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $AccessDescription ); $this->SubjectInfoAccessSyntax = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $AccessDescription ); $this->SubjectAltName = $GeneralNames; $this->PrivateKeyUsagePeriod = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'notBefore' => array( 'constant' => 0, 'optional' => true, 'implicit' => true, 'type' => ASN1::TYPE_GENERALIZED_TIME), 'notAfter' => array( 'constant' => 1, 'optional' => true, 'implicit' => true, 'type' => ASN1::TYPE_GENERALIZED_TIME) ) ); $BaseDistance = array('type' => ASN1::TYPE_INTEGER); $GeneralSubtree = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'base' => $GeneralName, 'minimum' => array( 'constant' => 0, 'optional' => true, 'implicit' => true, 'default' => new BigInteger(0) ) + $BaseDistance, 'maximum' => array( 'constant' => 1, 'optional' => true, 'implicit' => true, ) + $BaseDistance ) ); $GeneralSubtrees = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $GeneralSubtree ); $this->NameConstraints = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'permittedSubtrees' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $GeneralSubtrees, 'excludedSubtrees' => array( 'constant' => 1, 'optional' => true, 'implicit' => true ) + $GeneralSubtrees ) ); $this->CPSuri = array('type' => ASN1::TYPE_IA5_STRING); $DisplayText = array( 'type' => ASN1::TYPE_CHOICE, 'children' => array( 'ia5String' => array('type' => ASN1::TYPE_IA5_STRING), 'visibleString' => array('type' => ASN1::TYPE_VISIBLE_STRING), 'bmpString' => array('type' => ASN1::TYPE_BMP_STRING), 'utf8String' => array('type' => ASN1::TYPE_UTF8_STRING) ) ); $NoticeReference = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'organization' => $DisplayText, 'noticeNumbers' => array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => 200, 'children' => array('type' => ASN1::TYPE_INTEGER) ) ) ); $this->UserNotice = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'noticeRef' => array( 'optional' => true, 'implicit' => true ) + $NoticeReference, 'explicitText' => array( 'optional' => true, 'implicit' => true ) + $DisplayText ) ); $this->netscape_cert_type = array( 'type' => ASN1::TYPE_BIT_STRING, 'mapping' => array( 'SSLClient', 'SSLServer', 'Email', 'ObjectSigning', 'Reserved', 'SSLCA', 'EmailCA', 'ObjectSigningCA' ) ); $this->netscape_comment = array('type' => ASN1::TYPE_IA5_STRING); $this->netscape_ca_policy_url = array('type' => ASN1::TYPE_IA5_STRING); $Attribute = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'type' => $AttributeType, 'value'=> array( 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $this->AttributeValue ) ) ); $this->SubjectDirectoryAttributes = array( 'type' => ASN1::TYPE_SEQUENCE, 'min' => 1, 'max' => -1, 'children' => $Attribute ); $Attributes = array( 'type' => ASN1::TYPE_SET, 'min' => 1, 'max' => -1, 'children' => $Attribute ); $CertificationRequestInfo = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'version' => array( 'type' => ASN1::TYPE_INTEGER, 'mapping' => array('v1') ), 'subject' => $this->Name, 'subjectPKInfo' => $SubjectPublicKeyInfo, 'attributes' => array( 'constant' => 0, 'optional' => true, 'implicit' => true ) + $Attributes, ) ); $this->CertificationRequest = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'certificationRequestInfo' => $CertificationRequestInfo, 'signatureAlgorithm' => $AlgorithmIdentifier, 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); $RevokedCertificate = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'userCertificate' => $CertificateSerialNumber, 'revocationDate' => $Time, 'crlEntryExtensions' => array( 'optional' => true ) + $this->Extensions ) ); $TBSCertList = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'version' => array( 'optional' => true, 'default' => 'v1' ) + $Version, 'signature' => $AlgorithmIdentifier, 'issuer' => $this->Name, 'thisUpdate' => $Time, 'nextUpdate' => array( 'optional' => true ) + $Time, 'revokedCertificates' => array( 'type' => ASN1::TYPE_SEQUENCE, 'optional' => true, 'min' => 0, 'max' => -1, 'children' => $RevokedCertificate ), 'crlExtensions' => array( 'constant' => 0, 'optional' => true, 'explicit' => true ) + $this->Extensions ) ); $this->CertificateList = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'tbsCertList' => $TBSCertList, 'signatureAlgorithm' => $AlgorithmIdentifier, 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); $this->CRLNumber = array('type' => ASN1::TYPE_INTEGER); $this->CRLReason = array('type' => ASN1::TYPE_ENUMERATED, 'mapping' => array( 'unspecified', 'keyCompromise', 'cACompromise', 'affiliationChanged', 'superseded', 'cessationOfOperation', 'certificateHold', 8 => 'removeFromCRL', 'privilegeWithdrawn', 'aACompromise' ) ); $this->IssuingDistributionPoint = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'distributionPoint' => array( 'constant' => 0, 'optional' => true, 'explicit' => true ) + $DistributionPointName, 'onlyContainsUserCerts' => array( 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 1, 'optional' => true, 'default' => false, 'implicit' => true ), 'onlyContainsCACerts' => array( 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 2, 'optional' => true, 'default' => false, 'implicit' => true ), 'onlySomeReasons' => array( 'constant' => 3, 'optional' => true, 'implicit' => true ) + $ReasonFlags, 'indirectCRL' => array( 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 4, 'optional' => true, 'default' => false, 'implicit' => true ), 'onlyContainsAttributeCerts' => array( 'type' => ASN1::TYPE_BOOLEAN, 'constant' => 5, 'optional' => true, 'default' => false, 'implicit' => true ) ) ); $this->InvalidityDate = array('type' => ASN1::TYPE_GENERALIZED_TIME); $this->CertificateIssuer = $GeneralNames; $this->HoldInstructionCode = array('type' => ASN1::TYPE_OBJECT_IDENTIFIER); $PublicKeyAndChallenge = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'spki' => $SubjectPublicKeyInfo, 'challenge' => array('type' => ASN1::TYPE_IA5_STRING) ) ); $this->SignedPublicKeyAndChallenge = array( 'type' => ASN1::TYPE_SEQUENCE, 'children' => array( 'publicKeyAndChallenge' => $PublicKeyAndChallenge, 'signatureAlgorithm' => $AlgorithmIdentifier, 'signature' => array('type' => ASN1::TYPE_BIT_STRING) ) ); $this->PostalAddress = array( 'type' => ASN1::TYPE_SEQUENCE, 'optional' => true, 'min' => 1, 'max' => -1, 'children' => $this->DirectoryString ); $this->oids = array( '1.3.6.1.5.5.7' => 'id-pkix', '1.3.6.1.5.5.7.1' => 'id-pe', '1.3.6.1.5.5.7.2' => 'id-qt', '1.3.6.1.5.5.7.3' => 'id-kp', '1.3.6.1.5.5.7.48' => 'id-ad', '1.3.6.1.5.5.7.2.1' => 'id-qt-cps', '1.3.6.1.5.5.7.2.2' => 'id-qt-unotice', '1.3.6.1.5.5.7.48.1' =>'id-ad-ocsp', '1.3.6.1.5.5.7.48.2' => 'id-ad-caIssuers', '1.3.6.1.5.5.7.48.3' => 'id-ad-timeStamping', '1.3.6.1.5.5.7.48.5' => 'id-ad-caRepository', '2.5.4' => 'id-at', '2.5.4.41' => 'id-at-name', '2.5.4.4' => 'id-at-surname', '2.5.4.42' => 'id-at-givenName', '2.5.4.43' => 'id-at-initials', '2.5.4.44' => 'id-at-generationQualifier', '2.5.4.3' => 'id-at-commonName', '2.5.4.7' => 'id-at-localityName', '2.5.4.8' => 'id-at-stateOrProvinceName', '2.5.4.10' => 'id-at-organizationName', '2.5.4.11' => 'id-at-organizationalUnitName', '2.5.4.12' => 'id-at-title', '2.5.4.13' => 'id-at-description', '2.5.4.46' => 'id-at-dnQualifier', '2.5.4.6' => 'id-at-countryName', '2.5.4.5' => 'id-at-serialNumber', '2.5.4.65' => 'id-at-pseudonym', '2.5.4.17' => 'id-at-postalCode', '2.5.4.9' => 'id-at-streetAddress', '2.5.4.45' => 'id-at-uniqueIdentifier', '2.5.4.72' => 'id-at-role', '2.5.4.16' => 'id-at-postalAddress', '0.9.2342.19200300.100.1.25' => 'id-domainComponent', '1.2.840.113549.1.9' => 'pkcs-9', '1.2.840.113549.1.9.1' => 'pkcs-9-at-emailAddress', '2.5.29' => 'id-ce', '2.5.29.35' => 'id-ce-authorityKeyIdentifier', '2.5.29.14' => 'id-ce-subjectKeyIdentifier', '2.5.29.15' => 'id-ce-keyUsage', '2.5.29.16' => 'id-ce-privateKeyUsagePeriod', '2.5.29.32' => 'id-ce-certificatePolicies', '2.5.29.32.0' => 'anyPolicy', '2.5.29.33' => 'id-ce-policyMappings', '2.5.29.17' => 'id-ce-subjectAltName', '2.5.29.18' => 'id-ce-issuerAltName', '2.5.29.9' => 'id-ce-subjectDirectoryAttributes', '2.5.29.19' => 'id-ce-basicConstraints', '2.5.29.30' => 'id-ce-nameConstraints', '2.5.29.36' => 'id-ce-policyConstraints', '2.5.29.31' => 'id-ce-cRLDistributionPoints', '2.5.29.37' => 'id-ce-extKeyUsage', '2.5.29.37.0' => 'anyExtendedKeyUsage', '1.3.6.1.5.5.7.3.1' => 'id-kp-serverAuth', '1.3.6.1.5.5.7.3.2' => 'id-kp-clientAuth', '1.3.6.1.5.5.7.3.3' => 'id-kp-codeSigning', '1.3.6.1.5.5.7.3.4' => 'id-kp-emailProtection', '1.3.6.1.5.5.7.3.8' => 'id-kp-timeStamping', '1.3.6.1.5.5.7.3.9' => 'id-kp-OCSPSigning', '2.5.29.54' => 'id-ce-inhibitAnyPolicy', '2.5.29.46' => 'id-ce-freshestCRL', '1.3.6.1.5.5.7.1.1' => 'id-pe-authorityInfoAccess', '1.3.6.1.5.5.7.1.11' => 'id-pe-subjectInfoAccess', '2.5.29.20' => 'id-ce-cRLNumber', '2.5.29.28' => 'id-ce-issuingDistributionPoint', '2.5.29.27' => 'id-ce-deltaCRLIndicator', '2.5.29.21' => 'id-ce-cRLReasons', '2.5.29.29' => 'id-ce-certificateIssuer', '2.5.29.23' => 'id-ce-holdInstructionCode', '1.2.840.10040.2' => 'holdInstruction', '1.2.840.10040.2.1' => 'id-holdinstruction-none', '1.2.840.10040.2.2' => 'id-holdinstruction-callissuer', '1.2.840.10040.2.3' => 'id-holdinstruction-reject', '2.5.29.24' => 'id-ce-invalidityDate', '1.2.840.113549.2.2' => 'md2', '1.2.840.113549.2.5' => 'md5', '1.3.14.3.2.26' => 'id-sha1', '1.2.840.10040.4.1' => 'id-dsa', '1.2.840.10040.4.3' => 'id-dsa-with-sha1', '1.2.840.113549.1.1' => 'pkcs-1', '1.2.840.113549.1.1.1' => 'rsaEncryption', '1.2.840.113549.1.1.2' => 'md2WithRSAEncryption', '1.2.840.113549.1.1.4' => 'md5WithRSAEncryption', '1.2.840.113549.1.1.5' => 'sha1WithRSAEncryption', '1.2.840.10046.2.1' => 'dhpublicnumber', '2.16.840.1.101.2.1.1.22' => 'id-keyExchangeAlgorithm', '1.2.840.10045' => 'ansi-X9-62', '1.2.840.10045.4' => 'id-ecSigType', '1.2.840.10045.4.1' => 'ecdsa-with-SHA1', '1.2.840.10045.1' => 'id-fieldType', '1.2.840.10045.1.1' => 'prime-field', '1.2.840.10045.1.2' => 'characteristic-two-field', '1.2.840.10045.1.2.3' => 'id-characteristic-two-basis', '1.2.840.10045.1.2.3.1' => 'gnBasis', '1.2.840.10045.1.2.3.2' => 'tpBasis', '1.2.840.10045.1.2.3.3' => 'ppBasis', '1.2.840.10045.2' => 'id-publicKeyType', '1.2.840.10045.2.1' => 'id-ecPublicKey', '1.2.840.10045.3' => 'ellipticCurve', '1.2.840.10045.3.0' => 'c-TwoCurve', '1.2.840.10045.3.0.1' => 'c2pnb163v1', '1.2.840.10045.3.0.2' => 'c2pnb163v2', '1.2.840.10045.3.0.3' => 'c2pnb163v3', '1.2.840.10045.3.0.4' => 'c2pnb176w1', '1.2.840.10045.3.0.5' => 'c2pnb191v1', '1.2.840.10045.3.0.6' => 'c2pnb191v2', '1.2.840.10045.3.0.7' => 'c2pnb191v3', '1.2.840.10045.3.0.8' => 'c2pnb191v4', '1.2.840.10045.3.0.9' => 'c2pnb191v5', '1.2.840.10045.3.0.10' => 'c2pnb208w1', '1.2.840.10045.3.0.11' => 'c2pnb239v1', '1.2.840.10045.3.0.12' => 'c2pnb239v2', '1.2.840.10045.3.0.13' => 'c2pnb239v3', '1.2.840.10045.3.0.14' => 'c2pnb239v4', '1.2.840.10045.3.0.15' => 'c2pnb239v5', '1.2.840.10045.3.0.16' => 'c2pnb272w1', '1.2.840.10045.3.0.17' => 'c2pnb304w1', '1.2.840.10045.3.0.18' => 'c2pnb359v1', '1.2.840.10045.3.0.19' => 'c2pnb368w1', '1.2.840.10045.3.0.20' => 'c2pnb431r1', '1.2.840.10045.3.1' => 'primeCurve', '1.2.840.10045.3.1.1' => 'prime192v1', '1.2.840.10045.3.1.2' => 'prime192v2', '1.2.840.10045.3.1.3' => 'prime192v3', '1.2.840.10045.3.1.4' => 'prime239v1', '1.2.840.10045.3.1.5' => 'prime239v2', '1.2.840.10045.3.1.6' => 'prime239v3', '1.2.840.10045.3.1.7' => 'prime256v1', '1.2.840.113549.1.1.7' => 'id-RSAES-OAEP', '1.2.840.113549.1.1.9' => 'id-pSpecified', '1.2.840.113549.1.1.10' => 'id-RSASSA-PSS', '1.2.840.113549.1.1.8' => 'id-mgf1', '1.2.840.113549.1.1.14' => 'sha224WithRSAEncryption', '1.2.840.113549.1.1.11' => 'sha256WithRSAEncryption', '1.2.840.113549.1.1.12' => 'sha384WithRSAEncryption', '1.2.840.113549.1.1.13' => 'sha512WithRSAEncryption', '2.16.840.1.101.3.4.2.4' => 'id-sha224', '2.16.840.1.101.3.4.2.1' => 'id-sha256', '2.16.840.1.101.3.4.2.2' => 'id-sha384', '2.16.840.1.101.3.4.2.3' => 'id-sha512', '1.2.643.2.2.4' => 'id-GostR3411-94-with-GostR3410-94', '1.2.643.2.2.3' => 'id-GostR3411-94-with-GostR3410-2001', '1.2.643.2.2.20' => 'id-GostR3410-2001', '1.2.643.2.2.19' => 'id-GostR3410-94', '2.16.840.1.113730' => 'netscape', '2.16.840.1.113730.1' => 'netscape-cert-extension', '2.16.840.1.113730.1.1' => 'netscape-cert-type', '2.16.840.1.113730.1.13' => 'netscape-comment', '2.16.840.1.113730.1.8' => 'netscape-ca-policy-url', '1.3.6.1.5.5.7.1.12' => 'id-pe-logotype', '1.2.840.113533.7.65.0' => 'entrustVersInfo', '2.16.840.1.113733.1.6.9' => 'verisignPrivate', '1.2.840.113549.1.9.2' => 'pkcs-9-at-unstructuredName', '1.2.840.113549.1.9.7' => 'pkcs-9-at-challengePassword', '1.2.840.113549.1.9.14' => 'pkcs-9-at-extensionRequest' ); } function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($cert) && isset($cert['tbsCertificate'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); $this->dn = $cert['tbsCertificate']['subject']; if (!isset($this->dn)) { return false; } $this->currentCert = $cert; $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; unset($this->signatureSubject); return $cert; } $asn1 = new ASN1(); if ($mode != self::FORMAT_DER) { $newcert = $this->_extractBER($cert); if ($mode == self::FORMAT_PEM && $cert == $newcert) { return false; } $cert = $newcert; } if ($cert === false) { $this->currentCert = false; return false; } $asn1->loadOIDs($this->oids); $decoded = $asn1->decodeBER($cert); if (!empty($decoded)) { $x509 = $asn1->asn1map($decoded[0], $this->Certificate); } if (!isset($x509) || $x509 === false) { $this->currentCert = false; return false; } $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) { $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); } $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1); $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key); $this->currentCert = $x509; $this->dn = $x509['tbsCertificate']['subject']; $currentKeyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); $this->currentKeyIdentifier = is_string($currentKeyIdentifier) ? $currentKeyIdentifier : null; return $x509; } function saveX509($cert, $format = self::FORMAT_PEM) { if (!is_array($cert) || !isset($cert['tbsCertificate'])) { return false; } switch (true) { case !($algorithm = $this->_subArray($cert, 'tbsCertificate/subjectPublicKeyInfo/algorithm/algorithm')): case is_object($cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): break; default: switch ($algorithm) { case 'rsaEncryption': $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); $cert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = null; $cert['signatureAlgorithm']['parameters'] = null; $cert['tbsCertificate']['signature']['parameters'] = null; } } $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $type_utf8_string = array('type' => ASN1::TYPE_UTF8_STRING); $filters['tbsCertificate']['signature']['parameters'] = $type_utf8_string; $filters['tbsCertificate']['signature']['issuer']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['issuer']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['subject']['rdnSequence']['value'] = $type_utf8_string; $filters['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['parameters'] = $type_utf8_string; $filters['signatureAlgorithm']['parameters'] = $type_utf8_string; $filters['authorityCertIssuer']['directoryName']['rdnSequence']['value'] = $type_utf8_string; $filters['distributionPoint']['fullName']['directoryName']['rdnSequence']['value'] = $type_utf8_string; $filters['directoryName']['rdnSequence']['value'] = $type_utf8_string; $filters['policyQualifiers']['qualifier'] = array('type' => ASN1::TYPE_IA5_STRING); $asn1->loadFilters($filters); $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1); $this->_mapOutDNs($cert, 'tbsCertificate/issuer/rdnSequence', $asn1); $this->_mapOutDNs($cert, 'tbsCertificate/subject/rdnSequence', $asn1); $cert = $asn1->encodeDER($cert, $this->Certificate); switch ($format) { case self::FORMAT_DER: return $cert; default: return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----'; } } function _mapInExtensions(&$root, $path, $asn1) { $extensions = &$this->_subArrayUnchecked($root, $path); if ($extensions) { for ($i = 0; $i < count($extensions); $i++) { $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; $value = base64_decode($value); $map = $this->_getMapping($id); if (!is_bool($map)) { $decoder = $id == 'id-ce-nameConstraints' ? array($this, '_decodeNameConstraintIP') : array($this, '_decodeIP'); $decoded = $asn1->decodeBER($value); $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder)); $value = $mapped === false ? $decoded[0] : $mapped; if ($id == 'id-ce-certificatePolicies') { for ($j = 0; $j < count($value); $j++) { if (!isset($value[$j]['policyQualifiers'])) { continue; } for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; $map = $this->_getMapping($subid); $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; if ($map !== false) { $decoded = $asn1->decodeBER($subvalue); $mapped = $asn1->asn1map($decoded[0], $map); $subvalue = $mapped === false ? $decoded[0] : $mapped; } } } } } else { $value = base64_encode($value); } } } } function _mapOutExtensions(&$root, $path, $asn1) { $extensions = &$this->_subArray($root, $path); if (is_array($extensions)) { $size = count($extensions); for ($i = 0; $i < $size; $i++) { if ($extensions[$i] instanceof Element) { continue; } $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; switch ($id) { case 'id-ce-certificatePolicies': for ($j = 0; $j < count($value); $j++) { if (!isset($value[$j]['policyQualifiers'])) { continue; } for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; $map = $this->_getMapping($subid); $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; if ($map !== false) { $subvalue = new Element($asn1->encodeDER($subvalue, $map)); } } } break; case 'id-ce-authorityKeyIdentifier': if (isset($value['authorityCertSerialNumber'])) { if ($value['authorityCertSerialNumber']->toBytes() == '') { $temp = chr((ASN1::CLASS_CONTEXT_SPECIFIC << 6) | 2) . "\1\0"; $value['authorityCertSerialNumber'] = new Element($temp); } } } $map = $this->_getMapping($id); if (is_bool($map)) { if (!$map) { user_error($id . ' is not a currently supported extension'); unset($extensions[$i]); } } else { $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); $value = base64_encode($temp); } } } } function _mapInAttributes(&$root, $path, $asn1) { $attributes = &$this->_subArray($root, $path); if (is_array($attributes)) { for ($i = 0; $i < count($attributes); $i++) { $id = $attributes[$i]['type']; $map = $this->_getMapping($id); if (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; for ($j = 0; $j < count($values); $j++) { $value = $asn1->encodeDER($values[$j], $this->AttributeValue); $decoded = $asn1->decodeBER($value); if (!is_bool($map)) { $mapped = $asn1->asn1map($decoded[0], $map); if ($mapped !== false) { $values[$j] = $mapped; } if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) { $this->_mapInExtensions($values, $j, $asn1); } } elseif ($map) { $values[$j] = base64_encode($value); } } } } } } function _mapOutAttributes(&$root, $path, $asn1) { $attributes = &$this->_subArray($root, $path); if (is_array($attributes)) { $size = count($attributes); for ($i = 0; $i < $size; $i++) { $id = $attributes[$i]['type']; $map = $this->_getMapping($id); if ($map === false) { user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); unset($attributes[$i]); } elseif (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; for ($j = 0; $j < count($values); $j++) { switch ($id) { case 'pkcs-9-at-extensionRequest': $this->_mapOutExtensions($values, $j, $asn1); break; } if (!is_bool($map)) { $temp = $asn1->encodeDER($values[$j], $map); $decoded = $asn1->decodeBER($temp); $values[$j] = $asn1->asn1map($decoded[0], $this->AttributeValue); } } } } } } function _mapInDNs(&$root, $path, $asn1) { $dns = &$this->_subArray($root, $path); if (is_array($dns)) { for ($i = 0; $i < count($dns); $i++) { for ($j = 0; $j < count($dns[$i]); $j++) { $type = $dns[$i][$j]['type']; $value = &$dns[$i][$j]['value']; if (is_object($value) && $value instanceof Element) { $map = $this->_getMapping($type); if (!is_bool($map)) { $decoded = $asn1->decodeBER($value); $value = $asn1->asn1map($decoded[0], $map); } } } } } } function _mapOutDNs(&$root, $path, $asn1) { $dns = &$this->_subArray($root, $path); if (is_array($dns)) { $size = count($dns); for ($i = 0; $i < $size; $i++) { for ($j = 0; $j < count($dns[$i]); $j++) { $type = $dns[$i][$j]['type']; $value = &$dns[$i][$j]['value']; if (is_object($value) && $value instanceof Element) { continue; } $map = $this->_getMapping($type); if (!is_bool($map)) { $value = new Element($asn1->encodeDER($value, $map)); } } } } } function _getMapping($extnId) { if (!is_string($extnId)) { return true; } switch ($extnId) { case 'id-ce-keyUsage': return $this->KeyUsage; case 'id-ce-basicConstraints': return $this->BasicConstraints; case 'id-ce-subjectKeyIdentifier': return $this->KeyIdentifier; case 'id-ce-cRLDistributionPoints': return $this->CRLDistributionPoints; case 'id-ce-authorityKeyIdentifier': return $this->AuthorityKeyIdentifier; case 'id-ce-certificatePolicies': return $this->CertificatePolicies; case 'id-ce-extKeyUsage': return $this->ExtKeyUsageSyntax; case 'id-pe-authorityInfoAccess': return $this->AuthorityInfoAccessSyntax; case 'id-pe-subjectInfoAccess': return $this->SubjectInfoAccessSyntax; case 'id-ce-subjectAltName': return $this->SubjectAltName; case 'id-ce-subjectDirectoryAttributes': return $this->SubjectDirectoryAttributes; case 'id-ce-privateKeyUsagePeriod': return $this->PrivateKeyUsagePeriod; case 'id-ce-issuerAltName': return $this->IssuerAltName; case 'id-ce-policyMappings': return $this->PolicyMappings; case 'id-ce-nameConstraints': return $this->NameConstraints; case 'netscape-cert-type': return $this->netscape_cert_type; case 'netscape-comment': return $this->netscape_comment; case 'netscape-ca-policy-url': return $this->netscape_ca_policy_url; case 'id-qt-unotice': return $this->UserNotice; case 'id-pe-logotype': case 'entrustVersInfo': case '1.3.6.1.4.1.311.20.2': case '1.3.6.1.4.1.311.21.1': case '2.23.42.7.0': case '1.3.6.1.4.1.11129.2.4.2': case '1.3.6.1.5.5.7.1.3': return true; case 'pkcs-9-at-unstructuredName': return $this->PKCS9String; case 'pkcs-9-at-challengePassword': return $this->DirectoryString; case 'pkcs-9-at-extensionRequest': return $this->Extensions; case 'id-ce-cRLNumber': return $this->CRLNumber; case 'id-ce-deltaCRLIndicator': return $this->CRLNumber; case 'id-ce-issuingDistributionPoint': return $this->IssuingDistributionPoint; case 'id-ce-freshestCRL': return $this->CRLDistributionPoints; case 'id-ce-cRLReasons': return $this->CRLReason; case 'id-ce-invalidityDate': return $this->InvalidityDate; case 'id-ce-certificateIssuer': return $this->CertificateIssuer; case 'id-ce-holdInstructionCode': return $this->HoldInstructionCode; case 'id-at-postalAddress': return $this->PostalAddress; } return false; } function loadCA($cert) { $olddn = $this->dn; $oldcert = $this->currentCert; $oldsigsubj = $this->signatureSubject; $oldkeyid = $this->currentKeyIdentifier; $cert = $this->loadX509($cert); if (!$cert) { $this->dn = $olddn; $this->currentCert = $oldcert; $this->signatureSubject = $oldsigsubj; $this->currentKeyIdentifier = $oldkeyid; return false; } $this->CAs[] = $cert; $this->dn = $olddn; $this->currentCert = $oldcert; $this->signatureSubject = $oldsigsubj; return true; } function validateURL($url) { if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { return false; } $components = parse_url($url); if (!isset($components['host'])) { return false; } if ($names = $this->getExtension('id-ce-subjectAltName')) { foreach ($names as $name) { foreach ($name as $key => $value) { $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); switch ($key) { case 'dNSName': if (preg_match('#^' . $value . '$#', $components['host'])) { return true; } break; case 'iPAddress': if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { return true; } } } } return false; } if ($value = $this->getDNProp('id-at-commonName')) { $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]); return preg_match('#^' . $value . '$#', $components['host']); } return false; } function validateDate($date = null) { if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { return false; } if (!isset($date)) { $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get())); } $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; $notBefore = isset($notBefore['generalTime']) ? $notBefore['generalTime'] : $notBefore['utcTime']; $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; if (is_string($date)) { $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); } $notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())); $notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())); switch (true) { case $date < $notBefore: case $date > $notAfter: return false; } return true; } static function _fetchURL($url) { if (self::$disable_url_fetch) { return false; } $parts = parse_url($url); $data = ''; switch ($parts['scheme']) { case 'http': $fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80); if (!$fsock) { return false; } fputs($fsock, "GET $parts[path] HTTP/1.0\r\n"); fputs($fsock, "Host: $parts[host]\r\n\r\n"); $line = fgets($fsock, 1024); if (strlen($line) < 3) { return false; } preg_match('#HTTP/1.\d (\d{3})#', $line, $temp); if ($temp[1] != '200') { return false; } while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") { } while (!feof($fsock)) { $temp = fread($fsock, 1024); if ($temp === false) { return false; } $data.= $temp; } break; } return $data; } function _testForIntermediate($caonly, $count) { $opts = $this->getExtension('id-pe-authorityInfoAccess'); if (!is_array($opts)) { return false; } foreach ($opts as $opt) { if ($opt['accessMethod'] == 'id-ad-caIssuers') { if (isset($opt['accessLocation']['uniformResourceIdentifier'])) { $url = $opt['accessLocation']['uniformResourceIdentifier']; break; } } } if (!isset($url)) { return false; } $cert = static::_fetchURL($url); if (!is_string($cert)) { return false; } $parent = new static(); $parent->CAs = $this->CAs; if (!is_array($parent->loadX509($cert))) { return false; } if (!$parent->_validateSignatureCountable($caonly, ++$count)) { return false; } $this->CAs[] = $parent->currentCert; return true; } function validateSignature($caonly = true) { return $this->_validateSignatureCountable($caonly, 0); } function _validateSignatureCountable($caonly, $count) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { return null; } if ($count == self::$recur_limit) { return false; } switch (true) { case isset($this->currentCert['tbsCertificate']): switch (true) { case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $this->currentCert['tbsCertificate']['subject']: case defined('FILE_X509_IGNORE_TYPE') && $this->getIssuerDN(self::DN_STRING) === $this->getDN(self::DN_STRING): $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); switch (true) { case !is_array($authorityKey): case !$subjectKeyID: case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: $signingCert = $this->currentCert; } } if (!empty($this->CAs)) { for ($i = 0; $i < count($this->CAs); $i++) { $ca = $this->CAs[$i]; switch (true) { case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']: case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertificate']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): case !$subjectKeyID: case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { break 2; } $signingCert = $ca; break 3; } } } if (count($this->CAs) == $i && $caonly) { return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } } elseif (!isset($signingCert) || $caonly) { return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } return $this->_validateSignature( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['certificationRequestInfo']): return $this->_validateSignature( $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['publicKeyAndChallenge']): return $this->_validateSignature( $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['tbsCertList']): if (!empty($this->CAs)) { for ($i = 0; $i < count($this->CAs); $i++) { $ca = $this->CAs[$i]; switch (true) { case !defined('FILE_X509_IGNORE_TYPE') && $this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']: case defined('FILE_X509_IGNORE_TYPE') && $this->getDN(self::DN_STRING, $this->currentCert['tbsCertList']['issuer']) === $this->getDN(self::DN_STRING, $ca['tbsCertificate']['subject']): $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): case !$subjectKeyID: case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { break 2; } $signingCert = $ca; break 3; } } } } if (!isset($signingCert)) { return false; } return $this->_validateSignature( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); default: return false; } } function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) { switch ($publicKeyAlgorithm) { case 'rsaEncryption': $rsa = new RSA(); $rsa->loadKey($publicKey); switch ($signatureAlgorithm) { case 'md2WithRSAEncryption': case 'md5WithRSAEncryption': case 'sha1WithRSAEncryption': case 'sha224WithRSAEncryption': case 'sha256WithRSAEncryption': case 'sha384WithRSAEncryption': case 'sha512WithRSAEncryption': $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); if (!@$rsa->verify($signatureSubject, $signature)) { return false; } break; default: return null; } break; default: return null; } return true; } static function setRecurLimit($count) { self::$recur_limit = $count; } static function disableURLFetch() { self::$disable_url_fetch = true; } static function enableURLFetch() { self::$disable_url_fetch = false; } function _reformatKey($algorithm, $key) { switch ($algorithm) { case 'rsaEncryption': return "-----BEGIN RSA PUBLIC KEY-----\r\n" . chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) . '-----END RSA PUBLIC KEY-----'; default: return $key; } } function _decodeIP($ip) { return inet_ntop(base64_decode($ip)); } function _decodeNameConstraintIP($ip) { $ip = base64_decode($ip); $size = strlen($ip) >> 1; $mask = substr($ip, $size); $ip = substr($ip, 0, $size); return array(inet_ntop($ip), inet_ntop($mask)); } function _encodeIP($ip) { return is_string($ip) ? base64_encode(inet_pton($ip)) : base64_encode(inet_pton($ip[0]) . inet_pton($ip[1])); } function _translateDNProp($propName) { switch (strtolower($propName)) { case 'id-at-countryname': case 'countryname': case 'c': return 'id-at-countryName'; case 'id-at-organizationname': case 'organizationname': case 'o': return 'id-at-organizationName'; case 'id-at-dnqualifier': case 'dnqualifier': return 'id-at-dnQualifier'; case 'id-at-commonname': case 'commonname': case 'cn': return 'id-at-commonName'; case 'id-at-stateorprovincename': case 'stateorprovincename': case 'state': case 'province': case 'provincename': case 'st': return 'id-at-stateOrProvinceName'; case 'id-at-localityname': case 'localityname': case 'l': return 'id-at-localityName'; case 'id-emailaddress': case 'emailaddress': return 'pkcs-9-at-emailAddress'; case 'id-at-serialnumber': case 'serialnumber': return 'id-at-serialNumber'; case 'id-at-postalcode': case 'postalcode': return 'id-at-postalCode'; case 'id-at-streetaddress': case 'streetaddress': return 'id-at-streetAddress'; case 'id-at-name': case 'name': return 'id-at-name'; case 'id-at-givenname': case 'givenname': return 'id-at-givenName'; case 'id-at-surname': case 'surname': case 'sn': return 'id-at-surname'; case 'id-at-initials': case 'initials': return 'id-at-initials'; case 'id-at-generationqualifier': case 'generationqualifier': return 'id-at-generationQualifier'; case 'id-at-organizationalunitname': case 'organizationalunitname': case 'ou': return 'id-at-organizationalUnitName'; case 'id-at-pseudonym': case 'pseudonym': return 'id-at-pseudonym'; case 'id-at-title': case 'title': return 'id-at-title'; case 'id-at-description': case 'description': return 'id-at-description'; case 'id-at-role': case 'role': return 'id-at-role'; case 'id-at-uniqueidentifier': case 'uniqueidentifier': case 'x500uniqueidentifier': return 'id-at-uniqueIdentifier'; case 'postaladdress': case 'id-at-postaladdress': return 'id-at-postalAddress'; default: return false; } } function setDNProp($propName, $propValue, $type = 'utf8String') { if (empty($this->dn)) { $this->dn = array('rdnSequence' => array()); } if (($propName = $this->_translateDNProp($propName)) === false) { return false; } foreach ((array) $propValue as $v) { if (!is_array($v) && isset($type)) { $v = array($type => $v); } $this->dn['rdnSequence'][] = array( array( 'type' => $propName, 'value'=> $v ) ); } return true; } function removeDNProp($propName) { if (empty($this->dn)) { return; } if (($propName = $this->_translateDNProp($propName)) === false) { return; } $dn = &$this->dn['rdnSequence']; $size = count($dn); for ($i = 0; $i < $size; $i++) { if ($dn[$i][0]['type'] == $propName) { unset($dn[$i]); } } $dn = array_values($dn); if (!isset($dn[0])) { $dn = array_splice($dn, 0, 0); } } function getDNProp($propName, $dn = null, $withType = false) { if (!isset($dn)) { $dn = $this->dn; } if (empty($dn)) { return false; } if (($propName = $this->_translateDNProp($propName)) === false) { return false; } $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); $this->_mapOutDNs($dn, 'rdnSequence', $asn1); $dn = $dn['rdnSequence']; $result = array(); for ($i = 0; $i < count($dn); $i++) { if ($dn[$i][0]['type'] == $propName) { $v = $dn[$i][0]['value']; if (!$withType) { if (is_array($v)) { foreach ($v as $type => $s) { $type = array_search($type, $asn1->ANYmap, true); if ($type !== false && isset($asn1->stringTypeSize[$type])) { $s = $asn1->convert($s, $type); if ($s !== false) { $v = $s; break; } } } if (is_array($v)) { $v = array_pop($v); } } elseif (is_object($v) && $v instanceof Element) { $map = $this->_getMapping($propName); if (!is_bool($map)) { $decoded = $asn1->decodeBER($v); $v = $asn1->asn1map($decoded[0], $map); } } } $result[] = $v; } } return $result; } function setDN($dn, $merge = false, $type = 'utf8String') { if (!$merge) { $this->dn = null; } if (is_array($dn)) { if (isset($dn['rdnSequence'])) { $this->dn = $dn; return true; } foreach ($dn as $prop => $value) { if (!$this->setDNProp($prop, $value, $type)) { return false; } } return true; } $results = preg_split('#((?:^|, *|/)(?:C=|O=|OU=|CN=|L=|ST=|SN=|postalCode=|streetAddress=|emailAddress=|serialNumber=|organizationalUnitName=|title=|description=|role=|x500UniqueIdentifier=|postalAddress=))#', $dn, -1, PREG_SPLIT_DELIM_CAPTURE); for ($i = 1; $i < count($results); $i+=2) { $prop = trim($results[$i], ', =/'); $value = $results[$i + 1]; if (!$this->setDNProp($prop, $value, $type)) { return false; } } return true; } function getDN($format = self::DN_ARRAY, $dn = null) { if (!isset($dn)) { $dn = isset($this->currentCert['tbsCertList']) ? $this->currentCert['tbsCertList']['issuer'] : $this->dn; } switch ((int) $format) { case self::DN_ARRAY: return $dn; case self::DN_ASN1: $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); $this->_mapOutDNs($dn, 'rdnSequence', $asn1); return $asn1->encodeDER($dn, $this->Name); case self::DN_CANON: $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $filters['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); $result = ''; $this->_mapOutDNs($dn, 'rdnSequence', $asn1); foreach ($dn['rdnSequence'] as $rdn) { foreach ($rdn as $i => $attr) { $attr = &$rdn[$i]; if (is_array($attr['value'])) { foreach ($attr['value'] as $type => $v) { $type = array_search($type, $asn1->ANYmap, true); if ($type !== false && isset($asn1->stringTypeSize[$type])) { $v = $asn1->convert($v, $type); if ($v !== false) { $v = preg_replace('/\s+/', ' ', $v); $attr['value'] = strtolower(trim($v)); break; } } } } } $result .= $asn1->encodeDER($rdn, $this->RelativeDistinguishedName); } return $result; case self::DN_HASH: $dn = $this->getDN(self::DN_CANON, $dn); $hash = new Hash('sha1'); $hash = $hash->hash($dn); extract(unpack('Vhash', $hash)); return strtolower(bin2hex(pack('N', $hash))); } $start = true; $output = ''; $result = array(); $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $filters['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); $this->_mapOutDNs($dn, 'rdnSequence', $asn1); foreach ($dn['rdnSequence'] as $field) { $prop = $field[0]['type']; $value = $field[0]['value']; $delim = ', '; switch ($prop) { case 'id-at-countryName': $desc = 'C'; break; case 'id-at-stateOrProvinceName': $desc = 'ST'; break; case 'id-at-organizationName': $desc = 'O'; break; case 'id-at-organizationalUnitName': $desc = 'OU'; break; case 'id-at-commonName': $desc = 'CN'; break; case 'id-at-localityName': $desc = 'L'; break; case 'id-at-surname': $desc = 'SN'; break; case 'id-at-uniqueIdentifier': $delim = '/'; $desc = 'x500UniqueIdentifier'; break; case 'id-at-postalAddress': $delim = '/'; $desc = 'postalAddress'; break; default: $delim = '/'; $desc = preg_replace('#.+-([^-]+)$#', '$1', $prop); } if (!$start) { $output.= $delim; } if (is_array($value)) { foreach ($value as $type => $v) { $type = array_search($type, $asn1->ANYmap, true); if ($type !== false && isset($asn1->stringTypeSize[$type])) { $v = $asn1->convert($v, $type); if ($v !== false) { $value = $v; break; } } } if (is_array($value)) { $value = array_pop($value); } } elseif (is_object($value) && $value instanceof Element) { $callback = function ($x) { return "\x" . bin2hex($x[0]); }; $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); } $output.= $desc . '=' . $value; $result[$desc] = isset($result[$desc]) ? array_merge((array) $result[$desc], array($value)) : $value; $start = false; } return $format == self::DN_OPENSSL ? $result : $output; } function getIssuerDN($format = self::DN_ARRAY) { switch (true) { case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDN($format, $this->currentCert['tbsCertificate']['issuer']); case isset($this->currentCert['tbsCertList']): return $this->getDN($format, $this->currentCert['tbsCertList']['issuer']); } return false; } function getSubjectDN($format = self::DN_ARRAY) { switch (true) { case !empty($this->dn): return $this->getDN($format); case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDN($format, $this->currentCert['tbsCertificate']['subject']); case isset($this->currentCert['certificationRequestInfo']): return $this->getDN($format, $this->currentCert['certificationRequestInfo']['subject']); } return false; } function getIssuerDNProp($propName, $withType = false) { switch (true) { case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['issuer'], $withType); case isset($this->currentCert['tbsCertList']): return $this->getDNProp($propName, $this->currentCert['tbsCertList']['issuer'], $withType); } return false; } function getSubjectDNProp($propName, $withType = false) { switch (true) { case !empty($this->dn): return $this->getDNProp($propName, null, $withType); case !isset($this->currentCert) || !is_array($this->currentCert): break; case isset($this->currentCert['tbsCertificate']): return $this->getDNProp($propName, $this->currentCert['tbsCertificate']['subject'], $withType); case isset($this->currentCert['certificationRequestInfo']): return $this->getDNProp($propName, $this->currentCert['certificationRequestInfo']['subject'], $withType); } return false; } function getChain() { $chain = array($this->currentCert); if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { return false; } if (empty($this->CAs)) { return $chain; } while (true) { $currentCert = $chain[count($chain) - 1]; for ($i = 0; $i < count($this->CAs); $i++) { $ca = $this->CAs[$i]; if ($currentCert['tbsCertificate']['issuer'] === $ca['tbsCertificate']['subject']) { $authorityKey = $this->getExtension('id-ce-authorityKeyIdentifier', $currentCert); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: if ($currentCert === $ca) { break 3; } $chain[] = $ca; break 2; } } } if ($i == count($this->CAs)) { break; } } foreach ($chain as $key => $value) { $chain[$key] = new X509(); $chain[$key]->loadX509($value); } return $chain; } function setPublicKey($key) { $key->setPublicKey(); $this->publicKey = $key; } function setPrivateKey($key) { $this->privateKey = $key; } function setChallenge($challenge) { $this->challenge = $challenge; } function getPublicKey() { if (isset($this->publicKey)) { return $this->publicKey; } if (isset($this->currentCert) && is_array($this->currentCert)) { foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) { $keyinfo = $this->_subArray($this->currentCert, $path); if (!empty($keyinfo)) { break; } } } if (empty($keyinfo)) { return false; } $key = $keyinfo['subjectPublicKey']; switch ($keyinfo['algorithm']['algorithm']) { case 'rsaEncryption': $publicKey = new RSA(); $publicKey->loadKey($key); $publicKey->setPublicKey(); break; default: return false; } return $publicKey; } function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($csr) && isset($csr['certificationRequestInfo'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); unset($this->signatureSubject); $this->dn = $csr['certificationRequestInfo']['subject']; if (!isset($this->dn)) { return false; } $this->currentCert = $csr; return $csr; } $asn1 = new ASN1(); if ($mode != self::FORMAT_DER) { $newcsr = $this->_extractBER($csr); if ($mode == self::FORMAT_PEM && $csr == $newcsr) { return false; } $csr = $newcsr; } $orig = $csr; if ($csr === false) { $this->currentCert = false; return false; } $asn1->loadOIDs($this->oids); $decoded = $asn1->decodeBER($csr); if (empty($decoded)) { $this->currentCert = false; return false; } $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest); if (!isset($csr) || $csr === false) { $this->currentCert = false; return false; } $this->_mapInAttributes($csr, 'certificationRequestInfo/attributes', $asn1); $this->_mapInDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); $this->dn = $csr['certificationRequestInfo']['subject']; $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $algorithm = &$csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm']; $key = &$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']; $key = $this->_reformatKey($algorithm, $key); switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); $this->publicKey->loadKey($key); $this->publicKey->setPublicKey(); break; default: $this->publicKey = null; } $this->currentKeyIdentifier = null; $this->currentCert = $csr; return $csr; } function saveCSR($csr, $format = self::FORMAT_PEM) { if (!is_array($csr) || !isset($csr['certificationRequestInfo'])) { return false; } switch (true) { case !($algorithm = $this->_subArray($csr, 'certificationRequestInfo/subjectPKInfo/algorithm/algorithm')): case is_object($csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): break; default: switch ($algorithm) { case 'rsaEncryption': $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null; $csr['signatureAlgorithm']['parameters'] = null; $csr['certificationRequestInfo']['signature']['parameters'] = null; } } $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $filters['certificationRequestInfo']['subject']['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $asn1->loadFilters($filters); $this->_mapOutDNs($csr, 'certificationRequestInfo/subject/rdnSequence', $asn1); $this->_mapOutAttributes($csr, 'certificationRequestInfo/attributes', $asn1); $csr = $asn1->encodeDER($csr, $this->CertificationRequest); switch ($format) { case self::FORMAT_DER: return $csr; default: return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; } } function loadSPKAC($spkac) { if (is_array($spkac) && isset($spkac['publicKeyAndChallenge'])) { unset($this->currentCert); unset($this->currentKeyIdentifier); unset($this->signatureSubject); $this->currentCert = $spkac; return $spkac; } $asn1 = new ASN1(); $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; if ($temp != false) { $spkac = $temp; } $orig = $spkac; if ($spkac === false) { $this->currentCert = false; return false; } $asn1->loadOIDs($this->oids); $decoded = $asn1->decodeBER($spkac); if (empty($decoded)) { $this->currentCert = false; return false; } $spkac = $asn1->asn1map($decoded[0], $this->SignedPublicKeyAndChallenge); if (!isset($spkac) || $spkac === false) { $this->currentCert = false; return false; } $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $algorithm = &$spkac['publicKeyAndChallenge']['spki']['algorithm']['algorithm']; $key = &$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']; $key = $this->_reformatKey($algorithm, $key); switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); $this->publicKey->loadKey($key); $this->publicKey->setPublicKey(); break; default: $this->publicKey = null; } $this->currentKeyIdentifier = null; $this->currentCert = $spkac; return $spkac; } function saveSPKAC($spkac, $format = self::FORMAT_PEM) { if (!is_array($spkac) || !isset($spkac['publicKeyAndChallenge'])) { return false; } $algorithm = $this->_subArray($spkac, 'publicKeyAndChallenge/spki/algorithm/algorithm'); switch (true) { case !$algorithm: case is_object($spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']): break; default: switch ($algorithm) { case 'rsaEncryption': $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); } } $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $spkac = $asn1->encodeDER($spkac, $this->SignedPublicKeyAndChallenge); switch ($format) { case self::FORMAT_DER: return $spkac; default: return 'SPKAC=' . base64_encode($spkac); } } function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT) { if (is_array($crl) && isset($crl['tbsCertList'])) { $this->currentCert = $crl; unset($this->signatureSubject); return $crl; } $asn1 = new ASN1(); if ($mode != self::FORMAT_DER) { $newcrl = $this->_extractBER($crl); if ($mode == self::FORMAT_PEM && $crl == $newcrl) { return false; } $crl = $newcrl; } $orig = $crl; if ($crl === false) { $this->currentCert = false; return false; } $asn1->loadOIDs($this->oids); $decoded = $asn1->decodeBER($crl); if (empty($decoded)) { $this->currentCert = false; return false; } $crl = $asn1->asn1map($decoded[0], $this->CertificateList); if (!isset($crl) || $crl === false) { $this->currentCert = false; return false; } $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); } if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); if ($rclist_ref) { $rclist = $crl['tbsCertList']['revokedCertificates']; foreach ($rclist as $i => $extension) { if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) { $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1); } } } } $this->currentKeyIdentifier = null; $this->currentCert = $crl; return $crl; } function saveCRL($crl, $format = self::FORMAT_PEM) { if (!is_array($crl) || !isset($crl['tbsCertList'])) { return false; } $asn1 = new ASN1(); $asn1->loadOIDs($this->oids); $filters = array(); $filters['tbsCertList']['issuer']['rdnSequence']['value'] = array('type' => ASN1::TYPE_UTF8_STRING); $filters['tbsCertList']['signature']['parameters'] = array('type' => ASN1::TYPE_UTF8_STRING); $filters['signatureAlgorithm']['parameters'] = array('type' => ASN1::TYPE_UTF8_STRING); if (empty($crl['tbsCertList']['signature']['parameters'])) { $filters['tbsCertList']['signature']['parameters'] = array('type' => ASN1::TYPE_NULL); } if (empty($crl['signatureAlgorithm']['parameters'])) { $filters['signatureAlgorithm']['parameters'] = array('type' => ASN1::TYPE_NULL); } $asn1->loadFilters($filters); $this->_mapOutDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1); $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); if (is_array($rclist)) { foreach ($rclist as $i => $extension) { $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1); } } $crl = $asn1->encodeDER($crl, $this->CertificateList); switch ($format) { case self::FORMAT_DER: return $crl; default: return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----'; } } function _timeField($date) { if ($date instanceof Element) { return $date; } $dateObj = new DateTime($date, new DateTimeZone('GMT')); $year = $dateObj->format('Y'); if ($year < 2050) { return array('utcTime' => $date); } else { return array('generalTime' => $date); } } function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') { if (!is_object($issuer->privateKey) || empty($issuer->dn)) { return false; } if (isset($subject->publicKey) && !($subjectPublicKey = $subject->_formatSubjectPublicKey())) { return false; } $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) { $this->currentCert = $subject->currentCert; $this->currentCert['tbsCertificate']['signature']['algorithm'] = $signatureAlgorithm; $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; if (!empty($this->startDate)) { $this->currentCert['tbsCertificate']['validity']['notBefore'] = $this->_timeField($this->startDate); } if (!empty($this->endDate)) { $this->currentCert['tbsCertificate']['validity']['notAfter'] = $this->_timeField($this->endDate); } if (!empty($this->serialNumber)) { $this->currentCert['tbsCertificate']['serialNumber'] = $this->serialNumber; } if (!empty($subject->dn)) { $this->currentCert['tbsCertificate']['subject'] = $subject->dn; } if (!empty($subject->publicKey)) { $this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey; } $this->removeExtension('id-ce-authorityKeyIdentifier'); if (isset($subject->domains)) { $this->removeExtension('id-ce-subjectAltName'); } } elseif (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { return false; } else { if (!isset($subject->publicKey)) { return false; } $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); $startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O'); $endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get())); $endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O'); $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new BigInteger(Random::string(20) & ("\x7F" . str_repeat("\xFF", 19)), 256); $this->currentCert = array( 'tbsCertificate' => array( 'version' => 'v3', 'serialNumber' => $serialNumber, 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, 'validity' => array( 'notBefore' => $this->_timeField($startDate), 'notAfter' => $this->_timeField($endDate) ), 'subject' => $subject->dn, 'subjectPublicKeyInfo' => $subjectPublicKey ), 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), 'signature' => false ); $csrexts = $subject->getAttribute('pkcs-9-at-extensionRequest', 0); if (!empty($csrexts)) { $this->currentCert['tbsCertificate']['extensions'] = $csrexts; } } $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn; if (isset($issuer->currentKeyIdentifier)) { $this->setExtension('id-ce-authorityKeyIdentifier', array( 'keyIdentifier' => $issuer->currentKeyIdentifier )); } if (isset($subject->currentKeyIdentifier)) { $this->setExtension('id-ce-subjectKeyIdentifier', $subject->currentKeyIdentifier); } $altName = array(); if (isset($subject->domains) && count($subject->domains)) { $altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains); } if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { $ipAddresses = array(); foreach ($subject->ipAddresses as $ipAddress) { $encoded = $subject->_ipAddress($ipAddress); if ($encoded !== false) { $ipAddresses[] = $encoded; } } if (count($ipAddresses)) { $altName = array_merge($altName, $ipAddresses); } } if (!empty($altName)) { $this->setExtension('id-ce-subjectAltName', $altName); } if ($this->caFlag) { $keyUsage = $this->getExtension('id-ce-keyUsage'); if (!$keyUsage) { $keyUsage = array(); } $this->setExtension( 'id-ce-keyUsage', array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) ); $basicConstraints = $this->getExtension('id-ce-basicConstraints'); if (!$basicConstraints) { $basicConstraints = array(); } $this->setExtension( 'id-ce-basicConstraints', array_unique(array_merge(array('cA' => true), $basicConstraints)), true ); if (!isset($subject->currentKeyIdentifier)) { $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false); } } $tbsCertificate = $this->currentCert['tbsCertificate']; $this->loadX509($this->saveX509($this->currentCert)); $result = $this->_sign($issuer->privateKey, $signatureAlgorithm); $result['tbsCertificate'] = $tbsCertificate; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } function signCSR($signatureAlgorithm = 'sha1WithRSAEncryption') { if (!is_object($this->privateKey) || empty($this->dn)) { return false; } $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); $this->publicKey->loadKey($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); if (!($publicKey = $this->_formatSubjectPublicKey())) { return false; } $this->publicKey = $origPublicKey; $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) { $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; if (!empty($this->dn)) { $this->currentCert['certificationRequestInfo']['subject'] = $this->dn; } $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey; } else { $this->currentCert = array( 'certificationRequestInfo' => array( 'version' => 'v1', 'subject' => $this->dn, 'subjectPKInfo' => $publicKey ), 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), 'signature' => false ); } $certificationRequestInfo = $this->currentCert['certificationRequestInfo']; $this->loadCSR($this->saveCSR($this->currentCert)); $result = $this->_sign($this->privateKey, $signatureAlgorithm); $result['certificationRequestInfo'] = $certificationRequestInfo; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } function signSPKAC($signatureAlgorithm = 'sha1WithRSAEncryption') { if (!is_object($this->privateKey)) { return false; } $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); $this->publicKey->loadKey($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); $publicKey = $this->_formatSubjectPublicKey(); if (!$publicKey) { return false; } $this->publicKey = $origPublicKey; $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: null; if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['publicKeyAndChallenge'])) { $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; $this->currentCert['publicKeyAndChallenge']['spki'] = $publicKey; if (!empty($this->challenge)) { $this->currentCert['publicKeyAndChallenge']['challenge'] = $this->challenge & str_repeat("\x7F", strlen($this->challenge)); } } else { $this->currentCert = array( 'publicKeyAndChallenge' => array( 'spki' => $publicKey, 'challenge' => !empty($this->challenge) ? $this->challenge : '' ), 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), 'signature' => false ); } $publicKeyAndChallenge = $this->currentCert['publicKeyAndChallenge']; $this->loadSPKAC($this->saveSPKAC($this->currentCert)); $result = $this->_sign($this->privateKey, $signatureAlgorithm); $result['publicKeyAndChallenge'] = $publicKeyAndChallenge; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') { if (!is_object($issuer->privateKey) || empty($issuer->dn)) { return false; } $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; $thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); $thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O'); if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { $this->currentCert = $crl->currentCert; $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm; $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; } else { $this->currentCert = array( 'tbsCertList' => array( 'version' => 'v2', 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, 'thisUpdate' => $this->_timeField($thisUpdate) ), 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), 'signature' => false ); } $tbsCertList = &$this->currentCert['tbsCertList']; $tbsCertList['issuer'] = $issuer->dn; $tbsCertList['thisUpdate'] = $this->_timeField($thisUpdate); if (!empty($this->endDate)) { $tbsCertList['nextUpdate'] = $this->_timeField($this->endDate); } else { unset($tbsCertList['nextUpdate']); } if (!empty($this->serialNumber)) { $crlNumber = $this->serialNumber; } else { $crlNumber = $this->getExtension('id-ce-cRLNumber'); $crlNumber = $crlNumber !== false ? $crlNumber->add(new BigInteger(1)) : null; } $this->removeExtension('id-ce-authorityKeyIdentifier'); $this->removeExtension('id-ce-issuerAltName'); $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0; if (!$version) { if (!empty($tbsCertList['crlExtensions'])) { $version = 1; } elseif (!empty($tbsCertList['revokedCertificates'])) { foreach ($tbsCertList['revokedCertificates'] as $cert) { if (!empty($cert['crlEntryExtensions'])) { $version = 1; } } } if ($version) { $tbsCertList['version'] = $version; } } if (!empty($tbsCertList['version'])) { if (!empty($crlNumber)) { $this->setExtension('id-ce-cRLNumber', $crlNumber); } if (isset($issuer->currentKeyIdentifier)) { $this->setExtension('id-ce-authorityKeyIdentifier', array( 'keyIdentifier' => $issuer->currentKeyIdentifier )); } $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert); if ($issuerAltName !== false) { $this->setExtension('id-ce-issuerAltName', $issuerAltName); } } if (empty($tbsCertList['revokedCertificates'])) { unset($tbsCertList['revokedCertificates']); } unset($tbsCertList); $tbsCertList = $this->currentCert['tbsCertList']; $this->loadCRL($this->saveCRL($this->currentCert)); $result = $this->_sign($issuer->privateKey, $signatureAlgorithm); $result['tbsCertList'] = $tbsCertList; $this->currentCert = $currentCert; $this->signatureSubject = $signatureSubject; return $result; } function _sign($key, $signatureAlgorithm) { if ($key instanceof RSA) { switch ($signatureAlgorithm) { case 'md2WithRSAEncryption': case 'md5WithRSAEncryption': case 'sha1WithRSAEncryption': case 'sha224WithRSAEncryption': case 'sha256WithRSAEncryption': case 'sha384WithRSAEncryption': case 'sha512WithRSAEncryption': $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); $key->setSignatureMode(RSA::SIGNATURE_PKCS1); $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); return $this->currentCert; } } return false; } function setStartDate($date) { if (!is_object($date) || !is_a($date, 'DateTime')) { $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); } $this->startDate = $date->format('D, d M Y H:i:s O'); } function setEndDate($date) { if (strtolower($date) == 'lifetime') { $temp = '99991231235959Z'; $asn1 = new ASN1(); $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; $this->endDate = new Element($temp); } else { if (!is_object($date) || !is_a($date, 'DateTime')) { $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); } $this->endDate = $date->format('D, d M Y H:i:s O'); } } function setSerialNumber($serial, $base = -256) { $this->serialNumber = new BigInteger($serial, $base); } function makeCA() { $this->caFlag = true; } function _isSubArrayValid($root, $path) { if (!is_array($root)) { return false; } foreach (explode('/', $path) as $i) { if (!is_array($root)) { return false; } if (!isset($root[$i])) { return true; } $root = $root[$i]; } return true; } function &_subArrayUnchecked(&$root, $path, $create = false) { $false = false; foreach (explode('/', $path) as $i) { if (!isset($root[$i])) { if (!$create) { return $false; } $root[$i] = array(); } $root = &$root[$i]; } return $root; } function &_subArray(&$root, $path, $create = false) { $false = false; if (!is_array($root)) { return $false; } foreach (explode('/', $path) as $i) { if (!is_array($root)) { return $false; } if (!isset($root[$i])) { if (!$create) { return $false; } $root[$i] = array(); } $root = &$root[$i]; } return $root; } function &_extensions(&$root, $path = null, $create = false) { if (!isset($root)) { $root = $this->currentCert; } switch (true) { case !empty($path): case !is_array($root): break; case isset($root['tbsCertificate']): $path = 'tbsCertificate/extensions'; break; case isset($root['tbsCertList']): $path = 'tbsCertList/crlExtensions'; break; case isset($root['certificationRequestInfo']): $pth = 'certificationRequestInfo/attributes'; $attributes = &$this->_subArray($root, $pth, $create); if (is_array($attributes)) { foreach ($attributes as $key => $value) { if ($value['type'] == 'pkcs-9-at-extensionRequest') { $path = "$pth/$key/value/0"; break 2; } } if ($create) { $key = count($attributes); $attributes[] = array('type' => 'pkcs-9-at-extensionRequest', 'value' => array()); $path = "$pth/$key/value/0"; } } break; } $extensions = &$this->_subArray($root, $path, $create); if (!is_array($extensions)) { $false = false; return $false; } return $extensions; } function _removeExtension($id, $path = null) { $extensions = &$this->_extensions($this->currentCert, $path); if (!is_array($extensions)) { return false; } $result = false; foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { unset($extensions[$key]); $result = true; } } $extensions = array_values($extensions); if (!isset($extensions[0])) { $extensions = array_splice($extensions, 0, 0); } return $result; } function _getExtension($id, $cert = null, $path = null) { $extensions = $this->_extensions($cert, $path); if (!is_array($extensions)) { return false; } foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { return $value['extnValue']; } } return false; } function _getExtensions($cert = null, $path = null) { $exts = $this->_extensions($cert, $path); $extensions = array(); if (is_array($exts)) { foreach ($exts as $extension) { $extensions[] = $extension['extnId']; } } return $extensions; } function _setExtension($id, $value, $critical = false, $replace = true, $path = null) { $extensions = &$this->_extensions($this->currentCert, $path, true); if (!is_array($extensions)) { return false; } $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value); foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { if (!$replace) { return false; } $extensions[$key] = $newext; return true; } } $extensions[] = $newext; return true; } function removeExtension($id) { return $this->_removeExtension($id); } function getExtension($id, $cert = null) { return $this->_getExtension($id, $cert); } function getExtensions($cert = null) { return $this->_getExtensions($cert); } function setExtension($id, $value, $critical = false, $replace = true) { return $this->_setExtension($id, $value, $critical, $replace); } function removeAttribute($id, $disposition = self::ATTR_ALL) { $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes'); if (!is_array($attributes)) { return false; } $result = false; foreach ($attributes as $key => $attribute) { if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { case $disposition == self::ATTR_APPEND: case $disposition == self::ATTR_REPLACE: return false; case $disposition >= $n: $disposition -= $n; break; case $disposition == self::ATTR_ALL: case $n == 1: unset($attributes[$key]); $result = true; break; default: unset($attributes[$key]['value'][$disposition]); $attributes[$key]['value'] = array_values($attributes[$key]['value']); $result = true; break; } if ($result && $disposition != self::ATTR_ALL) { break; } } } $attributes = array_values($attributes); return $result; } function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null) { if (empty($csr)) { $csr = $this->currentCert; } $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes'); if (!is_array($attributes)) { return false; } foreach ($attributes as $key => $attribute) { if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { case $disposition == self::ATTR_APPEND: case $disposition == self::ATTR_REPLACE: return false; case $disposition == self::ATTR_ALL: return $attribute['value']; case $disposition >= $n: $disposition -= $n; break; default: return $attribute['value'][$disposition]; } } } return false; } function getAttributes($csr = null) { if (empty($csr)) { $csr = $this->currentCert; } $attributes = $this->_subArray($csr, 'certificationRequestInfo/attributes'); $attrs = array(); if (is_array($attributes)) { foreach ($attributes as $attribute) { $attrs[] = $attribute['type']; } } return $attrs; } function setAttribute($id, $value, $disposition = self::ATTR_ALL) { $attributes = &$this->_subArray($this->currentCert, 'certificationRequestInfo/attributes', true); if (!is_array($attributes)) { return false; } switch ($disposition) { case self::ATTR_REPLACE: $disposition = self::ATTR_APPEND; case self::ATTR_ALL: $this->removeAttribute($id); break; } foreach ($attributes as $key => $attribute) { if ($attribute['type'] == $id) { $n = count($attribute['value']); switch (true) { case $disposition == self::ATTR_APPEND: $last = $key; break; case $disposition >= $n: $disposition -= $n; break; default: $attributes[$key]['value'][$disposition] = $value; return true; } } } switch (true) { case $disposition >= 0: return false; case isset($last): $attributes[$last]['value'][] = $value; break; default: $attributes[] = array('type' => $id, 'value' => $disposition == self::ATTR_ALL ? $value: array($value)); break; } return true; } function setKeyIdentifier($value) { if (empty($value)) { unset($this->currentKeyIdentifier); } else { $this->currentKeyIdentifier = base64_encode($value); } } function computeKeyIdentifier($key = null, $method = 1) { if (is_null($key)) { $key = $this; } switch (true) { case is_string($key): break; case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method); case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); case !is_object($key): return false; case $key instanceof Element: $asn1 = new ASN1(); $decoded = $asn1->decodeBER($key->element); if (empty($decoded)) { return false; } $raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING)); if (empty($raw)) { return false; } $raw = base64_decode($raw); $key = new RSA(); if (!$key->loadKey($raw)) { return false; } if ($key->getPrivateKey() !== false) { return $this->computeKeyIdentifier($key, $method); } $key = $raw; break; case $key instanceof X509: if (isset($key->publicKey)) { return $this->computeKeyIdentifier($key->publicKey, $method); } if (isset($key->privateKey)) { return $this->computeKeyIdentifier($key->privateKey, $method); } if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) { return $this->computeKeyIdentifier($key->currentCert, $method); } return false; default: $key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); break; } $key = $this->_extractBER($key); $hash = new Hash('sha1'); $hash = $hash->hash($key); if ($method == 2) { $hash = substr($hash, -8); $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40); } return $hash; } function _formatSubjectPublicKey() { if ($this->publicKey instanceof RSA) { return array( 'algorithm' => array('algorithm' => 'rsaEncryption'), 'subjectPublicKey' => $this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1) ); } return false; } function setDomain() { $this->domains = func_get_args(); $this->removeDNProp('id-at-commonName'); $this->setDNProp('id-at-commonName', $this->domains[0]); } function setIPAddress() { $this->ipAddresses = func_get_args(); } function _dnsName($domain) { return array('dNSName' => $domain); } function _iPAddress($address) { return array('iPAddress' => $address); } function _revokedCertificate(&$rclist, $serial, $create = false) { $serial = new BigInteger($serial); foreach ($rclist as $i => $rc) { if (!($serial->compare($rc['userCertificate']))) { return $i; } } if (!$create) { return false; } $i = count($rclist); $revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); $rclist[] = array('userCertificate' => $serial, 'revocationDate' => $this->_timeField($revocationDate->format('D, d M Y H:i:s O'))); return $i; } function revoke($serial, $date = null) { if (isset($this->currentCert['tbsCertList'])) { if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { if ($this->_revokedCertificate($rclist, $serial) === false) { if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { if (!empty($date)) { $rclist[$i]['revocationDate'] = $this->_timeField($date); } return true; } } } } return false; } function unrevoke($serial) { if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { unset($rclist[$i]); $rclist = array_values($rclist); return true; } } return false; } function getRevoked($serial) { if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $rclist[$i]; } } return false; } function listRevoked($crl = null) { if (!isset($crl)) { $crl = $this->currentCert; } if (!isset($crl['tbsCertList'])) { return false; } $result = array(); if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { foreach ($rclist as $rc) { $result[] = $rc['userCertificate']->toString(); } } return $result; } function removeRevokedCertificateExtension($serial, $id) { if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $this->_removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } return false; } function getRevokedCertificateExtension($serial, $id, $crl = null) { if (!isset($crl)) { $crl = $this->currentCert; } if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $this->_getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } return false; } function getRevokedCertificateExtensions($serial, $crl = null) { if (!isset($crl)) { $crl = $this->currentCert; } if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $this->_getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } return false; } function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true) { if (isset($this->currentCert['tbsCertList'])) { if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { return $this->_setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } } } return false; } function _extractBER($str) { if (strlen($str) > ini_get('pcre.backtrack_limit')) { $temp = $str; } else { $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1); } $temp = str_replace(array("\r", "\n", ' '), '', $temp); $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; return $temp != false ? $temp : $str; } function getOID($name) { static $reverseMap; if (!isset($reverseMap)) { $reverseMap = array_flip($this->oids); } return isset($reverseMap[$name]) ? $reverseMap[$name] : $name; } } bold = false; $attr_cell->underline = false; $attr_cell->blink = false; $attr_cell->background = 'black'; $attr_cell->foreground = 'white'; $attr_cell->reverse = false; $this->base_attr_cell = clone $attr_cell; $this->attr_cell = clone $attr_cell; $this->setHistory(200); $this->setDimensions(80, 24); } function setDimensions($x, $y) { $this->max_x = $x - 1; $this->max_y = $y - 1; $this->x = $this->y = 0; $this->history = $this->history_attrs = array(); $this->attr_row = array_fill(0, $this->max_x + 2, $this->base_attr_cell); $this->screen = array_fill(0, $this->max_y + 1, ''); $this->attrs = array_fill(0, $this->max_y + 1, $this->attr_row); $this->ansi = ''; } function setHistory($history) { $this->max_history = $history; } function loadString($source) { $this->setDimensions($this->max_x + 1, $this->max_y + 1); $this->appendString($source); } function appendString($source) { $this->tokenization = array(''); for ($i = 0; $i < strlen($source); $i++) { if (strlen($this->ansi)) { $this->ansi.= $source[$i]; $chr = ord($source[$i]); switch (true) { case $this->ansi == "\x1B=": $this->ansi = ''; continue 2; case strlen($this->ansi) == 2 && $chr >= 64 && $chr <= 95 && $chr != ord('['): case strlen($this->ansi) > 2 && $chr >= 64 && $chr <= 126: break; default: continue 2; } $this->tokenization[] = $this->ansi; $this->tokenization[] = ''; switch ($this->ansi) { case "\x1B[H": $this->old_x = $this->x; $this->old_y = $this->y; $this->x = $this->y = 0; break; case "\x1B[J": $this->history = array_merge($this->history, array_slice(array_splice($this->screen, $this->y + 1), 0, $this->old_y)); $this->screen = array_merge($this->screen, array_fill($this->y, $this->max_y, '')); $this->history_attrs = array_merge($this->history_attrs, array_slice(array_splice($this->attrs, $this->y + 1), 0, $this->old_y)); $this->attrs = array_merge($this->attrs, array_fill($this->y, $this->max_y, $this->attr_row)); if (count($this->history) == $this->max_history) { array_shift($this->history); array_shift($this->history_attrs); } case "\x1B[K": $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell)); break; case "\x1B[2K": $this->screen[$this->y] = str_repeat(' ', $this->x); $this->attrs[$this->y] = $this->attr_row; break; case "\x1B[?1h": case "\x1B[?25h": case "\x1B(B": break; case "\x1BE": $this->_newLine(); $this->x = 0; break; default: switch (true) { case preg_match('#\x1B\[(\d+)B#', $this->ansi, $match): $this->old_y = $this->y; $this->y+= $match[1]; break; case preg_match('#\x1B\[(\d+);(\d+)H#', $this->ansi, $match): $this->old_x = $this->x; $this->old_y = $this->y; $this->x = $match[2] - 1; $this->y = $match[1] - 1; break; case preg_match('#\x1B\[(\d+)C#', $this->ansi, $match): $this->old_x = $this->x; $this->x+= $match[1]; break; case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): $this->old_x = $this->x; $this->x-= $match[1]; if ($this->x < 0) { $this->x = 0; } break; case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): break; case preg_match('#\x1B\[(\d*(?:;\d*)*)m#', $this->ansi, $match): $attr_cell = &$this->attr_cell; $mods = explode(';', $match[1]); foreach ($mods as $mod) { switch ($mod) { case '': case '0': $attr_cell = clone $this->base_attr_cell; break; case '1': $attr_cell->bold = true; break; case '4': $attr_cell->underline = true; break; case '5': $attr_cell->blink = true; break; case '7': $attr_cell->reverse = !$attr_cell->reverse; $temp = $attr_cell->background; $attr_cell->background = $attr_cell->foreground; $attr_cell->foreground = $temp; break; default: $front = &$attr_cell->{ $attr_cell->reverse ? 'background' : 'foreground' }; $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; switch ($mod) { case '30': $front = 'black'; break; case '31': $front = 'red'; break; case '32': $front = 'green'; break; case '33': $front = 'yellow'; break; case '34': $front = 'blue'; break; case '35': $front = 'magenta'; break; case '36': $front = 'cyan'; break; case '37': $front = 'white'; break; case '40': $back = 'black'; break; case '41': $back = 'red'; break; case '42': $back = 'green'; break; case '43': $back = 'yellow'; break; case '44': $back = 'blue'; break; case '45': $back = 'magenta'; break; case '46': $back = 'cyan'; break; case '47': $back = 'white'; break; default: $this->ansi = ''; break 2; } } } break; default: } } $this->ansi = ''; continue; } $this->tokenization[count($this->tokenization) - 1].= $source[$i]; switch ($source[$i]) { case "\r": $this->x = 0; break; case "\n": $this->_newLine(); break; case "\x08": if ($this->x) { $this->x--; $this->attrs[$this->y][$this->x] = clone $this->base_attr_cell; $this->screen[$this->y] = substr_replace( $this->screen[$this->y], $source[$i], $this->x, 1 ); } break; case "\x0F": break; case "\x1B": $this->tokenization[count($this->tokenization) - 1] = substr($this->tokenization[count($this->tokenization) - 1], 0, -1); $this->ansi.= "\x1B"; break; default: $this->attrs[$this->y][$this->x] = clone $this->attr_cell; if ($this->x > strlen($this->screen[$this->y])) { $this->screen[$this->y] = str_repeat(' ', $this->x); } $this->screen[$this->y] = substr_replace( $this->screen[$this->y], $source[$i], $this->x, 1 ); if ($this->x > $this->max_x) { $this->x = 0; $this->_newLine(); } else { $this->x++; } } } } function _newLine() { while ($this->y >= $this->max_y) { $this->history = array_merge($this->history, array(array_shift($this->screen))); $this->screen[] = ''; $this->history_attrs = array_merge($this->history_attrs, array(array_shift($this->attrs))); $this->attrs[] = $this->attr_row; if (count($this->history) >= $this->max_history) { array_shift($this->history); array_shift($this->history_attrs); } $this->y--; } $this->y++; } function _processCoordinate($last_attr, $cur_attr, $char) { $output = ''; if ($last_attr != $cur_attr) { $close = $open = ''; if ($last_attr->foreground != $cur_attr->foreground) { if ($cur_attr->foreground != 'white') { $open.= ''; } if ($last_attr->foreground != 'white') { $close = '' . $close; } } if ($last_attr->background != $cur_attr->background) { if ($cur_attr->background != 'black') { $open.= ''; } if ($last_attr->background != 'black') { $close = '' . $close; } } if ($last_attr->bold != $cur_attr->bold) { if ($cur_attr->bold) { $open.= ''; } else { $close = '' . $close; } } if ($last_attr->underline != $cur_attr->underline) { if ($cur_attr->underline) { $open.= ''; } else { $close = '' . $close; } } if ($last_attr->blink != $cur_attr->blink) { if ($cur_attr->blink) { $open.= ''; } else { $close = '' . $close; } } $output.= $close . $open; } $output.= htmlspecialchars($char); return $output; } function _getScreen() { $output = ''; $last_attr = $this->base_attr_cell; for ($i = 0; $i <= $this->max_y; $i++) { for ($j = 0; $j <= $this->max_x; $j++) { $cur_attr = $this->attrs[$i][$j]; $output.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->screen[$i][$j]) ? $this->screen[$i][$j] : ''); $last_attr = $this->attrs[$i][$j]; } $output.= "\r\n"; } $output = substr($output, 0, -2); $output.= $this->_processCoordinate($last_attr, $this->base_attr_cell, ''); return rtrim($output); } function getScreen() { return '
' . $this->_getScreen() . '
'; } function getHistory() { $scrollback = ''; $last_attr = $this->base_attr_cell; for ($i = 0; $i < count($this->history); $i++) { for ($j = 0; $j <= $this->max_x + 1; $j++) { $cur_attr = $this->history_attrs[$i][$j]; $scrollback.= $this->_processCoordinate($last_attr, $cur_attr, isset($this->history[$i][$j]) ? $this->history[$i][$j] : ''); $last_attr = $this->history_attrs[$i][$j]; } $scrollback.= "\r\n"; } $base_attr_cell = $this->base_attr_cell; $this->base_attr_cell = $last_attr; $scrollback.= $this->_getScreen(); $this->base_attr_cell = $base_attr_cell; return '
' . $scrollback . '
'; } } element = $encoded; } } true, self::TYPE_INTEGER => true, self::TYPE_BIT_STRING => 'bitString', self::TYPE_OCTET_STRING => 'octetString', self::TYPE_NULL => 'null', self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier', self::TYPE_REAL => true, self::TYPE_ENUMERATED => 'enumerated', self::TYPE_UTF8_STRING => 'utf8String', self::TYPE_NUMERIC_STRING => 'numericString', self::TYPE_PRINTABLE_STRING => 'printableString', self::TYPE_TELETEX_STRING => 'teletexString', self::TYPE_VIDEOTEX_STRING => 'videotexString', self::TYPE_IA5_STRING => 'ia5String', self::TYPE_UTC_TIME => 'utcTime', self::TYPE_GENERALIZED_TIME => 'generalTime', self::TYPE_GRAPHIC_STRING => 'graphicString', self::TYPE_VISIBLE_STRING => 'visibleString', self::TYPE_GENERAL_STRING => 'generalString', self::TYPE_UNIVERSAL_STRING => 'universalString', self::TYPE_BMP_STRING => 'bmpString' ); var $stringTypeSize = array( self::TYPE_UTF8_STRING => 0, self::TYPE_BMP_STRING => 2, self::TYPE_UNIVERSAL_STRING => 4, self::TYPE_PRINTABLE_STRING => 1, self::TYPE_TELETEX_STRING => 1, self::TYPE_IA5_STRING => 1, self::TYPE_VISIBLE_STRING => 1, ); function decodeBER($encoded) { if ($encoded instanceof Element) { $encoded = $encoded->element; } $this->encoded = $encoded; return array($this->_decode_ber($encoded)); } function _decode_ber($encoded, $start = 0, $encoded_pos = 0) { $current = array('start' => $start); if (!isset($encoded[$encoded_pos])) { return false; } $type = ord($encoded[$encoded_pos++]); $startOffset = 1; $constructed = ($type >> 5) & 1; $tag = $type & 0x1F; if ($tag == 0x1F) { $tag = 0; do { if (!isset($encoded[$encoded_pos])) { return false; } $temp = ord($encoded[$encoded_pos++]); $startOffset++; $loop = $temp >> 7; $tag <<= 7; $temp &= 0x7F; if ($startOffset == 2 && $temp == 0) { return false; } $tag |= $temp; } while ($loop); } $start+= $startOffset; if (!isset($encoded[$encoded_pos])) { return false; } $length = ord($encoded[$encoded_pos++]); $start++; if ($length == 0x80) { $length = strlen($encoded) - $encoded_pos; } elseif ($length & 0x80) { $length&= 0x7F; $temp = substr($encoded, $encoded_pos, $length); $encoded_pos += $length; $current+= array('headerlength' => $length + 2); $start+= $length; extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))); } else { $current+= array('headerlength' => 2); } if ($length > (strlen($encoded) - $encoded_pos)) { return false; } $content = substr($encoded, $encoded_pos, $length); $content_pos = 0; $class = ($type >> 6) & 3; switch ($class) { case self::CLASS_APPLICATION: case self::CLASS_PRIVATE: case self::CLASS_CONTEXT_SPECIFIC: if (!$constructed) { return array( 'type' => $class, 'constant' => $tag, 'content' => $content, 'length' => $length + $start - $current['start'] ); } $newcontent = array(); $remainingLength = $length; while ($remainingLength > 0) { $temp = $this->_decode_ber($content, $start, $content_pos); if ($temp === false) { break; } $length = $temp['length']; if (substr($content, $content_pos + $length, 2) == "\0\0") { $length+= 2; $start+= $length; $newcontent[] = $temp; break; } $start+= $length; $remainingLength-= $length; $newcontent[] = $temp; $content_pos += $length; } return array( 'type' => $class, 'constant' => $tag, 'content' => $newcontent, 'length' => $start - $current['start'] ) + $current; } $current+= array('type' => $tag); switch ($tag) { case self::TYPE_BOOLEAN: if ($constructed || strlen($content) != 1) { return false; } $current['content'] = (bool) ord($content[$content_pos]); break; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: if ($constructed) { return false; } $current['content'] = new BigInteger(substr($content, $content_pos), -256); break; case self::TYPE_REAL: return false; case self::TYPE_BIT_STRING: if (!$constructed) { $current['content'] = substr($content, $content_pos); } else { $temp = $this->_decode_ber($content, $start, $content_pos); if ($temp === false) { return false; } $length-= (strlen($content) - $content_pos); $last = count($temp) - 1; for ($i = 0; $i < $last; $i++) { if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { return false; } $current['content'].= substr($temp[$i]['content'], 1); } if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { return false; } $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); } break; case self::TYPE_OCTET_STRING: if (!$constructed) { $current['content'] = substr($content, $content_pos); } else { $current['content'] = ''; $length = 0; while (substr($content, $content_pos, 2) != "\0\0") { $temp = $this->_decode_ber($content, $length + $start, $content_pos); if ($temp === false) { return false; } $content_pos += $temp['length']; if ($temp['type'] != self::TYPE_OCTET_STRING) { return false; } $current['content'].= $temp['content']; $length+= $temp['length']; } if (substr($content, $content_pos, 2) == "\0\0") { $length+= 2; } } break; case self::TYPE_NULL: if ($constructed || strlen($content)) { return false; } break; case self::TYPE_SEQUENCE: case self::TYPE_SET: if (!$constructed) { return false; } $offset = 0; $current['content'] = array(); $content_len = strlen($content); while ($content_pos < $content_len) { if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") { $length = $offset + 2; break 2; } $temp = $this->_decode_ber($content, $start + $offset, $content_pos); if ($temp === false) { return false; } $content_pos += $temp['length']; $current['content'][] = $temp; $offset+= $temp['length']; } break; case self::TYPE_OBJECT_IDENTIFIER: if ($constructed) { return false; } $current['content'] = $this->_decodeOID(substr($content, $content_pos)); if ($current['content'] === false) { return false; } break; case self::TYPE_NUMERIC_STRING: case self::TYPE_PRINTABLE_STRING: case self::TYPE_TELETEX_STRING: case self::TYPE_VIDEOTEX_STRING: case self::TYPE_VISIBLE_STRING: case self::TYPE_IA5_STRING: case self::TYPE_GRAPHIC_STRING: case self::TYPE_GENERAL_STRING: case self::TYPE_UTF8_STRING: case self::TYPE_BMP_STRING: if ($constructed) { return false; } $current['content'] = substr($content, $content_pos); break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: if ($constructed) { return false; } $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); break; default: return false; } $start+= $length; return $current + array('length' => $start - $current['start']); } function asn1map($decoded, $mapping, $special = array()) { if (!is_array($decoded)) { return false; } if (isset($mapping['explicit']) && is_array($decoded['content'])) { $decoded = $decoded['content'][0]; } switch (true) { case $mapping['type'] == self::TYPE_ANY: $intype = $decoded['type']; if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || (ord($this->encoded[$decoded['start']]) & 0x20)) { return new Element(substr($this->encoded, $decoded['start'], $decoded['length'])); } $inmap = $this->ANYmap[$intype]; if (is_string($inmap)) { return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special)); } break; case $mapping['type'] == self::TYPE_CHOICE: foreach ($mapping['children'] as $key => $option) { switch (true) { case isset($option['constant']) && $option['constant'] == $decoded['constant']: case !isset($option['constant']) && $option['type'] == $decoded['type']: $value = $this->asn1map($decoded, $option, $special); break; case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE: $v = $this->asn1map($decoded, $option, $special); if (isset($v)) { $value = $v; } } if (isset($value)) { if (isset($special[$key])) { $value = call_user_func($special[$key], $value); } return array($key => $value); } } return null; case isset($mapping['implicit']): case isset($mapping['explicit']): case $decoded['type'] == $mapping['type']: break; default: switch (true) { case $decoded['type'] < 18: case $decoded['type'] > 30: case $mapping['type'] < 18: case $mapping['type'] > 30: return null; } } if (isset($mapping['implicit'])) { $decoded['type'] = $mapping['type']; } switch ($decoded['type']) { case self::TYPE_SEQUENCE: $map = array(); if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { if (($map[] = $this->asn1map($content, $child, $special)) === null) { return null; } } return $map; } $n = count($decoded['content']); $i = 0; foreach ($mapping['children'] as $key => $child) { $maymatch = $i < $n; if ($maymatch) { $temp = $decoded['content'][$i]; if ($child['type'] != self::TYPE_CHOICE) { $childClass = $tempClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($temp['constant'])) { $tempClass = $temp['type']; } if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { $childClass = self::CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } if (isset($constant) && isset($temp['constant'])) { $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; } } } if ($maymatch) { $candidate = $this->asn1map($temp, $child, $special); $maymatch = $candidate !== null; } if ($maymatch) { if (isset($special[$key])) { $candidate = call_user_func($special[$key], $candidate); } $map[$key] = $candidate; $i++; } elseif (isset($child['default'])) { $map[$key] = $child['default']; } elseif (!isset($child['optional'])) { return null; } } return $i < $n ? null: $map; case self::TYPE_SET: $map = array(); if (isset($mapping['min']) && isset($mapping['max'])) { $child = $mapping['children']; foreach ($decoded['content'] as $content) { if (($map[] = $this->asn1map($content, $child, $special)) === null) { return null; } } return $map; } for ($i = 0; $i < count($decoded['content']); $i++) { $temp = $decoded['content'][$i]; $tempClass = self::CLASS_UNIVERSAL; if (isset($temp['constant'])) { $tempClass = $temp['type']; } foreach ($mapping['children'] as $key => $child) { if (isset($map[$key])) { continue; } $maymatch = true; if ($child['type'] != self::TYPE_CHOICE) { $childClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($child['class'])) { $childClass = $child['class']; $constant = $child['cast']; } elseif (isset($child['constant'])) { $childClass = self::CLASS_CONTEXT_SPECIFIC; $constant = $child['constant']; } if (isset($constant) && isset($temp['constant'])) { $maymatch = $constant == $temp['constant'] && $childClass == $tempClass; } else { $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false; } } if ($maymatch) { $candidate = $this->asn1map($temp, $child, $special); $maymatch = $candidate !== null; } if (!$maymatch) { break; } if (isset($special[$key])) { $candidate = call_user_func($special[$key], $candidate); } $map[$key] = $candidate; break; } } foreach ($mapping['children'] as $key => $child) { if (!isset($map[$key])) { if (isset($child['default'])) { $map[$key] = $child['default']; } elseif (!isset($child['optional'])) { return null; } } } return $map; case self::TYPE_OBJECT_IDENTIFIER: return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content']; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: if (is_array($decoded['content'])) { $decoded['content'] = $decoded['content'][0]['content']; } if (!is_object($decoded['content'])) { $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); } return $decoded['content'] ? $decoded['content']->format($this->format) : false; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $offset = ord($decoded['content'][0]); $size = (strlen($decoded['content']) - 1) * 8 - $offset; $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false); for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) { $current = ord($decoded['content'][$i]); for ($j = $offset; $j < 8; $j++) { $bits[] = (bool) ($current & (1 << $j)); } $offset = 0; } $values = array(); $map = array_reverse($mapping['mapping']); foreach ($map as $i => $value) { if ($bits[$i]) { $values[] = $value; } } return $values; } case self::TYPE_OCTET_STRING: return base64_encode($decoded['content']); case self::TYPE_NULL: return ''; case self::TYPE_BOOLEAN: return $decoded['content']; case self::TYPE_NUMERIC_STRING: case self::TYPE_PRINTABLE_STRING: case self::TYPE_TELETEX_STRING: case self::TYPE_VIDEOTEX_STRING: case self::TYPE_IA5_STRING: case self::TYPE_GRAPHIC_STRING: case self::TYPE_VISIBLE_STRING: case self::TYPE_GENERAL_STRING: case self::TYPE_UNIVERSAL_STRING: case self::TYPE_UTF8_STRING: case self::TYPE_BMP_STRING: return $decoded['content']; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: $temp = $decoded['content']; if (isset($mapping['implicit'])) { $temp = new BigInteger($decoded['content'], -256); } if (isset($mapping['mapping'])) { $temp = (int) $temp->toString(); return isset($mapping['mapping'][$temp]) ? $mapping['mapping'][$temp] : false; } return $temp; } } function encodeDER($source, $mapping, $special = array()) { $this->location = array(); return $this->_encode_der($source, $mapping, null, $special); } function _encode_der($source, $mapping, $idx = null, $special = array()) { if ($source instanceof Element) { return $source->element; } if (isset($mapping['default']) && $source === $mapping['default']) { return ''; } if (isset($idx)) { if (isset($special[$idx])) { $source = call_user_func($special[$idx], $source); } $this->location[] = $idx; } $tag = $mapping['type']; switch ($tag) { case self::TYPE_SET: case self::TYPE_SEQUENCE: $tag|= 0x20; if (isset($mapping['min']) && isset($mapping['max'])) { $value = array(); $child = $mapping['children']; foreach ($source as $content) { $temp = $this->_encode_der($content, $child, null, $special); if ($temp === false) { return false; } $value[]= $temp; } if ($mapping['type'] == self::TYPE_SET) { sort($value); } $value = implode('', $value); break; } $value = ''; foreach ($mapping['children'] as $key => $child) { if (!array_key_exists($key, $source)) { if (!isset($child['optional'])) { return false; } continue; } $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } if ($temp === '') { continue; } if (isset($child['constant'])) { if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; } else { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } $value.= $temp; } break; case self::TYPE_CHOICE: $temp = false; foreach ($mapping['children'] as $key => $child) { if (!isset($source[$key])) { continue; } $temp = $this->_encode_der($source[$key], $child, $key, $special); if ($temp === false) { return false; } if ($temp === '') { continue; } $tag = ord($temp[0]); if (isset($child['constant'])) { if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; } else { $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } } } if (isset($idx)) { array_pop($this->location); } if ($temp && isset($mapping['cast'])) { $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']); } return $temp; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: if (!isset($mapping['mapping'])) { if (is_numeric($source)) { $source = new BigInteger($source); } $value = $source->toBytes(true); } else { $value = array_search($source, $mapping['mapping']); if ($value === false) { return false; } $value = new BigInteger($value); $value = $value->toBytes(true); } if (!strlen($value)) { $value = chr(0); } break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format.= 'mdHis'; $date = new DateTime($source, new DateTimeZone('GMT')); $date->setTimezone(new DateTimeZone('GMT')); $value = $date->format($format) . 'Z'; break; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $bits = array_fill(0, count($mapping['mapping']), 0); $size = 0; for ($i = 0; $i < count($mapping['mapping']); $i++) { if (in_array($mapping['mapping'][$i], $source)) { $bits[$i] = 1; $size = $i; } } if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) { $size = $mapping['min'] - 1; } $offset = 8 - (($size + 1) & 7); $offset = $offset !== 8 ? $offset : 0; $value = chr($offset); for ($i = $size + 1; $i < count($mapping['mapping']); $i++) { unset($bits[$i]); } $bits = implode('', array_pad($bits, $size + $offset + 1, 0)); $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' '))); foreach ($bytes as $byte) { $value.= chr(bindec($byte)); } break; } case self::TYPE_OCTET_STRING: $value = base64_decode($source); break; case self::TYPE_OBJECT_IDENTIFIER: $value = $this->_encodeOID($source); break; case self::TYPE_ANY: $loc = $this->location; if (isset($idx)) { array_pop($this->location); } switch (true) { case !isset($source): return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special); case is_int($source): case $source instanceof BigInteger: return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special); case is_float($source): return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special); case is_bool($source): return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special); case is_array($source) && count($source) == 1: $typename = implode('', array_keys($source)); $outtype = array_search($typename, $this->ANYmap, true); if ($outtype !== false) { return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special); } } $filters = $this->filters; foreach ($loc as $part) { if (!isset($filters[$part])) { $filters = false; break; } $filters = $filters[$part]; } if ($filters === false) { user_error('No filters defined for ' . implode('/', $loc)); return false; } return $this->_encode_der($source, $filters + $mapping, null, $special); case self::TYPE_NULL: $value = ''; break; case self::TYPE_NUMERIC_STRING: case self::TYPE_TELETEX_STRING: case self::TYPE_PRINTABLE_STRING: case self::TYPE_UNIVERSAL_STRING: case self::TYPE_UTF8_STRING: case self::TYPE_BMP_STRING: case self::TYPE_IA5_STRING: case self::TYPE_VISIBLE_STRING: case self::TYPE_VIDEOTEX_STRING: case self::TYPE_GRAPHIC_STRING: case self::TYPE_GENERAL_STRING: $value = $source; break; case self::TYPE_BOOLEAN: $value = $source ? "\xFF" : "\x00"; break; default: user_error('Mapping provides no type definition for ' . implode('/', $this->location)); return false; } if (isset($idx)) { array_pop($this->location); } if (isset($mapping['cast'])) { if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) { $value = chr($tag) . $this->_encodeLength(strlen($value)) . $value; $tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast']; } else { $tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast']; } } return chr($tag) . $this->_encodeLength(strlen($value)) . $value; } function _encodeLength($length) { if ($length <= 0x7F) { return chr($length); } $temp = ltrim(pack('N', $length), chr(0)); return pack('Ca*', 0x80 | strlen($temp), $temp); } function _decodeOID($content) { static $eighty; if (!$eighty) { $eighty = new BigInteger(80); } $oid = array(); $pos = 0; $len = strlen($content); if (ord($content[$len - 1]) & 0x80) { return false; } $n = new BigInteger(); while ($pos < $len) { $temp = ord($content[$pos++]); $n = $n->bitwise_leftShift(7); $n = $n->bitwise_or(new BigInteger($temp & 0x7F)); if (~$temp & 0x80) { $oid[] = $n; $n = new BigInteger(); } } $part1 = array_shift($oid); $first = floor(ord($content[0]) / 40); if ($first <= 2) { array_unshift($oid, ord($content[0]) % 40); array_unshift($oid, $first); } else { array_unshift($oid, $part1->subtract($eighty)); array_unshift($oid, 2); } return implode('.', $oid); } function _encodeOID($source) { static $mask, $zero, $forty; if (!$mask) { $mask = new BigInteger(0x7F); $zero = new BigInteger(); $forty = new BigInteger(40); } $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); if ($oid === false) { user_error('Invalid OID'); return false; } $parts = explode('.', $oid); $part1 = array_shift($parts); $part2 = array_shift($parts); $first = new BigInteger($part1); $first = $first->multiply($forty); $first = $first->add(new BigInteger($part2)); array_unshift($parts, $first->toString()); $value = ''; foreach ($parts as $part) { if (!$part) { $temp = "\0"; } else { $temp = ''; $part = new BigInteger($part); while (!$part->equals($zero)) { $submask = $part->bitwise_and($mask); $submask->setPrecision(8); $temp = (chr(0x80) | $submask->toBytes()) . $temp; $part = $part->bitwise_rightShift(7); } $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); } $value.= $temp; } return $value; } function _decodeTime($content, $tag) { $format = 'YmdHis'; if ($tag == self::TYPE_UTC_TIME) { if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) { $content = $matches[1] . '00' . $matches[2]; } $prefix = substr($content, 0, 2) >= 50 ? '19' : '20'; $content = $prefix . $content; } elseif (strpos($content, '.') !== false) { $format.= '.u'; } if ($content[strlen($content) - 1] == 'Z') { $content = substr($content, 0, -1) . '+0000'; } if (strpos($content, '-') !== false || strpos($content, '+') !== false) { $format.= 'O'; } return @DateTime::createFromFormat($format, $content); } function setTimeFormat($format) { $this->format = $format; } function loadOIDs($oids) { $this->oids = $oids; } function loadFilters($filters) { $this->filters = $filters; } function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING) { if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) { return false; } $insize = $this->stringTypeSize[$from]; $outsize = $this->stringTypeSize[$to]; $inlength = strlen($in); $out = ''; for ($i = 0; $i < $inlength;) { if ($inlength - $i < $insize) { return false; } $c = ord($in[$i++]); switch (true) { case $insize == 4: $c = ($c << 8) | ord($in[$i++]); $c = ($c << 8) | ord($in[$i++]); case $insize == 2: $c = ($c << 8) | ord($in[$i++]); case $insize == 1: break; case ($c & 0x80) == 0x00: break; case ($c & 0x40) == 0x00: return false; default: $bit = 6; do { if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) { return false; } $c = ($c << 6) | (ord($in[$i++]) & 0x3F); $bit += 5; $mask = 1 << $bit; } while ($c & $bit); $c &= $mask - 1; break; } $v = ''; switch (true) { case $outsize == 4: $v .= chr($c & 0xFF); $c >>= 8; $v .= chr($c & 0xFF); $c >>= 8; case $outsize == 2: $v .= chr($c & 0xFF); $c >>= 8; case $outsize == 1: $v .= chr($c & 0xFF); $c >>= 8; if ($c) { return false; } break; case ($c & 0x80000000) != 0: return false; case $c >= 0x04000000: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x04000000; case $c >= 0x00200000: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x00200000; case $c >= 0x00010000: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x00010000; case $c >= 0x00000800: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x00000800; case $c >= 0x00000080: $v .= chr(0x80 | ($c & 0x3F)); $c = ($c >> 6) | 0x000000C0; default: $v .= chr($c); break; } $out .= strrev($v); } return $out; } } fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); if (!$this->fsock) { user_error("Unable to connect to ssh-agent (Error $errno: $errstr)"); } } function requestIdentities() { if (!$this->fsock) { return array(); } $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); if (strlen($packet) != fputs($this->fsock, $packet)) { user_error('Connection closed while requesting identities'); return array(); } $temp = fread($this->fsock, 4); if (strlen($temp) != 4) { user_error('Connection closed while requesting identities'); return array(); } $length = current(unpack('N', $temp)); $type = ord(fread($this->fsock, 1)); if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { user_error('Unable to request identities'); return array(); } $identities = array(); $temp = fread($this->fsock, 4); if (strlen($temp) != 4) { user_error('Connection closed while requesting identities'); return array(); } $keyCount = current(unpack('N', $temp)); for ($i = 0; $i < $keyCount; $i++) { $temp = fread($this->fsock, 4); if (strlen($temp) != 4) { user_error('Connection closed while requesting identities'); return array(); } $length = current(unpack('N', $temp)); $key_blob = fread($this->fsock, $length); if (strlen($key_blob) != $length) { user_error('Connection closed while requesting identities'); return array(); } $key_str = 'ssh-rsa ' . base64_encode($key_blob); $temp = fread($this->fsock, 4); if (strlen($temp) != 4) { user_error('Connection closed while requesting identities'); return array(); } $length = current(unpack('N', $temp)); if ($length) { $temp = fread($this->fsock, $length); if (strlen($temp) != $length) { user_error('Connection closed while requesting identities'); return array(); } $key_str.= ' ' . $temp; } $length = current(unpack('N', substr($key_blob, 0, 4))); $key_type = substr($key_blob, 4, $length); switch ($key_type) { case 'ssh-rsa': $key = new RSA(); $key->loadKey($key_str); break; case 'ssh-dss': break; } if (isset($key)) { $identity = new Identity($this->fsock); $identity->setPublicKey($key); $identity->setPublicKeyBlob($key_blob); $identities[] = $identity; unset($key); } } return $identities; } function startSSHForwarding() { if ($this->forward_status == self::FORWARD_NONE) { $this->forward_status = self::FORWARD_REQUEST; } } function _request_forwarding($ssh) { $request_channel = $ssh->_get_open_channel(); if ($request_channel === false) { return false; } $packet = pack( 'CNNa*C', NET_SSH2_MSG_CHANNEL_REQUEST, $ssh->server_channels[$request_channel], strlen('auth-agent-req@openssh.com'), 'auth-agent-req@openssh.com', 1 ); $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; if (!$ssh->_send_binary_packet($packet)) { return false; } $response = $ssh->_get_channel_packet($request_channel); if ($response === false) { return false; } $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; $this->forward_status = self::FORWARD_ACTIVE; return true; } function _on_channel_open($ssh) { if ($this->forward_status == self::FORWARD_REQUEST) { $this->_request_forwarding($ssh); } } function _forward_data($data) { if ($this->expected_bytes > 0) { $this->socket_buffer.= $data; $this->expected_bytes -= strlen($data); } else { $agent_data_bytes = current(unpack('N', $data)); $current_data_bytes = strlen($data); $this->socket_buffer = $data; if ($current_data_bytes != $agent_data_bytes + 4) { $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes; return false; } } if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { user_error('Connection closed attempting to forward data to SSH agent'); return false; } $this->socket_buffer = ''; $this->expected_bytes = 0; $temp = fread($this->fsock, 4); if (strlen($temp) != 4) { user_error('Connection closed while reading data response'); return false; } $agent_reply_bytes = current(unpack('N', $temp)); $agent_reply_data = fread($this->fsock, $agent_reply_bytes); if (strlen($agent_reply_data) != $agent_reply_bytes) { user_error('Connection closed while reading data response'); return false; } $agent_reply_data = current(unpack('a*', $agent_reply_data)); return pack('Na*', $agent_reply_bytes, $agent_reply_data); } } fsock = $fsock; } function setPublicKey($key) { $this->key = $key; $this->key->setPublicKey(); } function setPublicKeyBlob($key_blob) { $this->key_blob = $key_blob; } function getPublicKey($format = null) { return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format); } function setSignatureMode($mode) { } function setHash($hash) { $this->flags = 0; switch ($hash) { case 'sha1': break; case 'sha256': $this->flags = self::SSH_AGENT_RSA2_256; break; case 'sha512': $this->flags = self::SSH_AGENT_RSA2_512; break; default: user_error('The only supported hashes for RSA are sha1, sha256 and sha512'); } } function sign($message) { $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags); $packet = pack('Na*', strlen($packet), $packet); if (strlen($packet) != fputs($this->fsock, $packet)) { user_error('Connection closed during signing'); return false; } $temp = fread($this->fsock, 4); if (strlen($temp) != 4) { user_error('Connection closed during signing'); return false; } $length = current(unpack('N', $temp)); $type = ord(fread($this->fsock, 1)); if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { user_error('Unable to retrieve signature'); return false; } $signature_blob = fread($this->fsock, $length - 1); if (strlen($signature_blob) != $length - 1) { user_error('Connection closed during signing'); return false; } $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); if ($length != strlen($signature_blob)) { user_error('Malformed signature blob'); } $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); if ($length > strlen($signature_blob) + 4) { user_error('Malformed signature blob'); } $type = $this->_string_shift($signature_blob, $length); $this->_string_shift($signature_blob, 4); return $signature_blob; } function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); return $substr; } } = $limit) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value less than %2$s. Got: %s', static::valueToString($value), static::valueToString($limit) )); } } public static function lessThanEq($value, $limit, $message = '') { if ($value > $limit) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value less than or equal to %2$s. Got: %s', static::valueToString($value), static::valueToString($limit) )); } } public static function range($value, $min, $max, $message = '') { if ($value < $min || $value > $max) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value between %2$s and %3$s. Got: %s', static::valueToString($value), static::valueToString($min), static::valueToString($max) )); } } public static function oneOf($value, array $values, $message = '') { static::inArray($value, $values, $message); } public static function inArray($value, array $values, $message = '') { if (!\in_array($value, $values, true)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected one of: %2$s. Got: %s', static::valueToString($value), \implode(', ', \array_map(array('static', 'valueToString'), $values)) )); } } public static function contains($value, $subString, $message = '') { if (false === \strpos($value, $subString)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain %2$s. Got: %s', static::valueToString($value), static::valueToString($subString) )); } } public static function notContains($value, $subString, $message = '') { if (false !== \strpos($value, $subString)) { static::reportInvalidArgument(\sprintf( $message ?: '%2$s was not expected to be contained in a value. Got: %s', static::valueToString($value), static::valueToString($subString) )); } } public static function notWhitespaceOnly($value, $message = '') { if (\preg_match('/^\s*$/', $value)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a non-whitespace string. Got: %s', static::valueToString($value) )); } } public static function startsWith($value, $prefix, $message = '') { if (0 !== \strpos($value, $prefix)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to start with %2$s. Got: %s', static::valueToString($value), static::valueToString($prefix) )); } } public static function notStartsWith($value, $prefix, $message = '') { if (0 === \strpos($value, $prefix)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value not to start with %2$s. Got: %s', static::valueToString($value), static::valueToString($prefix) )); } } public static function startsWithLetter($value, $message = '') { static::string($value); $valid = isset($value[0]); if ($valid) { $locale = \setlocale(LC_CTYPE, 0); \setlocale(LC_CTYPE, 'C'); $valid = \ctype_alpha($value[0]); \setlocale(LC_CTYPE, $locale); } if (!$valid) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to start with a letter. Got: %s', static::valueToString($value) )); } } public static function endsWith($value, $suffix, $message = '') { if ($suffix !== \substr($value, -\strlen($suffix))) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to end with %2$s. Got: %s', static::valueToString($value), static::valueToString($suffix) )); } } public static function notEndsWith($value, $suffix, $message = '') { if ($suffix === \substr($value, -\strlen($suffix))) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value not to end with %2$s. Got: %s', static::valueToString($value), static::valueToString($suffix) )); } } public static function regex($value, $pattern, $message = '') { if (!\preg_match($pattern, $value)) { static::reportInvalidArgument(\sprintf( $message ?: 'The value %s does not match the expected pattern.', static::valueToString($value) )); } } public static function notRegex($value, $pattern, $message = '') { if (\preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) { static::reportInvalidArgument(\sprintf( $message ?: 'The value %s matches the pattern %s (at offset %d).', static::valueToString($value), static::valueToString($pattern), $matches[0][1] )); } } public static function unicodeLetters($value, $message = '') { static::string($value); if (!\preg_match('/^\p{L}+$/u', $value)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain only Unicode letters. Got: %s', static::valueToString($value) )); } } public static function alpha($value, $message = '') { static::string($value); $locale = \setlocale(LC_CTYPE, 0); \setlocale(LC_CTYPE, 'C'); $valid = !\ctype_alpha($value); \setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain only letters. Got: %s', static::valueToString($value) )); } } public static function digits($value, $message = '') { $locale = \setlocale(LC_CTYPE, 0); \setlocale(LC_CTYPE, 'C'); $valid = !\ctype_digit($value); \setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain digits only. Got: %s', static::valueToString($value) )); } } public static function alnum($value, $message = '') { $locale = \setlocale(LC_CTYPE, 0); \setlocale(LC_CTYPE, 'C'); $valid = !\ctype_alnum($value); \setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain letters and digits only. Got: %s', static::valueToString($value) )); } } public static function lower($value, $message = '') { $locale = \setlocale(LC_CTYPE, 0); \setlocale(LC_CTYPE, 'C'); $valid = !\ctype_lower($value); \setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain lowercase characters only. Got: %s', static::valueToString($value) )); } } public static function upper($value, $message = '') { $locale = \setlocale(LC_CTYPE, 0); \setlocale(LC_CTYPE, 'C'); $valid = !\ctype_upper($value); \setlocale(LC_CTYPE, $locale); if ($valid) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain uppercase characters only. Got: %s', static::valueToString($value) )); } } public static function length($value, $length, $message = '') { if ($length !== static::strlen($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain %2$s characters. Got: %s', static::valueToString($value), $length )); } } public static function minLength($value, $min, $message = '') { if (static::strlen($value) < $min) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain at least %2$s characters. Got: %s', static::valueToString($value), $min )); } } public static function maxLength($value, $max, $message = '') { if (static::strlen($value) > $max) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain at most %2$s characters. Got: %s', static::valueToString($value), $max )); } } public static function lengthBetween($value, $min, $max, $message = '') { $length = static::strlen($value); if ($length < $min || $length > $max) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', static::valueToString($value), $min, $max )); } } public static function fileExists($value, $message = '') { static::string($value); if (!\file_exists($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'The file %s does not exist.', static::valueToString($value) )); } } public static function file($value, $message = '') { static::fileExists($value, $message); if (!\is_file($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'The path %s is not a file.', static::valueToString($value) )); } } public static function directory($value, $message = '') { static::fileExists($value, $message); if (!\is_dir($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'The path %s is no directory.', static::valueToString($value) )); } } public static function readable($value, $message = '') { if (!\is_readable($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'The path %s is not readable.', static::valueToString($value) )); } } public static function writable($value, $message = '') { if (!\is_writable($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'The path %s is not writable.', static::valueToString($value) )); } } public static function classExists($value, $message = '') { if (!\class_exists($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected an existing class name. Got: %s', static::valueToString($value) )); } } public static function subclassOf($value, $class, $message = '') { if (!\is_subclass_of($value, $class)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected a sub-class of %2$s. Got: %s', static::valueToString($value), static::valueToString($class) )); } } public static function interfaceExists($value, $message = '') { if (!\interface_exists($value)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected an existing interface name. got %s', static::valueToString($value) )); } } public static function implementsInterface($value, $interface, $message = '') { if (!\in_array($interface, \class_implements($value))) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected an implementation of %2$s. Got: %s', static::valueToString($value), static::valueToString($interface) )); } } public static function propertyExists($classOrObject, $property, $message = '') { if (!\property_exists($classOrObject, $property)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected the property %s to exist.', static::valueToString($property) )); } } public static function propertyNotExists($classOrObject, $property, $message = '') { if (\property_exists($classOrObject, $property)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected the property %s to not exist.', static::valueToString($property) )); } } public static function methodExists($classOrObject, $method, $message = '') { if (!(\is_string($classOrObject) || \is_object($classOrObject)) || !\method_exists($classOrObject, $method)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected the method %s to exist.', static::valueToString($method) )); } } public static function methodNotExists($classOrObject, $method, $message = '') { if ((\is_string($classOrObject) || \is_object($classOrObject)) && \method_exists($classOrObject, $method)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected the method %s to not exist.', static::valueToString($method) )); } } public static function keyExists($array, $key, $message = '') { if (!(isset($array[$key]) || \array_key_exists($key, $array))) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected the key %s to exist.', static::valueToString($key) )); } } public static function keyNotExists($array, $key, $message = '') { if (isset($array[$key]) || \array_key_exists($key, $array)) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected the key %s to not exist.', static::valueToString($key) )); } } public static function validArrayKey($value, $message = '') { if (!(\is_int($value) || \is_string($value))) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected string or integer. Got: %s', static::typeToString($value) )); } } public static function count($array, $number, $message = '') { static::eq( \count($array), $number, \sprintf( $message ?: 'Expected an array to contain %d elements. Got: %d.', $number, \count($array) ) ); } public static function minCount($array, $min, $message = '') { if (\count($array) < $min) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected an array to contain at least %2$d elements. Got: %d', \count($array), $min )); } } public static function maxCount($array, $max, $message = '') { if (\count($array) > $max) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected an array to contain at most %2$d elements. Got: %d', \count($array), $max )); } } public static function countBetween($array, $min, $max, $message = '') { $count = \count($array); if ($count < $min || $count > $max) { static::reportInvalidArgument(\sprintf( $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', $count, $min, $max )); } } public static function isList($array, $message = '') { if (!\is_array($array) || $array !== \array_values($array)) { static::reportInvalidArgument( $message ?: 'Expected list - non-associative array.' ); } } public static function isNonEmptyList($array, $message = '') { static::isList($array, $message); static::notEmpty($array, $message); } public static function isMap($array, $message = '') { if ( !\is_array($array) || \array_keys($array) !== \array_filter(\array_keys($array), '\is_string') ) { static::reportInvalidArgument( $message ?: 'Expected map - associative array with string keys.' ); } } public static function isNonEmptyMap($array, $message = '') { static::isMap($array, $message); static::notEmpty($array, $message); } public static function uuid($value, $message = '') { $value = \str_replace(array('urn:', 'uuid:', '{', '}'), '', $value); if ('00000000-0000-0000-0000-000000000000' === $value) { return; } if (!\preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) { static::reportInvalidArgument(\sprintf( $message ?: 'Value %s is not a valid UUID.', static::valueToString($value) )); } } public static function throws(Closure $expression, $class = 'Exception', $message = '') { static::string($class); $actual = 'none'; try { $expression(); } catch (Exception $e) { $actual = \get_class($e); if ($e instanceof $class) { return; } } catch (Throwable $e) { $actual = \get_class($e); if ($e instanceof $class) { return; } } static::reportInvalidArgument($message ?: \sprintf( 'Expected to throw "%s", got "%s"', $class, $actual )); } public static function __callStatic($name, $arguments) { if ('nullOr' === \substr($name, 0, 6)) { if (null !== $arguments[0]) { $method = \lcfirst(\substr($name, 6)); \call_user_func_array(array('static', $method), $arguments); } return; } if ('all' === \substr($name, 0, 3)) { static::isIterable($arguments[0]); $method = \lcfirst(\substr($name, 3)); $args = $arguments; foreach ($arguments[0] as $entry) { $args[0] = $entry; \call_user_func_array(array('static', $method), $args); } return; } throw new BadMethodCallException('No such method: '.$name); } protected static function valueToString($value) { if (null === $value) { return 'null'; } if (true === $value) { return 'true'; } if (false === $value) { return 'false'; } if (\is_array($value)) { return 'array'; } if (\is_object($value)) { if (\method_exists($value, '__toString')) { return \get_class($value).': '.self::valueToString($value->__toString()); } if ($value instanceof DateTime || $value instanceof DateTimeImmutable) { return \get_class($value).': '.self::valueToString($value->format('c')); } return \get_class($value); } if (\is_resource($value)) { return 'resource'; } if (\is_string($value)) { return '"'.$value.'"'; } return (string) $value; } protected static function typeToString($value) { return \is_object($value) ? \get_class($value) : \gettype($value); } protected static function strlen($value) { if (!\function_exists('mb_detect_encoding')) { return \strlen($value); } if (false === $encoding = \mb_detect_encoding($value)) { return \strlen($value); } return \mb_strlen($value, $encoding); } protected static function reportInvalidArgument($message) { throw new InvalidArgumentException($message); } private function __construct() { } } mechanism = $mechanism; $this->value = $value; } public function getMechanism() { return $this->mechanism; } public function getValue() { return $this->value; } public static function className($class_name) { return new static('class name', $class_name); } public static function cssSelector($css_selector) { return new static('css selector', $css_selector); } public static function id($id) { return new static('id', $id); } public static function name($name) { return new static('name', $name); } public static function linkText($link_text) { return new static('link text', $link_text); } public static function partialLinkText($partial_link_text) { return new static('partial link text', $partial_link_text); } public static function tagName($tag_name) { return new static('tag name', $tag_name); } public static function xpath($xpath) { return new static('xpath', $xpath); } } executor = $executor; } public function accept() { $this->executor->execute(DriverCommand::ACCEPT_ALERT); return $this; } public function dismiss() { $this->executor->execute(DriverCommand::DISMISS_ALERT); return $this; } public function getText() { return $this->executor->execute(DriverCommand::GET_ALERT_TEXT); } public function sendKeys($value) { $this->executor->execute( DriverCommand::SET_ALERT_VALUE, ['text' => $value] ); return $this; } } getTagName(); if ($tagName !== 'input') { throw new UnexpectedTagNameException('input', $tagName); } $this->name = $element->getAttribute('name'); if ($this->name === null) { throw new WebDriverException('The input does not have a "name" attribute.'); } $this->element = $element; } public function getOptions() { return $this->getRelatedElements(); } public function getAllSelectedOptions() { $selectedElement = []; foreach ($this->getRelatedElements() as $element) { if ($element->isSelected()) { $selectedElement[] = $element; if (!$this->isMultiple()) { return $selectedElement; } } } return $selectedElement; } public function getFirstSelectedOption() { foreach ($this->getRelatedElements() as $element) { if ($element->isSelected()) { return $element; } } throw new NoSuchElementException( sprintf('No %s are selected', $this->type === 'radio' ? 'radio buttons' : 'checkboxes') ); } public function selectByIndex($index) { $this->byIndex($index); } public function selectByValue($value) { $this->byValue($value); } public function selectByVisibleText($text) { $this->byVisibleText($text); } public function selectByVisiblePartialText($text) { $this->byVisibleText($text, true); } protected function byValue($value, $select = true) { $matched = false; foreach ($this->getRelatedElements($value) as $element) { $select ? $this->selectOption($element) : $this->deselectOption($element); if (!$this->isMultiple()) { return; } $matched = true; } if (!$matched) { throw new NoSuchElementException( sprintf('Cannot locate %s with value: %s', $this->type, $value) ); } } protected function byIndex($index, $select = true) { $elements = $this->getRelatedElements(); if (!isset($elements[$index])) { throw new NoSuchElementException(sprintf('Cannot locate %s with index: %d', $this->type, $index)); } $select ? $this->selectOption($elements[$index]) : $this->deselectOption($elements[$index]); } protected function byVisibleText($text, $partial = false, $select = true) { foreach ($this->getRelatedElements() as $element) { $normalizeFilter = sprintf( $partial ? 'contains(normalize-space(.), %s)' : 'normalize-space(.) = %s', XPathEscaper::escapeQuotes($text) ); $xpath = 'ancestor::label'; $xpathNormalize = sprintf('%s[%s]', $xpath, $normalizeFilter); $id = $element->getAttribute('id'); if ($id !== null) { $idFilter = sprintf('@for = %s', XPathEscaper::escapeQuotes($id)); $xpath .= sprintf(' | //label[%s]', $idFilter); $xpathNormalize .= sprintf(' | //label[%s and %s]', $idFilter, $normalizeFilter); } try { $element->findElement(WebDriverBy::xpath($xpathNormalize)); } catch (NoSuchElementException $e) { if ($partial) { continue; } try { if ($text !== $element->findElement(WebDriverBy::xpath($xpath))->getText()) { continue; } } catch (NoSuchElementException $e) { continue; } } $select ? $this->selectOption($element) : $this->deselectOption($element); if (!$this->isMultiple()) { return; } } } protected function getRelatedElements($value = null) { $valueSelector = $value ? sprintf(' and @value = %s', XPathEscaper::escapeQuotes($value)) : ''; $formId = $this->element->getAttribute('form'); if ($formId === null) { $form = $this->element->findElement(WebDriverBy::xpath('ancestor::form')); $formId = $form->getAttribute('id'); if ($formId === '' || $formId === null) { return $form->findElements(WebDriverBy::xpath( sprintf('.//input[@name = %s%s]', XPathEscaper::escapeQuotes($this->name), $valueSelector) )); } } return $this->element->findElements( WebDriverBy::xpath(sprintf( '//form[@id = %1$s]//input[@name = %2$s%3$s' . ' and ((boolean(@form) = true() and @form = %1$s) or boolean(@form) = false())]' . ' | //input[@form = %1$s and @name = %2$s%3$s]', XPathEscaper::escapeQuotes($formId), XPathEscaper::escapeQuotes($this->name), $valueSelector )) ); } protected function selectOption(WebDriverElement $element) { if (!$element->isSelected()) { $element->click(); } } protected function deselectOption(WebDriverElement $element) { if ($element->isSelected()) { $element->click(); } } } validateCookieName($name); $this->validateCookieValue($value); $this->cookie['name'] = $name; $this->cookie['value'] = $value; } public static function createFromArray(array $cookieArray) { if (!isset($cookieArray['name'])) { throw new InvalidArgumentException('Cookie name should be set'); } if (!isset($cookieArray['value'])) { throw new InvalidArgumentException('Cookie value should be set'); } $cookie = new self($cookieArray['name'], $cookieArray['value']); if (isset($cookieArray['path'])) { $cookie->setPath($cookieArray['path']); } if (isset($cookieArray['domain'])) { $cookie->setDomain($cookieArray['domain']); } if (isset($cookieArray['expiry'])) { $cookie->setExpiry($cookieArray['expiry']); } if (isset($cookieArray['secure'])) { $cookie->setSecure($cookieArray['secure']); } if (isset($cookieArray['httpOnly'])) { $cookie->setHttpOnly($cookieArray['httpOnly']); } if (isset($cookieArray['sameSite'])) { $cookie->setSameSite($cookieArray['sameSite']); } return $cookie; } public function getName() { return $this->offsetGet('name'); } public function getValue() { return $this->offsetGet('value'); } public function setPath($path) { $this->offsetSet('path', $path); } public function getPath() { return $this->offsetGet('path'); } public function setDomain($domain) { if (mb_strpos($domain, ':') !== false) { throw new InvalidArgumentException(sprintf('Cookie domain "%s" should not contain a port', $domain)); } $this->offsetSet('domain', $domain); } public function getDomain() { return $this->offsetGet('domain'); } public function setExpiry($expiry) { $this->offsetSet('expiry', (int) $expiry); } public function getExpiry() { return $this->offsetGet('expiry'); } public function setSecure($secure) { $this->offsetSet('secure', $secure); } public function isSecure() { return $this->offsetGet('secure'); } public function setHttpOnly($httpOnly) { $this->offsetSet('httpOnly', $httpOnly); } public function isHttpOnly() { return $this->offsetGet('httpOnly'); } public function setSameSite($sameSite) { $this->offsetSet('sameSite', $sameSite); } public function getSameSite() { return $this->offsetGet('sameSite'); } public function toArray() { $cookie = $this->cookie; if (!isset($cookie['secure'])) { $cookie['secure'] = false; } return $cookie; } #[\ReturnTypeWillChange] public function offsetExists($offset) { return isset($this->cookie[$offset]); } #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->offsetExists($offset) ? $this->cookie[$offset] : null; } #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { if ($value === null) { unset($this->cookie[$offset]); } else { $this->cookie[$offset] = $value; } } #[\ReturnTypeWillChange] public function offsetUnset($offset) { unset($this->cookie[$offset]); } protected function validateCookieName($name) { if ($name === null || $name === '') { throw new InvalidArgumentException('Cookie name should be non-empty'); } if (mb_strpos($name, ';') !== false) { throw new InvalidArgumentException('Cookie name should not contain a ";"'); } } protected function validateCookieValue($value) { if ($value === null) { throw new InvalidArgumentException('Cookie value is required when setting a cookie'); } } } microtime(true)) { if ($this->getHTTPResponseCode($url) === 200) { return $this; } usleep(self::POLL_INTERVAL_MS); } throw new TimeoutException(sprintf( 'Timed out waiting for %s to become available after %d ms.', $url, $timeout_in_ms )); } public function waitUntilUnavailable($timeout_in_ms, $url) { $end = microtime(true) + $timeout_in_ms / 1000; while ($end > microtime(true)) { if ($this->getHTTPResponseCode($url) !== 200) { return $this; } usleep(self::POLL_INTERVAL_MS); } throw new TimeoutException(sprintf( 'Timed out waiting for %s to become unavailable after %d ms.', $url, $timeout_in_ms )); } private function getHTTPResponseCode($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); if (!defined('CURLOPT_CONNECTTIMEOUT_MS')) { define('CURLOPT_CONNECTTIMEOUT_MS', 156); } curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, self::CONNECT_TIMEOUT_MS); $code = null; try { curl_exec($ch); $info = curl_getinfo($ch); $code = $info['http_code']; } catch (Exception $e) { } curl_close($ch); return $code; } } type = $element->getAttribute('type'); if ($this->type !== 'radio') { throw new WebDriverException('The input must be of type "radio".'); } } public function isMultiple() { return false; } public function deselectAll() { throw new UnsupportedOperationException('You cannot deselect radio buttons'); } public function deselectByIndex($index) { throw new UnsupportedOperationException('You cannot deselect radio buttons'); } public function deselectByValue($value) { throw new UnsupportedOperationException('You cannot deselect radio buttons'); } public function deselectByVisibleText($text) { throw new UnsupportedOperationException('You cannot deselect radio buttons'); } public function deselectByVisiblePartialText($text) { throw new UnsupportedOperationException('You cannot deselect radio buttons'); } } getTagName(); if ($tag_name !== 'select') { throw new UnexpectedTagNameException('select', $tag_name); } $this->element = $element; $value = $element->getAttribute('multiple'); $this->isMulti = $value === 'true'; } public function isMultiple() { return $this->isMulti; } public function getOptions() { return $this->element->findElements(WebDriverBy::tagName('option')); } public function getAllSelectedOptions() { $selected_options = []; foreach ($this->getOptions() as $option) { if ($option->isSelected()) { $selected_options[] = $option; if (!$this->isMultiple()) { return $selected_options; } } } return $selected_options; } public function getFirstSelectedOption() { foreach ($this->getOptions() as $option) { if ($option->isSelected()) { return $option; } } throw new NoSuchElementException('No options are selected'); } public function selectByIndex($index) { foreach ($this->getOptions() as $option) { if ($option->getAttribute('index') === (string) $index) { $this->selectOption($option); return; } } throw new NoSuchElementException(sprintf('Cannot locate option with index: %d', $index)); } public function selectByValue($value) { $matched = false; $xpath = './/option[@value = ' . XPathEscaper::escapeQuotes($value) . ']'; $options = $this->element->findElements(WebDriverBy::xpath($xpath)); foreach ($options as $option) { $this->selectOption($option); if (!$this->isMultiple()) { return; } $matched = true; } if (!$matched) { throw new NoSuchElementException( sprintf('Cannot locate option with value: %s', $value) ); } } public function selectByVisibleText($text) { $matched = false; $xpath = './/option[normalize-space(.) = ' . XPathEscaper::escapeQuotes($text) . ']'; $options = $this->element->findElements(WebDriverBy::xpath($xpath)); foreach ($options as $option) { $this->selectOption($option); if (!$this->isMultiple()) { return; } $matched = true; } if (!$matched) { foreach ($this->getOptions() as $option) { if ($option->getText() === $text) { $this->selectOption($option); if (!$this->isMultiple()) { return; } $matched = true; } } } if (!$matched) { throw new NoSuchElementException( sprintf('Cannot locate option with text: %s', $text) ); } } public function selectByVisiblePartialText($text) { $matched = false; $xpath = './/option[contains(normalize-space(.), ' . XPathEscaper::escapeQuotes($text) . ')]'; $options = $this->element->findElements(WebDriverBy::xpath($xpath)); foreach ($options as $option) { $this->selectOption($option); if (!$this->isMultiple()) { return; } $matched = true; } if (!$matched) { throw new NoSuchElementException( sprintf('Cannot locate option with text: %s', $text) ); } } public function deselectAll() { if (!$this->isMultiple()) { throw new UnsupportedOperationException('You may only deselect all options of a multi-select'); } foreach ($this->getOptions() as $option) { $this->deselectOption($option); } } public function deselectByIndex($index) { if (!$this->isMultiple()) { throw new UnsupportedOperationException('You may only deselect options of a multi-select'); } foreach ($this->getOptions() as $option) { if ($option->getAttribute('index') === (string) $index) { $this->deselectOption($option); return; } } } public function deselectByValue($value) { if (!$this->isMultiple()) { throw new UnsupportedOperationException('You may only deselect options of a multi-select'); } $xpath = './/option[@value = ' . XPathEscaper::escapeQuotes($value) . ']'; $options = $this->element->findElements(WebDriverBy::xpath($xpath)); foreach ($options as $option) { $this->deselectOption($option); } } public function deselectByVisibleText($text) { if (!$this->isMultiple()) { throw new UnsupportedOperationException('You may only deselect options of a multi-select'); } $xpath = './/option[normalize-space(.) = ' . XPathEscaper::escapeQuotes($text) . ']'; $options = $this->element->findElements(WebDriverBy::xpath($xpath)); foreach ($options as $option) { $this->deselectOption($option); } } public function deselectByVisiblePartialText($text) { if (!$this->isMultiple()) { throw new UnsupportedOperationException('You may only deselect options of a multi-select'); } $xpath = './/option[contains(normalize-space(.), ' . XPathEscaper::escapeQuotes($text) . ')]'; $options = $this->element->findElements(WebDriverBy::xpath($xpath)); foreach ($options as $option) { $this->deselectOption($option); } } protected function selectOption(WebDriverElement $option) { if (!$option->isSelected()) { $option->click(); } } protected function deselectOption(WebDriverElement $option) { if ($option->isSelected()) { $option->click(); } } } executor = $executor; } public function back() { $this->executor->execute(DriverCommand::GO_BACK); return $this; } public function forward() { $this->executor->execute(DriverCommand::GO_FORWARD); return $this; } public function refresh() { $this->executor->execute(DriverCommand::REFRESH); return $this; } public function to($url) { $params = ['url' => (string) $url]; $this->executor->execute(DriverCommand::GET, $params); return $this; } } executor = $executor; $this->isW3cCompliant = $isW3cCompliant; } public function addCookie($cookie) { if (is_array($cookie)) { $cookie = Cookie::createFromArray($cookie); } if (!$cookie instanceof Cookie) { throw new InvalidArgumentException('Cookie must be set from instance of Cookie class or from array.'); } $this->executor->execute( DriverCommand::ADD_COOKIE, ['cookie' => $cookie->toArray()] ); return $this; } public function deleteAllCookies() { $this->executor->execute(DriverCommand::DELETE_ALL_COOKIES); return $this; } public function deleteCookieNamed($name) { $this->executor->execute( DriverCommand::DELETE_COOKIE, [':name' => $name] ); return $this; } public function getCookieNamed($name) { if ($this->isW3cCompliant) { $cookieArray = $this->executor->execute( DriverCommand::GET_NAMED_COOKIE, [':name' => $name] ); if (!is_array($cookieArray)) { throw new NoSuchCookieException('no such cookie'); } return Cookie::createFromArray($cookieArray); } $cookies = $this->getCookies(); foreach ($cookies as $cookie) { if ($cookie['name'] === $name) { return $cookie; } } return null; } public function getCookies() { $cookieArrays = $this->executor->execute(DriverCommand::GET_ALL_COOKIES); if (!is_array($cookieArrays)) { return []; } $cookies = []; foreach ($cookieArrays as $cookieArray) { $cookies[] = Cookie::createFromArray($cookieArray); } return $cookies; } public function timeouts() { return new WebDriverTimeouts($this->executor, $this->isW3cCompliant); } public function window() { return new WebDriverWindow($this->executor, $this->isW3cCompliant); } public function getLog($log_type) { return $this->executor->execute( DriverCommand::GET_LOG, ['type' => $log_type] ); } public function getAvailableLogTypes() { return $this->executor->execute(DriverCommand::GET_AVAILABLE_LOG_TYPES); } } x = $x; $this->y = $y; } public function getX() { return (int) $this->x; } public function getY() { return (int) $this->y; } public function move($new_x, $new_y) { $this->x = $new_x; $this->y = $new_y; return $this; } public function moveBy($x_offset, $y_offset) { $this->x += $x_offset; $this->y += $y_offset; return $this; } public function equals(self $point) { return $this->x === $point->getX() && $this->y === $point->getY(); } } driver = $driver; $this->timeout = isset($timeout_in_second) ? $timeout_in_second : 30; $this->interval = $interval_in_millisecond ?: 250; } public function until($func_or_ec, $message = '') { $end = microtime(true) + $this->timeout; $last_exception = null; while ($end > microtime(true)) { try { if ($func_or_ec instanceof WebDriverExpectedCondition) { $ret_val = call_user_func($func_or_ec->getApply(), $this->driver); } else { $ret_val = call_user_func($func_or_ec, $this->driver); } if ($ret_val) { return $ret_val; } } catch (NoSuchElementException $e) { $last_exception = $e; } usleep($this->interval * 1000); } if ($last_exception) { throw $last_exception; } throw new TimeoutException($message); } } executor = $executor; $this->id = $id; $this->fileDetector = new UselessFileDetector(); $this->isW3cCompliant = $isW3cCompliant; } public function clear() { $this->executor->execute( DriverCommand::CLEAR_ELEMENT, [':id' => $this->id] ); return $this; } public function click() { try { $this->executor->execute( DriverCommand::CLICK_ELEMENT, [':id' => $this->id] ); } catch (ElementNotInteractableException $e) { $this->clickChildElement($e); } return $this; } public function findElement(WebDriverBy $by) { $params = JsonWireCompat::getUsing($by, $this->isW3cCompliant); $params[':id'] = $this->id; $raw_element = $this->executor->execute( DriverCommand::FIND_CHILD_ELEMENT, $params ); return $this->newElement(JsonWireCompat::getElement($raw_element)); } public function findElements(WebDriverBy $by) { $params = JsonWireCompat::getUsing($by, $this->isW3cCompliant); $params[':id'] = $this->id; $raw_elements = $this->executor->execute( DriverCommand::FIND_CHILD_ELEMENTS, $params ); $elements = []; foreach ($raw_elements as $raw_element) { $elements[] = $this->newElement(JsonWireCompat::getElement($raw_element)); } return $elements; } public function getAttribute($attribute_name) { $params = [ ':name' => $attribute_name, ':id' => $this->id, ]; if ($this->isW3cCompliant && ($attribute_name === 'value' || $attribute_name === 'index')) { $value = $this->executor->execute(DriverCommand::GET_ELEMENT_PROPERTY, $params); if ($value === true) { return 'true'; } if ($value === false) { return 'false'; } if ($value !== null) { return (string) $value; } } return $this->executor->execute(DriverCommand::GET_ELEMENT_ATTRIBUTE, $params); } public function getDomProperty($propertyName) { if (!$this->isW3cCompliant) { throw new UnsupportedOperationException('This method is only supported in W3C mode'); } $params = [ ':name' => $propertyName, ':id' => $this->id, ]; return $this->executor->execute(DriverCommand::GET_ELEMENT_PROPERTY, $params); } public function getCSSValue($css_property_name) { $params = [ ':propertyName' => $css_property_name, ':id' => $this->id, ]; return $this->executor->execute( DriverCommand::GET_ELEMENT_VALUE_OF_CSS_PROPERTY, $params ); } public function getLocation() { $location = $this->executor->execute( DriverCommand::GET_ELEMENT_LOCATION, [':id' => $this->id] ); return new WebDriverPoint($location['x'], $location['y']); } public function getLocationOnScreenOnceScrolledIntoView() { if ($this->isW3cCompliant) { $script = <<executor->execute(DriverCommand::EXECUTE_SCRIPT, [ 'script' => $script, 'args' => [[JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $this->id]], ]); $location = ['x' => $result['x'], 'y' => $result['y']]; } else { $location = $this->executor->execute( DriverCommand::GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW, [':id' => $this->id] ); } return new WebDriverPoint($location['x'], $location['y']); } public function getCoordinates() { $element = $this; $on_screen = null; $in_view_port = static function () use ($element) { return $element->getLocationOnScreenOnceScrolledIntoView(); }; $on_page = static function () use ($element) { return $element->getLocation(); }; $auxiliary = $this->getID(); return new WebDriverCoordinates( $on_screen, $in_view_port, $on_page, $auxiliary ); } public function getSize() { $size = $this->executor->execute( DriverCommand::GET_ELEMENT_SIZE, [':id' => $this->id] ); return new WebDriverDimension($size['width'], $size['height']); } public function getTagName() { return mb_strtolower($this->executor->execute( DriverCommand::GET_ELEMENT_TAG_NAME, [':id' => $this->id] )); } public function getText() { return $this->executor->execute( DriverCommand::GET_ELEMENT_TEXT, [':id' => $this->id] ); } public function isDisplayed() { return $this->executor->execute( DriverCommand::IS_ELEMENT_DISPLAYED, [':id' => $this->id] ); } public function isEnabled() { return $this->executor->execute( DriverCommand::IS_ELEMENT_ENABLED, [':id' => $this->id] ); } public function isSelected() { return $this->executor->execute( DriverCommand::IS_ELEMENT_SELECTED, [':id' => $this->id] ); } public function sendKeys($value) { $local_file = $this->fileDetector->getLocalFile($value); $params = []; if ($local_file === null) { if ($this->isW3cCompliant) { $encodedValues = explode(WebDriverKeys::NULL, WebDriverKeys::encode($value, true)); foreach ($encodedValues as $encodedValue) { $params[] = [ 'text' => $encodedValue, ':id' => $this->id, ]; } } else { $params[] = [ 'value' => WebDriverKeys::encode($value), ':id' => $this->id, ]; } } else { if ($this->isW3cCompliant) { try { $fileName = $this->upload($local_file); } catch (WebDriverException $e) { $fileName = $local_file; } $params[] = [ 'text' => $fileName, ':id' => $this->id, ]; } else { $params[] = [ 'value' => WebDriverKeys::encode($this->upload($local_file)), ':id' => $this->id, ]; } } foreach ($params as $param) { $this->executor->execute(DriverCommand::SEND_KEYS_TO_ELEMENT, $param); } return $this; } public function setFileDetector(FileDetector $detector) { $this->fileDetector = $detector; return $this; } public function submit() { if ($this->isW3cCompliant) { $submitPolyfill = $script = <<executor->execute(DriverCommand::EXECUTE_SCRIPT, [ 'script' => $submitPolyfill, 'args' => [[JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $this->id]], ]); return $this; } $this->executor->execute( DriverCommand::SUBMIT_ELEMENT, [':id' => $this->id] ); return $this; } public function getID() { return $this->id; } public function takeElementScreenshot($save_as = null) { $screenshot = base64_decode( $this->executor->execute( DriverCommand::TAKE_ELEMENT_SCREENSHOT, [':id' => $this->id] ), true ); if ($save_as !== null) { $directoryPath = dirname($save_as); if (!file_exists($directoryPath)) { mkdir($directoryPath, 0777, true); } file_put_contents($save_as, $screenshot); } return $screenshot; } public function equals(WebDriverElement $other) { if ($this->isW3cCompliant) { return $this->getID() === $other->getID(); } return $this->executor->execute(DriverCommand::ELEMENT_EQUALS, [ ':id' => $this->id, ':other' => $other->getID(), ]); } protected function clickChildElement(ElementNotInteractableException $originalException) { $children = $this->findElements(WebDriverBy::xpath('./*')); foreach ($children as $child) { try { $this->executor->execute( DriverCommand::CLICK_ELEMENT, [':id' => $child->id] ); return; } catch (ElementNotInteractableException $e) { } } throw $originalException; } protected function newElement($id) { return new static($this->executor, $id, $this->isW3cCompliant); } protected function upload($local_file) { if (!is_file($local_file)) { throw new WebDriverException('You may only upload files: ' . $local_file); } $temp_zip_path = $this->createTemporaryZipArchive($local_file); $remote_path = $this->executor->execute( DriverCommand::UPLOAD_FILE, ['file' => base64_encode(file_get_contents($temp_zip_path))] ); unlink($temp_zip_path); return $remote_path; } protected function createTemporaryZipArchive($fileToZip) { $tempZipPath = sys_get_temp_dir() . '/' . uniqid('WebDriverZip', false); $zip = new ZipArchive(); if (($errorCode = $zip->open($tempZipPath, ZipArchive::CREATE)) !== true) { throw new WebDriverException(sprintf('Error creating zip archive: %s', $errorCode)); } $info = pathinfo($fileToZip); $file_name = $info['basename']; $zip->addFile($fileToZip, $file_name); $zip->close(); return $tempZipPath; } } executor = $executor; } public function tap(WebDriverElement $element) { $this->executor->execute( DriverCommand::TOUCH_SINGLE_TAP, ['element' => $element->getID()] ); return $this; } public function doubleTap(WebDriverElement $element) { $this->executor->execute( DriverCommand::TOUCH_DOUBLE_TAP, ['element' => $element->getID()] ); return $this; } public function down($x, $y) { $this->executor->execute(DriverCommand::TOUCH_DOWN, [ 'x' => $x, 'y' => $y, ]); return $this; } public function flick($xspeed, $yspeed) { $this->executor->execute(DriverCommand::TOUCH_FLICK, [ 'xspeed' => $xspeed, 'yspeed' => $yspeed, ]); return $this; } public function flickFromElement(WebDriverElement $element, $xoffset, $yoffset, $speed) { $this->executor->execute(DriverCommand::TOUCH_FLICK, [ 'xoffset' => $xoffset, 'yoffset' => $yoffset, 'element' => $element->getID(), 'speed' => $speed, ]); return $this; } public function longPress(WebDriverElement $element) { $this->executor->execute( DriverCommand::TOUCH_LONG_PRESS, ['element' => $element->getID()] ); return $this; } public function move($x, $y) { $this->executor->execute(DriverCommand::TOUCH_MOVE, [ 'x' => $x, 'y' => $y, ]); return $this; } public function scroll($xoffset, $yoffset) { $this->executor->execute(DriverCommand::TOUCH_SCROLL, [ 'xoffset' => $xoffset, 'yoffset' => $yoffset, ]); return $this; } public function scrollFromElement(WebDriverElement $element, $xoffset, $yoffset) { $this->executor->execute(DriverCommand::TOUCH_SCROLL, [ 'element' => $element->getID(), 'xoffset' => $xoffset, 'yoffset' => $yoffset, ]); return $this; } public function up($x, $y) { $this->executor->execute(DriverCommand::TOUCH_UP, [ 'x' => $x, 'y' => $y, ]); return $this; } } executor = $executor; $this->driver = $driver; $this->isW3cCompliant = $isW3cCompliant; } public function sendKeys($keys) { if ($this->isW3cCompliant) { $activeElement = $this->driver->switchTo()->activeElement(); $activeElement->sendKeys($keys); } else { $this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [ 'value' => WebDriverKeys::encode($keys), ]); } return $this; } public function pressKey($key) { if ($this->isW3cCompliant) { $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'key', 'id' => 'keyboard', 'actions' => [['type' => 'keyDown', 'value' => $key]], ], ], ]); } else { $this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [ 'value' => [(string) $key], ]); } return $this; } public function releaseKey($key) { if ($this->isW3cCompliant) { $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'key', 'id' => 'keyboard', 'actions' => [['type' => 'keyUp', 'value' => $key]], ], ], ]); } else { $this->executor->execute(DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT, [ 'value' => [(string) $key], ]); } return $this; } } driver = $driver; } public function execute($command_name, array $parameters = []) { return $this->driver->execute($command_name, $parameters); } } executor = $executor; $this->driver = $driver; $this->isW3cCompliant = $isW3cCompliant; } public function defaultContent() { $params = ['id' => null]; $this->executor->execute(DriverCommand::SWITCH_TO_FRAME, $params); return $this->driver; } public function frame($frame) { if ($this->isW3cCompliant) { if ($frame instanceof WebDriverElement) { $id = [JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $frame->getID()]; } elseif ($frame === null) { $id = null; } elseif (is_int($frame)) { $id = $frame; } else { throw new \InvalidArgumentException( 'In W3C compliance mode frame must be either instance of WebDriverElement, integer or null' ); } } else { if ($frame instanceof WebDriverElement) { $id = ['ELEMENT' => $frame->getID()]; } elseif ($frame === null) { $id = null; } elseif (is_int($frame)) { $id = $frame; } else { $id = (string) $frame; } } $params = ['id' => $id]; $this->executor->execute(DriverCommand::SWITCH_TO_FRAME, $params); return $this->driver; } public function parent() { $this->executor->execute(DriverCommand::SWITCH_TO_PARENT_FRAME, []); return $this->driver; } public function window($handle) { if ($this->isW3cCompliant) { $params = ['handle' => (string) $handle]; } else { $params = ['name' => (string) $handle]; } $this->executor->execute(DriverCommand::SWITCH_TO_WINDOW, $params); return $this->driver; } public function newWindow($windowType = self::WINDOW_TYPE_TAB) { if ($windowType !== self::WINDOW_TYPE_TAB && $windowType !== self::WINDOW_TYPE_WINDOW) { throw new \InvalidArgumentException('Window type must by either "tab" or "window"'); } if (!$this->isW3cCompliant) { throw new UnsupportedOperationException('New window is only supported in W3C mode'); } $response = $this->executor->execute(DriverCommand::NEW_WINDOW, ['type' => $windowType]); $this->window($response['handle']); return $this->driver; } public function alert() { return new WebDriverAlert($this->executor); } public function activeElement() { $response = $this->driver->execute(DriverCommand::GET_ACTIVE_ELEMENT, []); $method = new RemoteExecuteMethod($this->driver); return new RemoteWebElement($method, JsonWireCompat::getElement($response), $this->isW3cCompliant); } } setCustomRequestParameters($url, $method); parent::__construct($session_id, DriverCommand::CUSTOM_COMMAND, $parameters); } public function getCustomUrl() { if ($this->customUrl === null) { throw new WebDriverException('URL of custom command is not set'); } return $this->customUrl; } public function getCustomMethod() { if ($this->customMethod === null) { throw new WebDriverException('Method of custom command is not set'); } return $this->customMethod; } protected function setCustomRequestParameters($custom_url, $custom_method) { $allowedMethods = [static::METHOD_GET, static::METHOD_POST]; if (!in_array($custom_method, $allowedMethods, true)) { throw new WebDriverException( sprintf( 'Invalid custom method "%s", must be one of [%s]', $custom_method, implode(', ', $allowedMethods) ) ); } $this->customMethod = $custom_method; if (mb_strpos($custom_url, '/') !== 0) { throw new WebDriverException( sprintf('URL of custom command has to start with / but is "%s"', $custom_url) ); } $this->customUrl = $custom_url; } } getURL()); $this->service = $service; } public function execute(WebDriverCommand $command) { if ($command->getName() === DriverCommand::NEW_SESSION) { $this->service->start(); } try { $value = parent::execute($command); if ($command->getName() === DriverCommand::QUIT) { $this->service->stop(); } return $value; } catch (\Exception $e) { if (!$this->service->isRunning()) { throw new DriverServerDiedException($e); } throw $e; } } } setExecutable($executable); $this->url = sprintf('http://localhost:%d', $port); $this->args = $args; $this->environment = $environment ?: $_ENV; } public function getURL() { return $this->url; } public function start() { if ($this->process !== null) { return $this; } $this->process = $this->createProcess(); $this->process->start(); $this->checkWasStarted($this->process); $checker = new URLChecker(); $checker->waitUntilAvailable(20 * 1000, $this->url . '/status'); return $this; } public function stop() { if ($this->process === null) { return $this; } $this->process->stop(); $this->process = null; $checker = new URLChecker(); $checker->waitUntilUnavailable(3 * 1000, $this->url . '/shutdown'); return $this; } public function isRunning() { if ($this->process === null) { return false; } return $this->process->isRunning(); } protected static function checkExecutable($executable) { return $executable; } protected function setExecutable($executable) { if ($this->isExecutable($executable)) { $this->executable = $executable; return; } throw new Exception( sprintf( '"%s" is not executable. Make sure the path is correct or use environment variable to specify' . ' location of the executable.', $executable ) ); } protected function checkWasStarted($process) { usleep(10000); if (!$process->isRunning()) { throw new Exception( sprintf( 'Error starting driver executable "%s": %s', $process->getCommandLine(), $process->getErrorOutput() ) ); } } private function createProcess() { if (class_exists(ProcessBuilder::class) && mb_strpos('@deprecated', (new \ReflectionClass(ProcessBuilder::class))->getDocComment()) === false ) { $processBuilder = (new ProcessBuilder()) ->setPrefix($this->executable) ->setArguments($this->args) ->addEnvironmentVariables($this->environment); return $processBuilder->getProcess(); } $commandLine = array_merge([$this->executable], $this->args); return new Process($commandLine, null, $this->environment); } private function isExecutable($filename) { if (is_executable($filename)) { return true; } if ($filename !== basename($filename)) { return false; } $paths = explode(PATH_SEPARATOR, getenv('PATH')); foreach ($paths as $path) { if (is_executable($path . DIRECTORY_SEPARATOR . $filename)) { return true; } } return false; } } sessionID = $session_id; $this->name = $name; $this->parameters = $parameters; } public static function newSession(array $parameters) { return new self(null, DriverCommand::NEW_SESSION, $parameters); } public function getName() { return $this->name; } public function getSessionID() { return $this->sessionID; } public function getParameters() { return $this->parameters; } } ['method' => 'POST', 'url' => '/session/:sessionId/accept_alert'], DriverCommand::ADD_COOKIE => ['method' => 'POST', 'url' => '/session/:sessionId/cookie'], DriverCommand::CLEAR_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/clear'], DriverCommand::CLICK_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/click'], DriverCommand::CLOSE => ['method' => 'DELETE', 'url' => '/session/:sessionId/window'], DriverCommand::DELETE_ALL_COOKIES => ['method' => 'DELETE', 'url' => '/session/:sessionId/cookie'], DriverCommand::DELETE_COOKIE => ['method' => 'DELETE', 'url' => '/session/:sessionId/cookie/:name'], DriverCommand::DISMISS_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/dismiss_alert'], DriverCommand::ELEMENT_EQUALS => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/equals/:other'], DriverCommand::FIND_CHILD_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/element'], DriverCommand::FIND_CHILD_ELEMENTS => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/elements'], DriverCommand::EXECUTE_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute'], DriverCommand::EXECUTE_ASYNC_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute_async'], DriverCommand::FIND_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element'], DriverCommand::FIND_ELEMENTS => ['method' => 'POST', 'url' => '/session/:sessionId/elements'], DriverCommand::SWITCH_TO_FRAME => ['method' => 'POST', 'url' => '/session/:sessionId/frame'], DriverCommand::SWITCH_TO_PARENT_FRAME => ['method' => 'POST', 'url' => '/session/:sessionId/frame/parent'], DriverCommand::SWITCH_TO_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window'], DriverCommand::GET => ['method' => 'POST', 'url' => '/session/:sessionId/url'], DriverCommand::GET_ACTIVE_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/active'], DriverCommand::GET_ALERT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/alert_text'], DriverCommand::GET_ALL_COOKIES => ['method' => 'GET', 'url' => '/session/:sessionId/cookie'], DriverCommand::GET_NAMED_COOKIE => ['method' => 'GET', 'url' => '/session/:sessionId/cookie/:name'], DriverCommand::GET_ALL_SESSIONS => ['method' => 'GET', 'url' => '/sessions'], DriverCommand::GET_AVAILABLE_LOG_TYPES => ['method' => 'GET', 'url' => '/session/:sessionId/log/types'], DriverCommand::GET_CURRENT_URL => ['method' => 'GET', 'url' => '/session/:sessionId/url'], DriverCommand::GET_CURRENT_WINDOW_HANDLE => ['method' => 'GET', 'url' => '/session/:sessionId/window_handle'], DriverCommand::GET_ELEMENT_ATTRIBUTE => [ 'method' => 'GET', 'url' => '/session/:sessionId/element/:id/attribute/:name', ], DriverCommand::GET_ELEMENT_VALUE_OF_CSS_PROPERTY => [ 'method' => 'GET', 'url' => '/session/:sessionId/element/:id/css/:propertyName', ], DriverCommand::GET_ELEMENT_LOCATION => [ 'method' => 'GET', 'url' => '/session/:sessionId/element/:id/location', ], DriverCommand::GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW => [ 'method' => 'GET', 'url' => '/session/:sessionId/element/:id/location_in_view', ], DriverCommand::GET_ELEMENT_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/size'], DriverCommand::GET_ELEMENT_TAG_NAME => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/name'], DriverCommand::GET_ELEMENT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/text'], DriverCommand::GET_LOG => ['method' => 'POST', 'url' => '/session/:sessionId/log'], DriverCommand::GET_PAGE_SOURCE => ['method' => 'GET', 'url' => '/session/:sessionId/source'], DriverCommand::GET_SCREEN_ORIENTATION => ['method' => 'GET', 'url' => '/session/:sessionId/orientation'], DriverCommand::GET_CAPABILITIES => ['method' => 'GET', 'url' => '/session/:sessionId'], DriverCommand::GET_TITLE => ['method' => 'GET', 'url' => '/session/:sessionId/title'], DriverCommand::GET_WINDOW_HANDLES => ['method' => 'GET', 'url' => '/session/:sessionId/window_handles'], DriverCommand::GET_WINDOW_POSITION => [ 'method' => 'GET', 'url' => '/session/:sessionId/window/:windowHandle/position', ], DriverCommand::GET_WINDOW_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/window/:windowHandle/size'], DriverCommand::GO_BACK => ['method' => 'POST', 'url' => '/session/:sessionId/back'], DriverCommand::GO_FORWARD => ['method' => 'POST', 'url' => '/session/:sessionId/forward'], DriverCommand::IS_ELEMENT_DISPLAYED => [ 'method' => 'GET', 'url' => '/session/:sessionId/element/:id/displayed', ], DriverCommand::IS_ELEMENT_ENABLED => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/enabled'], DriverCommand::IS_ELEMENT_SELECTED => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/selected'], DriverCommand::MAXIMIZE_WINDOW => [ 'method' => 'POST', 'url' => '/session/:sessionId/window/:windowHandle/maximize', ], DriverCommand::MOUSE_DOWN => ['method' => 'POST', 'url' => '/session/:sessionId/buttondown'], DriverCommand::MOUSE_UP => ['method' => 'POST', 'url' => '/session/:sessionId/buttonup'], DriverCommand::CLICK => ['method' => 'POST', 'url' => '/session/:sessionId/click'], DriverCommand::DOUBLE_CLICK => ['method' => 'POST', 'url' => '/session/:sessionId/doubleclick'], DriverCommand::MOVE_TO => ['method' => 'POST', 'url' => '/session/:sessionId/moveto'], DriverCommand::NEW_SESSION => ['method' => 'POST', 'url' => '/session'], DriverCommand::QUIT => ['method' => 'DELETE', 'url' => '/session/:sessionId'], DriverCommand::REFRESH => ['method' => 'POST', 'url' => '/session/:sessionId/refresh'], DriverCommand::UPLOAD_FILE => ['method' => 'POST', 'url' => '/session/:sessionId/file'], DriverCommand::SEND_KEYS_TO_ACTIVE_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/keys'], DriverCommand::SET_ALERT_VALUE => ['method' => 'POST', 'url' => '/session/:sessionId/alert_text'], DriverCommand::SEND_KEYS_TO_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/value'], DriverCommand::IMPLICITLY_WAIT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts/implicit_wait'], DriverCommand::SET_SCREEN_ORIENTATION => ['method' => 'POST', 'url' => '/session/:sessionId/orientation'], DriverCommand::SET_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'], DriverCommand::SET_SCRIPT_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts/async_script'], DriverCommand::SET_WINDOW_POSITION => [ 'method' => 'POST', 'url' => '/session/:sessionId/window/:windowHandle/position', ], DriverCommand::SET_WINDOW_SIZE => [ 'method' => 'POST', 'url' => '/session/:sessionId/window/:windowHandle/size', ], DriverCommand::STATUS => ['method' => 'GET', 'url' => '/status'], DriverCommand::SUBMIT_ELEMENT => ['method' => 'POST', 'url' => '/session/:sessionId/element/:id/submit'], DriverCommand::SCREENSHOT => ['method' => 'GET', 'url' => '/session/:sessionId/screenshot'], DriverCommand::TAKE_ELEMENT_SCREENSHOT => [ 'method' => 'GET', 'url' => '/session/:sessionId/element/:id/screenshot', ], DriverCommand::TOUCH_SINGLE_TAP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/click'], DriverCommand::TOUCH_DOWN => ['method' => 'POST', 'url' => '/session/:sessionId/touch/down'], DriverCommand::TOUCH_DOUBLE_TAP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/doubleclick'], DriverCommand::TOUCH_FLICK => ['method' => 'POST', 'url' => '/session/:sessionId/touch/flick'], DriverCommand::TOUCH_LONG_PRESS => ['method' => 'POST', 'url' => '/session/:sessionId/touch/longclick'], DriverCommand::TOUCH_MOVE => ['method' => 'POST', 'url' => '/session/:sessionId/touch/move'], DriverCommand::TOUCH_SCROLL => ['method' => 'POST', 'url' => '/session/:sessionId/touch/scroll'], DriverCommand::TOUCH_UP => ['method' => 'POST', 'url' => '/session/:sessionId/touch/up'], DriverCommand::CUSTOM_COMMAND => [], ]; protected static $w3cCompliantCommands = [ DriverCommand::ACCEPT_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/alert/accept'], DriverCommand::ACTIONS => ['method' => 'POST', 'url' => '/session/:sessionId/actions'], DriverCommand::DISMISS_ALERT => ['method' => 'POST', 'url' => '/session/:sessionId/alert/dismiss'], DriverCommand::EXECUTE_ASYNC_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute/async'], DriverCommand::EXECUTE_SCRIPT => ['method' => 'POST', 'url' => '/session/:sessionId/execute/sync'], DriverCommand::FULLSCREEN_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/fullscreen'], DriverCommand::GET_ACTIVE_ELEMENT => ['method' => 'GET', 'url' => '/session/:sessionId/element/active'], DriverCommand::GET_ALERT_TEXT => ['method' => 'GET', 'url' => '/session/:sessionId/alert/text'], DriverCommand::GET_CURRENT_WINDOW_HANDLE => ['method' => 'GET', 'url' => '/session/:sessionId/window'], DriverCommand::GET_ELEMENT_LOCATION => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/rect'], DriverCommand::GET_ELEMENT_PROPERTY => [ 'method' => 'GET', 'url' => '/session/:sessionId/element/:id/property/:name', ], DriverCommand::GET_ELEMENT_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/element/:id/rect'], DriverCommand::GET_WINDOW_HANDLES => ['method' => 'GET', 'url' => '/session/:sessionId/window/handles'], DriverCommand::GET_WINDOW_POSITION => ['method' => 'GET', 'url' => '/session/:sessionId/window/rect'], DriverCommand::GET_WINDOW_SIZE => ['method' => 'GET', 'url' => '/session/:sessionId/window/rect'], DriverCommand::IMPLICITLY_WAIT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'], DriverCommand::MAXIMIZE_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/maximize'], DriverCommand::MINIMIZE_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/minimize'], DriverCommand::NEW_WINDOW => ['method' => 'POST', 'url' => '/session/:sessionId/window/new'], DriverCommand::SET_ALERT_VALUE => ['method' => 'POST', 'url' => '/session/:sessionId/alert/text'], DriverCommand::SET_SCRIPT_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'], DriverCommand::SET_TIMEOUT => ['method' => 'POST', 'url' => '/session/:sessionId/timeouts'], DriverCommand::SET_WINDOW_SIZE => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'], DriverCommand::SET_WINDOW_POSITION => ['method' => 'POST', 'url' => '/session/:sessionId/window/rect'], ]; protected $url; protected $curl; protected $isW3cCompliant = true; public function __construct($url, $http_proxy = null, $http_proxy_port = null) { self::$w3cCompliantCommands = array_merge(self::$commands, self::$w3cCompliantCommands); $this->url = $url; $this->curl = curl_init(); if (!empty($http_proxy)) { curl_setopt($this->curl, CURLOPT_PROXY, $http_proxy); if ($http_proxy_port !== null) { curl_setopt($this->curl, CURLOPT_PROXYPORT, $http_proxy_port); } } $matches = null; if (preg_match("/^(https?:\/\/)(.*):(.*)@(.*?)/U", $url, $matches)) { $this->url = $matches[1] . $matches[4]; $auth_creds = $matches[2] . ':' . $matches[3]; curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_setopt($this->curl, CURLOPT_USERPWD, $auth_creds); } curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true); curl_setopt($this->curl, CURLOPT_HTTPHEADER, static::DEFAULT_HTTP_HEADERS); $this->setRequestTimeout(30000); $this->setConnectionTimeout(30000); } public function disableW3cCompliance() { $this->isW3cCompliant = false; } public function setConnectionTimeout($timeout_in_ms) { curl_setopt( $this->curl, 156, $timeout_in_ms ); return $this; } public function setRequestTimeout($timeout_in_ms) { curl_setopt( $this->curl, 155, $timeout_in_ms ); return $this; } public function execute(WebDriverCommand $command) { $http_options = $this->getCommandHttpOptions($command); $http_method = $http_options['method']; $url = $http_options['url']; $sessionID = $command->getSessionID(); $url = str_replace(':sessionId', $sessionID === null ? '' : $sessionID, $url); $params = $command->getParameters(); foreach ($params as $name => $value) { if ($name[0] === ':') { $url = str_replace($name, $value, $url); unset($params[$name]); } } if (is_array($params) && !empty($params) && $http_method !== 'POST') { throw new BadMethodCallException(sprintf( 'The http method called for %s is %s but it has to be POST' . ' if you want to pass the JSON params %s', $url, $http_method, json_encode($params) )); } curl_setopt($this->curl, CURLOPT_URL, $this->url . $url); if ($command->getName() === DriverCommand::NEW_SESSION) { curl_setopt($this->curl, CURLOPT_POST, 1); } else { curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $http_method); } if (in_array($http_method, ['POST', 'PUT'], true)) { curl_setopt($this->curl, CURLOPT_HTTPHEADER, array_merge(static::DEFAULT_HTTP_HEADERS, ['Expect:'])); } else { curl_setopt($this->curl, CURLOPT_HTTPHEADER, static::DEFAULT_HTTP_HEADERS); } $encoded_params = null; if ($http_method === 'POST') { if (is_array($params) && !empty($params)) { $encoded_params = json_encode($params); } elseif ($this->isW3cCompliant) { $encoded_params = '{}'; } } curl_setopt($this->curl, CURLOPT_POSTFIELDS, $encoded_params); $raw_results = trim(curl_exec($this->curl)); if ($error = curl_error($this->curl)) { $msg = sprintf( 'Curl error thrown for http %s to %s', $http_method, $url ); if (is_array($params) && !empty($params)) { $msg .= sprintf(' with params: %s', json_encode($params, JSON_UNESCAPED_SLASHES)); } throw new WebDriverCurlException($msg . "\n\n" . $error); } $results = json_decode($raw_results, true); if ($results === null && json_last_error() !== JSON_ERROR_NONE) { throw new WebDriverException( sprintf( "JSON decoding of remote response failed.\n" . "Error code: %d\n" . "The response: '%s'\n", json_last_error(), $raw_results ) ); } $value = null; if (is_array($results) && array_key_exists('value', $results)) { $value = $results['value']; } $message = null; if (is_array($value) && array_key_exists('message', $value)) { $message = $value['message']; } $sessionId = null; if (is_array($value) && array_key_exists('sessionId', $value)) { $sessionId = $value['sessionId']; } elseif (is_array($results) && array_key_exists('sessionId', $results)) { $sessionId = $results['sessionId']; } if (isset($value['error'])) { WebDriverException::throwException($value['error'], $message, $results); } $status = isset($results['status']) ? $results['status'] : 0; if ($status !== 0) { WebDriverException::throwException($status, $message, $results); } $response = new WebDriverResponse($sessionId); return $response ->setStatus($status) ->setValue($value); } public function getAddressOfRemoteServer() { return $this->url; } protected function getCommandHttpOptions(WebDriverCommand $command) { $commandName = $command->getName(); if (!isset(self::$commands[$commandName])) { if ($this->isW3cCompliant && !isset(self::$w3cCompliantCommands[$commandName])) { throw new InvalidArgumentException($command->getName() . ' is not a valid command.'); } } if ($this->isW3cCompliant) { $raw = self::$w3cCompliantCommands[$command->getName()]; } else { $raw = self::$commands[$command->getName()]; } if ($command instanceof CustomWebDriverCommand) { $url = $command->getCustomUrl(); $method = $command->getCustomMethod(); } else { $url = $raw['url']; $method = $raw['method']; } return [ 'url' => $url, 'method' => $method, ]; } } executor = $commandExecutor; $this->sessionID = $sessionId; $this->isW3cCompliant = $isW3cCompliant; if ($capabilities !== null) { $this->capabilities = $capabilities; } } public static function create( $selenium_server_url = 'http://localhost:4444/wd/hub', $desired_capabilities = null, $connection_timeout_in_ms = null, $request_timeout_in_ms = null, $http_proxy = null, $http_proxy_port = null, DesiredCapabilities $required_capabilities = null ) { $selenium_server_url = preg_replace('#/+$#', '', $selenium_server_url); $desired_capabilities = self::castToDesiredCapabilitiesObject($desired_capabilities); $executor = new HttpCommandExecutor($selenium_server_url, $http_proxy, $http_proxy_port); if ($connection_timeout_in_ms !== null) { $executor->setConnectionTimeout($connection_timeout_in_ms); } if ($request_timeout_in_ms !== null) { $executor->setRequestTimeout($request_timeout_in_ms); } $parameters = [ 'capabilities' => [ 'firstMatch' => [(object) $desired_capabilities->toW3cCompatibleArray()], ], ]; if ($required_capabilities !== null && !empty($required_capabilities->toArray())) { $parameters['capabilities']['alwaysMatch'] = (object) $required_capabilities->toW3cCompatibleArray(); } if ($required_capabilities !== null) { $desired_capabilities->setCapability('requiredCapabilities', (object) $required_capabilities->toArray()); } $parameters['desiredCapabilities'] = (object) $desired_capabilities->toArray(); $command = WebDriverCommand::newSession($parameters); $response = $executor->execute($command); return static::createFromResponse($response, $executor); } public static function createBySessionID( $session_id, $selenium_server_url = 'http://localhost:4444/wd/hub', $connection_timeout_in_ms = null, $request_timeout_in_ms = null ) { $isW3cCompliant = func_num_args() > 4 ? func_get_arg(4) : true; $executor = new HttpCommandExecutor($selenium_server_url, null, null); if ($connection_timeout_in_ms !== null) { $executor->setConnectionTimeout($connection_timeout_in_ms); } if ($request_timeout_in_ms !== null) { $executor->setRequestTimeout($request_timeout_in_ms); } if (!$isW3cCompliant) { $executor->disableW3cCompliance(); } return new static($executor, $session_id, null, $isW3cCompliant); } public function close() { $this->execute(DriverCommand::CLOSE, []); return $this; } public function newWindow() { return $this->switchTo()->newWindow(); } public function findElement(WebDriverBy $by) { $raw_element = $this->execute( DriverCommand::FIND_ELEMENT, JsonWireCompat::getUsing($by, $this->isW3cCompliant) ); return $this->newElement(JsonWireCompat::getElement($raw_element)); } public function findElements(WebDriverBy $by) { $raw_elements = $this->execute( DriverCommand::FIND_ELEMENTS, JsonWireCompat::getUsing($by, $this->isW3cCompliant) ); $elements = []; foreach ($raw_elements as $raw_element) { $elements[] = $this->newElement(JsonWireCompat::getElement($raw_element)); } return $elements; } public function get($url) { $params = ['url' => (string) $url]; $this->execute(DriverCommand::GET, $params); return $this; } public function getCurrentURL() { return $this->execute(DriverCommand::GET_CURRENT_URL); } public function getPageSource() { return $this->execute(DriverCommand::GET_PAGE_SOURCE); } public function getTitle() { return $this->execute(DriverCommand::GET_TITLE); } public function getWindowHandle() { return $this->execute( DriverCommand::GET_CURRENT_WINDOW_HANDLE, [] ); } public function getWindowHandles() { return $this->execute(DriverCommand::GET_WINDOW_HANDLES, []); } public function quit() { $this->execute(DriverCommand::QUIT); $this->executor = null; } public function executeScript($script, array $arguments = []) { $params = [ 'script' => $script, 'args' => $this->prepareScriptArguments($arguments), ]; return $this->execute(DriverCommand::EXECUTE_SCRIPT, $params); } public function executeAsyncScript($script, array $arguments = []) { $params = [ 'script' => $script, 'args' => $this->prepareScriptArguments($arguments), ]; return $this->execute( DriverCommand::EXECUTE_ASYNC_SCRIPT, $params ); } public function takeScreenshot($save_as = null) { $screenshot = base64_decode($this->execute(DriverCommand::SCREENSHOT), true); if ($save_as !== null) { $directoryPath = dirname($save_as); if (!file_exists($directoryPath)) { mkdir($directoryPath, 0777, true); } file_put_contents($save_as, $screenshot); } return $screenshot; } public function getStatus() { $response = $this->execute(DriverCommand::STATUS); return RemoteStatus::createFromResponse($response); } public function wait($timeout_in_second = 30, $interval_in_millisecond = 250) { return new WebDriverWait( $this, $timeout_in_second, $interval_in_millisecond ); } public function manage() { return new WebDriverOptions($this->getExecuteMethod(), $this->isW3cCompliant); } public function navigate() { return new WebDriverNavigation($this->getExecuteMethod()); } public function switchTo() { return new RemoteTargetLocator($this->getExecuteMethod(), $this, $this->isW3cCompliant); } public function getMouse() { if (!$this->mouse) { $this->mouse = new RemoteMouse($this->getExecuteMethod(), $this->isW3cCompliant); } return $this->mouse; } public function getKeyboard() { if (!$this->keyboard) { $this->keyboard = new RemoteKeyboard($this->getExecuteMethod(), $this, $this->isW3cCompliant); } return $this->keyboard; } public function getTouch() { if (!$this->touch) { $this->touch = new RemoteTouchScreen($this->getExecuteMethod()); } return $this->touch; } public function action() { return new WebDriverActions($this); } public function setCommandExecutor(WebDriverCommandExecutor $executor) { $this->executor = $executor; return $this; } public function getCommandExecutor() { return $this->executor; } public function setSessionID($session_id) { $this->sessionID = $session_id; return $this; } public function getSessionID() { return $this->sessionID; } public function getCapabilities() { return $this->capabilities; } public static function getAllSessions($selenium_server_url = 'http://localhost:4444/wd/hub', $timeout_in_ms = 30000) { $executor = new HttpCommandExecutor($selenium_server_url, null, null); $executor->setConnectionTimeout($timeout_in_ms); $command = new WebDriverCommand( null, DriverCommand::GET_ALL_SESSIONS, [] ); return $executor->execute($command)->getValue(); } public function execute($command_name, $params = []) { $command = new WebDriverCommand( $this->sessionID, $command_name, $params ); if ($this->executor) { $response = $this->executor->execute($command); return $response->getValue(); } return null; } public function executeCustomCommand($endpointUrl, $method = 'GET', $params = []) { $command = new CustomWebDriverCommand( $this->sessionID, $endpointUrl, $method, $params ); if ($this->executor) { $response = $this->executor->execute($command); return $response->getValue(); } return null; } public function isW3cCompliant() { return $this->isW3cCompliant; } protected static function createFromResponse(WebDriverResponse $response, HttpCommandExecutor $commandExecutor) { $responseValue = $response->getValue(); if (!$isW3cCompliant = isset($responseValue['capabilities'])) { $commandExecutor->disableW3cCompliance(); } if ($isW3cCompliant) { $returnedCapabilities = DesiredCapabilities::createFromW3cCapabilities($responseValue['capabilities']); } else { $returnedCapabilities = new DesiredCapabilities($responseValue); } return new static($commandExecutor, $response->getSessionID(), $returnedCapabilities, $isW3cCompliant); } protected function prepareScriptArguments(array $arguments) { $args = []; foreach ($arguments as $key => $value) { if ($value instanceof WebDriverElement) { $args[$key] = [ $this->isW3cCompliant ? JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER : 'ELEMENT' => $value->getID(), ]; } else { if (is_array($value)) { $value = $this->prepareScriptArguments($value); } $args[$key] = $value; } } return $args; } protected function getExecuteMethod() { if (!$this->executeMethod) { $this->executeMethod = new RemoteExecuteMethod($this); } return $this->executeMethod; } protected function newElement($id) { return new RemoteWebElement($this->getExecuteMethod(), $id, $this->isW3cCompliant); } protected static function castToDesiredCapabilitiesObject($desired_capabilities = null) { if ($desired_capabilities === null) { return new DesiredCapabilities(); } if (is_array($desired_capabilities)) { return new DesiredCapabilities($desired_capabilities); } return $desired_capabilities; } } executor = $executor; $this->isW3cCompliant = $isW3cCompliant; } public function click(WebDriverCoordinates $where = null) { if ($this->isW3cCompliant) { $moveAction = $where ? [$this->createMoveAction($where)] : []; $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'pointer', 'id' => 'mouse', 'parameters' => ['pointerType' => 'mouse'], 'actions' => array_merge($moveAction, $this->createClickActions()), ], ], ]); return $this; } $this->moveIfNeeded($where); $this->executor->execute(DriverCommand::CLICK, [ 'button' => self::BUTTON_LEFT, ]); return $this; } public function contextClick(WebDriverCoordinates $where = null) { if ($this->isW3cCompliant) { $moveAction = $where ? [$this->createMoveAction($where)] : []; $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'pointer', 'id' => 'mouse', 'parameters' => ['pointerType' => 'mouse'], 'actions' => array_merge($moveAction, [ [ 'type' => 'pointerDown', 'button' => self::BUTTON_RIGHT, ], [ 'type' => 'pointerUp', 'button' => self::BUTTON_RIGHT, ], ]), ], ], ]); return $this; } $this->moveIfNeeded($where); $this->executor->execute(DriverCommand::CLICK, [ 'button' => self::BUTTON_RIGHT, ]); return $this; } public function doubleClick(WebDriverCoordinates $where = null) { if ($this->isW3cCompliant) { $clickActions = $this->createClickActions(); $moveAction = $where === null ? [] : [$this->createMoveAction($where)]; $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'pointer', 'id' => 'mouse', 'parameters' => ['pointerType' => 'mouse'], 'actions' => array_merge($moveAction, $clickActions, $clickActions), ], ], ]); return $this; } $this->moveIfNeeded($where); $this->executor->execute(DriverCommand::DOUBLE_CLICK); return $this; } public function mouseDown(WebDriverCoordinates $where = null) { if ($this->isW3cCompliant) { $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'pointer', 'id' => 'mouse', 'parameters' => ['pointerType' => 'mouse'], 'actions' => [ $this->createMoveAction($where), [ 'type' => 'pointerDown', 'button' => self::BUTTON_LEFT, ], ], ], ], ]); return $this; } $this->moveIfNeeded($where); $this->executor->execute(DriverCommand::MOUSE_DOWN); return $this; } public function mouseMove( WebDriverCoordinates $where = null, $x_offset = null, $y_offset = null ) { if ($this->isW3cCompliant) { $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'pointer', 'id' => 'mouse', 'parameters' => ['pointerType' => 'mouse'], 'actions' => [$this->createMoveAction($where, $x_offset, $y_offset)], ], ], ]); return $this; } $params = []; if ($where !== null) { $params['element'] = $where->getAuxiliary(); } if ($x_offset !== null) { $params['xoffset'] = $x_offset; } if ($y_offset !== null) { $params['yoffset'] = $y_offset; } $this->executor->execute(DriverCommand::MOVE_TO, $params); return $this; } public function mouseUp(WebDriverCoordinates $where = null) { if ($this->isW3cCompliant) { $moveAction = $where ? [$this->createMoveAction($where)] : []; $this->executor->execute(DriverCommand::ACTIONS, [ 'actions' => [ [ 'type' => 'pointer', 'id' => 'mouse', 'parameters' => ['pointerType' => 'mouse'], 'actions' => array_merge($moveAction, [ [ 'type' => 'pointerUp', 'button' => self::BUTTON_LEFT, ], ]), ], ], ]); return $this; } $this->moveIfNeeded($where); $this->executor->execute(DriverCommand::MOUSE_UP); return $this; } protected function moveIfNeeded(WebDriverCoordinates $where = null) { if ($where) { $this->mouseMove($where); } } private function createMoveAction( WebDriverCoordinates $where = null, $x_offset = null, $y_offset = null ) { $move_action = [ 'type' => 'pointerMove', 'duration' => 100, 'x' => $x_offset === null ? 0 : $x_offset, 'y' => $y_offset === null ? 0 : $y_offset, ]; if ($where !== null) { $move_action['origin'] = [JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $where->getAuxiliary()]; } else { $move_action['origin'] = 'pointer'; } return $move_action; } private function createClickActions() { return [ [ 'type' => 'pointerDown', 'button' => self::BUTTON_LEFT, ], [ 'type' => 'pointerUp', 'button' => self::BUTTON_LEFT, ], ]; } } isReady = (bool) $isReady; $this->message = (string) $message; $this->setMeta($meta); } public static function createFromResponse(array $responseBody) { $object = new static($responseBody['ready'], $responseBody['message'], $responseBody); return $object; } public function isReady() { return $this->isReady; } public function getMessage() { return $this->message; } public function getMeta() { return $this->meta; } protected function setMeta(array $meta) { unset($meta['ready'], $meta['message']); $this->meta = $meta; } } getMechanism(); $value = $by->getValue(); if ($isW3cCompliant) { switch ($mechanism) { case 'class name': $mechanism = 'css selector'; $value = sprintf('.%s', self::escapeSelector($value)); break; case 'id': $mechanism = 'css selector'; $value = sprintf('#%s', self::escapeSelector($value)); break; case 'name': $mechanism = 'css selector'; $value = sprintf('[name=\'%s\']', self::escapeSelector($value)); break; } } return ['using' => $mechanism, 'value' => $value]; } private static function escapeSelector($selector) { return preg_replace_callback('/[^a-z0-9]/iSu', function ($matches) { $chr = $matches[0]; if (mb_strlen($chr) === 1) { $ord = ord($chr); } else { $chr = mb_convert_encoding($chr, 'UTF-32BE', 'UTF-8'); $ord = hexdec(bin2hex($chr)); } return sprintf('\\%X ', $ord); }, $selector); } } sessionID = $session_id; } public function getStatus() { return $this->status; } public function setStatus($status) { $this->status = $status; return $this; } public function getValue() { return $this->value; } public function setValue($value) { $this->value = $value; return $this; } public function getSessionID() { return $this->sessionID; } public function setSessionID($session_id) { $this->sessionID = $session_id; return $this; } } 'platformName', WebDriverCapabilityType::VERSION => 'browserVersion', WebDriverCapabilityType::ACCEPT_SSL_CERTS => 'acceptInsecureCerts', ChromeOptions::CAPABILITY => ChromeOptions::CAPABILITY_W3C, ]; public function __construct(array $capabilities = []) { $this->capabilities = $capabilities; } public static function createFromW3cCapabilities(array $capabilities = []) { $w3cToOss = array_flip(self::$ossToW3c); foreach ($w3cToOss as $w3cCapability => $ossCapability) { if (array_key_exists($w3cCapability, $capabilities)) { $capabilities[$ossCapability] = $capabilities[$w3cCapability]; } } return new self($capabilities); } public function getBrowserName() { return $this->get(WebDriverCapabilityType::BROWSER_NAME, ''); } public function setBrowserName($browser_name) { $this->set(WebDriverCapabilityType::BROWSER_NAME, $browser_name); return $this; } public function getVersion() { return $this->get(WebDriverCapabilityType::VERSION, ''); } public function setVersion($version) { $this->set(WebDriverCapabilityType::VERSION, $version); return $this; } public function getCapability($name) { return $this->get($name); } public function setCapability($name, $value) { if ($name === FirefoxOptions::CAPABILITY && is_array($value)) { $defaultOptions = (new FirefoxOptions())->toArray(); $value = array_merge($defaultOptions, $value); } $this->set($name, $value); return $this; } public function getPlatform() { return $this->get(WebDriverCapabilityType::PLATFORM, ''); } public function setPlatform($platform) { $this->set(WebDriverCapabilityType::PLATFORM, $platform); return $this; } public function is($capability_name) { return (bool) $this->get($capability_name); } public function isJavascriptEnabled() { return $this->get(WebDriverCapabilityType::JAVASCRIPT_ENABLED, false); } public function setJavascriptEnabled($enabled) { $browser = $this->getBrowserName(); if ($browser && $browser !== WebDriverBrowserType::HTMLUNIT) { throw new Exception( 'isJavascriptEnabled() is a htmlunit-only option. ' . 'See https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#read-write-capabilities.' ); } $this->set(WebDriverCapabilityType::JAVASCRIPT_ENABLED, $enabled); return $this; } public function toArray() { if (isset($this->capabilities[ChromeOptions::CAPABILITY]) && $this->capabilities[ChromeOptions::CAPABILITY] instanceof ChromeOptions ) { $this->capabilities[ChromeOptions::CAPABILITY] = $this->capabilities[ChromeOptions::CAPABILITY]->toArray(); } if (isset($this->capabilities[FirefoxOptions::CAPABILITY]) && $this->capabilities[FirefoxOptions::CAPABILITY] instanceof FirefoxOptions ) { $this->capabilities[FirefoxOptions::CAPABILITY] = $this->capabilities[FirefoxOptions::CAPABILITY]->toArray(); } if (isset($this->capabilities[FirefoxDriver::PROFILE]) && $this->capabilities[FirefoxDriver::PROFILE] instanceof FirefoxProfile ) { $this->capabilities[FirefoxDriver::PROFILE] = $this->capabilities[FirefoxDriver::PROFILE]->encode(); } return $this->capabilities; } public function toW3cCompatibleArray() { $allowedW3cCapabilities = [ 'browserName', 'browserVersion', 'platformName', 'acceptInsecureCerts', 'pageLoadStrategy', 'proxy', 'setWindowRect', 'timeouts', 'strictFileInteractability', 'unhandledPromptBehavior', ]; $ossCapabilities = $this->toArray(); $w3cCapabilities = []; foreach ($ossCapabilities as $capabilityKey => $capabilityValue) { if (in_array($capabilityKey, $allowedW3cCapabilities, true)) { $w3cCapabilities[$capabilityKey] = $capabilityValue; } if (array_key_exists($capabilityKey, self::$ossToW3c)) { if ($capabilityKey === WebDriverCapabilityType::PLATFORM) { $w3cCapabilities[self::$ossToW3c[$capabilityKey]] = mb_strtolower($capabilityValue); if ($w3cCapabilities[self::$ossToW3c[$capabilityKey]] === 'any') { unset($w3cCapabilities[self::$ossToW3c[$capabilityKey]]); } } else { $w3cCapabilities[self::$ossToW3c[$capabilityKey]] = $capabilityValue; } } if (mb_strpos($capabilityKey, ':') !== false) { $w3cCapabilities[$capabilityKey] = $capabilityValue; } } if (array_key_exists(ChromeOptions::CAPABILITY, $ossCapabilities)) { if (array_key_exists(ChromeOptions::CAPABILITY_W3C, $ossCapabilities)) { $w3cCapabilities[ChromeOptions::CAPABILITY_W3C] = new \ArrayObject( array_merge_recursive( (array) $ossCapabilities[ChromeOptions::CAPABILITY], (array) $ossCapabilities[ChromeOptions::CAPABILITY_W3C] ) ); } else { $w3cCapabilities[ChromeOptions::CAPABILITY_W3C] = $ossCapabilities[ChromeOptions::CAPABILITY]; } } if (array_key_exists(FirefoxDriver::PROFILE, $ossCapabilities)) { if (!array_key_exists(FirefoxOptions::CAPABILITY, $ossCapabilities) || !array_key_exists('profile', $ossCapabilities[FirefoxOptions::CAPABILITY])) { $w3cCapabilities[FirefoxOptions::CAPABILITY]['profile'] = $ossCapabilities[FirefoxDriver::PROFILE]; } } return $w3cCapabilities; } public static function android() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::ANDROID, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANDROID, ]); } public static function chrome() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::CHROME, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, ]); } public static function firefox() { $caps = new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::FIREFOX, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, ]); $caps->setCapability(FirefoxOptions::CAPABILITY, new FirefoxOptions()); return $caps; } public static function htmlUnit() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::HTMLUNIT, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, ]); } public static function htmlUnitWithJS() { $caps = new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::HTMLUNIT, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, ]); return $caps->setJavascriptEnabled(true); } public static function internetExplorer() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IE, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::WINDOWS, ]); } public static function microsoftEdge() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::MICROSOFT_EDGE, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::WINDOWS, ]); } public static function iphone() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IPHONE, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::MAC, ]); } public static function ipad() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IPAD, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::MAC, ]); } public static function opera() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::OPERA, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, ]); } public static function safari() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::SAFARI, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, ]); } public static function phantomjs() { return new static([ WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::PHANTOMJS, WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, ]); } private function set($key, $value) { $this->capabilities[$key] = $value; return $this; } private function get($key, $default = null) { return isset($this->capabilities[$key]) ? $this->capabilities[$key] : $default; } } apply = $apply; } public function getApply() { return $this->apply; } public static function titleIs($title) { return new static( function (WebDriver $driver) use ($title) { return $title === $driver->getTitle(); } ); } public static function titleContains($title) { return new static( function (WebDriver $driver) use ($title) { return mb_strpos($driver->getTitle(), $title) !== false; } ); } public static function titleMatches($titleRegexp) { return new static( function (WebDriver $driver) use ($titleRegexp) { return (bool) preg_match($titleRegexp, $driver->getTitle()); } ); } public static function urlIs($url) { return new static( function (WebDriver $driver) use ($url) { return $url === $driver->getCurrentURL(); } ); } public static function urlContains($url) { return new static( function (WebDriver $driver) use ($url) { return mb_strpos($driver->getCurrentURL(), $url) !== false; } ); } public static function urlMatches($urlRegexp) { return new static( function (WebDriver $driver) use ($urlRegexp) { return (bool) preg_match($urlRegexp, $driver->getCurrentURL()); } ); } public static function presenceOfElementLocated(WebDriverBy $by) { return new static( function (WebDriver $driver) use ($by) { try { return $driver->findElement($by); } catch (NoSuchElementException $e) { return false; } } ); } public static function presenceOfAllElementsLocatedBy(WebDriverBy $by) { return new static( function (WebDriver $driver) use ($by) { $elements = $driver->findElements($by); return count($elements) > 0 ? $elements : null; } ); } public static function visibilityOfElementLocated(WebDriverBy $by) { return new static( function (WebDriver $driver) use ($by) { try { $element = $driver->findElement($by); return $element->isDisplayed() ? $element : null; } catch (StaleElementReferenceException $e) { return null; } } ); } public static function visibilityOfAnyElementLocated(WebDriverBy $by) { return new static( function (WebDriver $driver) use ($by) { $elements = $driver->findElements($by); $visibleElements = []; foreach ($elements as $element) { try { if ($element->isDisplayed()) { $visibleElements[] = $element; } } catch (StaleElementReferenceException $e) { } } return count($visibleElements) > 0 ? $visibleElements : null; } ); } public static function visibilityOf(WebDriverElement $element) { return new static( function () use ($element) { return $element->isDisplayed() ? $element : null; } ); } public static function textToBePresentInElement(WebDriverBy $by, $text) { return self::elementTextContains($by, $text); } public static function elementTextContains(WebDriverBy $by, $text) { return new static( function (WebDriver $driver) use ($by, $text) { try { $element_text = $driver->findElement($by)->getText(); return mb_strpos($element_text, $text) !== false; } catch (StaleElementReferenceException $e) { return null; } } ); } public static function elementTextIs(WebDriverBy $by, $text) { return new static( function (WebDriver $driver) use ($by, $text) { try { return $driver->findElement($by)->getText() == $text; } catch (StaleElementReferenceException $e) { return null; } } ); } public static function elementTextMatches(WebDriverBy $by, $regexp) { return new static( function (WebDriver $driver) use ($by, $regexp) { try { return (bool) preg_match($regexp, $driver->findElement($by)->getText()); } catch (StaleElementReferenceException $e) { return null; } } ); } public static function textToBePresentInElementValue(WebDriverBy $by, $text) { return self::elementValueContains($by, $text); } public static function elementValueContains(WebDriverBy $by, $text) { return new static( function (WebDriver $driver) use ($by, $text) { try { $element_text = $driver->findElement($by)->getAttribute('value'); return mb_strpos($element_text, $text) !== false; } catch (StaleElementReferenceException $e) { return null; } } ); } public static function frameToBeAvailableAndSwitchToIt($frame_locator) { return new static( function (WebDriver $driver) use ($frame_locator) { try { return $driver->switchTo()->frame($frame_locator); } catch (NoSuchFrameException $e) { return false; } } ); } public static function invisibilityOfElementLocated(WebDriverBy $by) { return new static( function (WebDriver $driver) use ($by) { try { return !$driver->findElement($by)->isDisplayed(); } catch (NoSuchElementException $e) { return true; } catch (StaleElementReferenceException $e) { return true; } } ); } public static function invisibilityOfElementWithText(WebDriverBy $by, $text) { return new static( function (WebDriver $driver) use ($by, $text) { try { return !($driver->findElement($by)->getText() === $text); } catch (NoSuchElementException $e) { return true; } catch (StaleElementReferenceException $e) { return true; } } ); } public static function elementToBeClickable(WebDriverBy $by) { $visibility_of_element_located = self::visibilityOfElementLocated($by); return new static( function (WebDriver $driver) use ($visibility_of_element_located) { $element = call_user_func( $visibility_of_element_located->getApply(), $driver ); try { if ($element !== null && $element->isEnabled()) { return $element; } return null; } catch (StaleElementReferenceException $e) { return null; } } ); } public static function stalenessOf(WebDriverElement $element) { return new static( function () use ($element) { try { $element->isEnabled(); return false; } catch (StaleElementReferenceException $e) { return true; } } ); } public static function refreshed(self $condition) { return new static( function (WebDriver $driver) use ($condition) { try { return call_user_func($condition->getApply(), $driver); } catch (StaleElementReferenceException $e) { return null; } } ); } public static function elementToBeSelected($element_or_by) { return self::elementSelectionStateToBe( $element_or_by, true ); } public static function elementSelectionStateToBe($element_or_by, $selected) { if ($element_or_by instanceof WebDriverElement) { return new static( function () use ($element_or_by, $selected) { return $element_or_by->isSelected() === $selected; } ); } if ($element_or_by instanceof WebDriverBy) { return new static( function (WebDriver $driver) use ($element_or_by, $selected) { try { $element = $driver->findElement($element_or_by); return $element->isSelected() === $selected; } catch (StaleElementReferenceException $e) { return null; } } ); } throw new \InvalidArgumentException('Instance of either WebDriverElement or WebDriverBy must be given'); } public static function alertIsPresent() { return new static( function (WebDriver $driver) { try { $alert = $driver->switchTo()->alert(); $alert->getText(); return $alert; } catch (NoSuchAlertException $e) { return null; } } ); } public static function numberOfWindowsToBe($expectedNumberOfWindows) { return new static( function (WebDriver $driver) use ($expectedNumberOfWindows) { return count($driver->getWindowHandles()) == $expectedNumberOfWindows; } ); } public static function not(self $condition) { return new static( function (WebDriver $driver) use ($condition) { $result = call_user_func($condition->getApply(), $driver); return !$result; } ); } } executor = $executor; $this->isW3cCompliant = $isW3cCompliant; } public function getPosition() { $position = $this->executor->execute( DriverCommand::GET_WINDOW_POSITION, [':windowHandle' => 'current'] ); return new WebDriverPoint( $position['x'], $position['y'] ); } public function getSize() { $size = $this->executor->execute( DriverCommand::GET_WINDOW_SIZE, [':windowHandle' => 'current'] ); return new WebDriverDimension( $size['width'], $size['height'] ); } public function minimize() { if (!$this->isW3cCompliant) { throw new UnsupportedOperationException('Minimize window is only supported in W3C mode'); } $this->executor->execute(DriverCommand::MINIMIZE_WINDOW, []); return $this; } public function maximize() { if ($this->isW3cCompliant) { $this->executor->execute(DriverCommand::MAXIMIZE_WINDOW, []); } else { $this->executor->execute( DriverCommand::MAXIMIZE_WINDOW, [':windowHandle' => 'current'] ); } return $this; } public function fullscreen() { if (!$this->isW3cCompliant) { throw new UnsupportedOperationException('The Fullscreen window command is only supported in W3C mode'); } $this->executor->execute(DriverCommand::FULLSCREEN_WINDOW, []); return $this; } public function setSize(WebDriverDimension $size) { $params = [ 'width' => $size->getWidth(), 'height' => $size->getHeight(), ':windowHandle' => 'current', ]; $this->executor->execute(DriverCommand::SET_WINDOW_SIZE, $params); return $this; } public function setPosition(WebDriverPoint $position) { $params = [ 'x' => $position->getX(), 'y' => $position->getY(), ':windowHandle' => 'current', ]; $this->executor->execute(DriverCommand::SET_WINDOW_POSITION, $params); return $this; } public function getScreenOrientation() { return $this->executor->execute(DriverCommand::GET_SCREEN_ORIENTATION); } public function setScreenOrientation($orientation) { $orientation = mb_strtoupper($orientation); if (!in_array($orientation, ['PORTRAIT', 'LANDSCAPE'], true)) { throw new IndexOutOfBoundsException( 'Orientation must be either PORTRAIT, or LANDSCAPE' ); } $this->executor->execute( DriverCommand::SET_SCREEN_ORIENTATION, ['orientation' => $orientation] ); return $this; } } toArray(); } public function setBinary($path) { $this->binary = $path; return $this; } public function addArguments(array $arguments) { $this->arguments = array_merge($this->arguments, $arguments); return $this; } public function addExtensions(array $paths) { foreach ($paths as $path) { $this->addExtension($path); } return $this; } public function addEncodedExtensions(array $encoded_extensions) { foreach ($encoded_extensions as $encoded_extension) { $this->addEncodedExtension($encoded_extension); } return $this; } public function setExperimentalOption($name, $value) { $this->experimentalOptions[$name] = $value; return $this; } public function toCapabilities() { $capabilities = DesiredCapabilities::chrome(); $capabilities->setCapability(self::CAPABILITY, $this); return $capabilities; } public function toArray() { $options = new \ArrayObject($this->experimentalOptions); if (!empty($this->binary)) { $options['binary'] = $this->binary; } if (!empty($this->arguments)) { $options['args'] = $this->arguments; } if (!empty($this->extensions)) { $options['extensions'] = $this->extensions; } return $options; } private function addExtension($path) { $this->addEncodedExtension(base64_encode(file_get_contents($path))); return $this; } private function addEncodedExtension($encoded_extension) { $this->extensions[] = $encoded_extension; return $this; } } 'POST', 'url' => '/session/:sessionId/goog/cdp/execute', ]; private $driver; public function __construct(RemoteWebDriver $driver) { $this->driver = $driver; } public function execute($command, array $parameters = []) { $params = ['cmd' => $command, 'params' => (object) $parameters]; return $this->driver->executeCustomCommand( self::SEND_COMMAND['url'], self::SEND_COMMAND['method'], $params ); } } [ 'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()], ], 'desiredCapabilities' => (object) $capabilities->toArray(), ] ); $response = $executor->execute($newSessionCommand); return static::createFromResponse($response, $executor); } public function startSession(DesiredCapabilities $desired_capabilities) { $command = WebDriverCommand::newSession( [ 'capabilities' => [ 'firstMatch' => [(object) $desired_capabilities->toW3cCompatibleArray()], ], 'desiredCapabilities' => (object) $desired_capabilities->toArray(), ] ); $response = $this->executor->execute($command); $value = $response->getValue(); if (!$this->isW3cCompliant = isset($value['capabilities'])) { $this->executor->disableW3cCompliance(); } $this->sessionID = $response->getSessionID(); } public function getDevTools() { if ($this->devTools === null) { $this->devTools = new ChromeDevToolsDriver($this); } return $this->devTools; } } dispatcher = $dispatcher ?: new WebDriverDispatcher(); if (!$this->dispatcher->getDefaultDriver()) { $this->dispatcher->setDefaultDriver($this); } $this->driver = $driver; } public function getDispatcher() { return $this->dispatcher; } public function getWebDriver() { return $this->driver; } public function get($url) { $this->dispatch('beforeNavigateTo', $url, $this); try { $this->driver->get($url); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch('afterNavigateTo', $url, $this); return $this; } public function findElements(WebDriverBy $by) { $this->dispatch('beforeFindBy', $by, null, $this); $elements = []; try { foreach ($this->driver->findElements($by) as $element) { $elements[] = $this->newElement($element); } } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch('afterFindBy', $by, null, $this); return $elements; } public function findElement(WebDriverBy $by) { $this->dispatch('beforeFindBy', $by, null, $this); try { $element = $this->newElement($this->driver->findElement($by)); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch('afterFindBy', $by, null, $this); return $element; } public function executeScript($script, array $arguments = []) { if (!$this->driver instanceof JavaScriptExecutor) { throw new UnsupportedOperationException( 'driver does not implement JavaScriptExecutor' ); } $this->dispatch('beforeScript', $script, $this); try { $result = $this->driver->executeScript($script, $arguments); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch('afterScript', $script, $this); return $result; } public function executeAsyncScript($script, array $arguments = []) { if (!$this->driver instanceof JavaScriptExecutor) { throw new UnsupportedOperationException( 'driver does not implement JavaScriptExecutor' ); } $this->dispatch('beforeScript', $script, $this); try { $result = $this->driver->executeAsyncScript($script, $arguments); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch('afterScript', $script, $this); return $result; } public function close() { try { $this->driver->close(); return $this; } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getCurrentURL() { try { return $this->driver->getCurrentURL(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getPageSource() { try { return $this->driver->getPageSource(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getTitle() { try { return $this->driver->getTitle(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getWindowHandle() { try { return $this->driver->getWindowHandle(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getWindowHandles() { try { return $this->driver->getWindowHandles(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function quit() { try { $this->driver->quit(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function takeScreenshot($save_as = null) { try { return $this->driver->takeScreenshot($save_as); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function wait($timeout_in_second = 30, $interval_in_millisecond = 250) { try { return $this->driver->wait($timeout_in_second, $interval_in_millisecond); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function manage() { try { return $this->driver->manage(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function navigate() { try { return new EventFiringWebDriverNavigation( $this->driver->navigate(), $this->getDispatcher() ); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function switchTo() { try { return $this->driver->switchTo(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getTouch() { try { return $this->driver->getTouch(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function execute($name, $params) { try { return $this->driver->execute($name, $params); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } protected function newElement(WebDriverElement $element) { return new EventFiringWebElement($element, $this->getDispatcher()); } protected function dispatch($method, ...$arguments) { if (!$this->dispatcher) { return; } $this->dispatcher->dispatch($method, $arguments); } protected function dispatchOnException(WebDriverException $exception) { $this->dispatch('onException', $exception, $this); } } element = $element; $this->dispatcher = $dispatcher; } public function getDispatcher() { return $this->dispatcher; } public function getElement() { return $this->element; } public function sendKeys($value) { $this->dispatch('beforeChangeValueOf', $this); try { $this->element->sendKeys($value); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch('afterChangeValueOf', $this); return $this; } public function click() { $this->dispatch('beforeClickOn', $this); try { $this->element->click(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch('afterClickOn', $this); return $this; } public function findElement(WebDriverBy $by) { $this->dispatch( 'beforeFindBy', $by, $this, $this->dispatcher->getDefaultDriver() ); try { $element = $this->newElement($this->element->findElement($by)); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch( 'afterFindBy', $by, $this, $this->dispatcher->getDefaultDriver() ); return $element; } public function findElements(WebDriverBy $by) { $this->dispatch( 'beforeFindBy', $by, $this, $this->dispatcher->getDefaultDriver() ); try { $elements = []; foreach ($this->element->findElements($by) as $element) { $elements[] = $this->newElement($element); } } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch( 'afterFindBy', $by, $this, $this->dispatcher->getDefaultDriver() ); return $elements; } public function clear() { try { $this->element->clear(); return $this; } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getAttribute($attribute_name) { try { return $this->element->getAttribute($attribute_name); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getCSSValue($css_property_name) { try { return $this->element->getCSSValue($css_property_name); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getLocation() { try { return $this->element->getLocation(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getLocationOnScreenOnceScrolledIntoView() { try { return $this->element->getLocationOnScreenOnceScrolledIntoView(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getCoordinates() { try { return $this->element->getCoordinates(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getSize() { try { return $this->element->getSize(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getTagName() { try { return $this->element->getTagName(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getText() { try { return $this->element->getText(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function isDisplayed() { try { return $this->element->isDisplayed(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function isEnabled() { try { return $this->element->isEnabled(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function isSelected() { try { return $this->element->isSelected(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function submit() { try { $this->element->submit(); return $this; } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function getID() { try { return $this->element->getID(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function equals(WebDriverElement $other) { try { return $this->element->equals($other); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } protected function dispatchOnException(WebDriverException $exception) { $this->dispatch( 'onException', $exception, $this->dispatcher->getDefaultDriver() ); } protected function dispatch($method, ...$arguments) { if (!$this->dispatcher) { return; } $this->dispatcher->dispatch($method, $arguments); } protected function newElement(WebDriverElement $element) { return new static($element, $this->getDispatcher()); } } navigator = $navigator; $this->dispatcher = $dispatcher; } public function getDispatcher() { return $this->dispatcher; } public function getNavigator() { return $this->navigator; } public function back() { $this->dispatch( 'beforeNavigateBack', $this->getDispatcher()->getDefaultDriver() ); try { $this->navigator->back(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); } $this->dispatch( 'afterNavigateBack', $this->getDispatcher()->getDefaultDriver() ); return $this; } public function forward() { $this->dispatch( 'beforeNavigateForward', $this->getDispatcher()->getDefaultDriver() ); try { $this->navigator->forward(); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); } $this->dispatch( 'afterNavigateForward', $this->getDispatcher()->getDefaultDriver() ); return $this; } public function refresh() { try { $this->navigator->refresh(); return $this; } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } } public function to($url) { $this->dispatch( 'beforeNavigateTo', $url, $this->getDispatcher()->getDefaultDriver() ); try { $this->navigator->to($url); } catch (WebDriverException $exception) { $this->dispatchOnException($exception); throw $exception; } $this->dispatch( 'afterNavigateTo', $url, $this->getDispatcher()->getDefaultDriver() ); return $this; } protected function dispatch($method, ...$arguments) { if (!$this->dispatcher) { return; } $this->dispatcher->dispatch($method, $arguments); } protected function dispatchOnException(WebDriverException $exception) { $this->dispatch('onException', $exception); } } executor = $executor; $this->isW3cCompliant = $isW3cCompliant; } public function implicitlyWait($seconds) { if ($this->isW3cCompliant) { $this->executor->execute( DriverCommand::IMPLICITLY_WAIT, ['implicit' => $seconds * 1000] ); return $this; } $this->executor->execute( DriverCommand::IMPLICITLY_WAIT, ['ms' => $seconds * 1000] ); return $this; } public function setScriptTimeout($seconds) { if ($this->isW3cCompliant) { $this->executor->execute( DriverCommand::SET_SCRIPT_TIMEOUT, ['script' => $seconds * 1000] ); return $this; } $this->executor->execute( DriverCommand::SET_SCRIPT_TIMEOUT, ['ms' => $seconds * 1000] ); return $this; } public function pageLoadTimeout($seconds) { if ($this->isW3cCompliant) { $this->executor->execute( DriverCommand::SET_SCRIPT_TIMEOUT, ['pageLoad' => $seconds * 1000] ); return $this; } $this->executor->execute(DriverCommand::SET_TIMEOUT, [ 'type' => 'page load', 'ms' => $seconds * 1000, ]); return $this; } } width = $width; $this->height = $height; } public function getHeight() { return (int) $this->height; } public function getWidth() { return (int) $this->width; } public function equals(self $dimension) { return $this->height === $dimension->getHeight() && $this->width === $dimension->getWidth(); } } driver = $driver; return $this; } public function getDefaultDriver() { return $this->driver; } public function register(WebDriverEventListener $listener) { $this->listeners[] = $listener; return $this; } public function unregister(WebDriverEventListener $listener) { $key = array_search($listener, $this->listeners, true); if ($key !== false) { unset($this->listeners[$key]); } return $this; } public function dispatch($method, $arguments) { foreach ($this->listeners as $listener) { call_user_func_array([$listener, $method], $arguments); } return $this; } } x = $x; $this->y = $y; parent::__construct($touch_screen); } public function perform() { $this->touchScreen->up($this->x, $this->y); } } getMessage(), $this->getCode(), $previous); } } results = $results; } public function getResults() { return $this->results; } public static function throwException($status_code, $message, $results) { if (is_string($status_code)) { switch ($status_code) { case 'element click intercepted': throw new ElementClickInterceptedException($message, $results); case 'element not interactable': throw new ElementNotInteractableException($message, $results); case 'insecure certificate': throw new InsecureCertificateException($message, $results); case 'invalid argument': throw new InvalidArgumentException($message, $results); case 'invalid cookie domain': throw new InvalidCookieDomainException($message, $results); case 'invalid element state': throw new InvalidElementStateException($message, $results); case 'invalid selector': throw new InvalidSelectorException($message, $results); case 'invalid session id': throw new InvalidSessionIdException($message, $results); case 'javascript error': throw new JavascriptErrorException($message, $results); case 'move target out of bounds': throw new MoveTargetOutOfBoundsException($message, $results); case 'no such alert': throw new NoSuchAlertException($message, $results); case 'no such cookie': throw new NoSuchCookieException($message, $results); case 'no such element': throw new NoSuchElementException($message, $results); case 'no such frame': throw new NoSuchFrameException($message, $results); case 'no such window': throw new NoSuchWindowException($message, $results); case 'script timeout': throw new ScriptTimeoutException($message, $results); case 'session not created': throw new SessionNotCreatedException($message, $results); case 'stale element reference': throw new StaleElementReferenceException($message, $results); case 'timeout': throw new TimeoutException($message, $results); case 'unable to set cookie': throw new UnableToSetCookieException($message, $results); case 'unable to capture screen': throw new UnableToCaptureScreenException($message, $results); case 'unexpected alert open': throw new UnexpectedAlertOpenException($message, $results); case 'unknown command': throw new UnknownCommandException($message, $results); case 'unknown error': throw new UnknownErrorException($message, $results); case 'unknown method': throw new UnknownMethodException($message, $results); case 'unsupported operation': throw new UnsupportedOperationException($message, $results); default: throw new UnrecognizedExceptionException($message, $results); } } switch ($status_code) { case 1: throw new IndexOutOfBoundsException($message, $results); case 2: throw new NoCollectionException($message, $results); case 3: throw new NoStringException($message, $results); case 4: throw new NoStringLengthException($message, $results); case 5: throw new NoStringWrapperException($message, $results); case 6: throw new NoSuchDriverException($message, $results); case 7: throw new NoSuchElementException($message, $results); case 8: throw new NoSuchFrameException($message, $results); case 9: throw new UnknownCommandException($message, $results); case 10: throw new StaleElementReferenceException($message, $results); case 11: throw new ElementNotVisibleException($message, $results); case 12: throw new InvalidElementStateException($message, $results); case 13: throw new UnknownServerException($message, $results); case 14: throw new ExpectedException($message, $results); case 15: throw new ElementNotSelectableException($message, $results); case 16: throw new NoSuchDocumentException($message, $results); case 17: throw new UnexpectedJavascriptException($message, $results); case 18: throw new NoScriptResultException($message, $results); case 19: throw new XPathLookupException($message, $results); case 20: throw new NoSuchCollectionException($message, $results); case 21: throw new TimeoutException($message, $results); case 22: throw new NullPointerException($message, $results); case 23: throw new NoSuchWindowException($message, $results); case 24: throw new InvalidCookieDomainException($message, $results); case 25: throw new UnableToSetCookieException($message, $results); case 26: throw new UnexpectedAlertOpenException($message, $results); case 27: throw new NoAlertOpenException($message, $results); case 28: throw new ScriptTimeoutException($message, $results); case 29: throw new InvalidCoordinatesException($message, $results); case 30: throw new IMENotAvailableException($message, $results); case 31: throw new IMEEngineActivationFailedException($message, $results); case 32: throw new InvalidSelectorException($message, $results); case 33: throw new SessionNotCreatedException($message, $results); case 34: throw new MoveTargetOutOfBoundsException($message, $results); default: throw new UnrecognizedExceptionException($message, $results); } } } type = $element->getAttribute('type'); if ($this->type !== 'checkbox') { throw new WebDriverException('The input must be of type "checkbox".'); } } public function isMultiple() { return true; } public function deselectAll() { foreach ($this->getRelatedElements() as $checkbox) { $this->deselectOption($checkbox); } } public function deselectByIndex($index) { $this->byIndex($index, false); } public function deselectByValue($value) { $this->byValue($value, false); } public function deselectByVisibleText($text) { $this->byVisibleText($text, false, false); } public function deselectByVisiblePartialText($text) { $this->byVisibleText($text, true, false); } } [ 'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()], ], ] ); $response = $executor->execute($newSessionCommand); $returnedCapabilities = DesiredCapabilities::createFromW3cCapabilities($response->getValue()['capabilities']); $sessionId = $response->getSessionID(); return new static($executor, $sessionId, $returnedCapabilities, true); } } setPreference(FirefoxPreferences::READER_PARSE_ON_LOAD_ENABLED, false); $this->setPreference(FirefoxPreferences::DEVTOOLS_JSONVIEW, false); } public function setOption($name, $value) { if ($name === self::OPTION_PREFS) { throw new \InvalidArgumentException('Use setPreference() method to set Firefox preferences'); } if ($name === self::OPTION_ARGS) { throw new \InvalidArgumentException('Use addArguments() method to add Firefox arguments'); } $this->options[$name] = $value; return $this; } public function addArguments(array $arguments) { $this->arguments = array_merge($this->arguments, $arguments); return $this; } public function setPreference($name, $value) { $this->preferences[$name] = $value; return $this; } public function toArray() { $array = $this->options; if (!empty($this->arguments)) { $array[self::OPTION_ARGS] = $this->arguments; } if (!empty($this->preferences)) { $array[self::OPTION_PREFS] = $this->preferences; } return $array; } #[ReturnTypeWillChange] public function jsonSerialize() { return new \ArrayObject($this->toArray()); } } extensions[] = $extension; return $this; } public function addExtensionDatas($extension_datas) { if (!is_dir($extension_datas)) { return null; } $this->extensions_datas[basename($extension_datas)] = $extension_datas; return $this; } public function setRdfFile($rdf_file) { if (!is_file($rdf_file)) { return null; } $this->rdf_file = $rdf_file; return $this; } public function setPreference($key, $value) { if (is_string($value)) { $value = sprintf('"%s"', $value); } else { if (is_int($value)) { $value = sprintf('%d', $value); } else { if (is_bool($value)) { $value = $value ? 'true' : 'false'; } else { throw new WebDriverException( 'The value of the preference should be either a string, int or bool.' ); } } } $this->preferences[$key] = $value; return $this; } public function getPreference($key) { if (array_key_exists($key, $this->preferences)) { return $this->preferences[$key]; } return null; } public function encode() { $temp_dir = $this->createTempDirectory('WebDriverFirefoxProfile'); if (isset($this->rdf_file)) { copy($this->rdf_file, $temp_dir . DIRECTORY_SEPARATOR . 'mimeTypes.rdf'); } foreach ($this->extensions as $extension) { $this->installExtension($extension, $temp_dir); } foreach ($this->extensions_datas as $dirname => $extension_datas) { mkdir($temp_dir . DIRECTORY_SEPARATOR . $dirname); $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($extension_datas, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); foreach ($iterator as $item) { $target_dir = $temp_dir . DIRECTORY_SEPARATOR . $dirname . DIRECTORY_SEPARATOR . $iterator->getSubPathName(); if ($item->isDir()) { mkdir($target_dir); } else { copy($item, $target_dir); } } } $content = ''; foreach ($this->preferences as $key => $value) { $content .= sprintf("user_pref(\"%s\", %s);\n", $key, $value); } file_put_contents($temp_dir . '/user.js', $content); $temp_zip = sys_get_temp_dir() . '/' . uniqid('WebDriverFirefoxProfileZip', false); $zip = new ZipArchive(); $zip->open($temp_zip, ZipArchive::CREATE); $dir = new RecursiveDirectoryIterator($temp_dir); $files = new RecursiveIteratorIterator($dir); $dir_prefix = preg_replace( '#\\\\#', '\\\\\\\\', $temp_dir . DIRECTORY_SEPARATOR ); foreach ($files as $name => $object) { if (is_dir($name)) { continue; } $path = preg_replace("#^{$dir_prefix}#", '', $name); $zip->addFile($name, $path); } $zip->close(); $profile = base64_encode(file_get_contents($temp_zip)); $this->deleteDirectory($temp_dir); unlink($temp_zip); return $profile; } private function installExtension($extension, $profile_dir) { $temp_dir = $this->createTempDirectory('WebDriverFirefoxProfileExtension'); $this->extractTo($extension, $temp_dir); $install_rdf_path = $temp_dir . '/install.rdf'; $xml = simplexml_load_file($install_rdf_path); $ns = $xml->getDocNamespaces(); $prefix = ''; if (!empty($ns)) { foreach ($ns as $key => $value) { if (mb_strpos($value, '//www.mozilla.org/2004/em-rdf') > 0) { if ($key != '') { $prefix = $key . ':'; } break; } } } $matches = []; preg_match('#<' . $prefix . 'id>([^<]+)#', $xml->asXML(), $matches); if (isset($matches[1])) { $ext_dir = $profile_dir . '/extensions/' . $matches[1]; mkdir($ext_dir, 0777, true); $this->extractTo($extension, $ext_dir); } else { $this->deleteDirectory($temp_dir); throw new WebDriverException('Cannot get the extension id from the install manifest.'); } $this->deleteDirectory($temp_dir); return $ext_dir; } private function createTempDirectory($prefix = '') { $temp_dir = tempnam(sys_get_temp_dir(), $prefix); if (file_exists($temp_dir)) { unlink($temp_dir); mkdir($temp_dir); if (!is_dir($temp_dir)) { throw new WebDriverException('Cannot create firefox profile.'); } } return $temp_dir; } private function deleteDirectory($directory) { $dir = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS); $paths = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST); foreach ($paths as $path) { if ($path->isDir() && !$path->isLink()) { rmdir($path->getPathname()); } else { unlink($path->getPathname()); } } rmdir($directory); } private function extractTo($xpi, $target_dir) { $zip = new ZipArchive(); if (file_exists($xpi)) { if ($zip->open($xpi)) { $zip->extractTo($target_dir); $zip->close(); } else { throw new \Exception("Failed to open the firefox extension. '$xpi'"); } } else { throw new \Exception("Firefox extension doesn't exist. '$xpi'"); } return $this; } } x = $x; $this->y = $y; parent::__construct($touch_screen); } public function perform() { $this->touchScreen->scroll($this->x, $this->y); } } touchScreen->doubleTap($this->locationProvider); } } touchScreen->tap($this->locationProvider); } } x = $x; $this->y = $y; parent::__construct($touch_screen); } public function perform() { $this->touchScreen->down($this->x, $this->y); } } touchScreen->longPress($this->locationProvider); } } x = $x; $this->y = $y; parent::__construct($touch_screen, $element); } public function perform() { $this->touchScreen->scrollFromElement( $this->locationProvider, $this->x, $this->y ); } } x = $x; $this->y = $y; $this->speed = $speed; parent::__construct($touch_screen, $element); } public function perform() { $this->touchScreen->flickFromElement( $this->locationProvider, $this->x, $this->y, $this->speed ); } } x = $x; $this->y = $y; parent::__construct($touch_screen); } public function perform() { $this->touchScreen->flick($this->x, $this->y); } } x = $x; $this->y = $y; parent::__construct($touch_screen); } public function perform() { $this->touchScreen->move($this->x, $this->y); } } touchScreen = $touch_screen; $this->locationProvider = $location_provider; } protected function getActionLocation() { return $this->locationProvider !== null ? $this->locationProvider->getCoordinates() : null; } } driver = $driver; $this->keyboard = $driver->getKeyboard(); $this->mouse = $driver->getMouse(); $this->action = new WebDriverCompositeAction(); } public function perform() { $this->action->perform(); } public function click(WebDriverElement $element = null) { $this->action->addAction( new WebDriverClickAction($this->mouse, $element) ); return $this; } public function clickAndHold(WebDriverElement $element = null) { $this->action->addAction( new WebDriverClickAndHoldAction($this->mouse, $element) ); return $this; } public function contextClick(WebDriverElement $element = null) { $this->action->addAction( new WebDriverContextClickAction($this->mouse, $element) ); return $this; } public function doubleClick(WebDriverElement $element = null) { $this->action->addAction( new WebDriverDoubleClickAction($this->mouse, $element) ); return $this; } public function dragAndDrop(WebDriverElement $source, WebDriverElement $target) { $this->action->addAction( new WebDriverClickAndHoldAction($this->mouse, $source) ); $this->action->addAction( new WebDriverMouseMoveAction($this->mouse, $target) ); $this->action->addAction( new WebDriverButtonReleaseAction($this->mouse, $target) ); return $this; } public function dragAndDropBy(WebDriverElement $source, $x_offset, $y_offset) { $this->action->addAction( new WebDriverClickAndHoldAction($this->mouse, $source) ); $this->action->addAction( new WebDriverMoveToOffsetAction($this->mouse, null, $x_offset, $y_offset) ); $this->action->addAction( new WebDriverButtonReleaseAction($this->mouse, null) ); return $this; } public function moveByOffset($x_offset, $y_offset) { $this->action->addAction( new WebDriverMoveToOffsetAction($this->mouse, null, $x_offset, $y_offset) ); return $this; } public function moveToElement(WebDriverElement $element, $x_offset = null, $y_offset = null) { $this->action->addAction(new WebDriverMoveToOffsetAction( $this->mouse, $element, $x_offset, $y_offset )); return $this; } public function release(WebDriverElement $element = null) { $this->action->addAction( new WebDriverButtonReleaseAction($this->mouse, $element) ); return $this; } public function keyDown(WebDriverElement $element = null, $key = null) { $this->action->addAction( new WebDriverKeyDownAction($this->keyboard, $this->mouse, $element, $key) ); return $this; } public function keyUp(WebDriverElement $element = null, $key = null) { $this->action->addAction( new WebDriverKeyUpAction($this->keyboard, $this->mouse, $element, $key) ); return $this; } public function sendKeys(WebDriverElement $element = null, $keys = null) { $this->action->addAction( new WebDriverSendKeysAction( $this->keyboard, $this->mouse, $element, $keys ) ); return $this; } } touchScreen = $driver->getTouch(); } public function tap(WebDriverElement $element) { $this->action->addAction( new WebDriverTapAction($this->touchScreen, $element) ); return $this; } public function down($x, $y) { $this->action->addAction( new WebDriverDownAction($this->touchScreen, $x, $y) ); return $this; } public function up($x, $y) { $this->action->addAction( new WebDriverUpAction($this->touchScreen, $x, $y) ); return $this; } public function move($x, $y) { $this->action->addAction( new WebDriverMoveAction($this->touchScreen, $x, $y) ); return $this; } public function scroll($x, $y) { $this->action->addAction( new WebDriverScrollAction($this->touchScreen, $x, $y) ); return $this; } public function scrollFromElement(WebDriverElement $element, $x, $y) { $this->action->addAction( new WebDriverScrollFromElementAction($this->touchScreen, $element, $x, $y) ); return $this; } public function doubleTap(WebDriverElement $element) { $this->action->addAction( new WebDriverDoubleTapAction($this->touchScreen, $element) ); return $this; } public function longPress(WebDriverElement $element) { $this->action->addAction( new WebDriverLongPressAction($this->touchScreen, $element) ); return $this; } public function flick($x, $y) { $this->action->addAction( new WebDriverFlickAction($this->touchScreen, $x, $y) ); return $this; } public function flickFromElement(WebDriverElement $element, $x, $y, $speed) { $this->action->addAction( new WebDriverFlickFromElementAction( $this->touchScreen, $element, $x, $y, $speed ) ); return $this; } } focusOnElement(); $this->keyboard->releaseKey($this->key); } } keys = $keys; } public function perform() { $this->focusOnElement(); $this->keyboard->sendKeys($this->keys); } } mouse->mouseDown($this->getActionLocation()); } } mouse->click($this->getActionLocation()); } } xOffset = $x_offset; $this->yOffset = $y_offset; } public function perform() { $this->mouse->mouseMove( $this->getActionLocation(), $this->xOffset, $this->yOffset ); } } mouse->doubleClick($this->getActionLocation()); } } mouse->mouseMove($this->getActionLocation()); } } keyboard = $keyboard; $this->mouse = $mouse; $this->locationProvider = $location_provider; } protected function focusOnElement() { if ($this->locationProvider) { $this->mouse->click($this->locationProvider->getCoordinates()); } } } mouse->contextClick($this->getActionLocation()); } } mouse = $mouse; $this->locationProvider = $location_provider; } protected function getActionLocation() { if ($this->locationProvider !== null) { return $this->locationProvider->getCoordinates(); } return null; } protected function moveToLocation() { $this->mouse->mouseMove($this->locationProvider); } } focusOnElement(); $this->keyboard->pressKey($this->key); } } mouse->mouseUp($this->getActionLocation()); } } key = $key; } } onScreen = $on_screen; $this->inViewPort = $in_view_port; $this->onPage = $on_page; $this->auxiliary = $auxiliary; } public function onScreen() { throw new UnsupportedOperationException( 'onScreen is planned but not yet supported by Selenium' ); } public function inViewPort() { return call_user_func($this->inViewPort); } public function onPage() { return call_user_func($this->onPage); } public function getAuxiliary() { return $this->auxiliary; } } actions[] = $action; return $this; } public function getNumberOfActions() { return count($this->actions); } public function perform() { foreach ($this->actions as $action) { $action->perform(); } } } options = $this->createOptions($options ?: array()); $this->connection = $this->createConnection($parameters ?: array()); $this->profile = $this->options->profile; } protected function createOptions($options) { if (is_array($options)) { return new Options($options); } if ($options instanceof OptionsInterface) { return $options; } throw new \InvalidArgumentException('Invalid type for client options.'); } protected function createConnection($parameters) { if ($parameters instanceof ConnectionInterface) { return $parameters; } if ($parameters instanceof ParametersInterface || is_string($parameters)) { return $this->options->connections->create($parameters); } if (is_array($parameters)) { if (!isset($parameters[0])) { return $this->options->connections->create($parameters); } $options = $this->options; if ($options->defined('aggregate')) { $initializer = $this->getConnectionInitializerWrapper($options->aggregate); $connection = $initializer($parameters, $options); } elseif ($options->defined('replication')) { $replication = $options->replication; if ($replication instanceof AggregateConnectionInterface) { $connection = $replication; $options->connections->aggregate($connection, $parameters); } else { $initializer = $this->getConnectionInitializerWrapper($replication); $connection = $initializer($parameters, $options); } } else { $connection = $options->cluster; $options->connections->aggregate($connection, $parameters); } return $connection; } if (is_callable($parameters)) { $initializer = $this->getConnectionInitializerWrapper($parameters); $connection = $initializer($this->options); return $connection; } throw new \InvalidArgumentException('Invalid type for connection parameters.'); } protected function getConnectionInitializerWrapper($callable) { return function () use ($callable) { $connection = call_user_func_array($callable, func_get_args()); if (!$connection instanceof ConnectionInterface) { throw new \UnexpectedValueException( 'The callable connection initializer returned an invalid type.' ); } return $connection; }; } public function getProfile() { return $this->profile; } public function getOptions() { return $this->options; } public function getClientFor($connectionID) { if (!$connection = $this->getConnectionById($connectionID)) { throw new \InvalidArgumentException("Invalid connection ID: $connectionID."); } return new static($connection, $this->options); } public function connect() { $this->connection->connect(); } public function disconnect() { $this->connection->disconnect(); } public function quit() { $this->disconnect(); } public function isConnected() { return $this->connection->isConnected(); } public function getConnection() { return $this->connection; } public function getConnectionById($connectionID) { if (!$this->connection instanceof AggregateConnectionInterface) { throw new NotSupportedException( 'Retrieving connections by ID is supported only by aggregate connections.' ); } return $this->connection->getConnectionById($connectionID); } public function executeRaw(array $arguments, &$error = null) { $error = false; $response = $this->connection->executeCommand( new RawCommand($arguments) ); if ($response instanceof ResponseInterface) { if ($response instanceof ErrorResponseInterface) { $error = true; } return (string) $response; } return $response; } public function __call($commandID, $arguments) { return $this->executeCommand( $this->createCommand($commandID, $arguments) ); } public function createCommand($commandID, $arguments = array()) { return $this->profile->createCommand($commandID, $arguments); } public function executeCommand(CommandInterface $command) { $response = $this->connection->executeCommand($command); if ($response instanceof ResponseInterface) { if ($response instanceof ErrorResponseInterface) { $response = $this->onErrorResponse($command, $response); } return $response; } return $command->parseResponse($response); } protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response) { if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') { $eval = $this->createCommand('EVAL'); $eval->setRawArguments($command->getEvalArguments()); $response = $this->executeCommand($eval); if (!$response instanceof ResponseInterface) { $response = $command->parseResponse($response); } return $response; } if ($this->options->exceptions) { throw new ServerException($response->getMessage()); } return $response; } private function sharedContextFactory($initializer, $argv = null) { switch (count($argv)) { case 0: return $this->$initializer(); case 1: return is_array($argv[0]) ? $this->$initializer($argv[0]) : $this->$initializer(null, $argv[0]); case 2: list($arg0, $arg1) = $argv; return $this->$initializer($arg0, $arg1); default: return $this->$initializer($this, $argv); } } public function pipeline() { return $this->sharedContextFactory('createPipeline', func_get_args()); } protected function createPipeline(array $options = null, $callable = null) { if (isset($options['atomic']) && $options['atomic']) { $class = 'Predis\Pipeline\Atomic'; } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) { $class = 'Predis\Pipeline\FireAndForget'; } else { $class = 'Predis\Pipeline\Pipeline'; } $pipeline = new $class($this); if (isset($callable)) { return $pipeline->execute($callable); } return $pipeline; } public function transaction() { return $this->sharedContextFactory('createTransaction', func_get_args()); } protected function createTransaction(array $options = null, $callable = null) { $transaction = new MultiExecTransaction($this, $options); if (isset($callable)) { return $transaction->execute($callable); } return $transaction; } public function pubSubLoop() { return $this->sharedContextFactory('createPubSub', func_get_args()); } protected function createPubSub(array $options = null, $callable = null) { $pubsub = new PubSubConsumer($this, $options); if (!isset($callable)) { return $pubsub; } foreach ($pubsub as $message) { if (call_user_func($callable, $pubsub, $message) === false) { $pubsub->stop(); } } } public function monitor() { return new MonitorConsumer($this); } #[\ReturnTypeWillChange] public function getIterator() { $clients = array(); $connection = $this->getConnection(); if (!$connection instanceof \Traversable) { return new \ArrayIterator(array( (string) $connection => new static($connection, $this->getOptions()) )); } foreach ($connection as $node) { $clients[(string) $node] = new static($node, $this->getOptions()); } return new \ArrayIterator($clients); } } disallowed = $this->getDisallowedOperations(); $this->readonly = $this->getReadOnlyOperations(); $this->readonlySHA1 = array(); } public function isReadOperation(CommandInterface $command) { if (isset($this->disallowed[$id = $command->getId()])) { throw new NotSupportedException( "The command '$id' is not allowed in replication mode." ); } if (isset($this->readonly[$id])) { if (true === $readonly = $this->readonly[$id]) { return true; } return call_user_func($readonly, $command); } if (($eval = $id === 'EVAL') || $id === 'EVALSHA') { $argument = $command->getArgument(0); $sha1 = $eval ? sha1(strval($argument)) : $argument; if (isset($this->readonlySHA1[$sha1])) { if (true === $readonly = $this->readonlySHA1[$sha1]) { return true; } return call_user_func($readonly, $command); } } return false; } public function isDisallowedOperation(CommandInterface $command) { return isset($this->disallowed[$command->getId()]); } protected function isBitfieldReadOnly(CommandInterface $command) { $arguments = $command->getArguments(); $argc = count($arguments); if ($argc >= 2) { for ($i = 1; $i < $argc; ++$i) { $argument = strtoupper($arguments[$i]); if ($argument === 'SET' || $argument === 'INCRBY') { return false; } } } return true; } protected function isGeoradiusReadOnly(CommandInterface $command) { $arguments = $command->getArguments(); $argc = count($arguments); $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; if ($argc > $startIndex) { for ($i = $startIndex; $i < $argc; ++$i) { $argument = strtoupper($arguments[$i]); if ($argument === 'STORE' || $argument === 'STOREDIST') { return false; } } } return true; } public function setCommandReadOnly($commandID, $readonly = true) { $commandID = strtoupper($commandID); if ($readonly) { $this->readonly[$commandID] = $readonly; } else { unset($this->readonly[$commandID]); } } public function setScriptReadOnly($script, $readonly = true) { $sha1 = sha1($script); if ($readonly) { $this->readonlySHA1[$sha1] = $readonly; } else { unset($this->readonlySHA1[$sha1]); } } protected function getDisallowedOperations() { return array( 'SHUTDOWN' => true, 'INFO' => true, 'DBSIZE' => true, 'LASTSAVE' => true, 'CONFIG' => true, 'MONITOR' => true, 'SLAVEOF' => true, 'SAVE' => true, 'BGSAVE' => true, 'BGREWRITEAOF' => true, 'SLOWLOG' => true, ); } protected function getReadOnlyOperations() { return array( 'EXISTS' => true, 'TYPE' => true, 'KEYS' => true, 'SCAN' => true, 'RANDOMKEY' => true, 'TTL' => true, 'GET' => true, 'MGET' => true, 'SUBSTR' => true, 'STRLEN' => true, 'GETRANGE' => true, 'GETBIT' => true, 'LLEN' => true, 'LRANGE' => true, 'LINDEX' => true, 'SCARD' => true, 'SISMEMBER' => true, 'SINTER' => true, 'SUNION' => true, 'SDIFF' => true, 'SMEMBERS' => true, 'SSCAN' => true, 'SRANDMEMBER' => true, 'ZRANGE' => true, 'ZREVRANGE' => true, 'ZRANGEBYSCORE' => true, 'ZREVRANGEBYSCORE' => true, 'ZCARD' => true, 'ZSCORE' => true, 'ZCOUNT' => true, 'ZRANK' => true, 'ZREVRANK' => true, 'ZSCAN' => true, 'ZLEXCOUNT' => true, 'ZRANGEBYLEX' => true, 'ZREVRANGEBYLEX' => true, 'HGET' => true, 'HMGET' => true, 'HEXISTS' => true, 'HLEN' => true, 'HKEYS' => true, 'HVALS' => true, 'HGETALL' => true, 'HSCAN' => true, 'HSTRLEN' => true, 'PING' => true, 'AUTH' => true, 'SELECT' => true, 'ECHO' => true, 'QUIT' => true, 'OBJECT' => true, 'BITCOUNT' => true, 'BITPOS' => true, 'TIME' => true, 'PFCOUNT' => true, 'BITFIELD' => array($this, 'isBitfieldReadOnly'), 'GEOHASH' => true, 'GEOPOS' => true, 'GEODIST' => true, 'GEORADIUS' => array($this, 'isGeoradiusReadOnly'), 'GEORADIUSBYMEMBER' => array($this, 'isGeoradiusReadOnly'), ); } } client = $client; if (isset($options['gc_maxlifetime'])) { $this->ttl = (int) $options['gc_maxlifetime']; } else { $this->ttl = ini_get('session.gc_maxlifetime'); } } public function register() { if (PHP_VERSION_ID >= 50400) { session_set_save_handler($this, true); } else { session_set_save_handler( array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc') ); } } #[\ReturnTypeWillChange] public function open($save_path, $session_id) { return true; } #[\ReturnTypeWillChange] public function close() { return true; } #[\ReturnTypeWillChange] public function gc($maxlifetime) { return true; } #[\ReturnTypeWillChange] public function read($session_id) { if ($data = $this->client->get($session_id)) { return $data; } return ''; } #[\ReturnTypeWillChange] public function write($session_id, $session_data) { $this->client->setex($session_id, $this->ttl, $session_data); return true; } #[\ReturnTypeWillChange] public function destroy($session_id) { $this->client->del($session_id); return true; } public function getClient() { return $this->client; } public function getMaxLifeTime() { return $this->ttl; } } assertClient($client); $this->client = $client; $this->state = new MultiExecState(); $this->configure($client, $options ?: array()); $this->reset(); } private function assertClient(ClientInterface $client) { if ($client->getConnection() instanceof AggregateConnectionInterface) { throw new NotSupportedException( 'Cannot initialize a MULTI/EXEC transaction over aggregate connections.' ); } if (!$client->getProfile()->supportsCommands(array('MULTI', 'EXEC', 'DISCARD'))) { throw new NotSupportedException( 'The current profile does not support MULTI, EXEC and DISCARD.' ); } } protected function configure(ClientInterface $client, array $options) { if (isset($options['exceptions'])) { $this->exceptions = (bool) $options['exceptions']; } else { $this->exceptions = $client->getOptions()->exceptions; } if (isset($options['cas'])) { $this->modeCAS = (bool) $options['cas']; } if (isset($options['watch']) && $keys = $options['watch']) { $this->watchKeys = $keys; } if (isset($options['retry'])) { $this->attempts = (int) $options['retry']; } } protected function reset() { $this->state->reset(); $this->commands = new \SplQueue(); } protected function initialize() { if ($this->state->isInitialized()) { return; } if ($this->modeCAS) { $this->state->flag(MultiExecState::CAS); } if ($this->watchKeys) { $this->watch($this->watchKeys); } $cas = $this->state->isCAS(); $discarded = $this->state->isDiscarded(); if (!$cas || ($cas && $discarded)) { $this->call('MULTI'); if ($discarded) { $this->state->unflag(MultiExecState::CAS); } } $this->state->unflag(MultiExecState::DISCARDED); $this->state->flag(MultiExecState::INITIALIZED); } public function __call($method, $arguments) { return $this->executeCommand( $this->client->createCommand($method, $arguments) ); } protected function call($commandID, array $arguments = array()) { $response = $this->client->executeCommand( $this->client->createCommand($commandID, $arguments) ); if ($response instanceof ErrorResponseInterface) { throw new ServerException($response->getMessage()); } return $response; } public function executeCommand(CommandInterface $command) { $this->initialize(); if ($this->state->isCAS()) { return $this->client->executeCommand($command); } $response = $this->client->getConnection()->executeCommand($command); if ($response instanceof StatusResponse && $response == 'QUEUED') { $this->commands->enqueue($command); } elseif ($response instanceof ErrorResponseInterface) { throw new AbortedMultiExecException($this, $response->getMessage()); } else { $this->onProtocolError('The server did not return a +QUEUED status response.'); } return $this; } public function watch($keys) { if (!$this->client->getProfile()->supportsCommand('WATCH')) { throw new NotSupportedException('WATCH is not supported by the current profile.'); } if ($this->state->isWatchAllowed()) { throw new ClientException('Sending WATCH after MULTI is not allowed.'); } $response = $this->call('WATCH', is_array($keys) ? $keys : array($keys)); $this->state->flag(MultiExecState::WATCH); return $response; } public function multi() { if ($this->state->check(MultiExecState::INITIALIZED | MultiExecState::CAS)) { $this->state->unflag(MultiExecState::CAS); $this->call('MULTI'); } else { $this->initialize(); } return $this; } public function unwatch() { if (!$this->client->getProfile()->supportsCommand('UNWATCH')) { throw new NotSupportedException( 'UNWATCH is not supported by the current profile.' ); } $this->state->unflag(MultiExecState::WATCH); $this->__call('UNWATCH', array()); return $this; } public function discard() { if ($this->state->isInitialized()) { $this->call($this->state->isCAS() ? 'UNWATCH' : 'DISCARD'); $this->reset(); $this->state->flag(MultiExecState::DISCARDED); } return $this; } public function exec() { return $this->execute(); } private function checkBeforeExecution($callable) { if ($this->state->isExecuting()) { throw new ClientException( 'Cannot invoke "execute" or "exec" inside an active transaction context.' ); } if ($callable) { if (!is_callable($callable)) { throw new \InvalidArgumentException('The argument must be a callable object.'); } if (!$this->commands->isEmpty()) { $this->discard(); throw new ClientException( 'Cannot execute a transaction block after using fluent interface.' ); } } elseif ($this->attempts) { $this->discard(); throw new ClientException( 'Automatic retries are supported only when a callable block is provided.' ); } } public function execute($callable = null) { $this->checkBeforeExecution($callable); $execResponse = null; $attempts = $this->attempts; do { if ($callable) { $this->executeTransactionBlock($callable); } if ($this->commands->isEmpty()) { if ($this->state->isWatching()) { $this->discard(); } return; } $execResponse = $this->call('EXEC'); if ($execResponse === null) { if ($attempts === 0) { throw new AbortedMultiExecException( $this, 'The current transaction has been aborted by the server.' ); } $this->reset(); continue; } break; } while ($attempts-- > 0); $response = array(); $commands = $this->commands; $size = count($execResponse); if ($size !== count($commands)) { $this->onProtocolError('EXEC returned an unexpected number of response items.'); } for ($i = 0; $i < $size; ++$i) { $cmdResponse = $execResponse[$i]; if ($cmdResponse instanceof ErrorResponseInterface && $this->exceptions) { throw new ServerException($cmdResponse->getMessage()); } $response[$i] = $commands->dequeue()->parseResponse($cmdResponse); } return $response; } protected function executeTransactionBlock($callable) { $exception = null; $this->state->flag(MultiExecState::INSIDEBLOCK); try { call_user_func($callable, $this); } catch (CommunicationException $exception) { } catch (ServerException $exception) { } catch (\Exception $exception) { $this->discard(); } $this->state->unflag(MultiExecState::INSIDEBLOCK); if ($exception) { throw $exception; } } private function onProtocolError($message) { CommunicationException::handle(new ProtocolException( $this->client->getConnection(), $message )); } } transaction = $transaction; } public function getTransaction() { return $this->transaction; } } flags = 0; } public function set($flags) { $this->flags = $flags; } public function get() { return $this->flags; } public function flag($flags) { $this->flags |= $flags; } public function unflag($flags) { $this->flags &= ~$flags; } public function check($flags) { return ($this->flags & $flags) === $flags; } public function reset() { $this->flags = 0; } public function isReset() { return $this->flags === 0; } public function isInitialized() { return $this->check(self::INITIALIZED); } public function isExecuting() { return $this->check(self::INSIDEBLOCK); } public function isCAS() { return $this->check(self::CAS); } public function isWatchAllowed() { return $this->check(self::INITIALIZED) && !$this->check(self::CAS); } public function isWatching() { return $this->check(self::WATCH); } public function isDiscarded() { return $this->check(self::DISCARDED); } } input = $options; $this->options = array(); $this->handlers = $this->getHandlers(); } protected function getHandlers() { return array( 'cluster' => 'Predis\Configuration\ClusterOption', 'connections' => 'Predis\Configuration\ConnectionFactoryOption', 'exceptions' => 'Predis\Configuration\ExceptionsOption', 'prefix' => 'Predis\Configuration\PrefixOption', 'profile' => 'Predis\Configuration\ProfileOption', 'replication' => 'Predis\Configuration\ReplicationOption', ); } public function getDefault($option) { if (isset($this->handlers[$option])) { $handler = $this->handlers[$option]; $handler = new $handler(); return $handler->getDefault($this); } } public function defined($option) { return array_key_exists($option, $this->options) || array_key_exists($option, $this->input) ; } public function __isset($option) { return ( array_key_exists($option, $this->options) || array_key_exists($option, $this->input) ) && $this->__get($option) !== null; } public function __get($option) { if (isset($this->options[$option]) || array_key_exists($option, $this->options)) { return $this->options[$option]; } if (isset($this->input[$option]) || array_key_exists($option, $this->input)) { $value = $this->input[$option]; unset($this->input[$option]); if (is_object($value) && method_exists($value, '__invoke')) { $value = $value($this, $option); } if (isset($this->handlers[$option])) { $handler = $this->handlers[$option]; $handler = new $handler(); $value = $handler->filter($this, $value); } return $this->options[$option] = $value; } if (isset($this->handlers[$option])) { return $this->options[$option] = $this->getDefault($option); } return; } } connections); default: return; } } public function filter(OptionsInterface $options, $value) { if (is_string($value)) { $value = $this->createByDescription($options, $value); } if (!$value instanceof ClusterInterface) { throw new \InvalidArgumentException( "An instance of type 'Predis\Connection\Aggregate\ClusterInterface' was expected." ); } return $value; } public function getDefault(OptionsInterface $options) { return new PredisCluster(); } } service, $sentinels, $options->connections); }; } if ( !is_object($value) && null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ) { if (true === $asbool) { return $this->getDefault($options); } else { throw new \InvalidArgumentException( "Values evaluating to FALSE are not accepted for `replication`" ); } } throw new \InvalidArgumentException( "An instance of type 'Predis\Connection\Aggregate\ReplicationInterface' was expected." ); } public function getDefault(OptionsInterface $options) { $replication = new MasterSlaveReplication(); if ($options->autodiscovery) { $replication->setConnectionFactory($options->connections); $replication->setAutoDiscovery(true); } return $replication; } } prefix) && $profile instanceof RedisProfile) { $profile->setProcessor($options->__get('prefix')); } } public function filter(OptionsInterface $options, $value) { if (is_string($value)) { $value = Factory::get($value); $this->setProcessors($options, $value); } elseif (!$value instanceof ProfileInterface) { throw new \InvalidArgumentException('Invalid value for the profile option.'); } return $value; } public function getDefault(OptionsInterface $options) { $profile = Factory::getDefault(); $this->setProcessors($options, $profile); return $profile; } } getDefault($options); foreach ($value as $scheme => $initializer) { $factory->define($scheme, $initializer); } return $factory; } else { throw new \InvalidArgumentException( 'Invalid value provided for the connections option.' ); } } public function getDefault(OptionsInterface $options) { $factory = new Factory(); if ($options->defined('parameters')) { $factory->setDefaultParameters($options->parameters); } return $factory; } } getClient()->getConnection(); } protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) { if ($connection instanceof NodeConnectionInterface) { return $this->executeSingleNode($connection, $commands); } elseif ($connection instanceof ClusterInterface) { return $this->executeCluster($connection, $commands); } else { $class = get_class($connection); throw new NotSupportedException("The connection class '$class' is not supported."); } } protected function executeSingleNode(NodeConnectionInterface $connection, \SplQueue $commands) { $responses = array(); $sizeOfPipe = count($commands); foreach ($commands as $command) { try { $connection->writeRequest($command); } catch (CommunicationException $exception) { return array_fill(0, $sizeOfPipe, $exception); } } for ($i = 0; $i < $sizeOfPipe; ++$i) { $command = $commands->dequeue(); try { $responses[$i] = $connection->readResponse($command); } catch (CommunicationException $exception) { $add = count($commands) - count($responses); $responses = array_merge($responses, array_fill(0, $add, $exception)); break; } } return $responses; } protected function executeCluster(ClusterInterface $connection, \SplQueue $commands) { $responses = array(); $sizeOfPipe = count($commands); $exceptions = array(); foreach ($commands as $command) { $cmdConnection = $connection->getConnection($command); if (isset($exceptions[spl_object_hash($cmdConnection)])) { continue; } try { $cmdConnection->writeRequest($command); } catch (CommunicationException $exception) { $exceptions[spl_object_hash($cmdConnection)] = $exception; } } for ($i = 0; $i < $sizeOfPipe; ++$i) { $command = $commands->dequeue(); $cmdConnection = $connection->getConnection($command); $connectionHash = spl_object_hash($cmdConnection); if (isset($exceptions[$connectionHash])) { $responses[$i] = $exceptions[$connectionHash]; continue; } try { $responses[$i] = $cmdConnection->readResponse($command); } catch (CommunicationException $exception) { $responses[$i] = $exception; $exceptions[$connectionHash] = $exception; } } return $responses; } } getProfile()->supportsCommands(array('multi', 'exec', 'discard'))) { throw new ClientException( "The current profile does not support 'MULTI', 'EXEC' and 'DISCARD'." ); } parent::__construct($client); } protected function getConnection() { $connection = $this->getClient()->getConnection(); if (!$connection instanceof NodeConnectionInterface) { $class = __CLASS__; throw new ClientException("The class '$class' does not support aggregate connections."); } return $connection; } protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) { $profile = $this->getClient()->getProfile(); $connection->executeCommand($profile->createCommand('multi')); foreach ($commands as $command) { $connection->writeRequest($command); } foreach ($commands as $command) { $response = $connection->readResponse($command); if ($response instanceof ErrorResponseInterface) { $connection->executeCommand($profile->createCommand('discard')); throw new ServerException($response->getMessage()); } } $executed = $connection->executeCommand($profile->createCommand('exec')); if (!isset($executed)) { throw new ClientException( 'The underlying transaction has been aborted by the server.' ); } if (count($executed) !== count($commands)) { $expected = count($commands); $received = count($executed); throw new ClientException( "Invalid number of responses [expected $expected, received $received]." ); } $responses = array(); $sizeOfPipe = count($commands); $exceptions = $this->throwServerExceptions(); for ($i = 0; $i < $sizeOfPipe; ++$i) { $command = $commands->dequeue(); $response = $executed[$i]; if (!$response instanceof ResponseInterface) { $responses[] = $command->parseResponse($response); } elseif ($response instanceof ErrorResponseInterface && $exceptions) { $this->exception($connection, $response); } else { $responses[] = $response; } unset($executed[$i]); } return $responses; } } isEmpty()) { $connection->writeRequest($commands->dequeue()); } $connection->disconnect(); return array(); } } client = $client; $this->pipeline = new \SplQueue(); } public function __call($method, $arguments) { $command = $this->client->createCommand($method, $arguments); $this->recordCommand($command); return $this; } protected function recordCommand(CommandInterface $command) { $this->pipeline->enqueue($command); } public function executeCommand(CommandInterface $command) { $this->recordCommand($command); return $this; } protected function exception(ConnectionInterface $connection, ErrorResponseInterface $response) { $connection->disconnect(); $message = $response->getMessage(); throw new ServerException($message); } protected function getConnection() { $connection = $this->getClient()->getConnection(); if ($connection instanceof ReplicationInterface) { $connection->switchTo('master'); } return $connection; } protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) { foreach ($commands as $command) { $connection->writeRequest($command); } $responses = array(); $exceptions = $this->throwServerExceptions(); while (!$commands->isEmpty()) { $command = $commands->dequeue(); $response = $connection->readResponse($command); if (!$response instanceof ResponseInterface) { $responses[] = $command->parseResponse($response); } elseif ($response instanceof ErrorResponseInterface && $exceptions) { $this->exception($connection, $response); } else { $responses[] = $response; } } return $responses; } public function flushPipeline($send = true) { if ($send && !$this->pipeline->isEmpty()) { $responses = $this->executePipeline($this->getConnection(), $this->pipeline); $this->responses = array_merge($this->responses, $responses); } else { $this->pipeline = new \SplQueue(); } return $this; } private function setRunning($bool) { if ($bool && $this->running) { throw new ClientException('The current pipeline context is already being executed.'); } $this->running = $bool; } public function execute($callable = null) { if ($callable && !is_callable($callable)) { throw new \InvalidArgumentException('The argument must be a callable object.'); } $exception = null; $this->setRunning(true); try { if ($callable) { call_user_func($callable, $this); } $this->flushPipeline(); } catch (\Exception $exception) { } $this->setRunning(false); if ($exception) { throw $exception; } return $this->responses; } protected function throwServerExceptions() { return (bool) $this->client->getOptions()->exceptions; } public function getClient() { return $this->client; } } assertClient($client); $this->client = $client; $this->start(); } public function __destruct() { $this->stop(); } private function assertClient(ClientInterface $client) { if ($client->getConnection() instanceof AggregateConnectionInterface) { throw new NotSupportedException( 'Cannot initialize a monitor consumer over aggregate connections.' ); } if ($client->getProfile()->supportsCommand('MONITOR') === false) { throw new NotSupportedException("The current profile does not support 'MONITOR'."); } } protected function start() { $this->client->executeCommand( $this->client->createCommand('MONITOR') ); $this->valid = true; } public function stop() { $this->client->disconnect(); $this->valid = false; } #[\ReturnTypeWillChange] public function rewind() { } #[\ReturnTypeWillChange] public function current() { return $this->getValue(); } #[\ReturnTypeWillChange] public function key() { return $this->position; } #[\ReturnTypeWillChange] public function next() { ++$this->position; } #[\ReturnTypeWillChange] public function valid() { return $this->valid; } private function getValue() { $database = 0; $client = null; $event = $this->client->getConnection()->read(); $callback = function ($matches) use (&$database, &$client) { if (2 === $count = count($matches)) { $database = (int) $matches[1]; } if (4 === $count) { $database = (int) $matches[2]; $client = $matches[3]; } return ' '; }; $event = preg_replace_callback('/ \(db (\d+)\) | \[(\d+) (.*?)\] /', $callback, $event, 1); @list($timestamp, $command, $arguments) = explode(' ', $event, 3); return (object) array( 'timestamp' => (float) $timestamp, 'database' => $database, 'client' => $client, 'command' => substr($command, 1, -1), 'arguments' => $arguments, ); } } checkCapabilities($client); $this->options = $options ?: array(); $this->client = $client; $this->genericSubscribeInit('subscribe'); $this->genericSubscribeInit('psubscribe'); } public function getClient() { return $this->client; } private function checkCapabilities(ClientInterface $client) { if ($client->getConnection() instanceof AggregateConnectionInterface) { throw new NotSupportedException( 'Cannot initialize a PUB/SUB consumer over aggregate connections.' ); } $commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe'); if ($client->getProfile()->supportsCommands($commands) === false) { throw new NotSupportedException( 'The current profile does not support PUB/SUB related commands.' ); } } private function genericSubscribeInit($subscribeAction) { if (isset($this->options[$subscribeAction])) { $this->$subscribeAction($this->options[$subscribeAction]); } } protected function writeRequest($method, $arguments) { $this->client->getConnection()->writeRequest( $this->client->createCommand($method, Command::normalizeArguments($arguments) ) ); } protected function disconnect() { $this->client->disconnect(); } protected function getValue() { $response = $this->client->getConnection()->read(); switch ($response[0]) { case self::SUBSCRIBE: case self::UNSUBSCRIBE: case self::PSUBSCRIBE: case self::PUNSUBSCRIBE: if ($response[2] === 0) { $this->invalidate(); } case self::MESSAGE: return (object) array( 'kind' => $response[0], 'channel' => $response[1], 'payload' => $response[2], ); case self::PMESSAGE: return (object) array( 'kind' => $response[0], 'pattern' => $response[1], 'channel' => $response[2], 'payload' => $response[3], ); case self::PONG: return (object) array( 'kind' => $response[0], 'payload' => $response[1], ); default: throw new ClientException( "Unknown message type '{$response[0]}' received in the PUB/SUB context." ); } } } stop(true); } protected function isFlagSet($value) { return ($this->statusFlags & $value) === $value; } public function subscribe($channel ) { $this->writeRequest(self::SUBSCRIBE, func_get_args()); $this->statusFlags |= self::STATUS_SUBSCRIBED; } public function unsubscribe() { $this->writeRequest(self::UNSUBSCRIBE, func_get_args()); } public function psubscribe($pattern ) { $this->writeRequest(self::PSUBSCRIBE, func_get_args()); $this->statusFlags |= self::STATUS_PSUBSCRIBED; } public function punsubscribe() { $this->writeRequest(self::PUNSUBSCRIBE, func_get_args()); } public function ping($payload = null) { $this->writeRequest('PING', array($payload)); } public function stop($drop = false) { if (!$this->valid()) { return false; } if ($drop) { $this->invalidate(); $this->disconnect(); } else { if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) { $this->unsubscribe(); } if ($this->isFlagSet(self::STATUS_PSUBSCRIBED)) { $this->punsubscribe(); } } return !$drop; } abstract protected function disconnect(); abstract protected function writeRequest($method, $arguments); #[\ReturnTypeWillChange] public function rewind() { } #[\ReturnTypeWillChange] public function current() { return $this->getValue(); } #[\ReturnTypeWillChange] public function key() { return $this->position; } #[\ReturnTypeWillChange] public function next() { if ($this->valid()) { ++$this->position; } return $this->position; } #[\ReturnTypeWillChange] public function valid() { $isValid = $this->isFlagSet(self::STATUS_VALID); $subscriptionFlags = self::STATUS_SUBSCRIBED | self::STATUS_PSUBSCRIBED; $hasSubscriptions = ($this->statusFlags & $subscriptionFlags) > 0; return $isValid && $hasSubscriptions; } protected function invalidate() { $this->statusFlags = 0; } abstract protected function getValue(); } callbacks = array(); $this->pubsub = $pubsub; } protected function assertCallback($callable) { if (!is_callable($callable)) { throw new \InvalidArgumentException('The given argument must be a callable object.'); } } public function getPubSubConsumer() { return $this->pubsub; } public function subscriptionCallback($callable = null) { if (isset($callable)) { $this->assertCallback($callable); } $this->subscriptionCallback = $callable; } public function defaultCallback($callable = null) { if (isset($callable)) { $this->assertCallback($callable); } $this->subscriptionCallback = $callable; } public function attachCallback($channel, $callback) { $callbackName = $this->getPrefixKeys().$channel; $this->assertCallback($callback); $this->callbacks[$callbackName] = $callback; $this->pubsub->subscribe($channel); } public function detachCallback($channel) { $callbackName = $this->getPrefixKeys().$channel; if (isset($this->callbacks[$callbackName])) { unset($this->callbacks[$callbackName]); $this->pubsub->unsubscribe($channel); } } public function run() { foreach ($this->pubsub as $message) { $kind = $message->kind; if ($kind !== Consumer::MESSAGE && $kind !== Consumer::PMESSAGE) { if (isset($this->subscriptionCallback)) { $callback = $this->subscriptionCallback; call_user_func($callback, $message); } continue; } if (isset($this->callbacks[$message->channel])) { $callback = $this->callbacks[$message->channel]; call_user_func($callback, $message->payload); } elseif (isset($this->defaultCallback)) { $callback = $this->defaultCallback; call_user_func($callback, $message); } } } public function stop() { $this->pubsub->stop(); } protected function getPrefixKeys() { $options = $this->pubsub->getClient()->getOptions(); if (isset($options->prefix)) { return $options->prefix->getPrefix(); } return ''; } } 'Predis\Command\KeyExists', 'DEL' => 'Predis\Command\KeyDelete', 'TYPE' => 'Predis\Command\KeyType', 'KEYS' => 'Predis\Command\KeyKeys', 'RANDOMKEY' => 'Predis\Command\KeyRandom', 'RENAME' => 'Predis\Command\KeyRename', 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', 'EXPIRE' => 'Predis\Command\KeyExpire', 'EXPIREAT' => 'Predis\Command\KeyExpireAt', 'TTL' => 'Predis\Command\KeyTimeToLive', 'MOVE' => 'Predis\Command\KeyMove', 'SORT' => 'Predis\Command\KeySort', 'SET' => 'Predis\Command\StringSet', 'SETNX' => 'Predis\Command\StringSetPreserve', 'MSET' => 'Predis\Command\StringSetMultiple', 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', 'GET' => 'Predis\Command\StringGet', 'MGET' => 'Predis\Command\StringGetMultiple', 'GETSET' => 'Predis\Command\StringGetSet', 'INCR' => 'Predis\Command\StringIncrement', 'INCRBY' => 'Predis\Command\StringIncrementBy', 'DECR' => 'Predis\Command\StringDecrement', 'DECRBY' => 'Predis\Command\StringDecrementBy', 'RPUSH' => 'Predis\Command\ListPushTail', 'LPUSH' => 'Predis\Command\ListPushHead', 'LLEN' => 'Predis\Command\ListLength', 'LRANGE' => 'Predis\Command\ListRange', 'LTRIM' => 'Predis\Command\ListTrim', 'LINDEX' => 'Predis\Command\ListIndex', 'LSET' => 'Predis\Command\ListSet', 'LREM' => 'Predis\Command\ListRemove', 'LPOP' => 'Predis\Command\ListPopFirst', 'RPOP' => 'Predis\Command\ListPopLast', 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', 'SADD' => 'Predis\Command\SetAdd', 'SREM' => 'Predis\Command\SetRemove', 'SPOP' => 'Predis\Command\SetPop', 'SMOVE' => 'Predis\Command\SetMove', 'SCARD' => 'Predis\Command\SetCardinality', 'SISMEMBER' => 'Predis\Command\SetIsMember', 'SINTER' => 'Predis\Command\SetIntersection', 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', 'SUNION' => 'Predis\Command\SetUnion', 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', 'SDIFF' => 'Predis\Command\SetDifference', 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', 'SMEMBERS' => 'Predis\Command\SetMembers', 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', 'ZADD' => 'Predis\Command\ZSetAdd', 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', 'ZREM' => 'Predis\Command\ZSetRemove', 'ZRANGE' => 'Predis\Command\ZSetRange', 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', 'ZCARD' => 'Predis\Command\ZSetCardinality', 'ZSCORE' => 'Predis\Command\ZSetScore', 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', 'PING' => 'Predis\Command\ConnectionPing', 'AUTH' => 'Predis\Command\ConnectionAuth', 'SELECT' => 'Predis\Command\ConnectionSelect', 'ECHO' => 'Predis\Command\ConnectionEcho', 'QUIT' => 'Predis\Command\ConnectionQuit', 'INFO' => 'Predis\Command\ServerInfo', 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', 'MONITOR' => 'Predis\Command\ServerMonitor', 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', 'FLUSHALL' => 'Predis\Command\ServerFlushAll', 'SAVE' => 'Predis\Command\ServerSave', 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', 'LASTSAVE' => 'Predis\Command\ServerLastSave', 'SHUTDOWN' => 'Predis\Command\ServerShutdown', 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', 'SETEX' => 'Predis\Command\StringSetExpire', 'APPEND' => 'Predis\Command\StringAppend', 'SUBSTR' => 'Predis\Command\StringSubstr', 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', 'BRPOP' => 'Predis\Command\ListPopLastBlocking', 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', 'ZCOUNT' => 'Predis\Command\ZSetCount', 'ZRANK' => 'Predis\Command\ZSetRank', 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', 'HSET' => 'Predis\Command\HashSet', 'HSETNX' => 'Predis\Command\HashSetPreserve', 'HMSET' => 'Predis\Command\HashSetMultiple', 'HINCRBY' => 'Predis\Command\HashIncrementBy', 'HGET' => 'Predis\Command\HashGet', 'HMGET' => 'Predis\Command\HashGetMultiple', 'HDEL' => 'Predis\Command\HashDelete', 'HEXISTS' => 'Predis\Command\HashExists', 'HLEN' => 'Predis\Command\HashLength', 'HKEYS' => 'Predis\Command\HashKeys', 'HVALS' => 'Predis\Command\HashValues', 'HGETALL' => 'Predis\Command\HashGetAll', 'MULTI' => 'Predis\Command\TransactionMulti', 'EXEC' => 'Predis\Command\TransactionExec', 'DISCARD' => 'Predis\Command\TransactionDiscard', 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', 'PUBLISH' => 'Predis\Command\PubSubPublish', 'CONFIG' => 'Predis\Command\ServerConfig', 'PERSIST' => 'Predis\Command\KeyPersist', 'STRLEN' => 'Predis\Command\StringStrlen', 'SETRANGE' => 'Predis\Command\StringSetRange', 'GETRANGE' => 'Predis\Command\StringGetRange', 'SETBIT' => 'Predis\Command\StringSetBit', 'GETBIT' => 'Predis\Command\StringGetBit', 'RPUSHX' => 'Predis\Command\ListPushTailX', 'LPUSHX' => 'Predis\Command\ListPushHeadX', 'LINSERT' => 'Predis\Command\ListInsert', 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', 'WATCH' => 'Predis\Command\TransactionWatch', 'UNWATCH' => 'Predis\Command\TransactionUnwatch', 'OBJECT' => 'Predis\Command\ServerObject', 'SLOWLOG' => 'Predis\Command\ServerSlowlog', ); } } 'Predis\Command\KeyExists', 'DEL' => 'Predis\Command\KeyDelete', 'TYPE' => 'Predis\Command\KeyType', 'KEYS' => 'Predis\Command\KeyKeys', 'RANDOMKEY' => 'Predis\Command\KeyRandom', 'RENAME' => 'Predis\Command\KeyRename', 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', 'EXPIRE' => 'Predis\Command\KeyExpire', 'EXPIREAT' => 'Predis\Command\KeyExpireAt', 'TTL' => 'Predis\Command\KeyTimeToLive', 'MOVE' => 'Predis\Command\KeyMove', 'SORT' => 'Predis\Command\KeySort', 'SET' => 'Predis\Command\StringSet', 'SETNX' => 'Predis\Command\StringSetPreserve', 'MSET' => 'Predis\Command\StringSetMultiple', 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', 'GET' => 'Predis\Command\StringGet', 'MGET' => 'Predis\Command\StringGetMultiple', 'GETSET' => 'Predis\Command\StringGetSet', 'INCR' => 'Predis\Command\StringIncrement', 'INCRBY' => 'Predis\Command\StringIncrementBy', 'DECR' => 'Predis\Command\StringDecrement', 'DECRBY' => 'Predis\Command\StringDecrementBy', 'RPUSH' => 'Predis\Command\ListPushTail', 'LPUSH' => 'Predis\Command\ListPushHead', 'LLEN' => 'Predis\Command\ListLength', 'LRANGE' => 'Predis\Command\ListRange', 'LTRIM' => 'Predis\Command\ListTrim', 'LINDEX' => 'Predis\Command\ListIndex', 'LSET' => 'Predis\Command\ListSet', 'LREM' => 'Predis\Command\ListRemove', 'LPOP' => 'Predis\Command\ListPopFirst', 'RPOP' => 'Predis\Command\ListPopLast', 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', 'SADD' => 'Predis\Command\SetAdd', 'SREM' => 'Predis\Command\SetRemove', 'SPOP' => 'Predis\Command\SetPop', 'SMOVE' => 'Predis\Command\SetMove', 'SCARD' => 'Predis\Command\SetCardinality', 'SISMEMBER' => 'Predis\Command\SetIsMember', 'SINTER' => 'Predis\Command\SetIntersection', 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', 'SUNION' => 'Predis\Command\SetUnion', 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', 'SDIFF' => 'Predis\Command\SetDifference', 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', 'SMEMBERS' => 'Predis\Command\SetMembers', 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', 'ZADD' => 'Predis\Command\ZSetAdd', 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', 'ZREM' => 'Predis\Command\ZSetRemove', 'ZRANGE' => 'Predis\Command\ZSetRange', 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', 'ZCARD' => 'Predis\Command\ZSetCardinality', 'ZSCORE' => 'Predis\Command\ZSetScore', 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', 'PING' => 'Predis\Command\ConnectionPing', 'AUTH' => 'Predis\Command\ConnectionAuth', 'SELECT' => 'Predis\Command\ConnectionSelect', 'ECHO' => 'Predis\Command\ConnectionEcho', 'QUIT' => 'Predis\Command\ConnectionQuit', 'INFO' => 'Predis\Command\ServerInfo', 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', 'MONITOR' => 'Predis\Command\ServerMonitor', 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', 'FLUSHALL' => 'Predis\Command\ServerFlushAll', 'SAVE' => 'Predis\Command\ServerSave', 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', 'LASTSAVE' => 'Predis\Command\ServerLastSave', 'SHUTDOWN' => 'Predis\Command\ServerShutdown', 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', 'SETEX' => 'Predis\Command\StringSetExpire', 'APPEND' => 'Predis\Command\StringAppend', 'SUBSTR' => 'Predis\Command\StringSubstr', 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', 'BRPOP' => 'Predis\Command\ListPopLastBlocking', 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', 'ZCOUNT' => 'Predis\Command\ZSetCount', 'ZRANK' => 'Predis\Command\ZSetRank', 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', 'HSET' => 'Predis\Command\HashSet', 'HSETNX' => 'Predis\Command\HashSetPreserve', 'HMSET' => 'Predis\Command\HashSetMultiple', 'HINCRBY' => 'Predis\Command\HashIncrementBy', 'HGET' => 'Predis\Command\HashGet', 'HMGET' => 'Predis\Command\HashGetMultiple', 'HDEL' => 'Predis\Command\HashDelete', 'HEXISTS' => 'Predis\Command\HashExists', 'HLEN' => 'Predis\Command\HashLength', 'HKEYS' => 'Predis\Command\HashKeys', 'HVALS' => 'Predis\Command\HashValues', 'HGETALL' => 'Predis\Command\HashGetAll', 'MULTI' => 'Predis\Command\TransactionMulti', 'EXEC' => 'Predis\Command\TransactionExec', 'DISCARD' => 'Predis\Command\TransactionDiscard', 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', 'PUBLISH' => 'Predis\Command\PubSubPublish', 'CONFIG' => 'Predis\Command\ServerConfig', ); } } 'Predis\Command\KeyExists', 'DEL' => 'Predis\Command\KeyDelete', 'TYPE' => 'Predis\Command\KeyType', 'KEYS' => 'Predis\Command\KeyKeys', 'RANDOMKEY' => 'Predis\Command\KeyRandom', 'RENAME' => 'Predis\Command\KeyRename', 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', 'EXPIRE' => 'Predis\Command\KeyExpire', 'EXPIREAT' => 'Predis\Command\KeyExpireAt', 'TTL' => 'Predis\Command\KeyTimeToLive', 'MOVE' => 'Predis\Command\KeyMove', 'SORT' => 'Predis\Command\KeySort', 'DUMP' => 'Predis\Command\KeyDump', 'RESTORE' => 'Predis\Command\KeyRestore', 'SET' => 'Predis\Command\StringSet', 'SETNX' => 'Predis\Command\StringSetPreserve', 'MSET' => 'Predis\Command\StringSetMultiple', 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', 'GET' => 'Predis\Command\StringGet', 'MGET' => 'Predis\Command\StringGetMultiple', 'GETSET' => 'Predis\Command\StringGetSet', 'INCR' => 'Predis\Command\StringIncrement', 'INCRBY' => 'Predis\Command\StringIncrementBy', 'DECR' => 'Predis\Command\StringDecrement', 'DECRBY' => 'Predis\Command\StringDecrementBy', 'RPUSH' => 'Predis\Command\ListPushTail', 'LPUSH' => 'Predis\Command\ListPushHead', 'LLEN' => 'Predis\Command\ListLength', 'LRANGE' => 'Predis\Command\ListRange', 'LTRIM' => 'Predis\Command\ListTrim', 'LINDEX' => 'Predis\Command\ListIndex', 'LSET' => 'Predis\Command\ListSet', 'LREM' => 'Predis\Command\ListRemove', 'LPOP' => 'Predis\Command\ListPopFirst', 'RPOP' => 'Predis\Command\ListPopLast', 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', 'SADD' => 'Predis\Command\SetAdd', 'SREM' => 'Predis\Command\SetRemove', 'SPOP' => 'Predis\Command\SetPop', 'SMOVE' => 'Predis\Command\SetMove', 'SCARD' => 'Predis\Command\SetCardinality', 'SISMEMBER' => 'Predis\Command\SetIsMember', 'SINTER' => 'Predis\Command\SetIntersection', 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', 'SUNION' => 'Predis\Command\SetUnion', 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', 'SDIFF' => 'Predis\Command\SetDifference', 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', 'SMEMBERS' => 'Predis\Command\SetMembers', 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', 'ZADD' => 'Predis\Command\ZSetAdd', 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', 'ZREM' => 'Predis\Command\ZSetRemove', 'ZRANGE' => 'Predis\Command\ZSetRange', 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', 'ZCARD' => 'Predis\Command\ZSetCardinality', 'ZSCORE' => 'Predis\Command\ZSetScore', 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', 'PING' => 'Predis\Command\ConnectionPing', 'AUTH' => 'Predis\Command\ConnectionAuth', 'SELECT' => 'Predis\Command\ConnectionSelect', 'ECHO' => 'Predis\Command\ConnectionEcho', 'QUIT' => 'Predis\Command\ConnectionQuit', 'INFO' => 'Predis\Command\ServerInfoV26x', 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', 'MONITOR' => 'Predis\Command\ServerMonitor', 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', 'FLUSHALL' => 'Predis\Command\ServerFlushAll', 'SAVE' => 'Predis\Command\ServerSave', 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', 'LASTSAVE' => 'Predis\Command\ServerLastSave', 'SHUTDOWN' => 'Predis\Command\ServerShutdown', 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', 'SETEX' => 'Predis\Command\StringSetExpire', 'APPEND' => 'Predis\Command\StringAppend', 'SUBSTR' => 'Predis\Command\StringSubstr', 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', 'BRPOP' => 'Predis\Command\ListPopLastBlocking', 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', 'ZCOUNT' => 'Predis\Command\ZSetCount', 'ZRANK' => 'Predis\Command\ZSetRank', 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', 'HSET' => 'Predis\Command\HashSet', 'HSETNX' => 'Predis\Command\HashSetPreserve', 'HMSET' => 'Predis\Command\HashSetMultiple', 'HINCRBY' => 'Predis\Command\HashIncrementBy', 'HGET' => 'Predis\Command\HashGet', 'HMGET' => 'Predis\Command\HashGetMultiple', 'HDEL' => 'Predis\Command\HashDelete', 'HEXISTS' => 'Predis\Command\HashExists', 'HLEN' => 'Predis\Command\HashLength', 'HKEYS' => 'Predis\Command\HashKeys', 'HVALS' => 'Predis\Command\HashValues', 'HGETALL' => 'Predis\Command\HashGetAll', 'MULTI' => 'Predis\Command\TransactionMulti', 'EXEC' => 'Predis\Command\TransactionExec', 'DISCARD' => 'Predis\Command\TransactionDiscard', 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', 'PUBLISH' => 'Predis\Command\PubSubPublish', 'CONFIG' => 'Predis\Command\ServerConfig', 'PERSIST' => 'Predis\Command\KeyPersist', 'STRLEN' => 'Predis\Command\StringStrlen', 'SETRANGE' => 'Predis\Command\StringSetRange', 'GETRANGE' => 'Predis\Command\StringGetRange', 'SETBIT' => 'Predis\Command\StringSetBit', 'GETBIT' => 'Predis\Command\StringGetBit', 'RPUSHX' => 'Predis\Command\ListPushTailX', 'LPUSHX' => 'Predis\Command\ListPushHeadX', 'LINSERT' => 'Predis\Command\ListInsert', 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', 'WATCH' => 'Predis\Command\TransactionWatch', 'UNWATCH' => 'Predis\Command\TransactionUnwatch', 'OBJECT' => 'Predis\Command\ServerObject', 'SLOWLOG' => 'Predis\Command\ServerSlowlog', 'CLIENT' => 'Predis\Command\ServerClient', 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', 'MIGRATE' => 'Predis\Command\KeyMigrate', 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', 'BITOP' => 'Predis\Command\StringBitOp', 'BITCOUNT' => 'Predis\Command\StringBitCount', 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', 'EVAL' => 'Predis\Command\ServerEval', 'EVALSHA' => 'Predis\Command\ServerEvalSHA', 'SCRIPT' => 'Predis\Command\ServerScript', 'TIME' => 'Predis\Command\ServerTime', 'SENTINEL' => 'Predis\Command\ServerSentinel', 'SCAN' => 'Predis\Command\KeyScan', 'BITPOS' => 'Predis\Command\StringBitPos', 'SSCAN' => 'Predis\Command\SetScan', 'ZSCAN' => 'Predis\Command\ZSetScan', 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', 'HSCAN' => 'Predis\Command\HashScan', 'PUBSUB' => 'Predis\Command\PubSubPubsub', 'PFADD' => 'Predis\Command\HyperLogLogAdd', 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', 'COMMAND' => 'Predis\Command\ServerCommand', ); } } 'Predis\Command\KeyExists', 'DEL' => 'Predis\Command\KeyDelete', 'TYPE' => 'Predis\Command\KeyType', 'KEYS' => 'Predis\Command\KeyKeys', 'RANDOMKEY' => 'Predis\Command\KeyRandom', 'RENAME' => 'Predis\Command\KeyRename', 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', 'EXPIRE' => 'Predis\Command\KeyExpire', 'EXPIREAT' => 'Predis\Command\KeyExpireAt', 'TTL' => 'Predis\Command\KeyTimeToLive', 'MOVE' => 'Predis\Command\KeyMove', 'SORT' => 'Predis\Command\KeySort', 'DUMP' => 'Predis\Command\KeyDump', 'RESTORE' => 'Predis\Command\KeyRestore', 'SET' => 'Predis\Command\StringSet', 'SETNX' => 'Predis\Command\StringSetPreserve', 'MSET' => 'Predis\Command\StringSetMultiple', 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', 'GET' => 'Predis\Command\StringGet', 'MGET' => 'Predis\Command\StringGetMultiple', 'GETSET' => 'Predis\Command\StringGetSet', 'INCR' => 'Predis\Command\StringIncrement', 'INCRBY' => 'Predis\Command\StringIncrementBy', 'DECR' => 'Predis\Command\StringDecrement', 'DECRBY' => 'Predis\Command\StringDecrementBy', 'RPUSH' => 'Predis\Command\ListPushTail', 'LPUSH' => 'Predis\Command\ListPushHead', 'LLEN' => 'Predis\Command\ListLength', 'LRANGE' => 'Predis\Command\ListRange', 'LTRIM' => 'Predis\Command\ListTrim', 'LINDEX' => 'Predis\Command\ListIndex', 'LSET' => 'Predis\Command\ListSet', 'LREM' => 'Predis\Command\ListRemove', 'LPOP' => 'Predis\Command\ListPopFirst', 'RPOP' => 'Predis\Command\ListPopLast', 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', 'SADD' => 'Predis\Command\SetAdd', 'SREM' => 'Predis\Command\SetRemove', 'SPOP' => 'Predis\Command\SetPop', 'SMOVE' => 'Predis\Command\SetMove', 'SCARD' => 'Predis\Command\SetCardinality', 'SISMEMBER' => 'Predis\Command\SetIsMember', 'SINTER' => 'Predis\Command\SetIntersection', 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', 'SUNION' => 'Predis\Command\SetUnion', 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', 'SDIFF' => 'Predis\Command\SetDifference', 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', 'SMEMBERS' => 'Predis\Command\SetMembers', 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', 'ZADD' => 'Predis\Command\ZSetAdd', 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', 'ZREM' => 'Predis\Command\ZSetRemove', 'ZRANGE' => 'Predis\Command\ZSetRange', 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', 'ZCARD' => 'Predis\Command\ZSetCardinality', 'ZSCORE' => 'Predis\Command\ZSetScore', 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', 'PING' => 'Predis\Command\ConnectionPing', 'AUTH' => 'Predis\Command\ConnectionAuth', 'SELECT' => 'Predis\Command\ConnectionSelect', 'ECHO' => 'Predis\Command\ConnectionEcho', 'QUIT' => 'Predis\Command\ConnectionQuit', 'INFO' => 'Predis\Command\ServerInfoV26x', 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', 'MONITOR' => 'Predis\Command\ServerMonitor', 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', 'FLUSHALL' => 'Predis\Command\ServerFlushAll', 'SAVE' => 'Predis\Command\ServerSave', 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', 'LASTSAVE' => 'Predis\Command\ServerLastSave', 'SHUTDOWN' => 'Predis\Command\ServerShutdown', 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', 'SETEX' => 'Predis\Command\StringSetExpire', 'APPEND' => 'Predis\Command\StringAppend', 'SUBSTR' => 'Predis\Command\StringSubstr', 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', 'BRPOP' => 'Predis\Command\ListPopLastBlocking', 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', 'ZCOUNT' => 'Predis\Command\ZSetCount', 'ZRANK' => 'Predis\Command\ZSetRank', 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', 'HSET' => 'Predis\Command\HashSet', 'HSETNX' => 'Predis\Command\HashSetPreserve', 'HMSET' => 'Predis\Command\HashSetMultiple', 'HINCRBY' => 'Predis\Command\HashIncrementBy', 'HGET' => 'Predis\Command\HashGet', 'HMGET' => 'Predis\Command\HashGetMultiple', 'HDEL' => 'Predis\Command\HashDelete', 'HEXISTS' => 'Predis\Command\HashExists', 'HLEN' => 'Predis\Command\HashLength', 'HKEYS' => 'Predis\Command\HashKeys', 'HVALS' => 'Predis\Command\HashValues', 'HGETALL' => 'Predis\Command\HashGetAll', 'MULTI' => 'Predis\Command\TransactionMulti', 'EXEC' => 'Predis\Command\TransactionExec', 'DISCARD' => 'Predis\Command\TransactionDiscard', 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', 'PUBLISH' => 'Predis\Command\PubSubPublish', 'CONFIG' => 'Predis\Command\ServerConfig', 'PERSIST' => 'Predis\Command\KeyPersist', 'STRLEN' => 'Predis\Command\StringStrlen', 'SETRANGE' => 'Predis\Command\StringSetRange', 'GETRANGE' => 'Predis\Command\StringGetRange', 'SETBIT' => 'Predis\Command\StringSetBit', 'GETBIT' => 'Predis\Command\StringGetBit', 'RPUSHX' => 'Predis\Command\ListPushTailX', 'LPUSHX' => 'Predis\Command\ListPushHeadX', 'LINSERT' => 'Predis\Command\ListInsert', 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', 'WATCH' => 'Predis\Command\TransactionWatch', 'UNWATCH' => 'Predis\Command\TransactionUnwatch', 'OBJECT' => 'Predis\Command\ServerObject', 'SLOWLOG' => 'Predis\Command\ServerSlowlog', 'CLIENT' => 'Predis\Command\ServerClient', 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', 'MIGRATE' => 'Predis\Command\KeyMigrate', 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', 'BITOP' => 'Predis\Command\StringBitOp', 'BITCOUNT' => 'Predis\Command\StringBitCount', 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', 'EVAL' => 'Predis\Command\ServerEval', 'EVALSHA' => 'Predis\Command\ServerEvalSHA', 'SCRIPT' => 'Predis\Command\ServerScript', 'TIME' => 'Predis\Command\ServerTime', 'SENTINEL' => 'Predis\Command\ServerSentinel', 'SCAN' => 'Predis\Command\KeyScan', 'BITPOS' => 'Predis\Command\StringBitPos', 'SSCAN' => 'Predis\Command\SetScan', 'ZSCAN' => 'Predis\Command\ZSetScan', 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', 'HSCAN' => 'Predis\Command\HashScan', 'PUBSUB' => 'Predis\Command\PubSubPubsub', 'PFADD' => 'Predis\Command\HyperLogLogAdd', 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', 'COMMAND' => 'Predis\Command\ServerCommand', 'HSTRLEN' => 'Predis\Command\HashStringLength', 'BITFIELD' => 'Predis\Command\StringBitField', 'GEOADD' => 'Predis\Command\GeospatialGeoAdd', 'GEOHASH' => 'Predis\Command\GeospatialGeoHash', 'GEOPOS' => 'Predis\Command\GeospatialGeoPos', 'GEODIST' => 'Predis\Command\GeospatialGeoDist', 'GEORADIUS' => 'Predis\Command\GeospatialGeoRadius', 'GEORADIUSBYMEMBER' => 'Predis\Command\GeospatialGeoRadiusByMember', ); } } commands = $this->getSupportedCommands(); } abstract protected function getSupportedCommands(); public function supportsCommand($commandID) { return isset($this->commands[strtoupper($commandID)]); } public function supportsCommands(array $commandIDs) { foreach ($commandIDs as $commandID) { if (!$this->supportsCommand($commandID)) { return false; } } return true; } public function getCommandClass($commandID) { if (isset($this->commands[$commandID = strtoupper($commandID)])) { return $this->commands[$commandID]; } } public function createCommand($commandID, array $arguments = array()) { $commandID = strtoupper($commandID); if (!isset($this->commands[$commandID])) { throw new ClientException("Command '$commandID' is not a registered Redis command."); } $commandClass = $this->commands[$commandID]; $command = new $commandClass(); $command->setArguments($arguments); if (isset($this->processor)) { $this->processor->process($command); } return $command; } public function defineCommand($commandID, $class) { $reflection = new \ReflectionClass($class); if (!$reflection->isSubclassOf('Predis\Command\CommandInterface')) { throw new \InvalidArgumentException("The class '$class' is not a valid command class."); } $this->commands[strtoupper($commandID)] = $class; } public function setProcessor(ProcessorInterface $processor = null) { $this->processor = $processor; } public function getProcessor() { return $this->processor; } public function __toString() { return $this->getVersion(); } } 'Predis\Profile\RedisVersion200', '2.2' => 'Predis\Profile\RedisVersion220', '2.4' => 'Predis\Profile\RedisVersion240', '2.6' => 'Predis\Profile\RedisVersion260', '2.8' => 'Predis\Profile\RedisVersion280', '3.0' => 'Predis\Profile\RedisVersion300', '3.2' => 'Predis\Profile\RedisVersion320', 'dev' => 'Predis\Profile\RedisUnstable', 'default' => 'Predis\Profile\RedisVersion320', ); private function __construct() { } public static function getDefault() { return self::get('default'); } public static function getDevelopment() { return self::get('dev'); } public static function define($alias, $class) { $reflection = new \ReflectionClass($class); if (!$reflection->isSubclassOf('Predis\Profile\ProfileInterface')) { throw new \InvalidArgumentException("The class '$class' is not a valid profile class."); } self::$profiles[$alias] = $class; } public static function get($version) { if (!isset(self::$profiles[$version])) { throw new ClientException("Unknown server profile: '$version'."); } $profile = self::$profiles[$version]; return new $profile(); } } 'Predis\Command\KeyExists', 'DEL' => 'Predis\Command\KeyDelete', 'TYPE' => 'Predis\Command\KeyType', 'KEYS' => 'Predis\Command\KeyKeys', 'RANDOMKEY' => 'Predis\Command\KeyRandom', 'RENAME' => 'Predis\Command\KeyRename', 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', 'EXPIRE' => 'Predis\Command\KeyExpire', 'EXPIREAT' => 'Predis\Command\KeyExpireAt', 'TTL' => 'Predis\Command\KeyTimeToLive', 'MOVE' => 'Predis\Command\KeyMove', 'SORT' => 'Predis\Command\KeySort', 'DUMP' => 'Predis\Command\KeyDump', 'RESTORE' => 'Predis\Command\KeyRestore', 'SET' => 'Predis\Command\StringSet', 'SETNX' => 'Predis\Command\StringSetPreserve', 'MSET' => 'Predis\Command\StringSetMultiple', 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', 'GET' => 'Predis\Command\StringGet', 'MGET' => 'Predis\Command\StringGetMultiple', 'GETSET' => 'Predis\Command\StringGetSet', 'INCR' => 'Predis\Command\StringIncrement', 'INCRBY' => 'Predis\Command\StringIncrementBy', 'DECR' => 'Predis\Command\StringDecrement', 'DECRBY' => 'Predis\Command\StringDecrementBy', 'RPUSH' => 'Predis\Command\ListPushTail', 'LPUSH' => 'Predis\Command\ListPushHead', 'LLEN' => 'Predis\Command\ListLength', 'LRANGE' => 'Predis\Command\ListRange', 'LTRIM' => 'Predis\Command\ListTrim', 'LINDEX' => 'Predis\Command\ListIndex', 'LSET' => 'Predis\Command\ListSet', 'LREM' => 'Predis\Command\ListRemove', 'LPOP' => 'Predis\Command\ListPopFirst', 'RPOP' => 'Predis\Command\ListPopLast', 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', 'SADD' => 'Predis\Command\SetAdd', 'SREM' => 'Predis\Command\SetRemove', 'SPOP' => 'Predis\Command\SetPop', 'SMOVE' => 'Predis\Command\SetMove', 'SCARD' => 'Predis\Command\SetCardinality', 'SISMEMBER' => 'Predis\Command\SetIsMember', 'SINTER' => 'Predis\Command\SetIntersection', 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', 'SUNION' => 'Predis\Command\SetUnion', 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', 'SDIFF' => 'Predis\Command\SetDifference', 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', 'SMEMBERS' => 'Predis\Command\SetMembers', 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', 'ZADD' => 'Predis\Command\ZSetAdd', 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', 'ZREM' => 'Predis\Command\ZSetRemove', 'ZRANGE' => 'Predis\Command\ZSetRange', 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', 'ZCARD' => 'Predis\Command\ZSetCardinality', 'ZSCORE' => 'Predis\Command\ZSetScore', 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', 'PING' => 'Predis\Command\ConnectionPing', 'AUTH' => 'Predis\Command\ConnectionAuth', 'SELECT' => 'Predis\Command\ConnectionSelect', 'ECHO' => 'Predis\Command\ConnectionEcho', 'QUIT' => 'Predis\Command\ConnectionQuit', 'INFO' => 'Predis\Command\ServerInfoV26x', 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', 'MONITOR' => 'Predis\Command\ServerMonitor', 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', 'FLUSHALL' => 'Predis\Command\ServerFlushAll', 'SAVE' => 'Predis\Command\ServerSave', 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', 'LASTSAVE' => 'Predis\Command\ServerLastSave', 'SHUTDOWN' => 'Predis\Command\ServerShutdown', 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', 'SETEX' => 'Predis\Command\StringSetExpire', 'APPEND' => 'Predis\Command\StringAppend', 'SUBSTR' => 'Predis\Command\StringSubstr', 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', 'BRPOP' => 'Predis\Command\ListPopLastBlocking', 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', 'ZCOUNT' => 'Predis\Command\ZSetCount', 'ZRANK' => 'Predis\Command\ZSetRank', 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', 'HSET' => 'Predis\Command\HashSet', 'HSETNX' => 'Predis\Command\HashSetPreserve', 'HMSET' => 'Predis\Command\HashSetMultiple', 'HINCRBY' => 'Predis\Command\HashIncrementBy', 'HGET' => 'Predis\Command\HashGet', 'HMGET' => 'Predis\Command\HashGetMultiple', 'HDEL' => 'Predis\Command\HashDelete', 'HEXISTS' => 'Predis\Command\HashExists', 'HLEN' => 'Predis\Command\HashLength', 'HKEYS' => 'Predis\Command\HashKeys', 'HVALS' => 'Predis\Command\HashValues', 'HGETALL' => 'Predis\Command\HashGetAll', 'MULTI' => 'Predis\Command\TransactionMulti', 'EXEC' => 'Predis\Command\TransactionExec', 'DISCARD' => 'Predis\Command\TransactionDiscard', 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', 'PUBLISH' => 'Predis\Command\PubSubPublish', 'CONFIG' => 'Predis\Command\ServerConfig', 'PERSIST' => 'Predis\Command\KeyPersist', 'STRLEN' => 'Predis\Command\StringStrlen', 'SETRANGE' => 'Predis\Command\StringSetRange', 'GETRANGE' => 'Predis\Command\StringGetRange', 'SETBIT' => 'Predis\Command\StringSetBit', 'GETBIT' => 'Predis\Command\StringGetBit', 'RPUSHX' => 'Predis\Command\ListPushTailX', 'LPUSHX' => 'Predis\Command\ListPushHeadX', 'LINSERT' => 'Predis\Command\ListInsert', 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', 'WATCH' => 'Predis\Command\TransactionWatch', 'UNWATCH' => 'Predis\Command\TransactionUnwatch', 'OBJECT' => 'Predis\Command\ServerObject', 'SLOWLOG' => 'Predis\Command\ServerSlowlog', 'CLIENT' => 'Predis\Command\ServerClient', 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', 'MIGRATE' => 'Predis\Command\KeyMigrate', 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', 'BITOP' => 'Predis\Command\StringBitOp', 'BITCOUNT' => 'Predis\Command\StringBitCount', 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', 'EVAL' => 'Predis\Command\ServerEval', 'EVALSHA' => 'Predis\Command\ServerEvalSHA', 'SCRIPT' => 'Predis\Command\ServerScript', 'TIME' => 'Predis\Command\ServerTime', 'SENTINEL' => 'Predis\Command\ServerSentinel', ); } } 'Predis\Command\KeyExists', 'DEL' => 'Predis\Command\KeyDelete', 'TYPE' => 'Predis\Command\KeyType', 'KEYS' => 'Predis\Command\KeyKeys', 'RANDOMKEY' => 'Predis\Command\KeyRandom', 'RENAME' => 'Predis\Command\KeyRename', 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', 'EXPIRE' => 'Predis\Command\KeyExpire', 'EXPIREAT' => 'Predis\Command\KeyExpireAt', 'TTL' => 'Predis\Command\KeyTimeToLive', 'MOVE' => 'Predis\Command\KeyMove', 'SORT' => 'Predis\Command\KeySort', 'SET' => 'Predis\Command\StringSet', 'SETNX' => 'Predis\Command\StringSetPreserve', 'MSET' => 'Predis\Command\StringSetMultiple', 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', 'GET' => 'Predis\Command\StringGet', 'MGET' => 'Predis\Command\StringGetMultiple', 'GETSET' => 'Predis\Command\StringGetSet', 'INCR' => 'Predis\Command\StringIncrement', 'INCRBY' => 'Predis\Command\StringIncrementBy', 'DECR' => 'Predis\Command\StringDecrement', 'DECRBY' => 'Predis\Command\StringDecrementBy', 'RPUSH' => 'Predis\Command\ListPushTail', 'LPUSH' => 'Predis\Command\ListPushHead', 'LLEN' => 'Predis\Command\ListLength', 'LRANGE' => 'Predis\Command\ListRange', 'LTRIM' => 'Predis\Command\ListTrim', 'LINDEX' => 'Predis\Command\ListIndex', 'LSET' => 'Predis\Command\ListSet', 'LREM' => 'Predis\Command\ListRemove', 'LPOP' => 'Predis\Command\ListPopFirst', 'RPOP' => 'Predis\Command\ListPopLast', 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', 'SADD' => 'Predis\Command\SetAdd', 'SREM' => 'Predis\Command\SetRemove', 'SPOP' => 'Predis\Command\SetPop', 'SMOVE' => 'Predis\Command\SetMove', 'SCARD' => 'Predis\Command\SetCardinality', 'SISMEMBER' => 'Predis\Command\SetIsMember', 'SINTER' => 'Predis\Command\SetIntersection', 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', 'SUNION' => 'Predis\Command\SetUnion', 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', 'SDIFF' => 'Predis\Command\SetDifference', 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', 'SMEMBERS' => 'Predis\Command\SetMembers', 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', 'ZADD' => 'Predis\Command\ZSetAdd', 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', 'ZREM' => 'Predis\Command\ZSetRemove', 'ZRANGE' => 'Predis\Command\ZSetRange', 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', 'ZCARD' => 'Predis\Command\ZSetCardinality', 'ZSCORE' => 'Predis\Command\ZSetScore', 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', 'PING' => 'Predis\Command\ConnectionPing', 'AUTH' => 'Predis\Command\ConnectionAuth', 'SELECT' => 'Predis\Command\ConnectionSelect', 'ECHO' => 'Predis\Command\ConnectionEcho', 'QUIT' => 'Predis\Command\ConnectionQuit', 'INFO' => 'Predis\Command\ServerInfo', 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', 'MONITOR' => 'Predis\Command\ServerMonitor', 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', 'FLUSHALL' => 'Predis\Command\ServerFlushAll', 'SAVE' => 'Predis\Command\ServerSave', 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', 'LASTSAVE' => 'Predis\Command\ServerLastSave', 'SHUTDOWN' => 'Predis\Command\ServerShutdown', 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', 'SETEX' => 'Predis\Command\StringSetExpire', 'APPEND' => 'Predis\Command\StringAppend', 'SUBSTR' => 'Predis\Command\StringSubstr', 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', 'BRPOP' => 'Predis\Command\ListPopLastBlocking', 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', 'ZCOUNT' => 'Predis\Command\ZSetCount', 'ZRANK' => 'Predis\Command\ZSetRank', 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', 'HSET' => 'Predis\Command\HashSet', 'HSETNX' => 'Predis\Command\HashSetPreserve', 'HMSET' => 'Predis\Command\HashSetMultiple', 'HINCRBY' => 'Predis\Command\HashIncrementBy', 'HGET' => 'Predis\Command\HashGet', 'HMGET' => 'Predis\Command\HashGetMultiple', 'HDEL' => 'Predis\Command\HashDelete', 'HEXISTS' => 'Predis\Command\HashExists', 'HLEN' => 'Predis\Command\HashLength', 'HKEYS' => 'Predis\Command\HashKeys', 'HVALS' => 'Predis\Command\HashValues', 'HGETALL' => 'Predis\Command\HashGetAll', 'MULTI' => 'Predis\Command\TransactionMulti', 'EXEC' => 'Predis\Command\TransactionExec', 'DISCARD' => 'Predis\Command\TransactionDiscard', 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', 'PUBLISH' => 'Predis\Command\PubSubPublish', 'CONFIG' => 'Predis\Command\ServerConfig', 'PERSIST' => 'Predis\Command\KeyPersist', 'STRLEN' => 'Predis\Command\StringStrlen', 'SETRANGE' => 'Predis\Command\StringSetRange', 'GETRANGE' => 'Predis\Command\StringGetRange', 'SETBIT' => 'Predis\Command\StringSetBit', 'GETBIT' => 'Predis\Command\StringGetBit', 'RPUSHX' => 'Predis\Command\ListPushTailX', 'LPUSHX' => 'Predis\Command\ListPushHeadX', 'LINSERT' => 'Predis\Command\ListInsert', 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', 'WATCH' => 'Predis\Command\TransactionWatch', 'UNWATCH' => 'Predis\Command\TransactionUnwatch', 'OBJECT' => 'Predis\Command\ServerObject', 'SLOWLOG' => 'Predis\Command\ServerSlowlog', 'CLIENT' => 'Predis\Command\ServerClient', ); } } 'Predis\Command\KeyExists', 'DEL' => 'Predis\Command\KeyDelete', 'TYPE' => 'Predis\Command\KeyType', 'KEYS' => 'Predis\Command\KeyKeys', 'RANDOMKEY' => 'Predis\Command\KeyRandom', 'RENAME' => 'Predis\Command\KeyRename', 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', 'EXPIRE' => 'Predis\Command\KeyExpire', 'EXPIREAT' => 'Predis\Command\KeyExpireAt', 'TTL' => 'Predis\Command\KeyTimeToLive', 'MOVE' => 'Predis\Command\KeyMove', 'SORT' => 'Predis\Command\KeySort', 'DUMP' => 'Predis\Command\KeyDump', 'RESTORE' => 'Predis\Command\KeyRestore', 'SET' => 'Predis\Command\StringSet', 'SETNX' => 'Predis\Command\StringSetPreserve', 'MSET' => 'Predis\Command\StringSetMultiple', 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', 'GET' => 'Predis\Command\StringGet', 'MGET' => 'Predis\Command\StringGetMultiple', 'GETSET' => 'Predis\Command\StringGetSet', 'INCR' => 'Predis\Command\StringIncrement', 'INCRBY' => 'Predis\Command\StringIncrementBy', 'DECR' => 'Predis\Command\StringDecrement', 'DECRBY' => 'Predis\Command\StringDecrementBy', 'RPUSH' => 'Predis\Command\ListPushTail', 'LPUSH' => 'Predis\Command\ListPushHead', 'LLEN' => 'Predis\Command\ListLength', 'LRANGE' => 'Predis\Command\ListRange', 'LTRIM' => 'Predis\Command\ListTrim', 'LINDEX' => 'Predis\Command\ListIndex', 'LSET' => 'Predis\Command\ListSet', 'LREM' => 'Predis\Command\ListRemove', 'LPOP' => 'Predis\Command\ListPopFirst', 'RPOP' => 'Predis\Command\ListPopLast', 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', 'SADD' => 'Predis\Command\SetAdd', 'SREM' => 'Predis\Command\SetRemove', 'SPOP' => 'Predis\Command\SetPop', 'SMOVE' => 'Predis\Command\SetMove', 'SCARD' => 'Predis\Command\SetCardinality', 'SISMEMBER' => 'Predis\Command\SetIsMember', 'SINTER' => 'Predis\Command\SetIntersection', 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', 'SUNION' => 'Predis\Command\SetUnion', 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', 'SDIFF' => 'Predis\Command\SetDifference', 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', 'SMEMBERS' => 'Predis\Command\SetMembers', 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', 'ZADD' => 'Predis\Command\ZSetAdd', 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', 'ZREM' => 'Predis\Command\ZSetRemove', 'ZRANGE' => 'Predis\Command\ZSetRange', 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', 'ZCARD' => 'Predis\Command\ZSetCardinality', 'ZSCORE' => 'Predis\Command\ZSetScore', 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', 'PING' => 'Predis\Command\ConnectionPing', 'AUTH' => 'Predis\Command\ConnectionAuth', 'SELECT' => 'Predis\Command\ConnectionSelect', 'ECHO' => 'Predis\Command\ConnectionEcho', 'QUIT' => 'Predis\Command\ConnectionQuit', 'INFO' => 'Predis\Command\ServerInfoV26x', 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', 'MONITOR' => 'Predis\Command\ServerMonitor', 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', 'FLUSHALL' => 'Predis\Command\ServerFlushAll', 'SAVE' => 'Predis\Command\ServerSave', 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', 'LASTSAVE' => 'Predis\Command\ServerLastSave', 'SHUTDOWN' => 'Predis\Command\ServerShutdown', 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', 'SETEX' => 'Predis\Command\StringSetExpire', 'APPEND' => 'Predis\Command\StringAppend', 'SUBSTR' => 'Predis\Command\StringSubstr', 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', 'BRPOP' => 'Predis\Command\ListPopLastBlocking', 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', 'ZCOUNT' => 'Predis\Command\ZSetCount', 'ZRANK' => 'Predis\Command\ZSetRank', 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', 'HSET' => 'Predis\Command\HashSet', 'HSETNX' => 'Predis\Command\HashSetPreserve', 'HMSET' => 'Predis\Command\HashSetMultiple', 'HINCRBY' => 'Predis\Command\HashIncrementBy', 'HGET' => 'Predis\Command\HashGet', 'HMGET' => 'Predis\Command\HashGetMultiple', 'HDEL' => 'Predis\Command\HashDelete', 'HEXISTS' => 'Predis\Command\HashExists', 'HLEN' => 'Predis\Command\HashLength', 'HKEYS' => 'Predis\Command\HashKeys', 'HVALS' => 'Predis\Command\HashValues', 'HGETALL' => 'Predis\Command\HashGetAll', 'MULTI' => 'Predis\Command\TransactionMulti', 'EXEC' => 'Predis\Command\TransactionExec', 'DISCARD' => 'Predis\Command\TransactionDiscard', 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', 'PUBLISH' => 'Predis\Command\PubSubPublish', 'CONFIG' => 'Predis\Command\ServerConfig', 'PERSIST' => 'Predis\Command\KeyPersist', 'STRLEN' => 'Predis\Command\StringStrlen', 'SETRANGE' => 'Predis\Command\StringSetRange', 'GETRANGE' => 'Predis\Command\StringGetRange', 'SETBIT' => 'Predis\Command\StringSetBit', 'GETBIT' => 'Predis\Command\StringGetBit', 'RPUSHX' => 'Predis\Command\ListPushTailX', 'LPUSHX' => 'Predis\Command\ListPushHeadX', 'LINSERT' => 'Predis\Command\ListInsert', 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', 'WATCH' => 'Predis\Command\TransactionWatch', 'UNWATCH' => 'Predis\Command\TransactionUnwatch', 'OBJECT' => 'Predis\Command\ServerObject', 'SLOWLOG' => 'Predis\Command\ServerSlowlog', 'CLIENT' => 'Predis\Command\ServerClient', 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', 'MIGRATE' => 'Predis\Command\KeyMigrate', 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', 'BITOP' => 'Predis\Command\StringBitOp', 'BITCOUNT' => 'Predis\Command\StringBitCount', 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', 'EVAL' => 'Predis\Command\ServerEval', 'EVALSHA' => 'Predis\Command\ServerEvalSHA', 'SCRIPT' => 'Predis\Command\ServerScript', 'TIME' => 'Predis\Command\ServerTime', 'SENTINEL' => 'Predis\Command\ServerSentinel', 'SCAN' => 'Predis\Command\KeyScan', 'BITPOS' => 'Predis\Command\StringBitPos', 'SSCAN' => 'Predis\Command\SetScan', 'ZSCAN' => 'Predis\Command\ZSetScan', 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', 'HSCAN' => 'Predis\Command\HashScan', 'PUBSUB' => 'Predis\Command\PubSubPubsub', 'PFADD' => 'Predis\Command\HyperLogLogAdd', 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', 'COMMAND' => 'Predis\Command\ServerCommand', ); } } parameters = $this->assertParameters($parameters); } public function __destruct() { $this->disconnect(); } abstract protected function assertParameters(ParametersInterface $parameters); abstract protected function createResource(); public function isConnected() { return isset($this->resource); } public function connect() { if (!$this->isConnected()) { $this->resource = $this->createResource(); return true; } return false; } public function disconnect() { unset($this->resource); } public function addConnectCommand(CommandInterface $command) { $this->initCommands[] = $command; } public function executeCommand(CommandInterface $command) { $this->writeRequest($command); return $this->readResponse($command); } public function readResponse(CommandInterface $command) { return $this->read(); } private function createExceptionMessage($message) { $parameters = $this->parameters; if ($parameters->scheme === 'unix') { return "$message [$parameters->scheme:$parameters->path]"; } if (filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return "$message [$parameters->scheme://[$parameters->host]:$parameters->port]"; } return "$message [$parameters->scheme://$parameters->host:$parameters->port]"; } protected function onConnectionError($message, $code = null) { CommunicationException::handle( new ConnectionException($this, static::createExceptionMessage($message), $code) ); } protected function onProtocolError($message) { CommunicationException::handle( new ProtocolException($this, static::createExceptionMessage($message)) ); } public function getResource() { if (isset($this->resource)) { return $this->resource; } $this->connect(); return $this->resource; } public function getParameters() { return $this->parameters; } protected function getIdentifier() { if ($this->parameters->scheme === 'unix') { return $this->parameters->path; } return "{$this->parameters->host}:{$this->parameters->port}"; } public function __toString() { if (!isset($this->cachedId)) { $this->cachedId = $this->getIdentifier(); } return $this->cachedId; } public function __sleep() { return array('parameters', 'initCommands'); } } assertExtensions(); parent::__construct($parameters); $this->reader = $this->createReader(); } public function __destruct() { parent::__destruct(); phpiredis_reader_destroy($this->reader); } public function disconnect() { phpiredis_reader_reset($this->reader); parent::disconnect(); } private function assertExtensions() { if (!extension_loaded('phpiredis')) { throw new NotSupportedException( 'The "phpiredis" extension is required by this connection backend.' ); } } protected function assertParameters(ParametersInterface $parameters) { switch ($parameters->scheme) { case 'tcp': case 'redis': case 'unix': break; case 'tls': case 'rediss': throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.'); default: throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); } return $parameters; } protected function createStreamSocket(ParametersInterface $parameters, $address, $flags, $context = null) { $socket = null; $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); $resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags); if (!$resource) { $this->onConnectionError(trim($errstr), $errno); } if (isset($parameters->read_write_timeout) && function_exists('socket_import_stream')) { $rwtimeout = (float) $parameters->read_write_timeout; $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; $timeout = array( 'sec' => $timeoutSeconds = floor($rwtimeout), 'usec' => ($rwtimeout - $timeoutSeconds) * 1000000, ); $socket = $socket ?: socket_import_stream($resource); @socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout); @socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout); } if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) { $socket = $socket ?: socket_import_stream($resource); socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay); } return $resource; } private function createReader() { $reader = phpiredis_reader_create(); phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); return $reader; } protected function getReader() { return $this->reader; } protected function getStatusHandler() { static $statusHandler; if (!$statusHandler) { $statusHandler = function ($payload) { return StatusResponse::get($payload); }; } return $statusHandler; } protected function getErrorHandler() { static $errorHandler; if (!$errorHandler) { $errorHandler = function ($errorMessage) { return new ErrorResponse($errorMessage); }; } return $errorHandler; } public function read() { $socket = $this->getResource(); $reader = $this->reader; while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) { $buffer = stream_socket_recvfrom($socket, 4096); if ($buffer === false || $buffer === '') { $this->onConnectionError('Error while reading bytes from the server.'); } phpiredis_reader_feed($reader, $buffer); } if ($state === PHPIREDIS_READER_STATE_COMPLETE) { return phpiredis_reader_get_reply($reader); } else { $this->onProtocolError(phpiredis_reader_get_error($reader)); return; } } public function writeRequest(CommandInterface $command) { $arguments = $command->getArguments(); array_unshift($arguments, $command->getId()); $this->write(phpiredis_format_command($arguments)); } public function __wakeup() { $this->assertExtensions(); $this->reader = $this->createReader(); } } parameters = $this->assertParameters($parameters); $this->protocol = $protocol ?: new TextProtocolProcessor(); } public function getProtocol() { return $this->protocol; } public function writeBuffer($buffer) { $this->write($buffer); } public function readBuffer($length) { if ($length <= 0) { throw new \InvalidArgumentException('Length parameter must be greater than 0.'); } $value = ''; $socket = $this->getResource(); do { $chunk = fread($socket, $length); if ($chunk === false || $chunk === '') { $this->onConnectionError('Error while reading bytes from the server.'); } $value .= $chunk; } while (($length -= strlen($chunk)) > 0); return $value; } public function readLine() { $value = ''; $socket = $this->getResource(); do { $chunk = fgets($socket); if ($chunk === false || $chunk === '') { $this->onConnectionError('Error while reading line from the server.'); } $value .= $chunk; } while (substr($value, -2) !== "\r\n"); return substr($value, 0, -2); } public function writeRequest(CommandInterface $command) { $this->protocol->write($this, $command); } public function read() { return $this->protocol->read($this); } public function __sleep() { return array_merge(parent::__sleep(), array('protocol')); } } assertExtensions(); if ($parameters->scheme !== 'http') { throw new \InvalidArgumentException("Invalid scheme: '{$parameters->scheme}'."); } $this->parameters = $parameters; $this->resource = $this->createCurl(); $this->reader = $this->createReader(); } public function __destruct() { curl_close($this->resource); phpiredis_reader_destroy($this->reader); } private function throwNotSupportedException($method) { $class = __CLASS__; throw new NotSupportedException("The method $class::$method() is not supported."); } private function assertExtensions() { if (!extension_loaded('curl')) { throw new NotSupportedException( 'The "curl" extension is required by this connection backend.' ); } if (!extension_loaded('phpiredis')) { throw new NotSupportedException( 'The "phpiredis" extension is required by this connection backend.' ); } } private function createCurl() { $parameters = $this->getParameters(); $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000; if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $host = "[$host]"; } $options = array( CURLOPT_FAILONERROR => true, CURLOPT_CONNECTTIMEOUT_MS => $timeout, CURLOPT_URL => "$parameters->scheme://$host:$parameters->port", CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_POST => true, CURLOPT_WRITEFUNCTION => array($this, 'feedReader'), ); if (isset($parameters->user, $parameters->pass)) { $options[CURLOPT_USERPWD] = "{$parameters->user}:{$parameters->pass}"; } curl_setopt_array($resource = curl_init(), $options); return $resource; } private function createReader() { $reader = phpiredis_reader_create(); phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); return $reader; } protected function getStatusHandler() { static $statusHandler; if (!$statusHandler) { $statusHandler = function ($payload) { return StatusResponse::get($payload); }; } return $statusHandler; } protected function getErrorHandler() { static $errorHandler; if (!$errorHandler) { $errorHandler = function ($errorMessage) { return new ErrorResponse($errorMessage); }; } return $errorHandler; } protected function feedReader($resource, $buffer) { phpiredis_reader_feed($this->reader, $buffer); return strlen($buffer); } public function connect() { } public function disconnect() { } public function isConnected() { return true; } protected function getCommandId(CommandInterface $command) { switch ($commandID = $command->getId()) { case 'AUTH': case 'SELECT': case 'MULTI': case 'EXEC': case 'WATCH': case 'UNWATCH': case 'DISCARD': case 'MONITOR': throw new NotSupportedException("Command '$commandID' is not allowed by Webdis."); default: return $commandID; } } public function writeRequest(CommandInterface $command) { $this->throwNotSupportedException(__FUNCTION__); } public function readResponse(CommandInterface $command) { $this->throwNotSupportedException(__FUNCTION__); } public function executeCommand(CommandInterface $command) { $resource = $this->resource; $commandId = $this->getCommandId($command); if ($arguments = $command->getArguments()) { $arguments = implode('/', array_map('urlencode', $arguments)); $serializedCommand = "$commandId/$arguments.raw"; } else { $serializedCommand = "$commandId.raw"; } curl_setopt($resource, CURLOPT_POSTFIELDS, $serializedCommand); if (curl_exec($resource) === false) { $error = curl_error($resource); $errno = curl_errno($resource); throw new ConnectionException($this, trim($error), $errno); } if (phpiredis_reader_get_state($this->reader) !== PHPIREDIS_READER_STATE_COMPLETE) { throw new ProtocolException($this, phpiredis_reader_get_error($this->reader)); } return phpiredis_reader_get_reply($this->reader); } public function getResource() { return $this->resource; } public function getParameters() { return $this->parameters; } public function addConnectCommand(CommandInterface $command) { $this->throwNotSupportedException(__FUNCTION__); } public function read() { $this->throwNotSupportedException(__FUNCTION__); } public function __toString() { return "{$this->parameters->host}:{$this->parameters->port}"; } public function __sleep() { return array('parameters'); } public function __wakeup() { $this->assertExtensions(); $this->resource = $this->createCurl(); $this->reader = $this->createReader(); } } 'tcp', 'host' => '127.0.0.1', 'port' => 6379, ); public function __construct(array $parameters = array()) { $this->parameters = $this->filter($parameters) + $this->getDefaults(); } protected function getDefaults() { return self::$defaults; } public static function create($parameters) { if (is_string($parameters)) { $parameters = static::parse($parameters); } return new static($parameters ?: array()); } public static function parse($uri) { if (stripos($uri, 'unix://') === 0) { $uri = str_ireplace('unix://', 'unix:', $uri); } if (!$parsed = parse_url($uri)) { throw new \InvalidArgumentException("Invalid parameters URI: $uri"); } if ( isset($parsed['host']) && false !== strpos($parsed['host'], '[') && false !== strpos($parsed['host'], ']') ) { $parsed['host'] = substr($parsed['host'], 1, -1); } if (isset($parsed['query'])) { parse_str($parsed['query'], $queryarray); unset($parsed['query']); $parsed = array_merge($parsed, $queryarray); } if (stripos($uri, 'redis') === 0) { if (isset($parsed['user'])) { if (strlen($parsed['user'])) { $parsed['username'] = $parsed['user']; } unset($parsed['user']); } if (isset($parsed['pass'])) { if (strlen($parsed['pass'])) { $parsed['password'] = $parsed['pass']; } unset($parsed['pass']); } if (isset($parsed['path']) && preg_match('/^\/(\d+)(\/.*)?/', $parsed['path'], $path)) { $parsed['database'] = $path[1]; if (isset($path[2])) { $parsed['path'] = $path[2]; } else { unset($parsed['path']); } } } return $parsed; } protected function filter(array $parameters) { return $parameters ?: array(); } public function __get($parameter) { if (isset($this->parameters[$parameter])) { return $this->parameters[$parameter]; } } public function __isset($parameter) { return isset($this->parameters[$parameter]); } public function toArray() { return $this->parameters; } public function __sleep() { return array('parameters'); } } 'Predis\Connection\StreamConnection', 'unix' => 'Predis\Connection\StreamConnection', 'tls' => 'Predis\Connection\StreamConnection', 'redis' => 'Predis\Connection\StreamConnection', 'rediss' => 'Predis\Connection\StreamConnection', 'http' => 'Predis\Connection\WebdisConnection', ); protected function checkInitializer($initializer) { if (is_callable($initializer)) { return $initializer; } $class = new \ReflectionClass($initializer); if (!$class->isSubclassOf('Predis\Connection\NodeConnectionInterface')) { throw new \InvalidArgumentException( 'A connection initializer must be a valid connection class or a callable object.' ); } return $initializer; } public function define($scheme, $initializer) { $this->schemes[$scheme] = $this->checkInitializer($initializer); } public function undefine($scheme) { unset($this->schemes[$scheme]); } public function create($parameters) { if (!$parameters instanceof ParametersInterface) { $parameters = $this->createParameters($parameters); } $scheme = $parameters->scheme; if (!isset($this->schemes[$scheme])) { throw new \InvalidArgumentException("Unknown connection scheme: '$scheme'."); } $initializer = $this->schemes[$scheme]; if (is_callable($initializer)) { $connection = call_user_func($initializer, $parameters, $this); } else { $connection = new $initializer($parameters); $this->prepareConnection($connection); } if (!$connection instanceof NodeConnectionInterface) { throw new \UnexpectedValueException( 'Objects returned by connection initializers must implement '. "'Predis\Connection\NodeConnectionInterface'." ); } return $connection; } public function aggregate(AggregateConnectionInterface $connection, array $parameters) { foreach ($parameters as $node) { $connection->add($node instanceof NodeConnectionInterface ? $node : $this->create($node)); } } public function setDefaultParameters(array $parameters) { $this->defaults = $parameters; } public function getDefaultParameters() { return $this->defaults; } protected function createParameters($parameters) { if (is_string($parameters)) { $parameters = Parameters::parse($parameters); } else { $parameters = $parameters ?: array(); } if ($this->defaults) { $parameters += $this->defaults; } return new Parameters($parameters); } protected function prepareConnection(NodeConnectionInterface $connection) { $parameters = $connection->getParameters(); if (isset($parameters->password) && strlen($parameters->password)) { $cmdAuthArgs = isset($parameters->username) && strlen($parameters->username) ? array('AUTH', $parameters->username, $parameters->password) : array('AUTH', $parameters->password); $connection->addConnectCommand( new RawCommand($cmdAuthArgs) ); } if (isset($parameters->database) && strlen($parameters->database)) { $connection->addConnectCommand( new RawCommand(array('SELECT', $parameters->database)) ); } } } parameters->persistent) && $this->parameters->persistent) { return; } $this->disconnect(); } protected function assertParameters(ParametersInterface $parameters) { switch ($parameters->scheme) { case 'tcp': case 'redis': case 'unix': break; case 'tls': case 'rediss': $this->assertSslSupport($parameters); break; default: throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); } return $parameters; } protected function assertSslSupport(ParametersInterface $parameters) { if ( filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN) && version_compare(PHP_VERSION, '7.0.0beta') < 0 ) { throw new \InvalidArgumentException('Persistent SSL connections require PHP >= 7.0.0.'); } } protected function createResource() { switch ($this->parameters->scheme) { case 'tcp': case 'redis': return $this->tcpStreamInitializer($this->parameters); case 'unix': return $this->unixStreamInitializer($this->parameters); case 'tls': case 'rediss': return $this->tlsStreamInitializer($this->parameters); default: throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'."); } } protected function createStreamSocket(ParametersInterface $parameters, $address, $flags) { $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) { $this->onConnectionError(trim($errstr), $errno); } if (isset($parameters->read_write_timeout)) { $rwtimeout = (float) $parameters->read_write_timeout; $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; $timeoutSeconds = floor($rwtimeout); $timeoutUSeconds = ($rwtimeout - $timeoutSeconds) * 1000000; stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds); } if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) { $socket = socket_import_stream($resource); socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay); } return $resource; } protected function tcpStreamInitializer(ParametersInterface $parameters) { if (!filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { $address = "tcp://$parameters->host:$parameters->port"; } else { $address = "tcp://[$parameters->host]:$parameters->port"; } $flags = STREAM_CLIENT_CONNECT; if (isset($parameters->async_connect) && $parameters->async_connect) { $flags |= STREAM_CLIENT_ASYNC_CONNECT; } if (isset($parameters->persistent)) { if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) { $flags |= STREAM_CLIENT_PERSISTENT; if ($persistent === null) { $address = "{$address}/{$parameters->persistent}"; } } } $resource = $this->createStreamSocket($parameters, $address, $flags); return $resource; } protected function unixStreamInitializer(ParametersInterface $parameters) { if (!isset($parameters->path)) { throw new \InvalidArgumentException('Missing UNIX domain socket path.'); } $flags = STREAM_CLIENT_CONNECT; if (isset($parameters->persistent)) { if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) { $flags |= STREAM_CLIENT_PERSISTENT; if ($persistent === null) { throw new \InvalidArgumentException( 'Persistent connection IDs are not supported when using UNIX domain sockets.' ); } } } $resource = $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags); return $resource; } protected function tlsStreamInitializer(ParametersInterface $parameters) { $resource = $this->tcpStreamInitializer($parameters); $metadata = stream_get_meta_data($resource); if (isset($metadata['crypto'])) { return $resource; } if (is_array($parameters->ssl)) { $options = $parameters->ssl; } else { $options = array(); } if (!isset($options['crypto_type'])) { $options['crypto_type'] = STREAM_CRYPTO_METHOD_TLS_CLIENT; } if (!stream_context_set_option($resource, array('ssl' => $options))) { $this->onConnectionError('Error while setting SSL context options'); } if (!stream_socket_enable_crypto($resource, true, $options['crypto_type'])) { $this->onConnectionError('Error while switching to encrypted communication'); } return $resource; } public function connect() { if (parent::connect() && $this->initCommands) { foreach ($this->initCommands as $command) { $response = $this->executeCommand($command); if ($response instanceof ErrorResponseInterface) { $this->onConnectionError("`{$command->getId()}` failed: $response", 0); } } } } public function disconnect() { if ($this->isConnected()) { fclose($this->getResource()); parent::disconnect(); } } protected function write($buffer) { $socket = $this->getResource(); while (($length = strlen($buffer)) > 0) { $written = @fwrite($socket, $buffer); if ($length === $written) { return; } if ($written === false || $written === 0) { $this->onConnectionError('Error while writing bytes to the server.'); } $buffer = substr($buffer, $written); } } public function read() { $socket = $this->getResource(); $chunk = fgets($socket); if ($chunk === false || $chunk === '') { $this->onConnectionError('Error while reading line from the server.'); } $prefix = $chunk[0]; $payload = substr($chunk, 1, -2); switch ($prefix) { case '+': return StatusResponse::get($payload); case '$': $size = (int) $payload; if ($size === -1) { return; } $bulkData = ''; $bytesLeft = ($size += 2); do { $chunk = fread($socket, min($bytesLeft, 4096)); if ($chunk === false || $chunk === '') { $this->onConnectionError('Error while reading bytes from the server.'); } $bulkData .= $chunk; $bytesLeft = $size - strlen($bulkData); } while ($bytesLeft > 0); return substr($bulkData, 0, -2); case '*': $count = (int) $payload; if ($count === -1) { return; } $multibulk = array(); for ($i = 0; $i < $count; ++$i) { $multibulk[$i] = $this->read(); } return $multibulk; case ':': $integer = (int) $payload; return $integer == $payload ? $integer : $payload; case '-': return new ErrorResponse($payload); default: $this->onProtocolError("Unknown response prefix: '$prefix'."); return; } } public function writeRequest(CommandInterface $command) { $commandID = $command->getId(); $arguments = $command->getArguments(); $cmdlen = strlen($commandID); $reqlen = count($arguments) + 1; $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n"; foreach ($arguments as $argument) { $arglen = strlen(strval($argument)); $buffer .= "\${$arglen}\r\n{$argument}\r\n"; } $this->write($buffer); } } assertExtensions(); parent::__construct($parameters); $this->reader = $this->createReader(); } public function __destruct() { parent::__destruct(); phpiredis_reader_destroy($this->reader); } protected function assertExtensions() { if (!extension_loaded('sockets')) { throw new NotSupportedException( 'The "sockets" extension is required by this connection backend.' ); } if (!extension_loaded('phpiredis')) { throw new NotSupportedException( 'The "phpiredis" extension is required by this connection backend.' ); } } protected function assertParameters(ParametersInterface $parameters) { switch ($parameters->scheme) { case 'tcp': case 'redis': case 'unix': break; default: throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); } if (isset($parameters->persistent)) { throw new NotSupportedException( 'Persistent connections are not supported by this connection backend.' ); } return $parameters; } private function createReader() { $reader = phpiredis_reader_create(); phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); return $reader; } protected function getReader() { return $this->reader; } protected function getStatusHandler() { static $statusHandler; if (!$statusHandler) { $statusHandler = function ($payload) { return StatusResponse::get($payload); }; } return $statusHandler; } protected function getErrorHandler() { static $errorHandler; if (!$errorHandler) { $errorHandler = function ($errorMessage) { return new ErrorResponse($errorMessage); }; } return $errorHandler; } private function emitSocketError() { $errno = socket_last_error(); $errstr = socket_strerror($errno); $this->disconnect(); $this->onConnectionError(trim($errstr), $errno); } protected static function getAddress(ParametersInterface $parameters) { if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) { return $host; } if ($host === $address = gethostbyname($host)) { return false; } return $address; } protected function createResource() { $parameters = $this->parameters; if ($parameters->scheme === 'unix') { $address = $parameters->path; $domain = AF_UNIX; $protocol = 0; } else { if (false === $address = self::getAddress($parameters)) { $this->onConnectionError("Cannot resolve the address of '$parameters->host'."); } $domain = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? AF_INET6 : AF_INET; $protocol = SOL_TCP; } if (false === $socket = @socket_create($domain, SOCK_STREAM, $protocol)) { $this->emitSocketError(); } $this->setSocketOptions($socket, $parameters); $this->connectWithTimeout($socket, $address, $parameters); return $socket; } private function setSocketOptions($socket, ParametersInterface $parameters) { if ($parameters->scheme !== 'unix') { if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) { $this->emitSocketError(); } if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { $this->emitSocketError(); } } if (isset($parameters->read_write_timeout)) { $rwtimeout = (float) $parameters->read_write_timeout; $timeoutSec = floor($rwtimeout); $timeoutUsec = ($rwtimeout - $timeoutSec) * 1000000; $timeout = array( 'sec' => $timeoutSec, 'usec' => $timeoutUsec, ); if (!socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)) { $this->emitSocketError(); } if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)) { $this->emitSocketError(); } } } private function connectWithTimeout($socket, $address, ParametersInterface $parameters) { socket_set_nonblock($socket); if (@socket_connect($socket, $address, (int) $parameters->port) === false) { $error = socket_last_error(); if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) { $this->emitSocketError(); } } socket_set_block($socket); $null = null; $selectable = array($socket); $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); $timeoutSecs = floor($timeout); $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000; $selected = socket_select($selectable, $selectable, $null, $timeoutSecs, $timeoutUSecs); if ($selected === 2) { $this->onConnectionError('Connection refused.', SOCKET_ECONNREFUSED); } if ($selected === 0) { $this->onConnectionError('Connection timed out.', SOCKET_ETIMEDOUT); } if ($selected === false) { $this->emitSocketError(); } } public function connect() { if (parent::connect() && $this->initCommands) { foreach ($this->initCommands as $command) { $response = $this->executeCommand($command); if ($response instanceof ErrorResponseInterface) { $this->onConnectionError("`{$command->getId()}` failed: $response", 0); } } } } public function disconnect() { if ($this->isConnected()) { phpiredis_reader_reset($this->reader); socket_close($this->getResource()); parent::disconnect(); } } protected function write($buffer) { $socket = $this->getResource(); while (($length = strlen($buffer)) > 0) { $written = socket_write($socket, $buffer, $length); if ($length === $written) { return; } if ($written === false) { $this->onConnectionError('Error while writing bytes to the server.'); } $buffer = substr($buffer, $written); } } public function read() { $socket = $this->getResource(); $reader = $this->reader; while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) { if (@socket_recv($socket, $buffer, 4096, 0) === false || $buffer === '' || $buffer === null) { $this->emitSocketError(); } phpiredis_reader_feed($reader, $buffer); } if ($state === PHPIREDIS_READER_STATE_COMPLETE) { return phpiredis_reader_get_reply($reader); } else { $this->onProtocolError(phpiredis_reader_get_error($reader)); return; } } public function writeRequest(CommandInterface $command) { $arguments = $command->getArguments(); array_unshift($arguments, $command->getId()); $this->write(phpiredis_format_command($arguments)); } public function __wakeup() { $this->assertExtensions(); $this->reader = $this->createReader(); } } strategy = $strategy ?: new ReplicationStrategy(); } public function setAutoDiscovery($value) { if (!$this->connectionFactory) { throw new ClientException('Automatic discovery requires a connection factory'); } $this->autoDiscovery = (bool) $value; } public function setConnectionFactory(FactoryInterface $connectionFactory) { $this->connectionFactory = $connectionFactory; } protected function reset() { $this->current = null; } public function add(NodeConnectionInterface $connection) { $alias = $connection->getParameters()->alias; if ($alias === 'master') { $this->master = $connection; } else { $this->slaves[$alias ?: "slave-$connection"] = $connection; } $this->reset(); } public function remove(NodeConnectionInterface $connection) { if ($connection->getParameters()->alias === 'master') { $this->master = null; $this->reset(); return true; } else { if (($id = array_search($connection, $this->slaves, true)) !== false) { unset($this->slaves[$id]); $this->reset(); return true; } } return false; } public function getConnection(CommandInterface $command) { if (!$this->current) { if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) { $this->current = $slave; } else { $this->current = $this->getMasterOrDie(); } return $this->current; } if ($this->current === $master = $this->getMasterOrDie()) { return $master; } if (!$this->strategy->isReadOperation($command) || !$this->slaves) { $this->current = $master; } return $this->current; } public function getConnectionById($connectionId) { if ($connectionId === 'master') { return $this->master; } if (isset($this->slaves[$connectionId])) { return $this->slaves[$connectionId]; } return; } public function switchTo($connection) { if (!$connection instanceof NodeConnectionInterface) { $connection = $this->getConnectionById($connection); } if (!$connection) { throw new \InvalidArgumentException('Invalid connection or connection not found.'); } if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { throw new \InvalidArgumentException('Invalid connection or connection not found.'); } $this->current = $connection; } public function switchToMaster() { $this->switchTo('master'); } public function switchToSlave() { $connection = $this->pickSlave(); $this->switchTo($connection); } public function getCurrent() { return $this->current; } public function getMaster() { return $this->master; } private function getMasterOrDie() { if (!$connection = $this->getMaster()) { throw new MissingMasterException('No master server available for replication'); } return $connection; } public function getSlaves() { return array_values($this->slaves); } public function getReplicationStrategy() { return $this->strategy; } protected function pickSlave() { if ($this->slaves) { return $this->slaves[array_rand($this->slaves)]; } } public function isConnected() { return $this->current ? $this->current->isConnected() : false; } public function connect() { if (!$this->current) { if (!$this->current = $this->pickSlave()) { if (!$this->current = $this->getMaster()) { throw new ClientException('No available connection for replication'); } } } $this->current->connect(); } public function disconnect() { if ($this->master) { $this->master->disconnect(); } foreach ($this->slaves as $connection) { $connection->disconnect(); } } private function handleInfoResponse($response) { $info = array(); foreach (preg_split('/\r?\n/', $response) as $row) { if (strpos($row, ':') === false) { continue; } list($k, $v) = explode(':', $row, 2); $info[$k] = $v; } return $info; } public function discover() { if (!$this->connectionFactory) { throw new ClientException('Discovery requires a connection factory'); } RETRY_FETCH: { try { if ($connection = $this->getMaster()) { $this->discoverFromMaster($connection, $this->connectionFactory); } elseif ($connection = $this->pickSlave()) { $this->discoverFromSlave($connection, $this->connectionFactory); } else { throw new ClientException('No connection available for discovery'); } } catch (ConnectionException $exception) { $this->remove($connection); goto RETRY_FETCH; } } } protected function discoverFromMaster(NodeConnectionInterface $connection, FactoryInterface $connectionFactory) { $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION')); $replication = $this->handleInfoResponse($response); if ($replication['role'] !== 'master') { throw new ClientException("Role mismatch (expected master, got slave) [$connection]"); } $this->slaves = array(); foreach ($replication as $k => $v) { $parameters = null; if (strpos($k, 'slave') === 0 && preg_match('/ip=(?P.*),port=(?P\d+)/', $v, $parameters)) { $slaveConnection = $connectionFactory->create(array( 'host' => $parameters['host'], 'port' => $parameters['port'], )); $this->add($slaveConnection); } } } protected function discoverFromSlave(NodeConnectionInterface $connection, FactoryInterface $connectionFactory) { $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION')); $replication = $this->handleInfoResponse($response); if ($replication['role'] !== 'slave') { throw new ClientException("Role mismatch (expected slave, got master) [$connection]"); } $masterConnection = $connectionFactory->create(array( 'host' => $replication['master_host'], 'port' => $replication['master_port'], 'alias' => 'master', )); $this->add($masterConnection); $this->discoverFromMaster($masterConnection, $connectionFactory); } private function retryCommandOnFailure(CommandInterface $command, $method) { RETRY_COMMAND: { try { $connection = $this->getConnection($command); $response = $connection->$method($command); if ($response instanceof ResponseErrorInterface && $response->getErrorType() === 'LOADING') { throw new ConnectionException($connection, "Redis is loading the dataset in memory [$connection]"); } } catch (ConnectionException $exception) { $connection = $exception->getConnection(); $connection->disconnect(); if ($connection === $this->master && !$this->autoDiscovery) { throw $exception; } else { $this->remove($connection); } if (!$this->slaves && !$this->master) { throw $exception; } elseif ($this->autoDiscovery) { $this->discover(); } goto RETRY_COMMAND; } catch (MissingMasterException $exception) { if ($this->autoDiscovery) { $this->discover(); } else { throw $exception; } goto RETRY_COMMAND; } } return $response; } public function writeRequest(CommandInterface $command) { $this->retryCommandOnFailure($command, __FUNCTION__); } public function readResponse(CommandInterface $command) { return $this->retryCommandOnFailure($command, __FUNCTION__); } public function executeCommand(CommandInterface $command) { return $this->retryCommandOnFailure($command, __FUNCTION__); } public function __sleep() { return array('master', 'slaves', 'strategy'); } } pool = array(); $this->strategy = $strategy ?: new PredisStrategy(); $this->distributor = $this->strategy->getDistributor(); } public function isConnected() { foreach ($this->pool as $connection) { if ($connection->isConnected()) { return true; } } return false; } public function connect() { foreach ($this->pool as $connection) { $connection->connect(); } } public function disconnect() { foreach ($this->pool as $connection) { $connection->disconnect(); } } public function add(NodeConnectionInterface $connection) { $parameters = $connection->getParameters(); if (isset($parameters->alias)) { $this->pool[$parameters->alias] = $connection; } else { $this->pool[] = $connection; } $weight = isset($parameters->weight) ? $parameters->weight : null; $this->distributor->add($connection, $weight); } public function remove(NodeConnectionInterface $connection) { if (($id = array_search($connection, $this->pool, true)) !== false) { unset($this->pool[$id]); $this->distributor->remove($connection); return true; } return false; } public function removeById($connectionID) { if ($connection = $this->getConnectionById($connectionID)) { return $this->remove($connection); } return false; } public function getConnection(CommandInterface $command) { $slot = $this->strategy->getSlot($command); if (!isset($slot)) { throw new NotSupportedException( "Cannot use '{$command->getId()}' over clusters of connections." ); } $node = $this->distributor->getBySlot($slot); return $node; } public function getConnectionById($connectionID) { return isset($this->pool[$connectionID]) ? $this->pool[$connectionID] : null; } public function getConnectionByKey($key) { $hash = $this->strategy->getSlotByKey($key); $node = $this->distributor->getBySlot($hash); return $node; } public function getClusterStrategy() { return $this->strategy; } #[\ReturnTypeWillChange] public function count() { return count($this->pool); } #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator($this->pool); } public function writeRequest(CommandInterface $command) { $this->getConnection($command)->writeRequest($command); } public function readResponse(CommandInterface $command) { return $this->getConnection($command)->readResponse($command); } public function executeCommand(CommandInterface $command) { return $this->getConnection($command)->executeCommand($command); } public function executeCommandOnNodes(CommandInterface $command) { $responses = array(); foreach ($this->pool as $connection) { $responses[] = $connection->executeCommand($command); } return $responses; } } sentinels = $sentinels; $this->service = $service; $this->connectionFactory = $connectionFactory; $this->strategy = $strategy ?: new ReplicationStrategy(); } public function setSentinelTimeout($timeout) { $this->sentinelTimeout = (float) $timeout; } public function setRetryLimit($retry) { $this->retryLimit = (int) $retry; } public function setRetryWait($milliseconds) { $this->retryWait = (float) $milliseconds; } public function setUpdateSentinels($update) { $this->updateSentinels = (bool) $update; } protected function reset() { $this->current = null; } protected function wipeServerList() { $this->reset(); $this->master = null; $this->slaves = array(); } public function add(NodeConnectionInterface $connection) { $alias = $connection->getParameters()->alias; if ($alias === 'master') { $this->master = $connection; } else { $this->slaves[$alias ?: count($this->slaves)] = $connection; } $this->reset(); } public function remove(NodeConnectionInterface $connection) { if ($connection === $this->master) { $this->master = null; $this->reset(); return true; } if (false !== $id = array_search($connection, $this->slaves, true)) { unset($this->slaves[$id]); $this->reset(); return true; } return false; } protected function createSentinelConnection($parameters) { if ($parameters instanceof NodeConnectionInterface) { return $parameters; } if (is_string($parameters)) { $parameters = Parameters::parse($parameters); } if (is_array($parameters)) { $parameters['database'] = null; $parameters['username'] = null; $parameters['password'] = null; if (!isset($parameters['timeout'])) { $parameters['timeout'] = $this->sentinelTimeout; } } $connection = $this->connectionFactory->create($parameters); return $connection; } public function getSentinelConnection() { if (!$this->sentinelConnection) { if (!$this->sentinels) { throw new \Predis\ClientException('No sentinel server available for autodiscovery.'); } $sentinel = array_shift($this->sentinels); $this->sentinelConnection = $this->createSentinelConnection($sentinel); } return $this->sentinelConnection; } public function updateSentinels() { SENTINEL_QUERY: { $sentinel = $this->getSentinelConnection(); try { $payload = $sentinel->executeCommand( RawCommand::create('SENTINEL', 'sentinels', $this->service) ); $this->sentinels = array(); $this->sentinels[] = $sentinel->getParameters()->toArray(); foreach ($payload as $sentinel) { $this->sentinels[] = array( 'host' => $sentinel[3], 'port' => $sentinel[5], ); } } catch (ConnectionException $exception) { $this->sentinelConnection = null; goto SENTINEL_QUERY; } } } public function querySentinel() { $this->wipeServerList(); $this->updateSentinels(); $this->getMaster(); $this->getSlaves(); } private function handleSentinelErrorResponse(NodeConnectionInterface $sentinel, ErrorResponseInterface $error) { if ($error->getErrorType() === 'IDONTKNOW') { throw new ConnectionException($sentinel, $error->getMessage()); } else { throw new ServerException($error->getMessage()); } } protected function querySentinelForMaster(NodeConnectionInterface $sentinel, $service) { $payload = $sentinel->executeCommand( RawCommand::create('SENTINEL', 'get-master-addr-by-name', $service) ); if ($payload === null) { throw new ServerException('ERR No such master with that name'); } if ($payload instanceof ErrorResponseInterface) { $this->handleSentinelErrorResponse($sentinel, $payload); } return array( 'host' => $payload[0], 'port' => $payload[1], 'alias' => 'master', ); } protected function querySentinelForSlaves(NodeConnectionInterface $sentinel, $service) { $slaves = array(); $payload = $sentinel->executeCommand( RawCommand::create('SENTINEL', 'slaves', $service) ); if ($payload instanceof ErrorResponseInterface) { $this->handleSentinelErrorResponse($sentinel, $payload); } foreach ($payload as $slave) { $flags = explode(',', $slave[9]); if (array_intersect($flags, array('s_down', 'o_down', 'disconnected'))) { continue; } $slaves[] = array( 'host' => $slave[3], 'port' => $slave[5], 'alias' => "slave-$slave[1]", ); } return $slaves; } public function getCurrent() { return $this->current; } public function getMaster() { if ($this->master) { return $this->master; } if ($this->updateSentinels) { $this->updateSentinels(); } SENTINEL_QUERY: { $sentinel = $this->getSentinelConnection(); try { $masterParameters = $this->querySentinelForMaster($sentinel, $this->service); $masterConnection = $this->connectionFactory->create($masterParameters); $this->add($masterConnection); } catch (ConnectionException $exception) { $this->sentinelConnection = null; goto SENTINEL_QUERY; } } return $masterConnection; } public function getSlaves() { if ($this->slaves) { return array_values($this->slaves); } if ($this->updateSentinels) { $this->updateSentinels(); } SENTINEL_QUERY: { $sentinel = $this->getSentinelConnection(); try { $slavesParameters = $this->querySentinelForSlaves($sentinel, $this->service); foreach ($slavesParameters as $slaveParameters) { $this->add($this->connectionFactory->create($slaveParameters)); } } catch (ConnectionException $exception) { $this->sentinelConnection = null; goto SENTINEL_QUERY; } } return array_values($this->slaves ?: array()); } protected function pickSlave() { if ($slaves = $this->getSlaves()) { return $slaves[rand(1, count($slaves)) - 1]; } } private function getConnectionInternal(CommandInterface $command) { if (!$this->current) { if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) { $this->current = $slave; } else { $this->current = $this->getMaster(); } return $this->current; } if ($this->current === $this->master) { return $this->current; } if (!$this->strategy->isReadOperation($command)) { $this->current = $this->getMaster(); } return $this->current; } protected function assertConnectionRole(NodeConnectionInterface $connection, $role) { $role = strtolower($role); $actualRole = $connection->executeCommand(RawCommand::create('ROLE')); if ($actualRole instanceof Error) { throw new ConnectionException($connection, $actualRole->getMessage()); } if ($role !== $actualRole[0]) { throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]"); } } public function getConnection(CommandInterface $command) { $connection = $this->getConnectionInternal($command); if (!$connection->isConnected()) { $expectedRole = $this->strategy->isReadOperation($command) && $this->slaves ? 'slave' : 'master'; $this->assertConnectionRole($connection, $expectedRole); } return $connection; } public function getConnectionById($connectionId) { if ($connectionId === 'master') { return $this->getMaster(); } $this->getSlaves(); if (isset($this->slaves[$connectionId])) { return $this->slaves[$connectionId]; } } public function switchTo($connection) { if (!$connection instanceof NodeConnectionInterface) { $connection = $this->getConnectionById($connection); } if ($connection && $connection === $this->current) { return; } if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { throw new \InvalidArgumentException('Invalid connection or connection not found.'); } $connection->connect(); if ($this->current) { $this->current->disconnect(); } $this->current = $connection; } public function switchToMaster() { $this->switchTo('master'); } public function switchToSlave() { $connection = $this->pickSlave(); $this->switchTo($connection); } public function isConnected() { return $this->current ? $this->current->isConnected() : false; } public function connect() { if (!$this->current) { if (!$this->current = $this->pickSlave()) { $this->current = $this->getMaster(); } } $this->current->connect(); } public function disconnect() { if ($this->master) { $this->master->disconnect(); } foreach ($this->slaves as $connection) { $connection->disconnect(); } } private function retryCommandOnFailure(CommandInterface $command, $method) { $retries = 0; SENTINEL_RETRY: { try { $response = $this->getConnection($command)->$method($command); } catch (CommunicationException $exception) { $this->wipeServerList(); $exception->getConnection()->disconnect(); if ($retries == $this->retryLimit) { throw $exception; } usleep($this->retryWait * 1000); ++$retries; goto SENTINEL_RETRY; } } return $response; } public function writeRequest(CommandInterface $command) { $this->retryCommandOnFailure($command, __FUNCTION__); } public function readResponse(CommandInterface $command) { return $this->retryCommandOnFailure($command, __FUNCTION__); } public function executeCommand(CommandInterface $command) { return $this->retryCommandOnFailure($command, __FUNCTION__); } public function getReplicationStrategy() { return $this->strategy; } public function __sleep() { return array( 'master', 'slaves', 'service', 'sentinels', 'connectionFactory', 'strategy', ); } } connections = $connections; $this->strategy = $strategy ?: new RedisClusterStrategy(); } public function setRetryLimit($retry) { $this->retryLimit = (int) $retry; } public function isConnected() { foreach ($this->pool as $connection) { if ($connection->isConnected()) { return true; } } return false; } public function connect() { if ($connection = $this->getRandomConnection()) { $connection->connect(); } } public function disconnect() { foreach ($this->pool as $connection) { $connection->disconnect(); } } public function add(NodeConnectionInterface $connection) { $this->pool[(string) $connection] = $connection; unset($this->slotsMap); } public function remove(NodeConnectionInterface $connection) { if (false !== $id = array_search($connection, $this->pool, true)) { unset( $this->pool[$id], $this->slotsMap ); $this->slots = array_diff($this->slots, array($connection)); return true; } return false; } public function removeById($connectionID) { if (isset($this->pool[$connectionID])) { unset( $this->pool[$connectionID], $this->slotsMap ); return true; } return false; } public function buildSlotsMap() { $this->slotsMap = array(); foreach ($this->pool as $connectionID => $connection) { $parameters = $connection->getParameters(); if (!isset($parameters->slots)) { continue; } foreach (explode(',', $parameters->slots) as $slotRange) { $slots = explode('-', $slotRange, 2); if (!isset($slots[1])) { $slots[1] = $slots[0]; } $this->setSlots($slots[0], $slots[1], $connectionID); } } return $this->slotsMap; } private function queryClusterNodeForSlotsMap(NodeConnectionInterface $connection) { $retries = 0; $command = RawCommand::create('CLUSTER', 'SLOTS'); RETRY_COMMAND: { try { $response = $connection->executeCommand($command); } catch (ConnectionException $exception) { $connection = $exception->getConnection(); $connection->disconnect(); $this->remove($connection); if ($retries === $this->retryLimit) { throw $exception; } if (!$connection = $this->getRandomConnection()) { throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`'); } ++$retries; goto RETRY_COMMAND; } } return $response; } public function askSlotsMap(NodeConnectionInterface $connection = null) { if (!$connection && !$connection = $this->getRandomConnection()) { return array(); } $this->resetSlotsMap(); $response = $this->queryClusterNodeForSlotsMap($connection); foreach ($response as $slots) { list($start, $end, $master) = $slots; if ($master[0] === '') { $this->setSlots($start, $end, (string) $connection); } else { $this->setSlots($start, $end, "{$master[0]}:{$master[1]}"); } } return $this->slotsMap; } public function resetSlotsMap() { $this->slotsMap = array(); } public function getSlotsMap() { if (!isset($this->slotsMap)) { $this->slotsMap = array(); } return $this->slotsMap; } public function setSlots($first, $last, $connection) { if ($first < 0x0000 || $first > 0x3FFF || $last < 0x0000 || $last > 0x3FFF || $last < $first ) { throw new \OutOfBoundsException( "Invalid slot range for $connection: [$first-$last]." ); } $slots = array_fill($first, $last - $first + 1, (string) $connection); $this->slotsMap = $this->getSlotsMap() + $slots; } protected function guessNode($slot) { if (!$this->pool) { throw new ClientException('No connections available in the pool'); } if (!isset($this->slotsMap)) { $this->buildSlotsMap(); } if (isset($this->slotsMap[$slot])) { return $this->slotsMap[$slot]; } $count = count($this->pool); $index = min((int) ($slot / (int) (16384 / $count)), $count - 1); $nodes = array_keys($this->pool); return $nodes[$index]; } protected function createConnection($connectionID) { $separator = strrpos($connectionID, ':'); return $this->connections->create(array( 'host' => substr($connectionID, 0, $separator), 'port' => substr($connectionID, $separator + 1), )); } public function getConnection(CommandInterface $command) { $slot = $this->strategy->getSlot($command); if (!isset($slot)) { throw new NotSupportedException( "Cannot use '{$command->getId()}' with redis-cluster." ); } if (isset($this->slots[$slot])) { return $this->slots[$slot]; } else { return $this->getConnectionBySlot($slot); } } public function getConnectionBySlot($slot) { if ($slot < 0x0000 || $slot > 0x3FFF) { throw new \OutOfBoundsException("Invalid slot [$slot]."); } if (isset($this->slots[$slot])) { return $this->slots[$slot]; } $connectionID = $this->guessNode($slot); if (!$connection = $this->getConnectionById($connectionID)) { $connection = $this->createConnection($connectionID); $this->pool[$connectionID] = $connection; } return $this->slots[$slot] = $connection; } public function getConnectionById($connectionID) { if (isset($this->pool[$connectionID])) { return $this->pool[$connectionID]; } } protected function getRandomConnection() { if ($this->pool) { return $this->pool[array_rand($this->pool)]; } } protected function move(NodeConnectionInterface $connection, $slot) { $this->pool[(string) $connection] = $connection; $this->slots[(int) $slot] = $connection; } protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $error) { $details = explode(' ', $error->getMessage(), 2); switch ($details[0]) { case 'MOVED': return $this->onMovedResponse($command, $details[1]); case 'ASK': return $this->onAskResponse($command, $details[1]); default: return $error; } } protected function onMovedResponse(CommandInterface $command, $details) { list($slot, $connectionID) = explode(' ', $details, 2); if (!$connection = $this->getConnectionById($connectionID)) { $connection = $this->createConnection($connectionID); } if ($this->useClusterSlots) { $this->askSlotsMap($connection); } $this->move($connection, $slot); $response = $this->executeCommand($command); return $response; } protected function onAskResponse(CommandInterface $command, $details) { list($slot, $connectionID) = explode(' ', $details, 2); if (!$connection = $this->getConnectionById($connectionID)) { $connection = $this->createConnection($connectionID); } $connection->executeCommand(RawCommand::create('ASKING')); $response = $connection->executeCommand($command); return $response; } private function retryCommandOnFailure(CommandInterface $command, $method) { $failure = false; RETRY_COMMAND: { try { $response = $this->getConnection($command)->$method($command); } catch (ConnectionException $exception) { $connection = $exception->getConnection(); $connection->disconnect(); $this->remove($connection); if ($failure) { throw $exception; } elseif ($this->useClusterSlots) { $this->askSlotsMap(); } $failure = true; goto RETRY_COMMAND; } } return $response; } public function writeRequest(CommandInterface $command) { $this->retryCommandOnFailure($command, __FUNCTION__); } public function readResponse(CommandInterface $command) { return $this->retryCommandOnFailure($command, __FUNCTION__); } public function executeCommand(CommandInterface $command) { $response = $this->retryCommandOnFailure($command, __FUNCTION__); if ($response instanceof ErrorResponseInterface) { return $this->onErrorResponse($command, $response); } return $response; } #[\ReturnTypeWillChange] public function count() { return count($this->pool); } #[\ReturnTypeWillChange] public function getIterator() { if ($this->useClusterSlots) { $slotsmap = $this->getSlotsMap() ?: $this->askSlotsMap(); } else { $slotsmap = $this->getSlotsMap() ?: $this->buildSlotsMap(); } $connections = array(); foreach (array_unique($slotsmap) as $node) { if (!$connection = $this->getConnectionById($node)) { $this->add($connection = $this->createConnection($node)); } $connections[] = $connection; } return new \ArrayIterator($connections); } public function getClusterStrategy() { return $this->strategy; } public function getConnectionFactory() { return $this->connections; } public function useClusterSlots($value) { $this->useClusterSlots = (bool) $value; } } distributor = $distributor ?: new HashRing(); } public function getSlotByKey($key) { $key = $this->extractKeyTag($key); $hash = $this->distributor->hash($key); $slot = $this->distributor->getSlot($hash); return $slot; } protected function checkSameSlotForKeys(array $keys) { if (!$count = count($keys)) { return false; } $currentKey = $this->extractKeyTag($keys[0]); for ($i = 1; $i < $count; ++$i) { $nextKey = $this->extractKeyTag($keys[$i]); if ($currentKey !== $nextKey) { return false; } $currentKey = $nextKey; } return true; } public function getDistributor() { return $this->distributor; } } > 8) ^ ord($value[$i])]) & 0xFFFF; } return $crc; } } hashGenerator = $hashGenerator ?: new CRC16(); } public function getSlotByKey($key) { $key = $this->extractKeyTag($key); $slot = $this->hashGenerator->hash($key) & 0x3FFF; return $slot; } public function getDistributor() { throw new NotSupportedException( 'This cluster strategy does not provide an external distributor' ); } } commands = $this->getDefaultCommands(); } protected function getDefaultCommands() { $getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument'); $getKeyFromAllArguments = array($this, 'getKeyFromAllArguments'); return array( 'EXISTS' => $getKeyFromAllArguments, 'DEL' => $getKeyFromAllArguments, 'TYPE' => $getKeyFromFirstArgument, 'EXPIRE' => $getKeyFromFirstArgument, 'EXPIREAT' => $getKeyFromFirstArgument, 'PERSIST' => $getKeyFromFirstArgument, 'PEXPIRE' => $getKeyFromFirstArgument, 'PEXPIREAT' => $getKeyFromFirstArgument, 'TTL' => $getKeyFromFirstArgument, 'PTTL' => $getKeyFromFirstArgument, 'SORT' => array($this, 'getKeyFromSortCommand'), 'DUMP' => $getKeyFromFirstArgument, 'RESTORE' => $getKeyFromFirstArgument, 'APPEND' => $getKeyFromFirstArgument, 'DECR' => $getKeyFromFirstArgument, 'DECRBY' => $getKeyFromFirstArgument, 'GET' => $getKeyFromFirstArgument, 'GETBIT' => $getKeyFromFirstArgument, 'MGET' => $getKeyFromAllArguments, 'SET' => $getKeyFromFirstArgument, 'GETRANGE' => $getKeyFromFirstArgument, 'GETSET' => $getKeyFromFirstArgument, 'INCR' => $getKeyFromFirstArgument, 'INCRBY' => $getKeyFromFirstArgument, 'INCRBYFLOAT' => $getKeyFromFirstArgument, 'SETBIT' => $getKeyFromFirstArgument, 'SETEX' => $getKeyFromFirstArgument, 'MSET' => array($this, 'getKeyFromInterleavedArguments'), 'MSETNX' => array($this, 'getKeyFromInterleavedArguments'), 'SETNX' => $getKeyFromFirstArgument, 'SETRANGE' => $getKeyFromFirstArgument, 'STRLEN' => $getKeyFromFirstArgument, 'SUBSTR' => $getKeyFromFirstArgument, 'BITOP' => array($this, 'getKeyFromBitOp'), 'BITCOUNT' => $getKeyFromFirstArgument, 'BITFIELD' => $getKeyFromFirstArgument, 'LINSERT' => $getKeyFromFirstArgument, 'LINDEX' => $getKeyFromFirstArgument, 'LLEN' => $getKeyFromFirstArgument, 'LPOP' => $getKeyFromFirstArgument, 'RPOP' => $getKeyFromFirstArgument, 'RPOPLPUSH' => $getKeyFromAllArguments, 'BLPOP' => array($this, 'getKeyFromBlockingListCommands'), 'BRPOP' => array($this, 'getKeyFromBlockingListCommands'), 'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'), 'LPUSH' => $getKeyFromFirstArgument, 'LPUSHX' => $getKeyFromFirstArgument, 'RPUSH' => $getKeyFromFirstArgument, 'RPUSHX' => $getKeyFromFirstArgument, 'LRANGE' => $getKeyFromFirstArgument, 'LREM' => $getKeyFromFirstArgument, 'LSET' => $getKeyFromFirstArgument, 'LTRIM' => $getKeyFromFirstArgument, 'SADD' => $getKeyFromFirstArgument, 'SCARD' => $getKeyFromFirstArgument, 'SDIFF' => $getKeyFromAllArguments, 'SDIFFSTORE' => $getKeyFromAllArguments, 'SINTER' => $getKeyFromAllArguments, 'SINTERSTORE' => $getKeyFromAllArguments, 'SUNION' => $getKeyFromAllArguments, 'SUNIONSTORE' => $getKeyFromAllArguments, 'SISMEMBER' => $getKeyFromFirstArgument, 'SMEMBERS' => $getKeyFromFirstArgument, 'SSCAN' => $getKeyFromFirstArgument, 'SPOP' => $getKeyFromFirstArgument, 'SRANDMEMBER' => $getKeyFromFirstArgument, 'SREM' => $getKeyFromFirstArgument, 'ZADD' => $getKeyFromFirstArgument, 'ZCARD' => $getKeyFromFirstArgument, 'ZCOUNT' => $getKeyFromFirstArgument, 'ZINCRBY' => $getKeyFromFirstArgument, 'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), 'ZRANGE' => $getKeyFromFirstArgument, 'ZRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZRANK' => $getKeyFromFirstArgument, 'ZREM' => $getKeyFromFirstArgument, 'ZREMRANGEBYRANK' => $getKeyFromFirstArgument, 'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZREVRANGE' => $getKeyFromFirstArgument, 'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZREVRANK' => $getKeyFromFirstArgument, 'ZSCORE' => $getKeyFromFirstArgument, 'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), 'ZSCAN' => $getKeyFromFirstArgument, 'ZLEXCOUNT' => $getKeyFromFirstArgument, 'ZRANGEBYLEX' => $getKeyFromFirstArgument, 'ZREMRANGEBYLEX' => $getKeyFromFirstArgument, 'ZREVRANGEBYLEX' => $getKeyFromFirstArgument, 'HDEL' => $getKeyFromFirstArgument, 'HEXISTS' => $getKeyFromFirstArgument, 'HGET' => $getKeyFromFirstArgument, 'HGETALL' => $getKeyFromFirstArgument, 'HMGET' => $getKeyFromFirstArgument, 'HMSET' => $getKeyFromFirstArgument, 'HINCRBY' => $getKeyFromFirstArgument, 'HINCRBYFLOAT' => $getKeyFromFirstArgument, 'HKEYS' => $getKeyFromFirstArgument, 'HLEN' => $getKeyFromFirstArgument, 'HSET' => $getKeyFromFirstArgument, 'HSETNX' => $getKeyFromFirstArgument, 'HVALS' => $getKeyFromFirstArgument, 'HSCAN' => $getKeyFromFirstArgument, 'HSTRLEN' => $getKeyFromFirstArgument, 'PFADD' => $getKeyFromFirstArgument, 'PFCOUNT' => $getKeyFromAllArguments, 'PFMERGE' => $getKeyFromAllArguments, 'EVAL' => array($this, 'getKeyFromScriptingCommands'), 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), 'GEOADD' => $getKeyFromFirstArgument, 'GEOHASH' => $getKeyFromFirstArgument, 'GEOPOS' => $getKeyFromFirstArgument, 'GEODIST' => $getKeyFromFirstArgument, 'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'), 'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'), ); } public function getSupportedCommands() { return array_keys($this->commands); } public function setCommandHandler($commandID, $callback = null) { $commandID = strtoupper($commandID); if (!isset($callback)) { unset($this->commands[$commandID]); return; } if (!is_callable($callback)) { throw new \InvalidArgumentException( 'The argument must be a callable object or NULL.' ); } $this->commands[$commandID] = $callback; } protected function getKeyFromFirstArgument(CommandInterface $command) { return $command->getArgument(0); } protected function getKeyFromAllArguments(CommandInterface $command) { $arguments = $command->getArguments(); if ($this->checkSameSlotForKeys($arguments)) { return $arguments[0]; } } protected function getKeyFromInterleavedArguments(CommandInterface $command) { $arguments = $command->getArguments(); $keys = array(); for ($i = 0; $i < count($arguments); $i += 2) { $keys[] = $arguments[$i]; } if ($this->checkSameSlotForKeys($keys)) { return $arguments[0]; } } protected function getKeyFromSortCommand(CommandInterface $command) { $arguments = $command->getArguments(); $firstKey = $arguments[0]; if (1 === $argc = count($arguments)) { return $firstKey; } $keys = array($firstKey); for ($i = 1; $i < $argc; ++$i) { if (strtoupper($arguments[$i]) === 'STORE') { $keys[] = $arguments[++$i]; } } if ($this->checkSameSlotForKeys($keys)) { return $firstKey; } } protected function getKeyFromBlockingListCommands(CommandInterface $command) { $arguments = $command->getArguments(); if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) { return $arguments[0]; } } protected function getKeyFromBitOp(CommandInterface $command) { $arguments = $command->getArguments(); if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) { return $arguments[1]; } } protected function getKeyFromGeoradiusCommands(CommandInterface $command) { $arguments = $command->getArguments(); $argc = count($arguments); $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; if ($argc > $startIndex) { $keys = array($arguments[0]); for ($i = $startIndex; $i < $argc; ++$i) { $argument = strtoupper($arguments[$i]); if ($argument === 'STORE' || $argument === 'STOREDIST') { $keys[] = $arguments[++$i]; } } if ($this->checkSameSlotForKeys($keys)) { return $arguments[0]; } else { return; } } return $arguments[0]; } protected function getKeyFromZsetAggregationCommands(CommandInterface $command) { $arguments = $command->getArguments(); $keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1])); if ($this->checkSameSlotForKeys($keys)) { return $arguments[0]; } } protected function getKeyFromScriptingCommands(CommandInterface $command) { if ($command instanceof ScriptCommand) { $keys = $command->getKeys(); } else { $keys = array_slice($args = $command->getArguments(), 2, $args[1]); } if ($keys && $this->checkSameSlotForKeys($keys)) { return $keys[0]; } } public function getSlot(CommandInterface $command) { $slot = $command->getSlot(); if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) { $key = call_user_func($this->commands[$cmdID], $command); if (isset($key)) { $slot = $this->getSlotByKey($key); $command->setSlot($slot); } } return $slot; } protected function checkSameSlotForKeys(array $keys) { if (!$count = count($keys)) { return false; } $currentSlot = $this->getSlotByKey($keys[0]); for ($i = 1; $i < $count; ++$i) { $nextSlot = $this->getSlotByKey($keys[$i]); if ($currentSlot !== $nextSlot) { return false; } $currentSlot = $nextSlot; } return true; } protected function extractKeyTag($key) { if (false !== $start = strpos($key, '{')) { if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) { $key = substr($key, $start, $end - $start); } } return $key; } } replicas = $replicas; $this->nodeHashCallback = $nodeHashCallback; } public function add($node, $weight = null) { $this->nodes[] = array( 'object' => $node, 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT, ); $this->reset(); } public function remove($node) { for ($i = 0; $i < count($this->nodes); ++$i) { if ($this->nodes[$i]['object'] === $node) { array_splice($this->nodes, $i, 1); $this->reset(); break; } } } private function reset() { unset( $this->ring, $this->ringKeys, $this->ringKeysCount ); } private function isInitialized() { return isset($this->ringKeys); } private function computeTotalWeight() { $totalWeight = 0; foreach ($this->nodes as $node) { $totalWeight += $node['weight']; } return $totalWeight; } private function initialize() { if ($this->isInitialized()) { return; } if (!$this->nodes) { throw new EmptyRingException('Cannot initialize an empty hashring.'); } $this->ring = array(); $totalWeight = $this->computeTotalWeight(); $nodesCount = count($this->nodes); foreach ($this->nodes as $node) { $weightRatio = $node['weight'] / $totalWeight; $this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio); } ksort($this->ring, SORT_NUMERIC); $this->ringKeys = array_keys($this->ring); $this->ringKeysCount = count($this->ringKeys); } protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio) { $nodeObject = $node['object']; $nodeHash = $this->getNodeHash($nodeObject); $replicas = (int) round($weightRatio * $totalNodes * $replicas); for ($i = 0; $i < $replicas; ++$i) { $key = $this->hash("$nodeHash:$i"); $ring[$key] = $nodeObject; } } protected function getNodeHash($nodeObject) { if (!isset($this->nodeHashCallback)) { return (string) $nodeObject; } return call_user_func($this->nodeHashCallback, $nodeObject); } public function hash($value) { return crc32($value); } public function getByHash($hash) { return $this->ring[$this->getSlot($hash)]; } public function getBySlot($slot) { $this->initialize(); if (isset($this->ring[$slot])) { return $this->ring[$slot]; } } public function getSlot($hash) { $this->initialize(); $ringKeys = $this->ringKeys; $upper = $this->ringKeysCount - 1; $lower = 0; while ($lower <= $upper) { $index = ($lower + $upper) >> 1; $item = $ringKeys[$index]; if ($item > $hash) { $upper = $index - 1; } elseif ($item < $hash) { $lower = $index + 1; } else { return $item; } } return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)]; } public function get($value) { $hash = $this->hash($value); $node = $this->getByHash($hash); return $node; } protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) { return $upper >= 0 ? $upper : $ringKeysCount - 1; } public function getHashGenerator() { return $this; } } getNodeHash($nodeObject); $replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4)); for ($i = 0; $i < $replicas; ++$i) { $unpackedDigest = unpack('V4', md5("$nodeHash-$i", true)); foreach ($unpackedDigest as $key) { $ring[$key] = $nodeObject; } } } public function hash($value) { $hash = unpack('V', md5($value, true)); return $hash[1]; } protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) { return $lower < $ringKeysCount ? $lower : 0; } } setRequestSerializer($serializer ?: new RequestSerializer()); $this->setResponseReader($reader ?: new ResponseReader()); } public function write(CompositeConnectionInterface $connection, CommandInterface $command) { $connection->writeBuffer($this->serializer->serialize($command)); } public function read(CompositeConnectionInterface $connection) { return $this->reader->read($connection); } public function setRequestSerializer(RequestSerializerInterface $serializer) { $this->serializer = $serializer; } public function getRequestSerializer() { return $this->serializer; } public function setResponseReader(ResponseReaderInterface $reader) { $this->reader = $reader; } public function getResponseReader() { return $this->reader; } } mbiterable = false; $this->serializer = new RequestSerializer(); } public function write(CompositeConnectionInterface $connection, CommandInterface $command) { $request = $this->serializer->serialize($command); $connection->writeBuffer($request); } public function read(CompositeConnectionInterface $connection) { $chunk = $connection->readLine(); $prefix = $chunk[0]; $payload = substr($chunk, 1); switch ($prefix) { case '+': return new StatusResponse($payload); case '$': $size = (int) $payload; if ($size === -1) { return; } return substr($connection->readBuffer($size + 2), 0, -2); case '*': $count = (int) $payload; if ($count === -1) { return; } if ($this->mbiterable) { return new MultiBulkIterator($connection, $count); } $multibulk = array(); for ($i = 0; $i < $count; ++$i) { $multibulk[$i] = $this->read($connection); } return $multibulk; case ':': $integer = (int) $payload; return $integer == $payload ? $integer : $payload; case '-': return new ErrorResponse($payload); default: CommunicationException::handle(new ProtocolException( $connection, "Unknown response prefix: '$prefix'." )); return; } } public function useIterableMultibulk($value) { $this->mbiterable = (bool) $value; } } handlers = $this->getDefaultHandlers(); } protected function getDefaultHandlers() { return array( '+' => new Handler\StatusResponse(), '-' => new Handler\ErrorResponse(), ':' => new Handler\IntegerResponse(), '$' => new Handler\BulkResponse(), '*' => new Handler\MultiBulkResponse(), ); } public function setHandler($prefix, Handler\ResponseHandlerInterface $handler) { $this->handlers[$prefix] = $handler; } public function getHandler($prefix) { if (isset($this->handlers[$prefix])) { return $this->handlers[$prefix]; } return; } public function read(CompositeConnectionInterface $connection) { $header = $connection->readLine(); if ($header === '') { $this->onProtocolError($connection, 'Unexpected empty reponse header.'); } $prefix = $header[0]; if (!isset($this->handlers[$prefix])) { $this->onProtocolError($connection, "Unknown response prefix: '$prefix'."); } $payload = $this->handlers[$prefix]->handle($connection, substr($header, 1)); return $payload; } protected function onProtocolError(CompositeConnectionInterface $connection, $message) { CommunicationException::handle( new ProtocolException($connection, $message) ); } } 0) { $handlersCache = array(); $reader = $connection->getProtocol()->getResponseReader(); for ($i = 0; $i < $length; ++$i) { $header = $connection->readLine(); $prefix = $header[0]; if (isset($handlersCache[$prefix])) { $handler = $handlersCache[$prefix]; } else { $handler = $reader->getHandler($prefix); $handlersCache[$prefix] = $handler; } $list[$i] = $handler->handle($connection, substr($header, 1)); } } return $list; } } = 0) { return substr($connection->readBuffer($length + 2), 0, -2); } if ($length == -1) { return; } CommunicationException::handle(new ProtocolException( $connection, "Value '$payload' is not a valid length for a bulk response." )); return; } } getId(); $arguments = $command->getArguments(); $cmdlen = strlen($commandID); $reqlen = count($arguments) + 1; $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n"; foreach ($arguments as $argument) { $arglen = strlen($argument); $buffer .= "\${$arglen}\r\n{$argument}\r\n"; } return $buffer; } } directory = $baseDirectory; $this->prefix = __NAMESPACE__.'\\'; $this->prefixLength = strlen($this->prefix); } public static function register($prepend = false) { spl_autoload_register(array(new self(), 'autoload'), true, $prepend); } public function autoload($className) { if (0 === strpos($className, $this->prefix)) { $parts = explode('\\', substr($className, $this->prefixLength)); $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php'; if (is_file($filepath)) { require $filepath; } } } } getMessage(), 2); return $errorType; } public function toErrorResponse() { return new Error($this->getMessage()); } } checkPreconditions($iterator); $this->size = count($iterator) / 2; $this->iterator = $iterator; $this->position = $iterator->getPosition(); $this->current = $this->size > 0 ? $this->getValue() : null; } protected function checkPreconditions(MultiBulk $iterator) { if ($iterator->getPosition() !== 0) { throw new \InvalidArgumentException( 'Cannot initialize a tuple iterator using an already initiated iterator.' ); } if (($size = count($iterator)) % 2 !== 0) { throw new \UnexpectedValueException('Invalid response size for a tuple iterator.'); } } #[\ReturnTypeWillChange] public function getInnerIterator() { return $this->iterator; } public function __destruct() { $this->iterator->drop(true); } protected function getValue() { $k = $this->iterator->current(); $this->iterator->next(); $v = $this->iterator->current(); $this->iterator->next(); return array($k, $v); } } current; } #[\ReturnTypeWillChange] public function key() { return $this->position; } #[\ReturnTypeWillChange] public function next() { if (++$this->position < $this->size) { $this->current = $this->getValue(); } } #[\ReturnTypeWillChange] public function valid() { return $this->position < $this->size; } #[\ReturnTypeWillChange] public function count() { return $this->size; } public function getPosition() { return $this->position; } abstract protected function getValue(); } connection = $connection; $this->size = $size; $this->position = 0; $this->current = $size > 0 ? $this->getValue() : null; } public function __destruct() { $this->drop(true); } public function drop($disconnect = false) { if ($disconnect) { if ($this->valid()) { $this->position = $this->size; $this->connection->disconnect(); } } else { while ($this->valid()) { $this->next(); } } } protected function getValue() { return $this->connection->read(); } } payload = $payload; } public function __toString() { return $this->payload; } public function getPayload() { return $this->payload; } public static function get($payload) { switch ($payload) { case 'OK': case 'QUEUED': if (isset(self::$$payload)) { return self::$$payload; } return self::$$payload = new self($payload); default: return new self($payload); } } } message = $message; } public function getMessage() { return $this->message; } public function getErrorType() { list($errorType) = explode(' ', $this->getMessage(), 2); return $errorType; } public function __toString() { return $this->getMessage(); } } getArguments(); for ($i = 3; $i < count($arguments); ++$i) { switch (strtoupper($arguments[$i])) { case 'WITHSCORES': return true; case 'LIMIT': $i += 2; break; } } return false; } } 2 && is_array($arguments[$argc - 1])) { $options = $this->prepareOptions(array_pop($arguments)); } if (is_array($arguments[1])) { $arguments = array_merge( array($arguments[0], count($arguments[1])), $arguments[1] ); } return array_merge($arguments, $options); } private function prepareOptions($options) { $opts = array_change_key_case($options, CASE_UPPER); $finalizedOpts = array(); if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) { $finalizedOpts[] = 'WEIGHTS'; foreach ($opts['WEIGHTS'] as $weight) { $finalizedOpts[] = $weight; } } if (isset($opts['AGGREGATE'])) { $finalizedOpts[] = 'AGGREGATE'; $finalizedOpts[] = $opts['AGGREGATE']; } return $finalizedOpts; } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } return $arguments; } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = array(); if (!empty($options['MATCH'])) { $normalized[] = 'MATCH'; $normalized[] = $options['MATCH']; } if (!empty($options['COUNT'])) { $normalized[] = 'COUNT'; $normalized[] = $options['COUNT']; } return $normalized; } public function parseResponse($data) { if (is_array($data)) { $fields = $data[1]; $result = array(); for ($i = 0; $i < count($fields); ++$i) { $result[$fields[$i]] = $fields[++$i]; } $data[1] = $result; } return $data; } } getArgument(0))) { case 'numsub': return self::processNumsub($data); default: return $data; } } protected static function processNumsub(array $channels) { $processed = array(); $count = count($channels); for ($i = 0; $i < $count; ++$i) { $processed[$channels[$i]] = $channels[++$i]; } return $processed; } } getArguments(), 2, $this->getKeysCount()); } protected function filterArguments(array $arguments) { if (($numkeys = $this->getKeysCount()) && $numkeys < 0) { $numkeys = count($arguments) + $numkeys; } return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments); } public function getEvalArguments() { $arguments = $this->getArguments(); $arguments[0] = $this->getScript(); return $arguments; } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } return $arguments; } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = array(); if (!empty($options['MATCH'])) { $normalized[] = 'MATCH'; $normalized[] = $options['MATCH']; } if (!empty($options['COUNT'])) { $normalized[] = 'COUNT'; $normalized[] = $options['COUNT']; } return $normalized; } } $v) { $flattenedKVs[] = $k; $flattenedKVs[] = $v; } return $flattenedKVs; } return $arguments; } } $score) { $arguments[] = $score; $arguments[] = $member; } } return $arguments; } } parseRow($row); $current[$k] = $v; } return $info; } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } return $arguments; } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = array(); if (!empty($options['MATCH'])) { $normalized[] = 'MATCH'; $normalized[] = $options['MATCH']; } if (!empty($options['COUNT'])) { $normalized[] = 'COUNT'; $normalized[] = $options['COUNT']; } return $normalized; } public function parseResponse($data) { if (is_array($data)) { $members = $data[1]; $result = array(); for ($i = 0; $i < count($members); ++$i) { $result[$members[$i]] = (float) $members[++$i]; } $data[1] = $result; } return $data; } } parseRow($row); $info[$k] = $v; } return $info; } protected function parseRow($row) { list($k, $v) = explode(':', $row, 2); if (preg_match('/^db\d+$/', $k)) { $v = $this->parseDatabaseStats($v); } return array($k, $v); } protected function parseDatabaseStats($str) { $db = array(); foreach (explode(',', $str) as $dbvar) { list($dbvk, $dbvv) = explode('=', $dbvar); $db[trim($dbvk)] = $dbvv; } return $db; } protected function parseAllocationStats($str) { $stats = array(); foreach (explode(',', $str) as $kv) { @list($size, $objects, $extra) = explode('=', $kv); if (isset($extra)) { $size = ">=$objects"; $objects = $extra; } $stats[$size] = $objects; } return $stats; } } getArgument(0)); } } $entry) { $log[$index] = array( 'id' => $entry[0], 'timestamp' => $entry[1], 'duration' => $entry[2], 'command' => $entry[3], ); } return $log; } return $data; } } getArgument(0); $argument = is_null($argument) ? null : strtolower($argument); switch ($argument) { case 'masters': case 'slaves': return self::processMastersOrSlaves($data); default: return $data; } } protected static function processMastersOrSlaves(array $servers) { foreach ($servers as $idx => $node) { $processed = array(); $count = count($node); for ($i = 0; $i < $count; ++$i) { $processed[$node[$i]] = $node[++$i]; } $servers[$idx] = $processed; } return $servers; } } getArgument(0); } } getArguments(), CASE_UPPER); switch (strtoupper($args[0])) { case 'LIST': return $this->parseClientList($data); case 'KILL': case 'GETNAME': case 'SETNAME': default: return $data; } } protected function parseClientList($data) { $clients = array(); foreach (explode("\n", $data, -1) as $clientData) { $client = array(); foreach (explode(' ', $clientData) as $kv) { @list($k, $v) = explode('=', $kv); $client[$k] = $v; } $clients[] = $client; } return $clients; } } commandID = strtoupper(array_shift($arguments)); $this->arguments = $arguments; } public static function create($commandID ) { $arguments = func_get_args(); $command = new self($arguments); return $command; } public function getId() { return $this->commandID; } public function setArguments(array $arguments) { $this->arguments = $arguments; unset($this->slot); } public function setRawArguments(array $arguments) { $this->setArguments($arguments); } public function getArguments() { return $this->arguments; } public function getArgument($index) { if (isset($this->arguments[$index])) { return $this->arguments[$index]; } } public function setSlot($slot) { $this->slot = $slot; } public function getSlot() { if (isset($this->slot)) { return $this->slot; } } public function parseResponse($data) { return $data; } } $value) { $modifier = strtoupper($modifier); if ($modifier === 'COPY' && $value == true) { $arguments[] = $modifier; } if ($modifier === 'REPLACE' && $value == true) { $arguments[] = $modifier; } } } return $arguments; } } true); $lastType = 'array'; } if ($lastType === 'array') { $options = $this->prepareOptions(array_pop($arguments)); return array_merge($arguments, $options); } } return $arguments; } protected function prepareOptions($options) { $opts = array_change_key_case($options, CASE_UPPER); $finalizedOpts = array(); if (!empty($opts['WITHSCORES'])) { $finalizedOpts[] = 'WITHSCORES'; } return $finalizedOpts; } protected function withScores() { $arguments = $this->getArguments(); if (count($arguments) < 4) { return false; } return strtoupper($arguments[3]) === 'WITHSCORES'; } public function parseResponse($data) { if ($this->withScores()) { $result = array(); for ($i = 0; $i < count($data); ++$i) { $result[$data[$i]] = $data[++$i]; } return $result; } return $data; } } arguments = $this->filterArguments($arguments); unset($this->slot); } public function setRawArguments(array $arguments) { $this->arguments = $arguments; unset($this->slot); } public function getArguments() { return $this->arguments; } public function getArgument($index) { if (isset($this->arguments[$index])) { return $this->arguments[$index]; } } public function setSlot($slot) { $this->slot = $slot; } public function getSlot() { if (isset($this->slot)) { return $this->slot; } } public function parseResponse($data) { return $data; } public static function normalizeArguments(array $arguments) { if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) { return $arguments[0]; } return $arguments; } public static function normalizeVariadic(array $arguments) { if (count($arguments) === 2 && is_array($arguments[1])) { return array_merge(array($arguments[0]), $arguments[1]); } return $arguments; } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } return $arguments; } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = array(); if (!empty($options['MATCH'])) { $normalized[] = 'MATCH'; $normalized[] = $options['MATCH']; } if (!empty($options['COUNT'])) { $normalized[] = 'COUNT'; $normalized[] = $options['COUNT']; } return $normalized; } } $v) { $flattenedKVs[] = $k; $flattenedKVs[] = $v; } return $flattenedKVs; } return $arguments; } } prefix = $prefix; $this->commands = array( 'EXISTS' => 'static::all', 'DEL' => 'static::all', 'TYPE' => 'static::first', 'KEYS' => 'static::first', 'RENAME' => 'static::all', 'RENAMENX' => 'static::all', 'EXPIRE' => 'static::first', 'EXPIREAT' => 'static::first', 'TTL' => 'static::first', 'MOVE' => 'static::first', 'SORT' => 'static::sort', 'DUMP' => 'static::first', 'RESTORE' => 'static::first', 'SET' => 'static::first', 'SETNX' => 'static::first', 'MSET' => 'static::interleaved', 'MSETNX' => 'static::interleaved', 'GET' => 'static::first', 'MGET' => 'static::all', 'GETSET' => 'static::first', 'INCR' => 'static::first', 'INCRBY' => 'static::first', 'DECR' => 'static::first', 'DECRBY' => 'static::first', 'RPUSH' => 'static::first', 'LPUSH' => 'static::first', 'LLEN' => 'static::first', 'LRANGE' => 'static::first', 'LTRIM' => 'static::first', 'LINDEX' => 'static::first', 'LSET' => 'static::first', 'LREM' => 'static::first', 'LPOP' => 'static::first', 'RPOP' => 'static::first', 'RPOPLPUSH' => 'static::all', 'SADD' => 'static::first', 'SREM' => 'static::first', 'SPOP' => 'static::first', 'SMOVE' => 'static::skipLast', 'SCARD' => 'static::first', 'SISMEMBER' => 'static::first', 'SINTER' => 'static::all', 'SINTERSTORE' => 'static::all', 'SUNION' => 'static::all', 'SUNIONSTORE' => 'static::all', 'SDIFF' => 'static::all', 'SDIFFSTORE' => 'static::all', 'SMEMBERS' => 'static::first', 'SRANDMEMBER' => 'static::first', 'ZADD' => 'static::first', 'ZINCRBY' => 'static::first', 'ZREM' => 'static::first', 'ZRANGE' => 'static::first', 'ZREVRANGE' => 'static::first', 'ZRANGEBYSCORE' => 'static::first', 'ZCARD' => 'static::first', 'ZSCORE' => 'static::first', 'ZREMRANGEBYSCORE' => 'static::first', 'SETEX' => 'static::first', 'APPEND' => 'static::first', 'SUBSTR' => 'static::first', 'BLPOP' => 'static::skipLast', 'BRPOP' => 'static::skipLast', 'ZUNIONSTORE' => 'static::zsetStore', 'ZINTERSTORE' => 'static::zsetStore', 'ZCOUNT' => 'static::first', 'ZRANK' => 'static::first', 'ZREVRANK' => 'static::first', 'ZREMRANGEBYRANK' => 'static::first', 'HSET' => 'static::first', 'HSETNX' => 'static::first', 'HMSET' => 'static::first', 'HINCRBY' => 'static::first', 'HGET' => 'static::first', 'HMGET' => 'static::first', 'HDEL' => 'static::first', 'HEXISTS' => 'static::first', 'HLEN' => 'static::first', 'HKEYS' => 'static::first', 'HVALS' => 'static::first', 'HGETALL' => 'static::first', 'SUBSCRIBE' => 'static::all', 'UNSUBSCRIBE' => 'static::all', 'PSUBSCRIBE' => 'static::all', 'PUNSUBSCRIBE' => 'static::all', 'PUBLISH' => 'static::first', 'PERSIST' => 'static::first', 'STRLEN' => 'static::first', 'SETRANGE' => 'static::first', 'GETRANGE' => 'static::first', 'SETBIT' => 'static::first', 'GETBIT' => 'static::first', 'RPUSHX' => 'static::first', 'LPUSHX' => 'static::first', 'LINSERT' => 'static::first', 'BRPOPLPUSH' => 'static::skipLast', 'ZREVRANGEBYSCORE' => 'static::first', 'WATCH' => 'static::all', 'PTTL' => 'static::first', 'PEXPIRE' => 'static::first', 'PEXPIREAT' => 'static::first', 'PSETEX' => 'static::first', 'INCRBYFLOAT' => 'static::first', 'BITOP' => 'static::skipFirst', 'BITCOUNT' => 'static::first', 'HINCRBYFLOAT' => 'static::first', 'EVAL' => 'static::evalKeys', 'EVALSHA' => 'static::evalKeys', 'MIGRATE' => 'static::migrate', 'SSCAN' => 'static::first', 'ZSCAN' => 'static::first', 'HSCAN' => 'static::first', 'PFADD' => 'static::first', 'PFCOUNT' => 'static::all', 'PFMERGE' => 'static::all', 'ZLEXCOUNT' => 'static::first', 'ZRANGEBYLEX' => 'static::first', 'ZREMRANGEBYLEX' => 'static::first', 'ZREVRANGEBYLEX' => 'static::first', 'BITPOS' => 'static::first', 'HSTRLEN' => 'static::first', 'BITFIELD' => 'static::first', 'GEOADD' => 'static::first', 'GEOHASH' => 'static::first', 'GEOPOS' => 'static::first', 'GEODIST' => 'static::first', 'GEORADIUS' => 'static::georadius', 'GEORADIUSBYMEMBER' => 'static::georadius', ); } public function setPrefix($prefix) { $this->prefix = $prefix; } public function getPrefix() { return $this->prefix; } public function process(CommandInterface $command) { if ($command instanceof PrefixableCommandInterface) { $command->prefixKeys($this->prefix); } elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) { call_user_func($this->commands[$commandID], $command, $this->prefix); } } public function setCommandHandler($commandID, $callback = null) { $commandID = strtoupper($commandID); if (!isset($callback)) { unset($this->commands[$commandID]); return; } if (!is_callable($callback)) { throw new \InvalidArgumentException( 'Callback must be a valid callable object or NULL' ); } $this->commands[$commandID] = $callback; } public function __toString() { return $this->getPrefix(); } public static function first(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $arguments[0] = "$prefix{$arguments[0]}"; $command->setRawArguments($arguments); } } public static function all(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { foreach ($arguments as &$key) { $key = "$prefix$key"; } $command->setRawArguments($arguments); } } public static function interleaved(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $length = count($arguments); for ($i = 0; $i < $length; $i += 2) { $arguments[$i] = "$prefix{$arguments[$i]}"; } $command->setRawArguments($arguments); } } public static function skipFirst(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $length = count($arguments); for ($i = 1; $i < $length; ++$i) { $arguments[$i] = "$prefix{$arguments[$i]}"; } $command->setRawArguments($arguments); } } public static function skipLast(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $length = count($arguments); for ($i = 0; $i < $length - 1; ++$i) { $arguments[$i] = "$prefix{$arguments[$i]}"; } $command->setRawArguments($arguments); } } public static function sort(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $arguments[0] = "$prefix{$arguments[0]}"; if (($count = count($arguments)) > 1) { for ($i = 1; $i < $count; ++$i) { switch (strtoupper($arguments[$i])) { case 'BY': case 'STORE': $arguments[$i] = "$prefix{$arguments[++$i]}"; break; case 'GET': $value = $arguments[++$i]; if ($value !== '#') { $arguments[$i] = "$prefix$value"; } break; case 'LIMIT'; $i += 2; break; } } } $command->setRawArguments($arguments); } } public static function evalKeys(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { for ($i = 2; $i < $arguments[1] + 2; ++$i) { $arguments[$i] = "$prefix{$arguments[$i]}"; } $command->setRawArguments($arguments); } } public static function zsetStore(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $arguments[0] = "$prefix{$arguments[0]}"; $length = ((int) $arguments[1]) + 2; for ($i = 2; $i < $length; ++$i) { $arguments[$i] = "$prefix{$arguments[$i]}"; } $command->setRawArguments($arguments); } } public static function migrate(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $arguments[2] = "$prefix{$arguments[2]}"; $command->setRawArguments($arguments); } } public static function georadius(CommandInterface $command, $prefix) { if ($arguments = $command->getArguments()) { $arguments[0] = "$prefix{$arguments[0]}"; $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; if (($count = count($arguments)) > $startIndex) { for ($i = $startIndex; $i < $count; ++$i) { switch (strtoupper($arguments[$i])) { case 'STORE': case 'STOREDIST': $arguments[$i] = "$prefix{$arguments[++$i]}"; break; } } } $command->setRawArguments($arguments); } } } add($processor); } } public function add(ProcessorInterface $processor) { $this->processors[] = $processor; } public function remove(ProcessorInterface $processor) { if (false !== $index = array_search($processor, $this->processors, true)) { unset($this[$index]); } } public function process(CommandInterface $command) { for ($i = 0; $i < $count = count($this->processors); ++$i) { $this->processors[$i]->process($command); } } public function getProcessors() { return $this->processors; } public function getIterator() { return new \ArrayIterator($this->processors); } public function count() { return count($this->processors); } #[\ReturnTypeWillChange] public function offsetExists($index) { return isset($this->processors[$index]); } #[\ReturnTypeWillChange] public function offsetGet($index) { return $this->processors[$index]; } #[\ReturnTypeWillChange] public function offsetSet($index, $processor) { if (!$processor instanceof ProcessorInterface) { throw new \InvalidArgumentException( 'A processor chain accepts only instances of '. "'Predis\Command\Processor\ProcessorInterface'." ); } $this->processors[$index] = $processor; } #[\ReturnTypeWillChange] public function offsetUnset($index) { unset($this->processors[$index]); $this->processors = array_values($this->processors); } } requiredCommand($client, 'SCAN'); parent::__construct($client, $match, $count); } protected function executeCommand() { return $this->client->scan($this->cursor, $this->getScanOptions()); } } requiredCommand($client, 'HSCAN'); parent::__construct($client, $match, $count); $this->key = $key; } protected function executeCommand() { return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions()); } protected function extractNext() { $this->position = key($this->elements); $this->current = current($this->elements); unset($this->elements[$this->position]); } } requiredCommand($client, 'LRANGE'); if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) { throw new \InvalidArgumentException('The $count argument must be a positive integer.'); } $this->client = $client; $this->key = $key; $this->count = $count; $this->reset(); } protected function requiredCommand(ClientInterface $client, $commandID) { if (!$client->getProfile()->supportsCommand($commandID)) { throw new NotSupportedException("The current profile does not support '$commandID'."); } } protected function reset() { $this->valid = true; $this->fetchmore = true; $this->elements = array(); $this->position = -1; $this->current = null; } protected function executeCommand() { return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count); } protected function fetch() { $elements = $this->executeCommand(); if (count($elements) < $this->count) { $this->fetchmore = false; } $this->elements = $elements; } protected function extractNext() { ++$this->position; $this->current = array_shift($this->elements); } #[\ReturnTypeWillChange] public function rewind() { $this->reset(); $this->next(); } #[\ReturnTypeWillChange] public function current() { return $this->current; } #[\ReturnTypeWillChange] public function key() { return $this->position; } #[\ReturnTypeWillChange] public function next() { if (!$this->elements && $this->fetchmore) { $this->fetch(); } if ($this->elements) { $this->extractNext(); } else { $this->valid = false; } } #[\ReturnTypeWillChange] public function valid() { return $this->valid; } } requiredCommand($client, 'ZSCAN'); parent::__construct($client, $match, $count); $this->key = $key; } protected function executeCommand() { return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions()); } protected function extractNext() { $this->position = key($this->elements); $this->current = current($this->elements); unset($this->elements[$this->position]); } } client = $client; $this->match = $match; $this->count = $count; $this->reset(); } protected function requiredCommand(ClientInterface $client, $commandID) { if (!$client->getProfile()->supportsCommand($commandID)) { throw new NotSupportedException("The current profile does not support '$commandID'."); } } protected function reset() { $this->valid = true; $this->fetchmore = true; $this->elements = array(); $this->cursor = 0; $this->position = -1; $this->current = null; } protected function getScanOptions() { $options = array(); if (strlen(strval($this->match)) > 0) { $options['MATCH'] = $this->match; } if ($this->count > 0) { $options['COUNT'] = $this->count; } return $options; } abstract protected function executeCommand(); protected function fetch() { list($cursor, $elements) = $this->executeCommand(); if (!$cursor) { $this->fetchmore = false; } $this->cursor = $cursor; $this->elements = $elements; } protected function extractNext() { ++$this->position; $this->current = array_shift($this->elements); } #[\ReturnTypeWillChange] public function rewind() { $this->reset(); $this->next(); } #[\ReturnTypeWillChange] public function current() { return $this->current; } #[\ReturnTypeWillChange] public function key() { return $this->position; } #[\ReturnTypeWillChange] public function next() { tryFetch: { if (!$this->elements && $this->fetchmore) { $this->fetch(); } if ($this->elements) { $this->extractNext(); } elseif ($this->cursor) { goto tryFetch; } else { $this->valid = false; } } } #[\ReturnTypeWillChange] public function valid() { return $this->valid; } } requiredCommand($client, 'SSCAN'); parent::__construct($client, $match, $count); $this->key = $key; } protected function executeCommand() { return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions()); } } connection = $connection; } public function getConnection() { return $this->connection; } public function shouldResetConnection() { return true; } public static function handle(CommunicationException $exception) { if ($exception->shouldResetConnection()) { $connection = $exception->getConnection(); if ($connection->isConnected()) { $connection->disconnect(); } } throw $exception; } } function ($options, $option) { $profile = $options->getDefault($option); $profile->defineCommand('hmgetall', 'HashMultipleGetAll'); return $profile; }, 'replication' => function () { $strategy = new ReplicationStrategy(); $strategy->setScriptReadOnly(HashMultipleGetAll::BODY); $replication = new MasterSlaveReplication($strategy); return $replication; }, ); $client = new Predis\Client($parameters, $options); $hashes = $client->hmgetall('metavars', 'servers'); $replication = $client->getConnection(); $stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave'); echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL; var_export($hashes); 'nrk:')); $client->mset(array('foo' => 'bar', 'lol' => 'wut')); var_export($client->mget('foo', 'lol')); var_export($client->keys('*')); nodes = array(); $this->nodesCount = 0; } public function add($node, $weight = null) { $this->nodes[] = $node; ++$this->nodesCount; } public function remove($node) { $this->nodes = array_filter($this->nodes, function ($n) use ($node) { return $n !== $node; }); $this->nodesCount = count($this->nodes); } public function getSlot($hash) { return $this->nodesCount > 1 ? abs($hash % $this->nodesCount) : 0; } public function getBySlot($slot) { return isset($this->nodes[$slot]) ? $this->nodes[$slot] : null; } public function getByHash($hash) { if (!$this->nodesCount) { throw new RuntimeException('No connections.'); } $slot = $this->getSlot($hash); $node = $this->getBySlot($slot); return $node; } public function get($value) { $hash = $this->hash($value); $node = $this->getByHash($hash); return $node; } public function hash($value) { return crc32($value); } public function getHashGenerator() { return $this; } } $options = array( 'cluster' => function () { $distributor = new NaiveDistributor(); $strategy = new PredisStrategy($distributor); $cluster = new PredisCluster($strategy); return $cluster; }, ); $client = new Predis\Client($multiple_servers, $options); for ($i = 0; $i < 100; ++$i) { $client->set("key:$i", str_pad($i, 4, '0', 0)); $client->get("key:$i"); } $server1 = $client->getClientFor('first')->info(); $server2 = $client->getClientFor('second')->info(); if (isset($server1['Keyspace'], $server2['Keyspace'])) { $server1 = $server1['Keyspace']; $server2 = $server2['Keyspace']; } printf("Server '%s' has %d keys while server '%s' has %d keys.\n", 'first', $server1['db15']['keys'], 'second', $server2['db15']['keys'] ); true, 'watch' => $key, 'retry' => 3, ); $client->transaction($options, function ($tx) use ($key, &$element) { @list($element) = $tx->zrange($key, 0, 0); if (isset($element)) { $tx->multi(); $tx->zrem($key, $element); } }); return $element; } $client = new Predis\Client($single_server); $zpopped = zpop($client, 'zset'); echo isset($zpopped) ? "ZPOPed $zpopped" : 'Nothing to ZPOP!', PHP_EOL; '127.0.0.1', 'port' => 6379, 'database' => 15, ); $multiple_servers = array( array( 'host' => '127.0.0.1', 'port' => 6379, 'database' => 15, 'alias' => 'first', ), array( 'host' => '127.0.0.1', 'port' => 6380, 'database' => 15, 'alias' => 'second', ), ); tstart = microtime(true); parent::connect(); } private function storeDebug(CommandInterface $command, $direction) { $firtsArg = $command->getArgument(0); $timestamp = round(microtime(true) - $this->tstart, 4); $debug = $command->getId(); $debug .= isset($firtsArg) ? " $firtsArg " : ' '; $debug .= "$direction $this"; $debug .= " [{$timestamp}s]"; $this->debugBuffer[] = $debug; } public function writeRequest(CommandInterface $command) { parent::writeRequest($command); $this->storeDebug($command, '->'); } public function readResponse(CommandInterface $command) { $response = parent::readResponse($command); $this->storeDebug($command, '<-'); return $response; } public function getDebugBuffer() { return $this->debugBuffer; } } $options = array( 'connections' => array( 'tcp' => 'SimpleDebuggableConnection', ), ); $client = new Predis\Client($single_server, $options); $client->set('foo', 'bar'); $client->get('foo'); $client->info(); var_export($client->getConnection()->getDebugBuffer()); '2.8')); $client->del('predis:set', 'predis:zset', 'predis:hash'); for ($i = 0; $i < 5; ++$i) { $client->sadd('predis:set', "member:$i"); $client->zadd('predis:zset', -$i, "member:$i"); $client->hset('predis:hash', "field:$i", "value:$i"); } echo 'Scan the keyspace matching only our prefixed keys:', PHP_EOL; foreach (new Iterator\Keyspace($client, 'predis:*') as $key) { echo " - $key", PHP_EOL; } echo 'Scan members of `predis:set`:', PHP_EOL; foreach (new Iterator\SetKey($client, 'predis:set') as $member) { echo " - $member", PHP_EOL; } echo 'Scan members and ranks of `predis:zset`:', PHP_EOL; foreach (new Iterator\SortedSetKey($client, 'predis:zset') as $member => $rank) { echo " - $member [rank: $rank]", PHP_EOL; } echo 'Scan fields and values of `predis:hash`:', PHP_EOL; foreach (new Iterator\HashKey($client, 'predis:hash') as $field => $value) { echo " - $field => $value", PHP_EOL; } pipeline(function ($pipe) { $pipe->flushdb(); $pipe->incrby('counter', 10); $pipe->incrby('counter', 30); $pipe->exists('counter'); $pipe->get('counter'); $pipe->mget('does_not_exist', 'counter'); }); var_export($responses); 0)); $timestamp = new DateTime(); foreach (($monitor = $client->monitor()) as $event) { $timestamp->setTimestamp((int) $event->timestamp); if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') { echo 'Exiting the monitor loop...', PHP_EOL; $monitor->stop(); break; } echo "* Received {$event->command} on DB {$event->database} at {$timestamp->format(DateTime::W3C)}", PHP_EOL; if (isset($event->arguments)) { echo " Arguments: {$event->arguments}", PHP_EOL; } } $version = redis_version($client->info()); echo "Goodbye from Redis $version!", PHP_EOL; 'sentinel', 'service' => 'mymaster', )); $exists = $client->exists('foo') ? 'yes' : 'no'; $current = $client->getConnection()->getCurrent()->getParameters(); echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL; $client->set('foo', 'bar'); $current = $client->getConnection()->getCurrent()->getParameters(); echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL; $bar = $client->get('foo'); $current = $client->getConnection()->getCurrent()->getParameters(); echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL; 0)); $pubsub = $client->pubSubLoop(); $dispatcher = new Predis\PubSub\DispatcherLoop($pubsub); class EventsListener implements Countable { private $events; public function __construct() { $this->events = array(); } public function count() { return count($this->events); } public function getEvents() { return $this->events; } public function __invoke($payload) { $this->events[] = $payload; } } $dispatcher->attachCallback('events', ($events = new EventsListener())); $dispatcher->attachCallback('control', function ($payload) use ($dispatcher) { if ($payload === 'terminate_dispatcher') { $dispatcher->stop(); } }); $dispatcher->run(); echo "We received {$events->count()} messages!", PHP_EOL; $version = redis_version($client->info()); echo "Goodbye from Redis $version!", PHP_EOL; true); $client = new Predis\Client($parameters, $options); $exists = $client->exists('foo') ? 'yes' : 'no'; $current = $client->getConnection()->getCurrent()->getParameters(); echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL; $client->set('foo', 'bar'); $current = $client->getConnection()->getCurrent()->getParameters(); echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL; $bar = $client->get('foo'); $current = $client->getConnection()->getCurrent()->getParameters(); echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL; function ($options) { $profile = $options->getDefault('profile'); $profile->defineCommand('increxby', 'IncrementExistingKeysBy'); return $profile; }, )); $client->mset('foo', 10, 'foobar', 100); var_export($client->increxby('foo', 'foofoo', 'foobar', 50)); 0)); $pubsub = $client->pubSubLoop(); $pubsub->subscribe('control_channel', 'notifications'); foreach ($pubsub as $message) { switch ($message->kind) { case 'subscribe': echo "Subscribed to {$message->channel}", PHP_EOL; break; case 'message': if ($message->channel == 'control_channel') { if ($message->payload == 'quit_loop') { echo 'Aborting pubsub loop...', PHP_EOL; $pubsub->unsubscribe(); } else { echo "Received an unrecognized command: {$message->payload}.", PHP_EOL; } } else { echo "Received the following message from {$message->channel}:", PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL; } break; } } unset($pubsub); $version = redis_version($client->info()); echo "Goodbye from Redis $version!", PHP_EOL; set('library', 'predis'); $response = $client->get('library'); var_export($response); echo PHP_EOL; $mkv = array( 'uid:0001' => '1st user', 'uid:0002' => '2nd user', 'uid:0003' => '3rd user', ); $client->mset($mkv); $response = $client->mget(array_keys($mkv)); var_export($response); echo PHP_EOL; $response = $client->executeRaw(array( 'MGET', 'uid:0001', 'uid:0002', 'uid:0003', )); var_export($response); echo PHP_EOL; = 5.4.0 '. "or a polyfill for SessionHandlerInterface provided by an external package.\n"); } $client = new Predis\Client($single_server, array('prefix' => 'sessions:')); $handler = new Predis\Session\Handler($client, array('gc_maxlifetime' => 5)); $handler->register(); session_id('example_session_id'); session_start(); if (isset($_SESSION['foo'])) { echo "Session has `foo` set to {$_SESSION['foo']}", PHP_EOL; } else { $_SESSION['foo'] = $value = mt_rand(); echo "Empty session, `foo` has been set with $value", PHP_EOL; } type = $type; } public function matches($element) { return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type; } } copy($value); } property = $property; } public function matches($object, $property) { return $property == $this->property; } } class = $class; $this->property = $property; } public function matches($object, $property) { return ($object instanceof $this->class) && $property == $this->property; } } propertyType = $propertyType; } public function matches($object, $property) { try { $reflectionProperty = ReflectionHelper::getProperty($object, $property); } catch (ReflectionException $exception) { return false; } $reflectionProperty->setAccessible(true); return $reflectionProperty->getValue($object) instanceof $this->propertyType; } } getProperties(); $propsArr = array(); foreach ($props as $prop) { $propertyName = $prop->getName(); $propsArr[$propertyName] = $prop; } if ($parentClass = $ref->getParentClass()) { $parentPropsArr = self::getProperties($parentClass); foreach ($propsArr as $key => $property) { $parentPropsArr[$key] = $property; } return $parentPropsArr; } return $propsArr; } public static function getProperty($object, $name) { $reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object); if ($reflection->hasProperty($name)) { return $reflection->getProperty($name); } if ($parentClass = $reflection->getParentClass()) { return self::getProperty($parentClass->getName(), $name); } throw new PropertyException( sprintf( 'The class "%s" doesn\'t have a property with the given name: "%s".', is_object($object) ? get_class($object) : $object, $name ) ); } } useCloneMethod = $useCloneMethod; $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class)); $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class)); } public function skipUncloneable($skipUncloneable = true) { $this->skipUncloneable = $skipUncloneable; return $this; } public function copy($object) { $this->hashMap = []; return $this->recursiveCopy($object); } public function addFilter(Filter $filter, Matcher $matcher) { $this->filters[] = [ 'matcher' => $matcher, 'filter' => $filter, ]; } public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher) { $this->typeFilters[] = [ 'matcher' => $matcher, 'filter' => $filter, ]; } private function recursiveCopy($var) { if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { return $filter->apply($var); } if (is_resource($var)) { return $var; } if (is_array($var)) { return $this->copyArray($var); } if (! is_object($var)) { return $var; } return $this->copyObject($var); } private function copyArray(array $array) { foreach ($array as $key => $value) { $array[$key] = $this->recursiveCopy($value); } return $array; } private function copyObject($object) { $objectHash = spl_object_hash($object); if (isset($this->hashMap[$objectHash])) { return $this->hashMap[$objectHash]; } $reflectedObject = new ReflectionObject($object); $isCloneable = $reflectedObject->isCloneable(); if (false === $isCloneable) { if ($this->skipUncloneable) { $this->hashMap[$objectHash] = $object; return $object; } throw new CloneException( sprintf( 'The class "%s" is not cloneable.', $reflectedObject->getName() ) ); } $newObject = clone $object; $this->hashMap[$objectHash] = $newObject; if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) { return $newObject; } if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) { return $newObject; } foreach (ReflectionHelper::getProperties($reflectedObject) as $property) { $this->copyObjectProperty($newObject, $property); } return $newObject; } private function copyObjectProperty($object, ReflectionProperty $property) { if ($property->isStatic()) { return; } foreach ($this->filters as $item) { $matcher = $item['matcher']; $filter = $item['filter']; if ($matcher->matches($object, $property->getName())) { $filter->apply( $object, $property->getName(), function ($object) { return $this->recursiveCopy($object); } ); return; } } $property->setAccessible(true); $propertyValue = $property->getValue($object); $property->setValue($object, $this->recursiveCopy($propertyValue)); } private function getFirstMatchedTypeFilter(array $filterRecords, $var) { $matched = $this->first( $filterRecords, function (array $record) use ($var) { $matcher = $record['matcher']; return $matcher->matches($var); } ); return isset($matched) ? $matched['filter'] : null; } private function first(array $elements, callable $predicate) { foreach ($elements as $element) { if (call_user_func($predicate, $element)) { return $element; } } return null; } } $propertyValue) { $copy->{$propertyName} = $propertyValue; } return $copy; } } callback = $callable; } public function apply($element) { return call_user_func($this->callback, $element); } } copier = $copier; } public function apply($element) { $newElement = clone $element; $copy = $this->createCopyClosure(); return $copy($newElement); } private function createCopyClosure() { $copier = $this->copier; $copy = function (SplDoublyLinkedList $list) use ($copier) { for ($i = 1; $i <= $list->count(); $i++) { $copy = $copier->recursiveCopy($list->shift()); $list->push($copy); } return $list; }; return Closure::bind($copy, null, DeepCopy::class); } } __load(); } } setAccessible(true); $oldCollection = $reflectionProperty->getValue($object); $newCollection = $oldCollection->map( function ($item) use ($objectCopier) { return $objectCopier($item); } ); $reflectionProperty->setValue($object, $newCollection); } } setAccessible(true); $reflectionProperty->setValue($object, new ArrayCollection()); } } setAccessible(true); $reflectionProperty->setValue($object, null); } } callback = $callable; } public function apply($object, $property, $objectCopier) { $reflectionProperty = ReflectionHelper::getProperty($object, $property); $reflectionProperty->setAccessible(true); $value = call_user_func($this->callback, $reflectionProperty->getValue($object)); $reflectionProperty->setValue($object, $value); } } _c9kQU֚c̱'yfUWZo9f]wp= {$ H0 a`@= {$ H0 a`@= {$ H0 a`@= {$ H0 a`@= {$ Hǐg P=Tk0}j վǐJSO~:(M9*s@;C*M?4yd{ IqҥU}|2Uv=Tʏ~#A#}Z 怾Ўǐ4J`N怾ЎǐJS:(M9*s@;C*M9*s@;C*M9*s@;C*M?4yd{ I4yd{ 492#1$M92#1$M92#1TJ`N怾Ўǐ4J`N怾ЎǐJSw:(M9*s@;C*M9*s@;C*M9*s@;C*M;4yd{ I4yd{ 4o۪92#1$M92#17׼5]wa=O_Ӹ7W]Ji̩9`:j_c2#1F} 7|K_nSsG:UghGcH|\T}_?xK^rؘU뮻Sos<7]wqd?^zvs9a!7}s2^}ݫF򗿼9K(M96lkmyYooRGVۦjp2#1To,M {|.ЎǐJSɂ߾jz?|>#SOs@>;aʕ+v]v=SߤNkmvcK(M92޿2?Ri*&~ 5=?o|^<seh%=(s@;CoRYxՌ7^M/G[7&zZߵ9gRM~Ӛy%'۽կi4yjNUv=T#<}?&viO|]0|ы^zwm,s@;CoRM9xԶK}_>rIV<sj=ؿRv=TJ.e>ig'?<@V<sj=̽ϥ7tӑwm2#1M~zի^u1zj'&Zs?O}s˥m[9a!nT(Y[o~J`N-9g{ЎǐJSw}9o}nSi̩0}}o>rJuehGcH-5e/{YuZ}=rv=&Zs׃Y?-ЎǐJS+>i|;*r-͑˗/W4yjc^ zVv=ԪoZFK:(M9*s@;C*M9*s@;C*M9*s@;C*MP&}9a!i}&}9a!rJ`N怾Ўǐ4J`N怾Ўǐ4J`N怾ЎǐJST0's@_ehGcH|_0's@_ehGcH{ェ92#1$M92#1T/J`N怾Ўǐ4J`N怾ЎǐJSn馃o*cv]<svUO{nᆃ믿 OxBu?{l'1Zu^wuOyS{]&jmTBSOzғS+>wJ<0?a׾&U6=_y-T`.go?_>8_>tZys}{[zsTf$˿|//~ {]4HvmrPT K]`aY>j/!~?S'7.];VMwwX;s=?=j4y4_~m/_:ܿ/mmvg?>~y>^0aGY{ _{}?| _8x;qWep4|,-oy=g}{WfRYC-uCQa%~ʁ؄׽uJ@ʁl`>0SBj 'JѵX*;r0G `)J[?U*dz]`7˴%dQpŨ?˔ 9,9U>mZj?6 {DJ#dg-D$$eߺ:or[sM-@cۄ=@tY <5 ,S*'6zMd~H]vZ-$)҄Q;rSVvHY༲O)7PI9m]K\٭^lTc.Jއ<=l@uvٯd3g*KǡaN](;MdTvTqTeI.˾tN"#;ҸɎvXk}]7W.[aqdϸ'ӁaA؃JNk_M}n=ԔF]0 x%`Mvٹ%ryNvjUYg T:<@~;Y=d x, 3%_'eeJ@IXˁ̲U>m)ԔW}!1 {0owܔ˒``qK*ɾW~'K'O;Yd`v!'7P)+/F&O]`/#Ks/>oSW~O 0 TN[gWng x {3V)r2g@Sn:r^-X/=Zvr46Sh(vdv:mIJ+RLco%#~q4 g*>:i8VSp'Z-Yf {rv*;;ϳsV`3L٭Ryuv'+/_ x.c#챗NʎN-;ټ ?Tmr6ؾ=;JnWvr`ߔuv9Xt`rjӼ/t{%,`;)A9t T:gI/oNco$XcYCQvH`Ǧ7Pd]w67'<^sµ)^Ȏ C|"kze;7P9jgKc/#ٱ؁쎳lή@%p\ xgqa=g=ټ2xh)}?uv v_Xg= p-aMi2]*ӃN v9uvۓq/nbǰaj+T~[l[tz([g;y-=Z%9{ );FɿQ%TV]Yg'IsNi'Vy,2lrШVuvn\{<|L{ ٤d+AL% $إ?l T# { %Gx$GyA yO97J9^yYg*ʾ3ZKNxr{ #;CrԷ?9j_AƗ@0i첝]{e2aͨrjuv*ٍC$5gzS9p9<]gw\Oj}|h:슰G+; dj7(`ޖ `4?0'a.e'Xfg>24C*KF хڬ]uЕ!6=C& T_9czUyKɁh.XY)sMe@ٙVEF TrU5tq ^eYSztq./7Ps:;9.@@; BأrYUvJ+Ù!wYg~ݴ Tc*<'r``9S 0"af2eGg'Yy+g<Ƨ@%$z玀a&.;ݼ Q@;3Le&7P<k |̥>tU78rI_}^yKyN"OcVd x .s_l.AP&s|#{hz&;h7Jm/eHdgQ^P'12fGm̶ Rڭ 7YgWn1y}ruvƧ7P1s^y嵼\<6#s9_vvRF9k`5mu\Aٳ8a`X@/VuvlK^y><5ؙНawN2\q TMBam'٩3an* IYgL幘a ˀvGc'ʙ ~BBL,G 3y}%g@Tޟ`uɮW;,Kcr X.]Ck*y:A5^e-幚랇تwO9U3ZԆR0<t(d=fHѷQ(7fQn6YgWnbs;.祀aP7y4jK @=!\M__*~ {\X2dp^8tq./7PΎ<1{\H٥b?^\soۦ6JB.8<k$ ,u?HBУre@5J\ӫbxDT y`WmrA8!pCoV ̥rIJseg!q\V(gm+2Ğt uv6J^s^༄=_bJPz TZ=rְ|?m)︿xlGU9ʜAy`&NJ lc^xp'<<vA$jΆՆRXekKxn9E9~s ]8Z%N6ύM*s[.9')v6[`n+RParYY.d]l`70adh=u-מ߄=M^dhgdd]' ~ٮuv2=띟@iք=|O]Yg TvrP`wI}]^^?yxFc*7ήvRX٥ۥˀ2oDޞJ@(LPI7PIuv vX} !g%!SmrveS7_^9XR{=J^Cz0aod)N5!ChM%%dXgׇ<yl{Sjg<F'$2pȥa9 : T<^*P(Dd'o veq.UnBC4{<B2ۑ>a-vÇRXO7}9}9kΒO_c<# ن(2Lv:;O|8OuD3]gw Ty9R#ٕW xeup:ao`>Ax%؝tTYg`g$y_;] x6-G3 u]dh6TDzp^ xppzsE L9 2$..7kئ'yM΢'5L(l7^E]~Mev vJv-<>:.ף! d_"d(<:rJ9/gKZ?7ze$s2I Tr zo ;>?<苰7|9T]>:;"ο#\!לa_xEy;{XuvAqZcyPM^*@? ei7PMPy'-Am-,eΰ xao2|+o*e/z~y=5\%m ~ʠJ6=*ǥKV+z@_@CG*Gi}m{jecnz@_@C217= aO zMhCS;/s*=:?PA}m{jecnz@_@C217= aO zMhCS;/s*=:}{SA}m{jecnz@_@C~217= aO zMhCS;/s*=:};QA}m{jecnz@_@Co۪217= aO zMhCkT3ăOկszI׼RwWK6{ַ~An*u{,s[BX7xc5_??~K6FuZR31%it&鷺/UfO@Co~G>n7m zW^nSz@_K^#q=s5ghCkT {a﮻nSz@;X /nKaFȀ|]b6=A-9k\}=ThCkT {!O؃k|3b>Cm~zֳ hCkTžA{ӛT~9J6{A}h5=?\ݮV~sT z̭pҥk8`~Sn7JІר{uF ~]r]Oec[^-o9O{z6g=س^Za/+ݴ^W>'zz+ېW<ȣ=v/~W?mVn aQMY+}}򓟬~ˠ6$%%M_/}KT= s7VyXn aQ]$쥖4 ئ\™K9yM<=x;qM=@`.C 2LT=կS屨}JS[{Fғ=רa/k^ZnZCY>e屘~Jn{'\{B|`5|͇P]aw]z)/|pu|y4\Ԟ+qE/Z}߷zkuJ6Fu;wtMˠǶ};\W\׽ȟcUZw@^HKM'?<@uʠ6+9rCu?w'>QZ?K7]?m}ݫ-a/qַu {uʠǶRa?uӳ iN~O}SW_|gu֥@^h[ 쬟?gzYJ^.q._\ݮVszVe/{Yu^ʠܖ>9+w>0!׿z(=E+_#C^.ݪmKz@j3|w_uiM/N]z!53zKR=涄ʝuo#^gS/rͶ=m Nj{KS>W~OecnKrZۤ$^h^{okV=涄^wyg~\=y;x_^.ǡki/S5{-s[B8r3?77hCS;/s*=:e-j_=Uz!쩝A}m{jecnz@_@C]:վ zMhCS;/s*=:]rEuP=Uz!쩝A}m{jecnz@_@C˗/ʠJ6=217=ރ>:(s*=ˠJ6=217=<:(s*=ˠJ6{ zMhCS;/s*=ˠJ6{w zMhCS;/s*=:{ェ217= aO zMhc/~QuP=Uz!쩝A}m T_ec.j_zKSAԞ}0!^k_k ն`Nz@얰Iڛw%Q[yڕ7a`{$ H0 a`@= {$ H0 a`@= {$ H0 a`@= {$ H0 a`@= {$ ($IENDB`PNG  IHDR]ݽsRGBgAMA a pHYsodIDATx^ݹoTqҦB2NED("҄%R2U@$+$& Q5+P@J?\YHk̽g&S*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$sPJ(@H իWA(@(!) ܗ/_$ W/ÇG~[O $ aIPPDBRϟ?'BBIPPDB+ QgψPPDB+ QӧOPPDB+ QD E $  QD E $c  QD E $QBBIa>z8E $QBBIa>|8E $QBBIaÆʔ)S6mZԩSTBBIa>x@.Νtuuehپ}{XPPDBrO5g1cFBBIa޿_*wޭ,[,+/2+G?G! QZgϞ..ZrW^PPDBRݓʮ]۶m[M!f'OF?{(@(!Io߾]3gNMݹs'$J1CBIxѬ͛7?+\) Qm*Qɺu벢oߺu'j>O! QVo+|9ZBBIa=49r$+ V_7ڟ#._\CBIJh_.]֭[8g}qPPDBR?5/u8~xkf{9 Q5vڵqV8+cǎE (@HR)@dɒg8ˍ7j.uPPDBRvs:D3}ŋ_[(@(!I׬Y-f޽ѯ- QPeboV\BBIa=6rPܹ3_=S(@(!I?WX=&3gTfΜ}-[y E $k/ ӧOה؞={bN/^} ;K(@(!{/cq%h$X .Ǝ+ '5 (@H ^<-wMyƊ[LDY@X$cǎׁcL+ Q]n۬2k֬>qIך?~ab (@HZ" {ePPDB+ QD E ${U  QD E ${  QD E $QBBIaڻCBIPPDBR.]"BBIPPDB+ QH$ W(@(!) ܡ!  QD E $QBBIa$ W(@(!) ܁  QD E ${  QD E $̞=3OѾ؟: PԩS+SLtvvFC{ﱣ#% Jcƍo fO>ᅬ!7oξŋGig(@F 𫯾J=vx~(@F___ӳ~L>1Vw}3Ďiu(@xGuww|TǼy߻ _("0l'S@3D/<{gΟ?=]|G S- 'Of?ϦMF> rvޝ{YA#^( {X6v ~6$Zg@(@w|WjæDR-i?[x4YQH^jcg( IKX|H d3Ҋ/V@j(@$HpVPDRR9^ E(@$CBϊ" ~PH9> EJ(@^ 72RAԸ59lB (@'8 bx}eGlBQ(v26m r@YP(^'΃!g"3ᑏ:(@H^T u {~PBB :g|v|X(!͞Nah`g(Pp:G % \cǧvBx6+3Qp+?Mzo (@Ďre+; B |h= m@İ.P mÎO%\ڂD[. x+=+?[#V@Qh9vaX+h' -ŎOLmbag(ZDKPagAQh v|Vt{8D#;Cr:L ]|h6ZD:.4  5h%JXh`ځuf0^v+ DCx^P4Ĥ;Ea(@L ;>Q4vQ &h c'<ɯᑏoGbB ɢ1nsW/l(06 ®;xgk4<@(0: o뮠 c سPlX(@Pdúeg(FCbT rŽcg(ކ`g(B;>Q6\h(@dobg(b(@d;( *!z Gd y`%kZÚ׺"]`xRU 0QAx?PJ8vLDQ.;>7寎|eF&34-`_xRr`+=+pE;>agh(bWΡޑL(+V|Z>`ɰh.X2DT`gh9Q%O^>` whM}8e2C ˍ} o/ P;Ӏb?'& P@bY#܃F _x . P6O7Ez(@<43T \;>{zzF> (@]h@:ad.{J0ܺ;(D8Y츏C(<;\t"_~a'P;Cţ?Q";>rg~኎=# ]< Ёp3v^˚绝(X> ;?2hq$ pܹHWWWD\-@֟Z켷.\1j(@Ҵ[.W^Q]  ]/_$+3$ '>>|x43WTgtx8H}؂hf 4]_Qs :(@pT?t1|EuP.~b .gϞQ] @+3@>}JDuC3WTgH.f 4]_Q :(@pT?t1|EuHǏ.~b IQ] @+3@=zDDuC3WTgH.f|!q]_Q i8:(L2eB6mZԩSѯ9fXٰaCtXx .o]I(] 3>ΝtuuEX@(EU?t)̀|z7Vf̘Q~zkz .ͯ-EUnݺ=.]v]=ST?t)ݻw+˖-9-̈́gϾ,Qa ELZOG] 3~\,0zq^: ޽{nsњEqO[crqmp kbæzg@]ǎ?ܲeK: Uk<-Z+@{A{Ѳsy?v^ _zgݷ_ű9s2sٳ'z\Q`h_B3f@UZϟ_}+ ]vykEآžר.~R.}Κ5+z[y IQ]  ]vm]_Q i8:(@pT?t1|EuHn]_Q i8: ]_Q i8:(@pT?t1|EuH)>:(@pT?t1|EuHQ,)>:(@pT?t1|EuP.~b .@⣺+3$ GuC3WTgt Q] @+3$ GuC3WTgtQ] @+3@.~b IQ]  ].\ +3$ GuC3WTgt_Q[[ 4-jb Y17n|/HZ]i 8G!). NQ$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$Q$QU*uWIENDB`PNG  IHDR^:DsRGBgAMA a pHYsod*$IDATx^ɯee`~#b 82&BL8˜8@84!N1LҌT&0F#*^>{jWm~ȭ}Z9i *3|P Tg:'> @uO3|P Tg:'> @uO3|P Tg:'> @uO3|P Tg:'U}_z>jI|$(OܿoIMr:hOg}& Вu@XIUNt/rmjYu TO?T}F`+D3|R+-Yu TO>DD%\ ᓪ4\th: Wf*M'W4Z>+r$@Kb@4'Ui:Вu@XIU]裏$A4Z>JMr:hOtrE%\ ᓪ"Jh:d+D3|R+-Yu T>@D%\ ᓪ4\th: Wf*M'W4Z>+r׿$A4Z>JMr:hO?OIMr:hOtrE%\ ᓪtꪵN:i=r_MVߟ}ٵ38c5;?'"D3|RUWg9\ێ%-e\}c C_efMgpT8^{M2V|Ή=ZIU]{Fwyg{z( G?Ѣq~탟3h:m~ᵳ:k%]wQ]y)СCGm*D3|RU<Ӌh;U{8.C4ZʶXUr_JxŶ%CCjXIU]{wG~S)M߬Jy?/{4ZʶXEgO3]?3e}7>*[Lzť4]#NM2V߷3Q.}W>*Sl t4NRu{ ϩ>+rg?٢ WZ|7pyc@K[|2|*KLʃ.õ^{6>itN4Zʰ{hm>*ClG&%9>v?O4F 뀨+rғ̹{64SO]{nh:h˥gy憯s- n>*l6W\qņFdj,Nj@K뀖Z;];mTf観 j.--EZ3Y ᓪ"WȚ{Cx饗[΍7޸]v.S4Z\Ko+r͛3'`nR. [t8p`p,th)jТ du Tt6~(⋃+ˍg+-"-Ejkßa, ᓪf|ds;cjR:vO]fȕ7nΖo펗Cmh@ /0mh:ߗzn5!F?Q߳Efp Fq mɵ@K/ɜ>+rL)|K_Z4m&_sYomRu@^=NC ~ڱ ᓪZ79~4Z>+r @Kb@4'Ui:Вu@XIUNh:d+D3|RUW^}UIMr:hOtrE%\ ᓪ"wAIMr:hOtrE%\ ᓪ4\th: Wf8p@D%\ ᓪ4\th: WfȽ+ -Yu T䊦@Kb@4'Ui:Вu@XIU]{%A4Z>JMr:hOK/$ Вu@XIUNh:d+D3|R+-Yu T_|QD%\ ᓪ4\th: WfȽ -Yu T䊦@Kb@4'UuE矗th: Wf*M'W4Z>+r-sgq)g,?ПՎ@ C=vN>j'vI'޽{3lgܵk׎@ C=vJo:Yvb|^/%oɐ_ .ܦeSZz6R(Y.C۴ bZM?񢐗&S2Њuc>in+ϕW^(_ɘ~?MzNSzh饥:MƔ5K~O~2MXВ;|>={x}?{;?9Q`3J,=ul.:;Q&'VNyR\)cަw(]-ufdʙha9:VHgwNl*g ٭ Sedݻ^Kp?\s5G> 馛};v' P\@4Sed4pw_ NyPtk9]'1ÜeES{\3OFax|D35Oқ`=x9`S|;ǿ?b$5}_<KM~3Ws?t>I˥&u}x.sǍv+d$% hp W_&ᓔdr69?܂98>IǑ\L;2pv|,d$7o'0EN4G:dr)Yw0|#xΕPpx)p5cL yq1󼇝b, rn54nƨy-_Ef%+'aܧZiw >rgϞ=G>v10|{xL2cPu:S:sr& 4\=9c?XZ΀:n!3'Maȓm*p d6ĕPdb29+N;;L&Oq?b[<80ږ[qIKl<ٶ(nNIur vg21|Re7DrM<'I5eqG彼m7EOp-rk:{=QJwJ'Кᓕrfm<Z=`3D1|2 x8* @ =5hJ<=v<UrњsmQ8 v'DҒۡhD'WҌSj[y5>vLlV l3f?^NgAЂm) <1 z[4w޹ l#cUNt8;s1!pe5>gǦ~p<TN_ZYmVɖlih'CAݻwrr?/5>4OMei_ͪ>gp8(9?tjON4ndyt/|9=_eX8߽{|)'/8l;OB{٠oRǮLМH9 Q|}Ľlᓅ~!TSǾ0M;Ux+>6:PUǮ{.l)8KV>Yqp;~-8'`ʎO-g'?ls\.*8Z0^baJwa={lN&>!?DY 3e_*( 1|T$+f*اEfSsgb\ 0.Ox 35QCk:zSS>b3|Όт 1hj`sF<͎V\#hv>gVLi6e\xS(g?6Yfl!hɾC={|{>'4O%Kb9D4g>'k6lK$O/d`4s42>QjrL'-XΓsƓme_m6LY>><>'e dUxz@}eQ[|#91 ٕ}%:ze1adVn9!^Ȍtoad<>'400&:z6ȘF۷G2Dh6Q 3*a9} u{([ u(a9G9;Oؙ~uc.e17j3e_.q'l+H <>G ӓmgνL+g!G*Oؼ3L9B S߿Xj=]ϑqfsf\XNsDb.,r]}=cO׿3޳h aN\ 5=>̕S) \\39G|;-}’;Ga܎3\kG:ׁ0oC(C(̑𙘧|+g|m q2|&ld̍op4㌛3BOO\`Nrú2wg2;l G_`[o݁ 5⪀q1|&R^8.s 0en런q;x>qy lKn)r lq395e_r0v7w l+ lKnp l_wI \.;S:Խ:0V+P`k^C>9kQn\`lo_J[q0|r40F7z;ӿo<ȓ| A<V;  V*7ggh2pz.0ay>bP{s2|6ԿÓmP Zو1Ж#@F+<*W gxxooh:<g@_? J- \m1geμ@2pv {~x᳢}-vru@GOԂZ.g?<܇bݎɶVJ7xzu/6 + !P\0e^^%nk"{<sIP0.nvZ']`cܢ|wmx0>Coн',U gܢnZvP4tͥr\X끲0u ƭ׶X=uor뭷zLr)0X`'빻z`u []bSvHHa7Y0~W2Xs Kl#%lJ>(rVyw ʒ/| u [tu,NKp+_X!ttן)t;r{qi,u}ĥ62UW]Xj>7;#}n7eG-˥{8ki0cIw6KmN za=s@;gܤK.dإ)G;U^R]R߉e`g |$Maz{H(`0|nB}kׅV^w}7 :ַe="U/˓gk׮/~>@+CR^_.1Z%>'`aO hehDh2|ј'5 WК+5gIDh̓+jQԂ\Q hM ȕ`Q{`Ԁ\QZ@kj@Dր&租~* >Ԣ@ WZSr%>gyRrE Z+jY |$H<E-Ԁ\%rGcԀ\QZ@kj@D1Oj@DQ rE-55 W"k@?ј'5 WК+59Dh̓+jQԂ\Q hM ȕd裏$A"w4I 5(jAJd 0|(;@ WZSr%>gyRrE Z+jY ~$H<E-Ԁ\%rGcԀ\QZ@kj@Dր&| 1Oj@DQ rE-55 W"ksFј'5 WК+59Dh̓+jQԂ\Q hM ȕd׿% 1Oj@DQ rE-55 W"ksFј'5 WК+5S$rGcԀ\QZ@kj@D1Oj@DQ rE-55 W"kχzhNRN9唵_/s"w4i 5xꪫk@ɯ5(ug8{w ~NYZ}>{M?m|c3͘yC X3FYSϏDRɘyC xG@N;m^%s]x2O5}~Lu 4>{エy;y睷on ;N!:4mDhj@;S}oX5駟>@ZP>|xڰu]wvW^ym"kZ@k5v[m4o.S"w4i 5dlwze n%jQ"kA>߯,_J?KEZ%@d h2|is/~eG|7JG.;61Oc ̭ցn!jQ"kA>oo8R,5*jEր}~u >wД;,7r˫:[om&s-PY jv?vj*ZT hX"ky, ʡkvFD. 9Z@kQ5EcMrow}s]{~vmdJٔl8b<iٳgھ}Ȱ5tQY ~ݹKUɾ@d h2|뭳f)ڮ))GC˖(2t'wG\=wRRDւL}Kj}=g5kJ[M9}裏~݌јXJ)z袋Q_P?@knJQY fQ X끌}>K41-X#w4\bS.ٽ{gvM7o>ډ3Z*e]d@d h2|##YS`)+_?Sb҄JրrrSnJjrDa޺}oh>_ /nm[.?HD2lug)S߇ܮy+"k׮5ٶ0oݾ7_Nd8xEg@>TIzYN3g=ܳovCoaG[˒izʽc6?F\o\rvS@Z}\gy?fV-z CZ"ksMiyNƴyrt~Ik5+wQY "SO=v/># nUɾ@d 0|)wSO=u_.C"w4<_rpD|msZG-`ư@d h2|k^)7X7X/v1-RˏR_g5J3Dy'7ݼ@ZвkUɾc5æ|:?c1Ocg5si,u@ Jd-h_|vZd hR"k@᳜ΚktR.M);vY1OcO<Ć[R_bp QY ZvYZd hT"ksM/߰SCeI<,ˑ_|qp~.J i5(v_^f?:Y M^fHvRvƇ~xgJ<t)%jLY j~ڡZd RϏDwr^1Oc˹_9眳+ ~lQY jK'sY>?:Y tt֔7N#Y1OcJdK_?e@ZPϗKs?lʢ5Zd 8VVZ"k9%rGcԀ\QZ@kj@Dր&gZ1Oj@DQ rE-55 W"ksFј'5 WК+59Dh̓+jQԂ\Q hM ȕd,7J|"w4I 5(jAJd 0|(;@ WZSr%4><( 1Oj@DQ rE-55 W"ksFј'5 WК+59Dh̓+jQԂ\Q hM ȕd_z%IyRrE Z+jY 3J<E-Ԁ\%rGcԀ\QZ@kj@Dր&/( 1Oj@DQ rE-55 W"ksFј'5 WК+5 /HDh̓+jQԂ\Q hM ȕ`Q"w4I 5(jAJd h2|> ;@ WZSr%>gyRrE Z+jY +;4I|Z%>j `a"v4ih6J|Z$>59ꫯ>/*Ԁ|jA@Kj@`#hC  K ͇KѠ3J\ ֆCdO @uO3|P Tg:'> @uO3|P Tg:'> @uO3|P Tg:'> @uO3|P Tg:'> @uO*[[{-IENDB`PNG  IHDRN`sRGBgAMA a pHYsod6NIDATx^۫g}?)-aBK#B/ze(C;QA QBo4z5rXT nDD$XVѨbE$XV ,QPu@X +hTQPu@X +hTQPu@X +t>Ѩ(: Vhb4XѨ(: VhbJhTd+J1UhTd+J17[oum۶_WZF@IQo';w~~ӟfFhb4W^yeE{۶ "s?k53J:VX)&bZokE 'zvQPRu?\=Z +to*k]j.<#i!%E[̫9sfr%L7^ݾ}m~ӧOخThb5_^sUWM{iB4*JWߗ}ۥuTvE:VX)&RZoMTѭDHys3߳X +t1=䠕r-pzkّ~ "jg; 'OnX +DjTɹVp ~hTePGY?X +t1#BG@ʥw'zhTeP7^$]X +DiTIz5\3m(wu׊m}-Mg3Ѩ():fsrX +DhTMh\n?Lh4*Jgյ~RV 3g??5ӧOgK|}vQPRu@ꫧ?/n[2J1z2XBRxe mhTT{PGVShb˜.|YcH.ɱcDz?7Z4*J(eĻvZsz5b@+ SQ77|DjFkE뀒׿䢋.~owYqnZVݨ֓ЍhѨ(:d=+Za`09[`=n6wo>/.R4*J(ۧ\q'OfZa`j_.~7M./ҲKvQPRu@>;nZa`0rG̋/)=rIٳg'FF@I%{nX&Zhbj5lBGnZfFbhTTk裏.Za`Zj==f3{7zҨ(:`+X +t1;ZwY3vk˚V:{QPRuՍ&=gY{u0RLF{汕Asqm1F@I57tӲo4V87+t1]>)ҝ/x<_%z2.w嶭 Jӽw_LOgE:VX)tQPu@X +t1 "QPu@X +hTQPu@X +hTQPu@X +tԩS %YĊu0RF+%YĊu0RLWO<)QPu@X +hTQPu@X +hTQPu@X +tĉ %YĊu0RF+%YĊu0RLW?.QPu@X +hTQPu@X +hTQPu@X +tرc %YĊu0RF+%YĊu0RLW=*QPu@X +hTQPu@X +hTQPu@X +tȑ# %YĊu0RF+%YĊu0RLW>,QPu@X +hTQPu@X +t^Ѩ(: Vhb4XѨ(: VhbX2vܹ3kϚEG\Zt3RLH.:wd۶m;vd=Bҟ-oߞEG\ZtRome,__tJUvۊB9RH&BsϞ=mJJY: Ԯ^&BsKnh TFoo=MD@)uJU{7-&bofo|c!͢QPRm5^zjꭹm"&Y˿gYtʠ9sfzݻ~]vҟ=7|g G=뮛^}ճ_q32I'B7iha02jwT2imU:ڝ%NGZs4v4X{.!\BsС~@<0V@vca`%4GT >4v|_/f`%,XO`\I>?1tVBQ˙hdrW?L4Cc`%G 7}, Wl+*+鷛>UCCd`%,n+Z1Т7{X }};̙3+v}l++yb8c @ 2Vg'WX2+U985ie~&X& PXqw@-,Hq-2RMwSYl"rt_p,6-2R+˲( Sr-3R\O.AjvrObX)ʙ\@mn ǙmZd`E>P[sX)" F.C!kT۳hsV/tTkT{=UXʚVNA%AOL ,ǖήv?&0o-}tOlCTV6xr01':+ oUZ+ä^i_II2oVVpyĽ'MOx ng3<9`8מQFZ Z``eO~e+?S f`eʥ̃r [z[˘'+K z"0[;i-ΰ}) V.芊2)̛}y1\l#`̓KN[u ,BkTHf3 #)n`?{ "Y ֑@Jq@Lnݠ$g,RcJ *s P_:խYVX4,Ȥ FI=w}x}2HjPLM.?Y.jsv2Hנ<<9!HhPDϜ9s,D=:ڔXGߠ<\PVhE5N䂨ҙU^X4tCl:`ѥ}4)0iE"Kh^;r O+\8i .,:@!xy"-޿ٯu4(Z'lWhQTփBX#i+#n-'^e`GSi'lMVuӾ6ցp:Cxr0R/g`!UOf\-q8C8G*OX3,L?ڸT *!c`m3P +Iw rK {ڠU3)+PW]1SX'5K$:Ua`m#KdZr_qpX},Q>cӿE" 0Fk#< ~ǕX9+ G`96`l*4҇V=~ϓ1q;urv1Pg<V_}r `ۂEҐq0XJ62OUhx A@-ϭBe` #ؘ4/h]z}]wmA>H&k0>h9.mAy݉'~K`k҃ϐ_iδJk|AZ=9hM:`հt 8W) 5*/-_% ؚ}a'u<9h+`|Z3AiH`,Z,#@ \X+ߧ8 "s.k@YTsXtd`-}*P#@$޽YZ#;PGjPKu\kA@]iH횔w58xyL[ ߿p *PݻOCL+PuZ@(N\4){Nk+>W?`@L @ĔnJKW=e`]t_'A\%^@+@&,Z+ݰaoS>sb1.37I`6z/D`z"8x mqyL9C|vy:;h2c`݂ҁZY@{rޙArWGXc`݂nqvj0t )}CRa`5@Z:x mKϭ1nAʚ~C` fT|Ow@'繻rz<&u;eоw Vݿ;x 훽bz&utGRSҍ٩AtHtYwДXˀYziAߓX7=ܳTKB;>ǩu`Xr[ou끲 Нy?鲝8鲁tY!ІyMMLΚtq_2[/=~( _K:ڒoڕ>2ٳ&]إzоs<@M=cMÒ>CT?@b=`=Pu|[Z9ge@@=ozF }zb륗^:ܹs}__f@)}˃rb~(? #~~(?Ȋ_}H ;'âĊ@-jAJ 5_Js2,j@ԢĊZ@ij@P #Mqix≳[0j@ԢĊZ@ij@P_|Hv~`QԀXQE-ԀXiXGvNE 5ZԂXQ (M j@?iadXԀXQE-ԀXiXGvNE 5ZԂXQ (M juiadXԀXQE-ԀXiX?3 vNE 5ZԂXQ (M juiadXԀXQE-ԀXiX?S vNE 5ZԂXQ (M juiadXԀXQE-ԀXiXGvNE 5ZԂXQ (M j@O>iadXԀXQE-ԀXiXGvNE 5ZԂXQ (M j@?iadXԀXQE-ԀXiXGvNE 5ZԂXQ (M juiadXԀXQE-ԀXiX?# vNE 5ZԂXQ (M juiadXԀXQE-ԀXiX?C vNE 5ZԂXQ (M juiadXԀXQE-ԀXiX{y'۶mP?ɯ~ϋvNVnl Hя~Qf-(Nvܙ?OS:jլkeQ}>zhX?ݙ֓~nĴs2,-Ԁټ+6\eNĨRiPZ0EV@ 5VYJKCk ;'B g#k /ٟ)jԬ%f~UƤf g}:B (>aSOMwj[oe_m#aikv:e/8BPf-Xt?sK.Yw߽b}-ۦfP (f HYtoP lȏbhX{J#VX)z m2ҜSNe]-oˀ"5ZjւEBxԜyD-Z5DoP X5('z =꺞kfFRX׻;jժ%|uP|`M7:G~xWNxvr-O~&RZ95p:*gJjY '-TO_PmdK?t5]s_7;޽{{RKj5ZjւH}7ͲkJQu``6tis=I|i8MCjV뮻ni\}l(u]5Zjւ(}~vyQ (F ԁj4-윴#]. ޱcDze]6y\ jԬ"ojԀh}>RhXI9ݎ٤{[?.r~E$ )Qk@eRA1Lݾ/}ޚ\}~?ݶTJQ"hu``%t;N:-v~65.fEgak5fb}/_.:5|a5E-5 Jwjsx<ӝ+XzRn\?cCevNꫯ.ݻ{[1?NT@vC@-5kA>K۵kצQ (F ցj4ٝp_:-윴Kv~Jo_5on;R']tҽj>lvQ (V #ׁj4 .;v,]s_zRϧ\Yk@F2{;nW;jԬ|[Z@ij@>PyǦ;SjdV}ݷlgJhXiyG'NnK&픹aiˎnkHwܑ.BjY J"5]V-Ձj4niَ.m%- K 5 e9r$m?~6RCJG `}~v1}XMQ (f HYtoP lqL ;'B HIOݳgϲZ٣)靳/Vl)jԬ󳵣@Z@i5k@"|uP|`M/#"IځyϏvN%=!5V,ogתjլ][-k/[%hiadXZ{rO?YѢPKZ>?{Fc`e,jր̻ϷXZtkƥ~Z95 VjQ bE-45 VZ֑aQbE  VJSb``iZ95 VjQ bE-45 VZtCO ;'âĊ@-jAJ 5:Ҵs2,j@ԢĊZ@ij@P'OiadXԀXQE-ԀXiXGvNE 5ZԂXQ (M juiadXԀXQE-ԀXiXO8!ɰPZ+jB 04- +jP+-Ԁ%@Z95 VjQ bE-45 VZ֑aQbE  VJSb``iZ95 VjQ bE-45 VZcǎIs2,j@ԢĊZ@ij@P #M ;'âĊ@-jAJ 5zQ vNE 5ZԂXQ (M juiadXԀXQE-ԀXiXGvNE 5ZԂXQ (M j@ȑ# - +jP+-HɰPZ+jB (>>|XaQbE  VJSb``iZ95 VjQ bE-45 VZ^{MaQbE  VJSb``iZ95 VjQ bE-45 VZV;'Ò~J퇭fǎ]ve~"ydXrԏ@iٟd۶m??ܹ3MKQ (%ID֜nm(I ZgϞ5&ݮ@IUJRjh?^Vo~sZ d=/TX8 mdrԋ@-?''}ek!jC+9sf{e·~믿>l`<89$C6 0g޽{g[:g[6 j:˚t5}` gζ^veKpnV^}եa;ۺj A{(Ӯ]Y yX* X* X  xX 18X ` ` `l `, ` ` 8X+p1 WCa`  0`^ 0^ 0^ 02^ 0B^ 0b^Df`92+ph ,8@VV  + &+k +8@iV+p lW%X4 x(V+py307^̓ ` ,W[e``,+ExQV `# 8zX+ps1PW1PW9V  @8^$VB  @h^e` Ґ``*kX @U݀KX @UX1 b`%+Uug}&b`%+UXcJ$VO?TJ$V2ƊH Te`+X> +X+V"1PU7 } V"1P5V Db`*kX @U݀GIX @UX1?1 b`%+UEXoɶm۲я~駟Ϝg Db`n@W^ܹ3;P}/srYϾ{_*V"1PUu3k /ٟep͡J$VJ}ɵ^bhꪫ&o֊_~gZO>b۔Gms̙%\6b`%+UX۷lP\mP߿nvO=t? /dlWyH T H^̞-Mo6КC-wygzw/sugYs-*V"1PU5oW\qԩSJ_ڵkg{衇-*V"1PUu^g2gy֛ٟg`e T H͒9}3imwK.hs|vM?nm4V"1PU5Y\09zhv2{iɓ'cm۷O~_f[T Db`n@J–΋/h8O86G}t)wqDz_liso3X @U5֔ni: 3GnOLYms \"V"1PU5=wϞ=!jP./~bmx1)=lV}u=I3<]f/>WYÇg֢c`%+UEX{r\~Ǐgl;ng."V"1PU7 Ks$]{gʔ[O|XJ$V2ƊH T HH Te`+X$V D``ܰ$c`%+vm+& @8x s +X @HVB2 d` $+!X @HVB2 d` $+!X @HVB2 d` $+!X @HVB2 d` $+M&X&Nk6IENDB`name = $name; } public function getProp() { return $this->prop; } public function setProp($prop) { $this->prop = $prop; return $this; } }cloned = true; } } cloned = true; } } prop1; } public function setProp1($prop) { $this->prop1 = $prop; return $this; } public function getProp2() { return $this->prop2; } public function setProp2($prop) { $this->prop2 = $prop; return $this; } } cloned = true; } } foo = $foo; } public function getFoo() { return $this->foo; } } bProp; } public function setBProp($prop) { $this->bProp = $prop; return $this; } public function __clone() { $this->cloned = true; } } aProp; } public function setAProp($prop) { $this->aProp = $prop; return $this; } public function __clone() { $this->cloned = true; } } bProp; } public function setBProp($prop) { $this->bProp = $prop; return $this; } } aProp; } public function setAProp($prop) { $this->aProp = $prop; return $this; } } 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); private static $ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; public static function isNormalized($s, $form = self::NFC) { if (!\in_array($form, array(self::NFD, self::NFKD, self::NFC, self::NFKC))) { return false; } $s = (string) $s; if (!isset($s[strspn($s, self::$ASCII)])) { return true; } if (self::NFC == $form && preg_match('//u', $s) && !preg_match('/[^\x00-\x{2FF}]/u', $s)) { return true; } return self::normalize($s, $form) === $s; } public static function normalize($s, $form = self::NFC) { $s = (string) $s; if (!preg_match('//u', $s)) { return false; } switch ($form) { case self::NFC: $C = true; $K = false; break; case self::NFD: $C = false; $K = false; break; case self::NFKC: $C = true; $K = true; break; case self::NFKD: $C = false; $K = true; break; default: if (\defined('Normalizer::NONE') && \Normalizer::NONE == $form) { return $s; } return false; } if ('' === $s) { return ''; } if ($K && null === self::$KD) { self::$KD = self::getData('compatibilityDecomposition'); } if (null === self::$D) { self::$D = self::getData('canonicalDecomposition'); self::$cC = self::getData('combiningClass'); } if (null !== $mbEncoding = (2 & (int) ini_get('mbstring.func_overload')) ? mb_internal_encoding() : null) { mb_internal_encoding('8bit'); } $r = self::decompose($s, $K); if ($C) { if (null === self::$C) { self::$C = self::getData('canonicalComposition'); } $r = self::recompose($r); } if (null !== $mbEncoding) { mb_internal_encoding($mbEncoding); } return $r; } private static function recompose($s) { $ASCII = self::$ASCII; $compMap = self::$C; $combClass = self::$cC; $ulenMask = self::$ulenMask; $result = $tail = ''; $i = $s[0] < "\x80" ? 1 : $ulenMask[$s[0] & "\xF0"]; $len = \strlen($s); $lastUchr = substr($s, 0, $i); $lastUcls = isset($combClass[$lastUchr]) ? 256 : 0; while ($i < $len) { if ($s[$i] < "\x80") { if ($tail) { $lastUchr .= $tail; $tail = ''; } if ($j = strspn($s, $ASCII, $i + 1)) { $lastUchr .= substr($s, $i, $j); $i += $j; } $result .= $lastUchr; $lastUchr = $s[$i]; $lastUcls = 0; ++$i; continue; } $ulen = $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); if ($lastUchr < "\xE1\x84\x80" || "\xE1\x84\x92" < $lastUchr || $uchr < "\xE1\x85\xA1" || "\xE1\x85\xB5" < $uchr || $lastUcls) { $ucls = isset($combClass[$uchr]) ? $combClass[$uchr] : 0; if (isset($compMap[$lastUchr.$uchr]) && (!$lastUcls || $lastUcls < $ucls)) { $lastUchr = $compMap[$lastUchr.$uchr]; } elseif ($lastUcls = $ucls) { $tail .= $uchr; } else { if ($tail) { $lastUchr .= $tail; $tail = ''; } $result .= $lastUchr; $lastUchr = $uchr; } } else { $L = \ord($lastUchr[2]) - 0x80; $V = \ord($uchr[2]) - 0xA1; $T = 0; $uchr = substr($s, $i + $ulen, 3); if ("\xE1\x86\xA7" <= $uchr && $uchr <= "\xE1\x87\x82") { $T = \ord($uchr[2]) - 0xA7; 0 > $T && $T += 0x40; $ulen += 3; } $L = 0xAC00 + ($L * 21 + $V) * 28 + $T; $lastUchr = \chr(0xE0 | $L >> 12).\chr(0x80 | $L >> 6 & 0x3F).\chr(0x80 | $L & 0x3F); } $i += $ulen; } return $result.$lastUchr.$tail; } private static function decompose($s, $c) { $result = ''; $ASCII = self::$ASCII; $decompMap = self::$D; $combClass = self::$cC; $ulenMask = self::$ulenMask; if ($c) { $compatMap = self::$KD; } $c = array(); $i = 0; $len = \strlen($s); while ($i < $len) { if ($s[$i] < "\x80") { if ($c) { ksort($c); $result .= implode('', $c); $c = array(); } $j = 1 + strspn($s, $ASCII, $i + 1); $result .= substr($s, $i, $j); $i += $j; continue; } $ulen = $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); $i += $ulen; if ($uchr < "\xEA\xB0\x80" || "\xED\x9E\xA3" < $uchr) { if ($uchr !== $j = isset($compatMap[$uchr]) ? $compatMap[$uchr] : (isset($decompMap[$uchr]) ? $decompMap[$uchr] : $uchr)) { $uchr = $j; $j = \strlen($uchr); $ulen = $uchr[0] < "\x80" ? 1 : $ulenMask[$uchr[0] & "\xF0"]; if ($ulen != $j) { $j -= $ulen; $i -= $j; if (0 > $i) { $s = str_repeat(' ', -$i).$s; $len -= $i; $i = 0; } while ($j--) { $s[$i + $j] = $uchr[$ulen + $j]; } $uchr = substr($uchr, 0, $ulen); } } if (isset($combClass[$uchr])) { if (!isset($c[$combClass[$uchr]])) { $c[$combClass[$uchr]] = ''; } $c[$combClass[$uchr]] .= $uchr; continue; } } else { $uchr = unpack('C*', $uchr); $j = (($uchr[1] - 224) << 12) + (($uchr[2] - 128) << 6) + $uchr[3] - 0xAC80; $uchr = "\xE1\x84".\chr(0x80 + (int) ($j / 588)) ."\xE1\x85".\chr(0xA1 + (int) (($j % 588) / 28)); if ($j %= 28) { $uchr .= $j < 25 ? ("\xE1\x86".\chr(0xA7 + $j)) : ("\xE1\x87".\chr(0x67 + $j)); } } if ($c) { ksort($c); $result .= implode('', $c); $c = array(); } $result .= $uchr; } if ($c) { ksort($c); $result .= implode('', $c); } return $result; } private static function getData($file) { if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { return require $file; } return false; } } 230, '́' => 230, '̂' => 230, '̃' => 230, '̄' => 230, '̅' => 230, '̆' => 230, '̇' => 230, '̈' => 230, '̉' => 230, '̊' => 230, '̋' => 230, '̌' => 230, '̍' => 230, '̎' => 230, '̏' => 230, '̐' => 230, '̑' => 230, '̒' => 230, '̓' => 230, '̔' => 230, '̕' => 232, '̖' => 220, '̗' => 220, '̘' => 220, '̙' => 220, '̚' => 232, '̛' => 216, '̜' => 220, '̝' => 220, '̞' => 220, '̟' => 220, '̠' => 220, '̡' => 202, '̢' => 202, '̣' => 220, '̤' => 220, '̥' => 220, '̦' => 220, '̧' => 202, '̨' => 202, '̩' => 220, '̪' => 220, '̫' => 220, '̬' => 220, '̭' => 220, '̮' => 220, '̯' => 220, '̰' => 220, '̱' => 220, '̲' => 220, '̳' => 220, '̴' => 1, '̵' => 1, '̶' => 1, '̷' => 1, '̸' => 1, '̹' => 220, '̺' => 220, '̻' => 220, '̼' => 220, '̽' => 230, '̾' => 230, '̿' => 230, '̀' => 230, '́' => 230, '͂' => 230, '̓' => 230, '̈́' => 230, 'ͅ' => 240, '͆' => 230, '͇' => 220, '͈' => 220, '͉' => 220, '͊' => 230, '͋' => 230, '͌' => 230, '͍' => 220, '͎' => 220, '͐' => 230, '͑' => 230, '͒' => 230, '͓' => 220, '͔' => 220, '͕' => 220, '͖' => 220, '͗' => 230, '͘' => 232, '͙' => 220, '͚' => 220, '͛' => 230, '͜' => 233, '͝' => 234, '͞' => 234, '͟' => 233, '͠' => 234, '͡' => 234, '͢' => 233, 'ͣ' => 230, 'ͤ' => 230, 'ͥ' => 230, 'ͦ' => 230, 'ͧ' => 230, 'ͨ' => 230, 'ͩ' => 230, 'ͪ' => 230, 'ͫ' => 230, 'ͬ' => 230, 'ͭ' => 230, 'ͮ' => 230, 'ͯ' => 230, '҃' => 230, '҄' => 230, '҅' => 230, '҆' => 230, '҇' => 230, '֑' => 220, '֒' => 230, '֓' => 230, '֔' => 230, '֕' => 230, '֖' => 220, '֗' => 230, '֘' => 230, '֙' => 230, '֚' => 222, '֛' => 220, '֜' => 230, '֝' => 230, '֞' => 230, '֟' => 230, '֠' => 230, '֡' => 230, '֢' => 220, '֣' => 220, '֤' => 220, '֥' => 220, '֦' => 220, '֧' => 220, '֨' => 230, '֩' => 230, '֪' => 220, '֫' => 230, '֬' => 230, '֭' => 222, '֮' => 228, '֯' => 230, 'ְ' => 10, 'ֱ' => 11, 'ֲ' => 12, 'ֳ' => 13, 'ִ' => 14, 'ֵ' => 15, 'ֶ' => 16, 'ַ' => 17, 'ָ' => 18, 'ֹ' => 19, 'ֺ' => 19, 'ֻ' => 20, 'ּ' => 21, 'ֽ' => 22, 'ֿ' => 23, 'ׁ' => 24, 'ׂ' => 25, 'ׄ' => 230, 'ׅ' => 220, 'ׇ' => 18, 'ؐ' => 230, 'ؑ' => 230, 'ؒ' => 230, 'ؓ' => 230, 'ؔ' => 230, 'ؕ' => 230, 'ؖ' => 230, 'ؗ' => 230, 'ؘ' => 30, 'ؙ' => 31, 'ؚ' => 32, 'ً' => 27, 'ٌ' => 28, 'ٍ' => 29, 'َ' => 30, 'ُ' => 31, 'ِ' => 32, 'ّ' => 33, 'ْ' => 34, 'ٓ' => 230, 'ٔ' => 230, 'ٕ' => 220, 'ٖ' => 220, 'ٗ' => 230, '٘' => 230, 'ٙ' => 230, 'ٚ' => 230, 'ٛ' => 230, 'ٜ' => 220, 'ٝ' => 230, 'ٞ' => 230, 'ٟ' => 220, 'ٰ' => 35, 'ۖ' => 230, 'ۗ' => 230, 'ۘ' => 230, 'ۙ' => 230, 'ۚ' => 230, 'ۛ' => 230, 'ۜ' => 230, '۟' => 230, '۠' => 230, 'ۡ' => 230, 'ۢ' => 230, 'ۣ' => 220, 'ۤ' => 230, 'ۧ' => 230, 'ۨ' => 230, '۪' => 220, '۫' => 230, '۬' => 230, 'ۭ' => 220, 'ܑ' => 36, 'ܰ' => 230, 'ܱ' => 220, 'ܲ' => 230, 'ܳ' => 230, 'ܴ' => 220, 'ܵ' => 230, 'ܶ' => 230, 'ܷ' => 220, 'ܸ' => 220, 'ܹ' => 220, 'ܺ' => 230, 'ܻ' => 220, 'ܼ' => 220, 'ܽ' => 230, 'ܾ' => 220, 'ܿ' => 230, '݀' => 230, '݁' => 230, '݂' => 220, '݃' => 230, '݄' => 220, '݅' => 230, '݆' => 220, '݇' => 230, '݈' => 220, '݉' => 230, '݊' => 230, '߫' => 230, '߬' => 230, '߭' => 230, '߮' => 230, '߯' => 230, '߰' => 230, '߱' => 230, '߲' => 220, '߳' => 230, '߽' => 220, 'ࠖ' => 230, 'ࠗ' => 230, '࠘' => 230, '࠙' => 230, 'ࠛ' => 230, 'ࠜ' => 230, 'ࠝ' => 230, 'ࠞ' => 230, 'ࠟ' => 230, 'ࠠ' => 230, 'ࠡ' => 230, 'ࠢ' => 230, 'ࠣ' => 230, 'ࠥ' => 230, 'ࠦ' => 230, 'ࠧ' => 230, 'ࠩ' => 230, 'ࠪ' => 230, 'ࠫ' => 230, 'ࠬ' => 230, '࠭' => 230, '࡙' => 220, '࡚' => 220, '࡛' => 220, '࣓' => 220, 'ࣔ' => 230, 'ࣕ' => 230, 'ࣖ' => 230, 'ࣗ' => 230, 'ࣘ' => 230, 'ࣙ' => 230, 'ࣚ' => 230, 'ࣛ' => 230, 'ࣜ' => 230, 'ࣝ' => 230, 'ࣞ' => 230, 'ࣟ' => 230, '࣠' => 230, '࣡' => 230, 'ࣣ' => 220, 'ࣤ' => 230, 'ࣥ' => 230, 'ࣦ' => 220, 'ࣧ' => 230, 'ࣨ' => 230, 'ࣩ' => 220, '࣪' => 230, '࣫' => 230, '࣬' => 230, '࣭' => 220, '࣮' => 220, '࣯' => 220, 'ࣰ' => 27, 'ࣱ' => 28, 'ࣲ' => 29, 'ࣳ' => 230, 'ࣴ' => 230, 'ࣵ' => 230, 'ࣶ' => 220, 'ࣷ' => 230, 'ࣸ' => 230, 'ࣹ' => 220, 'ࣺ' => 220, 'ࣻ' => 230, 'ࣼ' => 230, 'ࣽ' => 230, 'ࣾ' => 230, 'ࣿ' => 230, '़' => 7, '्' => 9, '॑' => 230, '॒' => 220, '॓' => 230, '॔' => 230, '়' => 7, '্' => 9, '৾' => 230, '਼' => 7, '੍' => 9, '઼' => 7, '્' => 9, '଼' => 7, '୍' => 9, '்' => 9, '్' => 9, 'ౕ' => 84, 'ౖ' => 91, '಼' => 7, '್' => 9, '഻' => 9, '഼' => 9, '്' => 9, '්' => 9, 'ุ' => 103, 'ู' => 103, 'ฺ' => 9, '่' => 107, '้' => 107, '๊' => 107, '๋' => 107, 'ຸ' => 118, 'ູ' => 118, '຺' => 9, '່' => 122, '້' => 122, '໊' => 122, '໋' => 122, '༘' => 220, '༙' => 220, '༵' => 220, '༷' => 220, '༹' => 216, 'ཱ' => 129, 'ི' => 130, 'ུ' => 132, 'ེ' => 130, 'ཻ' => 130, 'ོ' => 130, 'ཽ' => 130, 'ྀ' => 130, 'ྂ' => 230, 'ྃ' => 230, '྄' => 9, '྆' => 230, '྇' => 230, '࿆' => 220, '့' => 7, '္' => 9, '်' => 9, 'ႍ' => 220, '፝' => 230, '፞' => 230, '፟' => 230, '᜔' => 9, '᜴' => 9, '្' => 9, '៝' => 230, 'ᢩ' => 228, '᤹' => 222, '᤺' => 230, '᤻' => 220, 'ᨗ' => 230, 'ᨘ' => 220, '᩠' => 9, '᩵' => 230, '᩶' => 230, '᩷' => 230, '᩸' => 230, '᩹' => 230, '᩺' => 230, '᩻' => 230, '᩼' => 230, '᩿' => 220, '᪰' => 230, '᪱' => 230, '᪲' => 230, '᪳' => 230, '᪴' => 230, '᪵' => 220, '᪶' => 220, '᪷' => 220, '᪸' => 220, '᪹' => 220, '᪺' => 220, '᪻' => 230, '᪼' => 230, '᪽' => 220, 'ᪿ' => 220, 'ᫀ' => 220, '᬴' => 7, '᭄' => 9, '᭫' => 230, '᭬' => 220, '᭭' => 230, '᭮' => 230, '᭯' => 230, '᭰' => 230, '᭱' => 230, '᭲' => 230, '᭳' => 230, '᮪' => 9, '᮫' => 9, '᯦' => 7, '᯲' => 9, '᯳' => 9, '᰷' => 7, '᳐' => 230, '᳑' => 230, '᳒' => 230, '᳔' => 1, '᳕' => 220, '᳖' => 220, '᳗' => 220, '᳘' => 220, '᳙' => 220, '᳚' => 230, '᳛' => 230, '᳜' => 220, '᳝' => 220, '᳞' => 220, '᳟' => 220, '᳠' => 230, '᳢' => 1, '᳣' => 1, '᳤' => 1, '᳥' => 1, '᳦' => 1, '᳧' => 1, '᳨' => 1, '᳭' => 220, '᳴' => 230, '᳸' => 230, '᳹' => 230, '᷀' => 230, '᷁' => 230, '᷂' => 220, '᷃' => 230, '᷄' => 230, '᷅' => 230, '᷆' => 230, '᷇' => 230, '᷈' => 230, '᷉' => 230, '᷊' => 220, '᷋' => 230, '᷌' => 230, '᷍' => 234, '᷎' => 214, '᷏' => 220, '᷐' => 202, '᷑' => 230, '᷒' => 230, 'ᷓ' => 230, 'ᷔ' => 230, 'ᷕ' => 230, 'ᷖ' => 230, 'ᷗ' => 230, 'ᷘ' => 230, 'ᷙ' => 230, 'ᷚ' => 230, 'ᷛ' => 230, 'ᷜ' => 230, 'ᷝ' => 230, 'ᷞ' => 230, 'ᷟ' => 230, 'ᷠ' => 230, 'ᷡ' => 230, 'ᷢ' => 230, 'ᷣ' => 230, 'ᷤ' => 230, 'ᷥ' => 230, 'ᷦ' => 230, 'ᷧ' => 230, 'ᷨ' => 230, 'ᷩ' => 230, 'ᷪ' => 230, 'ᷫ' => 230, 'ᷬ' => 230, 'ᷭ' => 230, 'ᷮ' => 230, 'ᷯ' => 230, 'ᷰ' => 230, 'ᷱ' => 230, 'ᷲ' => 230, 'ᷳ' => 230, 'ᷴ' => 230, '᷵' => 230, '᷶' => 232, '᷷' => 228, '᷸' => 228, '᷹' => 220, '᷻' => 230, '᷼' => 233, '᷽' => 220, '᷾' => 230, '᷿' => 220, '⃐' => 230, '⃑' => 230, '⃒' => 1, '⃓' => 1, '⃔' => 230, '⃕' => 230, '⃖' => 230, '⃗' => 230, '⃘' => 1, '⃙' => 1, '⃚' => 1, '⃛' => 230, '⃜' => 230, '⃡' => 230, '⃥' => 1, '⃦' => 1, '⃧' => 230, '⃨' => 220, '⃩' => 230, '⃪' => 1, '⃫' => 1, '⃬' => 220, '⃭' => 220, '⃮' => 220, '⃯' => 220, '⃰' => 230, '⳯' => 230, '⳰' => 230, '⳱' => 230, '⵿' => 9, 'ⷠ' => 230, 'ⷡ' => 230, 'ⷢ' => 230, 'ⷣ' => 230, 'ⷤ' => 230, 'ⷥ' => 230, 'ⷦ' => 230, 'ⷧ' => 230, 'ⷨ' => 230, 'ⷩ' => 230, 'ⷪ' => 230, 'ⷫ' => 230, 'ⷬ' => 230, 'ⷭ' => 230, 'ⷮ' => 230, 'ⷯ' => 230, 'ⷰ' => 230, 'ⷱ' => 230, 'ⷲ' => 230, 'ⷳ' => 230, 'ⷴ' => 230, 'ⷵ' => 230, 'ⷶ' => 230, 'ⷷ' => 230, 'ⷸ' => 230, 'ⷹ' => 230, 'ⷺ' => 230, 'ⷻ' => 230, 'ⷼ' => 230, 'ⷽ' => 230, 'ⷾ' => 230, 'ⷿ' => 230, '〪' => 218, '〫' => 228, '〬' => 232, '〭' => 222, '〮' => 224, '〯' => 224, '゙' => 8, '゚' => 8, '꙯' => 230, 'ꙴ' => 230, 'ꙵ' => 230, 'ꙶ' => 230, 'ꙷ' => 230, 'ꙸ' => 230, 'ꙹ' => 230, 'ꙺ' => 230, 'ꙻ' => 230, '꙼' => 230, '꙽' => 230, 'ꚞ' => 230, 'ꚟ' => 230, '꛰' => 230, '꛱' => 230, '꠆' => 9, '꠬' => 9, '꣄' => 9, '꣠' => 230, '꣡' => 230, '꣢' => 230, '꣣' => 230, '꣤' => 230, '꣥' => 230, '꣦' => 230, '꣧' => 230, '꣨' => 230, '꣩' => 230, '꣪' => 230, '꣫' => 230, '꣬' => 230, '꣭' => 230, '꣮' => 230, '꣯' => 230, '꣰' => 230, '꣱' => 230, '꤫' => 220, '꤬' => 220, '꤭' => 220, '꥓' => 9, '꦳' => 7, '꧀' => 9, 'ꪰ' => 230, 'ꪲ' => 230, 'ꪳ' => 230, 'ꪴ' => 220, 'ꪷ' => 230, 'ꪸ' => 230, 'ꪾ' => 230, '꪿' => 230, '꫁' => 230, '꫶' => 9, '꯭' => 9, 'ﬞ' => 26, '︠' => 230, '︡' => 230, '︢' => 230, '︣' => 230, '︤' => 230, '︥' => 230, '︦' => 230, '︧' => 220, '︨' => 220, '︩' => 220, '︪' => 220, '︫' => 220, '︬' => 220, '︭' => 220, '︮' => 230, '︯' => 230, '𐇽' => 220, '𐋠' => 220, '𐍶' => 230, '𐍷' => 230, '𐍸' => 230, '𐍹' => 230, '𐍺' => 230, '𐨍' => 220, '𐨏' => 230, '𐨸' => 230, '𐨹' => 1, '𐨺' => 220, '𐨿' => 9, '𐫥' => 230, '𐫦' => 220, '𐴤' => 230, '𐴥' => 230, '𐴦' => 230, '𐴧' => 230, '𐺫' => 230, '𐺬' => 230, '𐽆' => 220, '𐽇' => 220, '𐽈' => 230, '𐽉' => 230, '𐽊' => 230, '𐽋' => 220, '𐽌' => 230, '𐽍' => 220, '𐽎' => 220, '𐽏' => 220, '𐽐' => 220, '𑁆' => 9, '𑁿' => 9, '𑂹' => 9, '𑂺' => 7, '𑄀' => 230, '𑄁' => 230, '𑄂' => 230, '𑄳' => 9, '𑄴' => 9, '𑅳' => 7, '𑇀' => 9, '𑇊' => 7, '𑈵' => 9, '𑈶' => 7, '𑋩' => 7, '𑋪' => 9, '𑌻' => 7, '𑌼' => 7, '𑍍' => 9, '𑍦' => 230, '𑍧' => 230, '𑍨' => 230, '𑍩' => 230, '𑍪' => 230, '𑍫' => 230, '𑍬' => 230, '𑍰' => 230, '𑍱' => 230, '𑍲' => 230, '𑍳' => 230, '𑍴' => 230, '𑑂' => 9, '𑑆' => 7, '𑑞' => 230, '𑓂' => 9, '𑓃' => 7, '𑖿' => 9, '𑗀' => 7, '𑘿' => 9, '𑚶' => 9, '𑚷' => 7, '𑜫' => 9, '𑠹' => 9, '𑠺' => 7, '𑤽' => 9, '𑤾' => 9, '𑥃' => 7, '𑧠' => 9, '𑨴' => 9, '𑩇' => 9, '𑪙' => 9, '𑰿' => 9, '𑵂' => 7, '𑵄' => 9, '𑵅' => 9, '𑶗' => 9, '𖫰' => 1, '𖫱' => 1, '𖫲' => 1, '𖫳' => 1, '𖫴' => 1, '𖬰' => 230, '𖬱' => 230, '𖬲' => 230, '𖬳' => 230, '𖬴' => 230, '𖬵' => 230, '𖬶' => 230, '𖿰' => 6, '𖿱' => 6, '𛲞' => 1, '𝅥' => 216, '𝅦' => 216, '𝅧' => 1, '𝅨' => 1, '𝅩' => 1, '𝅭' => 226, '𝅮' => 216, '𝅯' => 216, '𝅰' => 216, '𝅱' => 216, '𝅲' => 216, '𝅻' => 220, '𝅼' => 220, '𝅽' => 220, '𝅾' => 220, '𝅿' => 220, '𝆀' => 220, '𝆁' => 220, '𝆂' => 220, '𝆅' => 230, '𝆆' => 230, '𝆇' => 230, '𝆈' => 230, '𝆉' => 230, '𝆊' => 220, '𝆋' => 220, '𝆪' => 230, '𝆫' => 230, '𝆬' => 230, '𝆭' => 230, '𝉂' => 230, '𝉃' => 230, '𝉄' => 230, '𞀀' => 230, '𞀁' => 230, '𞀂' => 230, '𞀃' => 230, '𞀄' => 230, '𞀅' => 230, '𞀆' => 230, '𞀈' => 230, '𞀉' => 230, '𞀊' => 230, '𞀋' => 230, '𞀌' => 230, '𞀍' => 230, '𞀎' => 230, '𞀏' => 230, '𞀐' => 230, '𞀑' => 230, '𞀒' => 230, '𞀓' => 230, '𞀔' => 230, '𞀕' => 230, '𞀖' => 230, '𞀗' => 230, '𞀘' => 230, '𞀛' => 230, '𞀜' => 230, '𞀝' => 230, '𞀞' => 230, '𞀟' => 230, '𞀠' => 230, '𞀡' => 230, '𞀣' => 230, '𞀤' => 230, '𞀦' => 230, '𞀧' => 230, '𞀨' => 230, '𞀩' => 230, '𞀪' => 230, '𞄰' => 230, '𞄱' => 230, '𞄲' => 230, '𞄳' => 230, '𞄴' => 230, '𞄵' => 230, '𞄶' => 230, '𞋬' => 230, '𞋭' => 230, '𞋮' => 230, '𞋯' => 230, '𞣐' => 220, '𞣑' => 220, '𞣒' => 220, '𞣓' => 220, '𞣔' => 220, '𞣕' => 220, '𞣖' => 220, '𞥄' => 230, '𞥅' => 230, '𞥆' => 230, '𞥇' => 230, '𞥈' => 230, '𞥉' => 230, '𞥊' => 7, ); ' ', '¨' => ' ̈', 'ª' => 'a', '¯' => ' ̄', '²' => '2', '³' => '3', '´' => ' ́', 'µ' => 'μ', '¸' => ' ̧', '¹' => '1', 'º' => 'o', '¼' => '1⁄4', '½' => '1⁄2', '¾' => '3⁄4', 'IJ' => 'IJ', 'ij' => 'ij', 'Ŀ' => 'L·', 'ŀ' => 'l·', 'ʼn' => 'ʼn', 'ſ' => 's', 'DŽ' => 'DŽ', 'Dž' => 'Dž', 'dž' => 'dž', 'LJ' => 'LJ', 'Lj' => 'Lj', 'lj' => 'lj', 'NJ' => 'NJ', 'Nj' => 'Nj', 'nj' => 'nj', 'DZ' => 'DZ', 'Dz' => 'Dz', 'dz' => 'dz', 'ʰ' => 'h', 'ʱ' => 'ɦ', 'ʲ' => 'j', 'ʳ' => 'r', 'ʴ' => 'ɹ', 'ʵ' => 'ɻ', 'ʶ' => 'ʁ', 'ʷ' => 'w', 'ʸ' => 'y', '˘' => ' ̆', '˙' => ' ̇', '˚' => ' ̊', '˛' => ' ̨', '˜' => ' ̃', '˝' => ' ̋', 'ˠ' => 'ɣ', 'ˡ' => 'l', 'ˢ' => 's', 'ˣ' => 'x', 'ˤ' => 'ʕ', 'ͺ' => ' ͅ', '΄' => ' ́', '΅' => ' ̈́', 'ϐ' => 'β', 'ϑ' => 'θ', 'ϒ' => 'Υ', 'ϓ' => 'Ύ', 'ϔ' => 'Ϋ', 'ϕ' => 'φ', 'ϖ' => 'π', 'ϰ' => 'κ', 'ϱ' => 'ρ', 'ϲ' => 'ς', 'ϴ' => 'Θ', 'ϵ' => 'ε', 'Ϲ' => 'Σ', 'և' => 'եւ', 'ٵ' => 'اٴ', 'ٶ' => 'وٴ', 'ٷ' => 'ۇٴ', 'ٸ' => 'يٴ', 'ำ' => 'ํา', 'ຳ' => 'ໍາ', 'ໜ' => 'ຫນ', 'ໝ' => 'ຫມ', '༌' => '་', 'ཷ' => 'ྲཱྀ', 'ཹ' => 'ླཱྀ', 'ჼ' => 'ნ', 'ᴬ' => 'A', 'ᴭ' => 'Æ', 'ᴮ' => 'B', 'ᴰ' => 'D', 'ᴱ' => 'E', 'ᴲ' => 'Ǝ', 'ᴳ' => 'G', 'ᴴ' => 'H', 'ᴵ' => 'I', 'ᴶ' => 'J', 'ᴷ' => 'K', 'ᴸ' => 'L', 'ᴹ' => 'M', 'ᴺ' => 'N', 'ᴼ' => 'O', 'ᴽ' => 'Ȣ', 'ᴾ' => 'P', 'ᴿ' => 'R', 'ᵀ' => 'T', 'ᵁ' => 'U', 'ᵂ' => 'W', 'ᵃ' => 'a', 'ᵄ' => 'ɐ', 'ᵅ' => 'ɑ', 'ᵆ' => 'ᴂ', 'ᵇ' => 'b', 'ᵈ' => 'd', 'ᵉ' => 'e', 'ᵊ' => 'ə', 'ᵋ' => 'ɛ', 'ᵌ' => 'ɜ', 'ᵍ' => 'g', 'ᵏ' => 'k', 'ᵐ' => 'm', 'ᵑ' => 'ŋ', 'ᵒ' => 'o', 'ᵓ' => 'ɔ', 'ᵔ' => 'ᴖ', 'ᵕ' => 'ᴗ', 'ᵖ' => 'p', 'ᵗ' => 't', 'ᵘ' => 'u', 'ᵙ' => 'ᴝ', 'ᵚ' => 'ɯ', 'ᵛ' => 'v', 'ᵜ' => 'ᴥ', 'ᵝ' => 'β', 'ᵞ' => 'γ', 'ᵟ' => 'δ', 'ᵠ' => 'φ', 'ᵡ' => 'χ', 'ᵢ' => 'i', 'ᵣ' => 'r', 'ᵤ' => 'u', 'ᵥ' => 'v', 'ᵦ' => 'β', 'ᵧ' => 'γ', 'ᵨ' => 'ρ', 'ᵩ' => 'φ', 'ᵪ' => 'χ', 'ᵸ' => 'н', 'ᶛ' => 'ɒ', 'ᶜ' => 'c', 'ᶝ' => 'ɕ', 'ᶞ' => 'ð', 'ᶟ' => 'ɜ', 'ᶠ' => 'f', 'ᶡ' => 'ɟ', 'ᶢ' => 'ɡ', 'ᶣ' => 'ɥ', 'ᶤ' => 'ɨ', 'ᶥ' => 'ɩ', 'ᶦ' => 'ɪ', 'ᶧ' => 'ᵻ', 'ᶨ' => 'ʝ', 'ᶩ' => 'ɭ', 'ᶪ' => 'ᶅ', 'ᶫ' => 'ʟ', 'ᶬ' => 'ɱ', 'ᶭ' => 'ɰ', 'ᶮ' => 'ɲ', 'ᶯ' => 'ɳ', 'ᶰ' => 'ɴ', 'ᶱ' => 'ɵ', 'ᶲ' => 'ɸ', 'ᶳ' => 'ʂ', 'ᶴ' => 'ʃ', 'ᶵ' => 'ƫ', 'ᶶ' => 'ʉ', 'ᶷ' => 'ʊ', 'ᶸ' => 'ᴜ', 'ᶹ' => 'ʋ', 'ᶺ' => 'ʌ', 'ᶻ' => 'z', 'ᶼ' => 'ʐ', 'ᶽ' => 'ʑ', 'ᶾ' => 'ʒ', 'ᶿ' => 'θ', 'ẚ' => 'aʾ', 'ẛ' => 'ṡ', '᾽' => ' ̓', '᾿' => ' ̓', '῀' => ' ͂', '῁' => ' ̈͂', '῍' => ' ̓̀', '῎' => ' ̓́', '῏' => ' ̓͂', '῝' => ' ̔̀', '῞' => ' ̔́', '῟' => ' ̔͂', '῭' => ' ̈̀', '΅' => ' ̈́', '´' => ' ́', '῾' => ' ̔', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', '‑' => '‐', '‗' => ' ̳', '․' => '.', '‥' => '..', '…' => '...', ' ' => ' ', '″' => '′′', '‴' => '′′′', '‶' => '‵‵', '‷' => '‵‵‵', '‼' => '!!', '‾' => ' ̅', '⁇' => '??', '⁈' => '?!', '⁉' => '!?', '⁗' => '′′′′', ' ' => ' ', '⁰' => '0', 'ⁱ' => 'i', '⁴' => '4', '⁵' => '5', '⁶' => '6', '⁷' => '7', '⁸' => '8', '⁹' => '9', '⁺' => '+', '⁻' => '−', '⁼' => '=', '⁽' => '(', '⁾' => ')', 'ⁿ' => 'n', '₀' => '0', '₁' => '1', '₂' => '2', '₃' => '3', '₄' => '4', '₅' => '5', '₆' => '6', '₇' => '7', '₈' => '8', '₉' => '9', '₊' => '+', '₋' => '−', '₌' => '=', '₍' => '(', '₎' => ')', 'ₐ' => 'a', 'ₑ' => 'e', 'ₒ' => 'o', 'ₓ' => 'x', 'ₔ' => 'ə', 'ₕ' => 'h', 'ₖ' => 'k', 'ₗ' => 'l', 'ₘ' => 'm', 'ₙ' => 'n', 'ₚ' => 'p', 'ₛ' => 's', 'ₜ' => 't', '₨' => 'Rs', '℀' => 'a/c', '℁' => 'a/s', 'ℂ' => 'C', '℃' => '°C', '℅' => 'c/o', '℆' => 'c/u', 'ℇ' => 'Ɛ', '℉' => '°F', 'ℊ' => 'g', 'ℋ' => 'H', 'ℌ' => 'H', 'ℍ' => 'H', 'ℎ' => 'h', 'ℏ' => 'ħ', 'ℐ' => 'I', 'ℑ' => 'I', 'ℒ' => 'L', 'ℓ' => 'l', 'ℕ' => 'N', '№' => 'No', 'ℙ' => 'P', 'ℚ' => 'Q', 'ℛ' => 'R', 'ℜ' => 'R', 'ℝ' => 'R', '℠' => 'SM', '℡' => 'TEL', '™' => 'TM', 'ℤ' => 'Z', 'ℨ' => 'Z', 'ℬ' => 'B', 'ℭ' => 'C', 'ℯ' => 'e', 'ℰ' => 'E', 'ℱ' => 'F', 'ℳ' => 'M', 'ℴ' => 'o', 'ℵ' => 'א', 'ℶ' => 'ב', 'ℷ' => 'ג', 'ℸ' => 'ד', 'ℹ' => 'i', '℻' => 'FAX', 'ℼ' => 'π', 'ℽ' => 'γ', 'ℾ' => 'Γ', 'ℿ' => 'Π', '⅀' => '∑', 'ⅅ' => 'D', 'ⅆ' => 'd', 'ⅇ' => 'e', 'ⅈ' => 'i', 'ⅉ' => 'j', '⅐' => '1⁄7', '⅑' => '1⁄9', '⅒' => '1⁄10', '⅓' => '1⁄3', '⅔' => '2⁄3', '⅕' => '1⁄5', '⅖' => '2⁄5', '⅗' => '3⁄5', '⅘' => '4⁄5', '⅙' => '1⁄6', '⅚' => '5⁄6', '⅛' => '1⁄8', '⅜' => '3⁄8', '⅝' => '5⁄8', '⅞' => '7⁄8', '⅟' => '1⁄', 'Ⅰ' => 'I', 'Ⅱ' => 'II', 'Ⅲ' => 'III', 'Ⅳ' => 'IV', 'Ⅴ' => 'V', 'Ⅵ' => 'VI', 'Ⅶ' => 'VII', 'Ⅷ' => 'VIII', 'Ⅸ' => 'IX', 'Ⅹ' => 'X', 'Ⅺ' => 'XI', 'Ⅻ' => 'XII', 'Ⅼ' => 'L', 'Ⅽ' => 'C', 'Ⅾ' => 'D', 'Ⅿ' => 'M', 'ⅰ' => 'i', 'ⅱ' => 'ii', 'ⅲ' => 'iii', 'ⅳ' => 'iv', 'ⅴ' => 'v', 'ⅵ' => 'vi', 'ⅶ' => 'vii', 'ⅷ' => 'viii', 'ⅸ' => 'ix', 'ⅹ' => 'x', 'ⅺ' => 'xi', 'ⅻ' => 'xii', 'ⅼ' => 'l', 'ⅽ' => 'c', 'ⅾ' => 'd', 'ⅿ' => 'm', '↉' => '0⁄3', '∬' => '∫∫', '∭' => '∫∫∫', '∯' => '∮∮', '∰' => '∮∮∮', '①' => '1', '②' => '2', '③' => '3', '④' => '4', '⑤' => '5', '⑥' => '6', '⑦' => '7', '⑧' => '8', '⑨' => '9', '⑩' => '10', '⑪' => '11', '⑫' => '12', '⑬' => '13', '⑭' => '14', '⑮' => '15', '⑯' => '16', '⑰' => '17', '⑱' => '18', '⑲' => '19', '⑳' => '20', '⑴' => '(1)', '⑵' => '(2)', '⑶' => '(3)', '⑷' => '(4)', '⑸' => '(5)', '⑹' => '(6)', '⑺' => '(7)', '⑻' => '(8)', '⑼' => '(9)', '⑽' => '(10)', '⑾' => '(11)', '⑿' => '(12)', '⒀' => '(13)', '⒁' => '(14)', '⒂' => '(15)', '⒃' => '(16)', '⒄' => '(17)', '⒅' => '(18)', '⒆' => '(19)', '⒇' => '(20)', '⒈' => '1.', '⒉' => '2.', '⒊' => '3.', '⒋' => '4.', '⒌' => '5.', '⒍' => '6.', '⒎' => '7.', '⒏' => '8.', '⒐' => '9.', '⒑' => '10.', '⒒' => '11.', '⒓' => '12.', '⒔' => '13.', '⒕' => '14.', '⒖' => '15.', '⒗' => '16.', '⒘' => '17.', '⒙' => '18.', '⒚' => '19.', '⒛' => '20.', '⒜' => '(a)', '⒝' => '(b)', '⒞' => '(c)', '⒟' => '(d)', '⒠' => '(e)', '⒡' => '(f)', '⒢' => '(g)', '⒣' => '(h)', '⒤' => '(i)', '⒥' => '(j)', '⒦' => '(k)', '⒧' => '(l)', '⒨' => '(m)', '⒩' => '(n)', '⒪' => '(o)', '⒫' => '(p)', '⒬' => '(q)', '⒭' => '(r)', '⒮' => '(s)', '⒯' => '(t)', '⒰' => '(u)', '⒱' => '(v)', '⒲' => '(w)', '⒳' => '(x)', '⒴' => '(y)', '⒵' => '(z)', 'Ⓐ' => 'A', 'Ⓑ' => 'B', 'Ⓒ' => 'C', 'Ⓓ' => 'D', 'Ⓔ' => 'E', 'Ⓕ' => 'F', 'Ⓖ' => 'G', 'Ⓗ' => 'H', 'Ⓘ' => 'I', 'Ⓙ' => 'J', 'Ⓚ' => 'K', 'Ⓛ' => 'L', 'Ⓜ' => 'M', 'Ⓝ' => 'N', 'Ⓞ' => 'O', 'Ⓟ' => 'P', 'Ⓠ' => 'Q', 'Ⓡ' => 'R', 'Ⓢ' => 'S', 'Ⓣ' => 'T', 'Ⓤ' => 'U', 'Ⓥ' => 'V', 'Ⓦ' => 'W', 'Ⓧ' => 'X', 'Ⓨ' => 'Y', 'Ⓩ' => 'Z', 'ⓐ' => 'a', 'ⓑ' => 'b', 'ⓒ' => 'c', 'ⓓ' => 'd', 'ⓔ' => 'e', 'ⓕ' => 'f', 'ⓖ' => 'g', 'ⓗ' => 'h', 'ⓘ' => 'i', 'ⓙ' => 'j', 'ⓚ' => 'k', 'ⓛ' => 'l', 'ⓜ' => 'm', 'ⓝ' => 'n', 'ⓞ' => 'o', 'ⓟ' => 'p', 'ⓠ' => 'q', 'ⓡ' => 'r', 'ⓢ' => 's', 'ⓣ' => 't', 'ⓤ' => 'u', 'ⓥ' => 'v', 'ⓦ' => 'w', 'ⓧ' => 'x', 'ⓨ' => 'y', 'ⓩ' => 'z', '⓪' => '0', '⨌' => '∫∫∫∫', '⩴' => '::=', '⩵' => '==', '⩶' => '===', 'ⱼ' => 'j', 'ⱽ' => 'V', 'ⵯ' => 'ⵡ', '⺟' => '母', '⻳' => '龟', '⼀' => '一', '⼁' => '丨', '⼂' => '丶', '⼃' => '丿', '⼄' => '乙', '⼅' => '亅', '⼆' => '二', '⼇' => '亠', '⼈' => '人', '⼉' => '儿', '⼊' => '入', '⼋' => '八', '⼌' => '冂', '⼍' => '冖', '⼎' => '冫', '⼏' => '几', '⼐' => '凵', '⼑' => '刀', '⼒' => '力', '⼓' => '勹', '⼔' => '匕', '⼕' => '匚', '⼖' => '匸', '⼗' => '十', '⼘' => '卜', '⼙' => '卩', '⼚' => '厂', '⼛' => '厶', '⼜' => '又', '⼝' => '口', '⼞' => '囗', '⼟' => '土', '⼠' => '士', '⼡' => '夂', '⼢' => '夊', '⼣' => '夕', '⼤' => '大', '⼥' => '女', '⼦' => '子', '⼧' => '宀', '⼨' => '寸', '⼩' => '小', '⼪' => '尢', '⼫' => '尸', '⼬' => '屮', '⼭' => '山', '⼮' => '巛', '⼯' => '工', '⼰' => '己', '⼱' => '巾', '⼲' => '干', '⼳' => '幺', '⼴' => '广', '⼵' => '廴', '⼶' => '廾', '⼷' => '弋', '⼸' => '弓', '⼹' => '彐', '⼺' => '彡', '⼻' => '彳', '⼼' => '心', '⼽' => '戈', '⼾' => '戶', '⼿' => '手', '⽀' => '支', '⽁' => '攴', '⽂' => '文', '⽃' => '斗', '⽄' => '斤', '⽅' => '方', '⽆' => '无', '⽇' => '日', '⽈' => '曰', '⽉' => '月', '⽊' => '木', '⽋' => '欠', '⽌' => '止', '⽍' => '歹', '⽎' => '殳', '⽏' => '毋', '⽐' => '比', '⽑' => '毛', '⽒' => '氏', '⽓' => '气', '⽔' => '水', '⽕' => '火', '⽖' => '爪', '⽗' => '父', '⽘' => '爻', '⽙' => '爿', '⽚' => '片', '⽛' => '牙', '⽜' => '牛', '⽝' => '犬', '⽞' => '玄', '⽟' => '玉', '⽠' => '瓜', '⽡' => '瓦', '⽢' => '甘', '⽣' => '生', '⽤' => '用', '⽥' => '田', '⽦' => '疋', '⽧' => '疒', '⽨' => '癶', '⽩' => '白', '⽪' => '皮', '⽫' => '皿', '⽬' => '目', '⽭' => '矛', '⽮' => '矢', '⽯' => '石', '⽰' => '示', '⽱' => '禸', '⽲' => '禾', '⽳' => '穴', '⽴' => '立', '⽵' => '竹', '⽶' => '米', '⽷' => '糸', '⽸' => '缶', '⽹' => '网', '⽺' => '羊', '⽻' => '羽', '⽼' => '老', '⽽' => '而', '⽾' => '耒', '⽿' => '耳', '⾀' => '聿', '⾁' => '肉', '⾂' => '臣', '⾃' => '自', '⾄' => '至', '⾅' => '臼', '⾆' => '舌', '⾇' => '舛', '⾈' => '舟', '⾉' => '艮', '⾊' => '色', '⾋' => '艸', '⾌' => '虍', '⾍' => '虫', '⾎' => '血', '⾏' => '行', '⾐' => '衣', '⾑' => '襾', '⾒' => '見', '⾓' => '角', '⾔' => '言', '⾕' => '谷', '⾖' => '豆', '⾗' => '豕', '⾘' => '豸', '⾙' => '貝', '⾚' => '赤', '⾛' => '走', '⾜' => '足', '⾝' => '身', '⾞' => '車', '⾟' => '辛', '⾠' => '辰', '⾡' => '辵', '⾢' => '邑', '⾣' => '酉', '⾤' => '釆', '⾥' => '里', '⾦' => '金', '⾧' => '長', '⾨' => '門', '⾩' => '阜', '⾪' => '隶', '⾫' => '隹', '⾬' => '雨', '⾭' => '靑', '⾮' => '非', '⾯' => '面', '⾰' => '革', '⾱' => '韋', '⾲' => '韭', '⾳' => '音', '⾴' => '頁', '⾵' => '風', '⾶' => '飛', '⾷' => '食', '⾸' => '首', '⾹' => '香', '⾺' => '馬', '⾻' => '骨', '⾼' => '高', '⾽' => '髟', '⾾' => '鬥', '⾿' => '鬯', '⿀' => '鬲', '⿁' => '鬼', '⿂' => '魚', '⿃' => '鳥', '⿄' => '鹵', '⿅' => '鹿', '⿆' => '麥', '⿇' => '麻', '⿈' => '黃', '⿉' => '黍', '⿊' => '黑', '⿋' => '黹', '⿌' => '黽', '⿍' => '鼎', '⿎' => '鼓', '⿏' => '鼠', '⿐' => '鼻', '⿑' => '齊', '⿒' => '齒', '⿓' => '龍', '⿔' => '龜', '⿕' => '龠', ' ' => ' ', '〶' => '〒', '〸' => '十', '〹' => '卄', '〺' => '卅', '゛' => ' ゙', '゜' => ' ゚', 'ゟ' => 'より', 'ヿ' => 'コト', 'ㄱ' => 'ᄀ', 'ㄲ' => 'ᄁ', 'ㄳ' => 'ᆪ', 'ㄴ' => 'ᄂ', 'ㄵ' => 'ᆬ', 'ㄶ' => 'ᆭ', 'ㄷ' => 'ᄃ', 'ㄸ' => 'ᄄ', 'ㄹ' => 'ᄅ', 'ㄺ' => 'ᆰ', 'ㄻ' => 'ᆱ', 'ㄼ' => 'ᆲ', 'ㄽ' => 'ᆳ', 'ㄾ' => 'ᆴ', 'ㄿ' => 'ᆵ', 'ㅀ' => 'ᄚ', 'ㅁ' => 'ᄆ', 'ㅂ' => 'ᄇ', 'ㅃ' => 'ᄈ', 'ㅄ' => 'ᄡ', 'ㅅ' => 'ᄉ', 'ㅆ' => 'ᄊ', 'ㅇ' => 'ᄋ', 'ㅈ' => 'ᄌ', 'ㅉ' => 'ᄍ', 'ㅊ' => 'ᄎ', 'ㅋ' => 'ᄏ', 'ㅌ' => 'ᄐ', 'ㅍ' => 'ᄑ', 'ㅎ' => 'ᄒ', 'ㅏ' => 'ᅡ', 'ㅐ' => 'ᅢ', 'ㅑ' => 'ᅣ', 'ㅒ' => 'ᅤ', 'ㅓ' => 'ᅥ', 'ㅔ' => 'ᅦ', 'ㅕ' => 'ᅧ', 'ㅖ' => 'ᅨ', 'ㅗ' => 'ᅩ', 'ㅘ' => 'ᅪ', 'ㅙ' => 'ᅫ', 'ㅚ' => 'ᅬ', 'ㅛ' => 'ᅭ', 'ㅜ' => 'ᅮ', 'ㅝ' => 'ᅯ', 'ㅞ' => 'ᅰ', 'ㅟ' => 'ᅱ', 'ㅠ' => 'ᅲ', 'ㅡ' => 'ᅳ', 'ㅢ' => 'ᅴ', 'ㅣ' => 'ᅵ', 'ㅤ' => 'ᅠ', 'ㅥ' => 'ᄔ', 'ㅦ' => 'ᄕ', 'ㅧ' => 'ᇇ', 'ㅨ' => 'ᇈ', 'ㅩ' => 'ᇌ', 'ㅪ' => 'ᇎ', 'ㅫ' => 'ᇓ', 'ㅬ' => 'ᇗ', 'ㅭ' => 'ᇙ', 'ㅮ' => 'ᄜ', 'ㅯ' => 'ᇝ', 'ㅰ' => 'ᇟ', 'ㅱ' => 'ᄝ', 'ㅲ' => 'ᄞ', 'ㅳ' => 'ᄠ', 'ㅴ' => 'ᄢ', 'ㅵ' => 'ᄣ', 'ㅶ' => 'ᄧ', 'ㅷ' => 'ᄩ', 'ㅸ' => 'ᄫ', 'ㅹ' => 'ᄬ', 'ㅺ' => 'ᄭ', 'ㅻ' => 'ᄮ', 'ㅼ' => 'ᄯ', 'ㅽ' => 'ᄲ', 'ㅾ' => 'ᄶ', 'ㅿ' => 'ᅀ', 'ㆀ' => 'ᅇ', 'ㆁ' => 'ᅌ', 'ㆂ' => 'ᇱ', 'ㆃ' => 'ᇲ', 'ㆄ' => 'ᅗ', 'ㆅ' => 'ᅘ', 'ㆆ' => 'ᅙ', 'ㆇ' => 'ᆄ', 'ㆈ' => 'ᆅ', 'ㆉ' => 'ᆈ', 'ㆊ' => 'ᆑ', 'ㆋ' => 'ᆒ', 'ㆌ' => 'ᆔ', 'ㆍ' => 'ᆞ', 'ㆎ' => 'ᆡ', '㆒' => '一', '㆓' => '二', '㆔' => '三', '㆕' => '四', '㆖' => '上', '㆗' => '中', '㆘' => '下', '㆙' => '甲', '㆚' => '乙', '㆛' => '丙', '㆜' => '丁', '㆝' => '天', '㆞' => '地', '㆟' => '人', '㈀' => '(ᄀ)', '㈁' => '(ᄂ)', '㈂' => '(ᄃ)', '㈃' => '(ᄅ)', '㈄' => '(ᄆ)', '㈅' => '(ᄇ)', '㈆' => '(ᄉ)', '㈇' => '(ᄋ)', '㈈' => '(ᄌ)', '㈉' => '(ᄎ)', '㈊' => '(ᄏ)', '㈋' => '(ᄐ)', '㈌' => '(ᄑ)', '㈍' => '(ᄒ)', '㈎' => '(가)', '㈏' => '(나)', '㈐' => '(다)', '㈑' => '(라)', '㈒' => '(마)', '㈓' => '(바)', '㈔' => '(사)', '㈕' => '(아)', '㈖' => '(자)', '㈗' => '(차)', '㈘' => '(카)', '㈙' => '(타)', '㈚' => '(파)', '㈛' => '(하)', '㈜' => '(주)', '㈝' => '(오전)', '㈞' => '(오후)', '㈠' => '(一)', '㈡' => '(二)', '㈢' => '(三)', '㈣' => '(四)', '㈤' => '(五)', '㈥' => '(六)', '㈦' => '(七)', '㈧' => '(八)', '㈨' => '(九)', '㈩' => '(十)', '㈪' => '(月)', '㈫' => '(火)', '㈬' => '(水)', '㈭' => '(木)', '㈮' => '(金)', '㈯' => '(土)', '㈰' => '(日)', '㈱' => '(株)', '㈲' => '(有)', '㈳' => '(社)', '㈴' => '(名)', '㈵' => '(特)', '㈶' => '(財)', '㈷' => '(祝)', '㈸' => '(労)', '㈹' => '(代)', '㈺' => '(呼)', '㈻' => '(学)', '㈼' => '(監)', '㈽' => '(企)', '㈾' => '(資)', '㈿' => '(協)', '㉀' => '(祭)', '㉁' => '(休)', '㉂' => '(自)', '㉃' => '(至)', '㉄' => '問', '㉅' => '幼', '㉆' => '文', '㉇' => '箏', '㉐' => 'PTE', '㉑' => '21', '㉒' => '22', '㉓' => '23', '㉔' => '24', '㉕' => '25', '㉖' => '26', '㉗' => '27', '㉘' => '28', '㉙' => '29', '㉚' => '30', '㉛' => '31', '㉜' => '32', '㉝' => '33', '㉞' => '34', '㉟' => '35', '㉠' => 'ᄀ', '㉡' => 'ᄂ', '㉢' => 'ᄃ', '㉣' => 'ᄅ', '㉤' => 'ᄆ', '㉥' => 'ᄇ', '㉦' => 'ᄉ', '㉧' => 'ᄋ', '㉨' => 'ᄌ', '㉩' => 'ᄎ', '㉪' => 'ᄏ', '㉫' => 'ᄐ', '㉬' => 'ᄑ', '㉭' => 'ᄒ', '㉮' => '가', '㉯' => '나', '㉰' => '다', '㉱' => '라', '㉲' => '마', '㉳' => '바', '㉴' => '사', '㉵' => '아', '㉶' => '자', '㉷' => '차', '㉸' => '카', '㉹' => '타', '㉺' => '파', '㉻' => '하', '㉼' => '참고', '㉽' => '주의', '㉾' => '우', '㊀' => '一', '㊁' => '二', '㊂' => '三', '㊃' => '四', '㊄' => '五', '㊅' => '六', '㊆' => '七', '㊇' => '八', '㊈' => '九', '㊉' => '十', '㊊' => '月', '㊋' => '火', '㊌' => '水', '㊍' => '木', '㊎' => '金', '㊏' => '土', '㊐' => '日', '㊑' => '株', '㊒' => '有', '㊓' => '社', '㊔' => '名', '㊕' => '特', '㊖' => '財', '㊗' => '祝', '㊘' => '労', '㊙' => '秘', '㊚' => '男', '㊛' => '女', '㊜' => '適', '㊝' => '優', '㊞' => '印', '㊟' => '注', '㊠' => '項', '㊡' => '休', '㊢' => '写', '㊣' => '正', '㊤' => '上', '㊥' => '中', '㊦' => '下', '㊧' => '左', '㊨' => '右', '㊩' => '医', '㊪' => '宗', '㊫' => '学', '㊬' => '監', '㊭' => '企', '㊮' => '資', '㊯' => '協', '㊰' => '夜', '㊱' => '36', '㊲' => '37', '㊳' => '38', '㊴' => '39', '㊵' => '40', '㊶' => '41', '㊷' => '42', '㊸' => '43', '㊹' => '44', '㊺' => '45', '㊻' => '46', '㊼' => '47', '㊽' => '48', '㊾' => '49', '㊿' => '50', '㋀' => '1月', '㋁' => '2月', '㋂' => '3月', '㋃' => '4月', '㋄' => '5月', '㋅' => '6月', '㋆' => '7月', '㋇' => '8月', '㋈' => '9月', '㋉' => '10月', '㋊' => '11月', '㋋' => '12月', '㋌' => 'Hg', '㋍' => 'erg', '㋎' => 'eV', '㋏' => 'LTD', '㋐' => 'ア', '㋑' => 'イ', '㋒' => 'ウ', '㋓' => 'エ', '㋔' => 'オ', '㋕' => 'カ', '㋖' => 'キ', '㋗' => 'ク', '㋘' => 'ケ', '㋙' => 'コ', '㋚' => 'サ', '㋛' => 'シ', '㋜' => 'ス', '㋝' => 'セ', '㋞' => 'ソ', '㋟' => 'タ', '㋠' => 'チ', '㋡' => 'ツ', '㋢' => 'テ', '㋣' => 'ト', '㋤' => 'ナ', '㋥' => 'ニ', '㋦' => 'ヌ', '㋧' => 'ネ', '㋨' => 'ノ', '㋩' => 'ハ', '㋪' => 'ヒ', '㋫' => 'フ', '㋬' => 'ヘ', '㋭' => 'ホ', '㋮' => 'マ', '㋯' => 'ミ', '㋰' => 'ム', '㋱' => 'メ', '㋲' => 'モ', '㋳' => 'ヤ', '㋴' => 'ユ', '㋵' => 'ヨ', '㋶' => 'ラ', '㋷' => 'リ', '㋸' => 'ル', '㋹' => 'レ', '㋺' => 'ロ', '㋻' => 'ワ', '㋼' => 'ヰ', '㋽' => 'ヱ', '㋾' => 'ヲ', '㋿' => '令和', '㌀' => 'アパート', '㌁' => 'アルファ', '㌂' => 'アンペア', '㌃' => 'アール', '㌄' => 'イニング', '㌅' => 'インチ', '㌆' => 'ウォン', '㌇' => 'エスクード', '㌈' => 'エーカー', '㌉' => 'オンス', '㌊' => 'オーム', '㌋' => 'カイリ', '㌌' => 'カラット', '㌍' => 'カロリー', '㌎' => 'ガロン', '㌏' => 'ガンマ', '㌐' => 'ギガ', '㌑' => 'ギニー', '㌒' => 'キュリー', '㌓' => 'ギルダー', '㌔' => 'キロ', '㌕' => 'キログラム', '㌖' => 'キロメートル', '㌗' => 'キロワット', '㌘' => 'グラム', '㌙' => 'グラムトン', '㌚' => 'クルゼイロ', '㌛' => 'クローネ', '㌜' => 'ケース', '㌝' => 'コルナ', '㌞' => 'コーポ', '㌟' => 'サイクル', '㌠' => 'サンチーム', '㌡' => 'シリング', '㌢' => 'センチ', '㌣' => 'セント', '㌤' => 'ダース', '㌥' => 'デシ', '㌦' => 'ドル', '㌧' => 'トン', '㌨' => 'ナノ', '㌩' => 'ノット', '㌪' => 'ハイツ', '㌫' => 'パーセント', '㌬' => 'パーツ', '㌭' => 'バーレル', '㌮' => 'ピアストル', '㌯' => 'ピクル', '㌰' => 'ピコ', '㌱' => 'ビル', '㌲' => 'ファラッド', '㌳' => 'フィート', '㌴' => 'ブッシェル', '㌵' => 'フラン', '㌶' => 'ヘクタール', '㌷' => 'ペソ', '㌸' => 'ペニヒ', '㌹' => 'ヘルツ', '㌺' => 'ペンス', '㌻' => 'ページ', '㌼' => 'ベータ', '㌽' => 'ポイント', '㌾' => 'ボルト', '㌿' => 'ホン', '㍀' => 'ポンド', '㍁' => 'ホール', '㍂' => 'ホーン', '㍃' => 'マイクロ', '㍄' => 'マイル', '㍅' => 'マッハ', '㍆' => 'マルク', '㍇' => 'マンション', '㍈' => 'ミクロン', '㍉' => 'ミリ', '㍊' => 'ミリバール', '㍋' => 'メガ', '㍌' => 'メガトン', '㍍' => 'メートル', '㍎' => 'ヤード', '㍏' => 'ヤール', '㍐' => 'ユアン', '㍑' => 'リットル', '㍒' => 'リラ', '㍓' => 'ルピー', '㍔' => 'ルーブル', '㍕' => 'レム', '㍖' => 'レントゲン', '㍗' => 'ワット', '㍘' => '0点', '㍙' => '1点', '㍚' => '2点', '㍛' => '3点', '㍜' => '4点', '㍝' => '5点', '㍞' => '6点', '㍟' => '7点', '㍠' => '8点', '㍡' => '9点', '㍢' => '10点', '㍣' => '11点', '㍤' => '12点', '㍥' => '13点', '㍦' => '14点', '㍧' => '15点', '㍨' => '16点', '㍩' => '17点', '㍪' => '18点', '㍫' => '19点', '㍬' => '20点', '㍭' => '21点', '㍮' => '22点', '㍯' => '23点', '㍰' => '24点', '㍱' => 'hPa', '㍲' => 'da', '㍳' => 'AU', '㍴' => 'bar', '㍵' => 'oV', '㍶' => 'pc', '㍷' => 'dm', '㍸' => 'dm2', '㍹' => 'dm3', '㍺' => 'IU', '㍻' => '平成', '㍼' => '昭和', '㍽' => '大正', '㍾' => '明治', '㍿' => '株式会社', '㎀' => 'pA', '㎁' => 'nA', '㎂' => 'μA', '㎃' => 'mA', '㎄' => 'kA', '㎅' => 'KB', '㎆' => 'MB', '㎇' => 'GB', '㎈' => 'cal', '㎉' => 'kcal', '㎊' => 'pF', '㎋' => 'nF', '㎌' => 'μF', '㎍' => 'μg', '㎎' => 'mg', '㎏' => 'kg', '㎐' => 'Hz', '㎑' => 'kHz', '㎒' => 'MHz', '㎓' => 'GHz', '㎔' => 'THz', '㎕' => 'μl', '㎖' => 'ml', '㎗' => 'dl', '㎘' => 'kl', '㎙' => 'fm', '㎚' => 'nm', '㎛' => 'μm', '㎜' => 'mm', '㎝' => 'cm', '㎞' => 'km', '㎟' => 'mm2', '㎠' => 'cm2', '㎡' => 'm2', '㎢' => 'km2', '㎣' => 'mm3', '㎤' => 'cm3', '㎥' => 'm3', '㎦' => 'km3', '㎧' => 'm∕s', '㎨' => 'm∕s2', '㎩' => 'Pa', '㎪' => 'kPa', '㎫' => 'MPa', '㎬' => 'GPa', '㎭' => 'rad', '㎮' => 'rad∕s', '㎯' => 'rad∕s2', '㎰' => 'ps', '㎱' => 'ns', '㎲' => 'μs', '㎳' => 'ms', '㎴' => 'pV', '㎵' => 'nV', '㎶' => 'μV', '㎷' => 'mV', '㎸' => 'kV', '㎹' => 'MV', '㎺' => 'pW', '㎻' => 'nW', '㎼' => 'μW', '㎽' => 'mW', '㎾' => 'kW', '㎿' => 'MW', '㏀' => 'kΩ', '㏁' => 'MΩ', '㏂' => 'a.m.', '㏃' => 'Bq', '㏄' => 'cc', '㏅' => 'cd', '㏆' => 'C∕kg', '㏇' => 'Co.', '㏈' => 'dB', '㏉' => 'Gy', '㏊' => 'ha', '㏋' => 'HP', '㏌' => 'in', '㏍' => 'KK', '㏎' => 'KM', '㏏' => 'kt', '㏐' => 'lm', '㏑' => 'ln', '㏒' => 'log', '㏓' => 'lx', '㏔' => 'mb', '㏕' => 'mil', '㏖' => 'mol', '㏗' => 'PH', '㏘' => 'p.m.', '㏙' => 'PPM', '㏚' => 'PR', '㏛' => 'sr', '㏜' => 'Sv', '㏝' => 'Wb', '㏞' => 'V∕m', '㏟' => 'A∕m', '㏠' => '1日', '㏡' => '2日', '㏢' => '3日', '㏣' => '4日', '㏤' => '5日', '㏥' => '6日', '㏦' => '7日', '㏧' => '8日', '㏨' => '9日', '㏩' => '10日', '㏪' => '11日', '㏫' => '12日', '㏬' => '13日', '㏭' => '14日', '㏮' => '15日', '㏯' => '16日', '㏰' => '17日', '㏱' => '18日', '㏲' => '19日', '㏳' => '20日', '㏴' => '21日', '㏵' => '22日', '㏶' => '23日', '㏷' => '24日', '㏸' => '25日', '㏹' => '26日', '㏺' => '27日', '㏻' => '28日', '㏼' => '29日', '㏽' => '30日', '㏾' => '31日', '㏿' => 'gal', 'ꚜ' => 'ъ', 'ꚝ' => 'ь', 'ꝰ' => 'ꝯ', 'ꟸ' => 'Ħ', 'ꟹ' => 'œ', 'ꭜ' => 'ꜧ', 'ꭝ' => 'ꬷ', 'ꭞ' => 'ɫ', 'ꭟ' => 'ꭒ', 'ꭩ' => 'ʍ', 'ff' => 'ff', 'fi' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', 'ffl' => 'ffl', 'ſt' => 'st', 'st' => 'st', 'ﬓ' => 'մն', 'ﬔ' => 'մե', 'ﬕ' => 'մի', 'ﬖ' => 'վն', 'ﬗ' => 'մխ', 'ﬠ' => 'ע', 'ﬡ' => 'א', 'ﬢ' => 'ד', 'ﬣ' => 'ה', 'ﬤ' => 'כ', 'ﬥ' => 'ל', 'ﬦ' => 'ם', 'ﬧ' => 'ר', 'ﬨ' => 'ת', '﬩' => '+', 'ﭏ' => 'אל', 'ﭐ' => 'ٱ', 'ﭑ' => 'ٱ', 'ﭒ' => 'ٻ', 'ﭓ' => 'ٻ', 'ﭔ' => 'ٻ', 'ﭕ' => 'ٻ', 'ﭖ' => 'پ', 'ﭗ' => 'پ', 'ﭘ' => 'پ', 'ﭙ' => 'پ', 'ﭚ' => 'ڀ', 'ﭛ' => 'ڀ', 'ﭜ' => 'ڀ', 'ﭝ' => 'ڀ', 'ﭞ' => 'ٺ', 'ﭟ' => 'ٺ', 'ﭠ' => 'ٺ', 'ﭡ' => 'ٺ', 'ﭢ' => 'ٿ', 'ﭣ' => 'ٿ', 'ﭤ' => 'ٿ', 'ﭥ' => 'ٿ', 'ﭦ' => 'ٹ', 'ﭧ' => 'ٹ', 'ﭨ' => 'ٹ', 'ﭩ' => 'ٹ', 'ﭪ' => 'ڤ', 'ﭫ' => 'ڤ', 'ﭬ' => 'ڤ', 'ﭭ' => 'ڤ', 'ﭮ' => 'ڦ', 'ﭯ' => 'ڦ', 'ﭰ' => 'ڦ', 'ﭱ' => 'ڦ', 'ﭲ' => 'ڄ', 'ﭳ' => 'ڄ', 'ﭴ' => 'ڄ', 'ﭵ' => 'ڄ', 'ﭶ' => 'ڃ', 'ﭷ' => 'ڃ', 'ﭸ' => 'ڃ', 'ﭹ' => 'ڃ', 'ﭺ' => 'چ', 'ﭻ' => 'چ', 'ﭼ' => 'چ', 'ﭽ' => 'چ', 'ﭾ' => 'ڇ', 'ﭿ' => 'ڇ', 'ﮀ' => 'ڇ', 'ﮁ' => 'ڇ', 'ﮂ' => 'ڍ', 'ﮃ' => 'ڍ', 'ﮄ' => 'ڌ', 'ﮅ' => 'ڌ', 'ﮆ' => 'ڎ', 'ﮇ' => 'ڎ', 'ﮈ' => 'ڈ', 'ﮉ' => 'ڈ', 'ﮊ' => 'ژ', 'ﮋ' => 'ژ', 'ﮌ' => 'ڑ', 'ﮍ' => 'ڑ', 'ﮎ' => 'ک', 'ﮏ' => 'ک', 'ﮐ' => 'ک', 'ﮑ' => 'ک', 'ﮒ' => 'گ', 'ﮓ' => 'گ', 'ﮔ' => 'گ', 'ﮕ' => 'گ', 'ﮖ' => 'ڳ', 'ﮗ' => 'ڳ', 'ﮘ' => 'ڳ', 'ﮙ' => 'ڳ', 'ﮚ' => 'ڱ', 'ﮛ' => 'ڱ', 'ﮜ' => 'ڱ', 'ﮝ' => 'ڱ', 'ﮞ' => 'ں', 'ﮟ' => 'ں', 'ﮠ' => 'ڻ', 'ﮡ' => 'ڻ', 'ﮢ' => 'ڻ', 'ﮣ' => 'ڻ', 'ﮤ' => 'ۀ', 'ﮥ' => 'ۀ', 'ﮦ' => 'ہ', 'ﮧ' => 'ہ', 'ﮨ' => 'ہ', 'ﮩ' => 'ہ', 'ﮪ' => 'ھ', 'ﮫ' => 'ھ', 'ﮬ' => 'ھ', 'ﮭ' => 'ھ', 'ﮮ' => 'ے', 'ﮯ' => 'ے', 'ﮰ' => 'ۓ', 'ﮱ' => 'ۓ', 'ﯓ' => 'ڭ', 'ﯔ' => 'ڭ', 'ﯕ' => 'ڭ', 'ﯖ' => 'ڭ', 'ﯗ' => 'ۇ', 'ﯘ' => 'ۇ', 'ﯙ' => 'ۆ', 'ﯚ' => 'ۆ', 'ﯛ' => 'ۈ', 'ﯜ' => 'ۈ', 'ﯝ' => 'ۇٴ', 'ﯞ' => 'ۋ', 'ﯟ' => 'ۋ', 'ﯠ' => 'ۅ', 'ﯡ' => 'ۅ', 'ﯢ' => 'ۉ', 'ﯣ' => 'ۉ', 'ﯤ' => 'ې', 'ﯥ' => 'ې', 'ﯦ' => 'ې', 'ﯧ' => 'ې', 'ﯨ' => 'ى', 'ﯩ' => 'ى', 'ﯪ' => 'ئا', 'ﯫ' => 'ئا', 'ﯬ' => 'ئە', 'ﯭ' => 'ئە', 'ﯮ' => 'ئو', 'ﯯ' => 'ئو', 'ﯰ' => 'ئۇ', 'ﯱ' => 'ئۇ', 'ﯲ' => 'ئۆ', 'ﯳ' => 'ئۆ', 'ﯴ' => 'ئۈ', 'ﯵ' => 'ئۈ', 'ﯶ' => 'ئې', 'ﯷ' => 'ئې', 'ﯸ' => 'ئې', 'ﯹ' => 'ئى', 'ﯺ' => 'ئى', 'ﯻ' => 'ئى', 'ﯼ' => 'ی', 'ﯽ' => 'ی', 'ﯾ' => 'ی', 'ﯿ' => 'ی', 'ﰀ' => 'ئج', 'ﰁ' => 'ئح', 'ﰂ' => 'ئم', 'ﰃ' => 'ئى', 'ﰄ' => 'ئي', 'ﰅ' => 'بج', 'ﰆ' => 'بح', 'ﰇ' => 'بخ', 'ﰈ' => 'بم', 'ﰉ' => 'بى', 'ﰊ' => 'بي', 'ﰋ' => 'تج', 'ﰌ' => 'تح', 'ﰍ' => 'تخ', 'ﰎ' => 'تم', 'ﰏ' => 'تى', 'ﰐ' => 'تي', 'ﰑ' => 'ثج', 'ﰒ' => 'ثم', 'ﰓ' => 'ثى', 'ﰔ' => 'ثي', 'ﰕ' => 'جح', 'ﰖ' => 'جم', 'ﰗ' => 'حج', 'ﰘ' => 'حم', 'ﰙ' => 'خج', 'ﰚ' => 'خح', 'ﰛ' => 'خم', 'ﰜ' => 'سج', 'ﰝ' => 'سح', 'ﰞ' => 'سخ', 'ﰟ' => 'سم', 'ﰠ' => 'صح', 'ﰡ' => 'صم', 'ﰢ' => 'ضج', 'ﰣ' => 'ضح', 'ﰤ' => 'ضخ', 'ﰥ' => 'ضم', 'ﰦ' => 'طح', 'ﰧ' => 'طم', 'ﰨ' => 'ظم', 'ﰩ' => 'عج', 'ﰪ' => 'عم', 'ﰫ' => 'غج', 'ﰬ' => 'غم', 'ﰭ' => 'فج', 'ﰮ' => 'فح', 'ﰯ' => 'فخ', 'ﰰ' => 'فم', 'ﰱ' => 'فى', 'ﰲ' => 'في', 'ﰳ' => 'قح', 'ﰴ' => 'قم', 'ﰵ' => 'قى', 'ﰶ' => 'قي', 'ﰷ' => 'كا', 'ﰸ' => 'كج', 'ﰹ' => 'كح', 'ﰺ' => 'كخ', 'ﰻ' => 'كل', 'ﰼ' => 'كم', 'ﰽ' => 'كى', 'ﰾ' => 'كي', 'ﰿ' => 'لج', 'ﱀ' => 'لح', 'ﱁ' => 'لخ', 'ﱂ' => 'لم', 'ﱃ' => 'لى', 'ﱄ' => 'لي', 'ﱅ' => 'مج', 'ﱆ' => 'مح', 'ﱇ' => 'مخ', 'ﱈ' => 'مم', 'ﱉ' => 'مى', 'ﱊ' => 'مي', 'ﱋ' => 'نج', 'ﱌ' => 'نح', 'ﱍ' => 'نخ', 'ﱎ' => 'نم', 'ﱏ' => 'نى', 'ﱐ' => 'ني', 'ﱑ' => 'هج', 'ﱒ' => 'هم', 'ﱓ' => 'هى', 'ﱔ' => 'هي', 'ﱕ' => 'يج', 'ﱖ' => 'يح', 'ﱗ' => 'يخ', 'ﱘ' => 'يم', 'ﱙ' => 'يى', 'ﱚ' => 'يي', 'ﱛ' => 'ذٰ', 'ﱜ' => 'رٰ', 'ﱝ' => 'ىٰ', 'ﱞ' => ' ٌّ', 'ﱟ' => ' ٍّ', 'ﱠ' => ' َّ', 'ﱡ' => ' ُّ', 'ﱢ' => ' ِّ', 'ﱣ' => ' ّٰ', 'ﱤ' => 'ئر', 'ﱥ' => 'ئز', 'ﱦ' => 'ئم', 'ﱧ' => 'ئن', 'ﱨ' => 'ئى', 'ﱩ' => 'ئي', 'ﱪ' => 'بر', 'ﱫ' => 'بز', 'ﱬ' => 'بم', 'ﱭ' => 'بن', 'ﱮ' => 'بى', 'ﱯ' => 'بي', 'ﱰ' => 'تر', 'ﱱ' => 'تز', 'ﱲ' => 'تم', 'ﱳ' => 'تن', 'ﱴ' => 'تى', 'ﱵ' => 'تي', 'ﱶ' => 'ثر', 'ﱷ' => 'ثز', 'ﱸ' => 'ثم', 'ﱹ' => 'ثن', 'ﱺ' => 'ثى', 'ﱻ' => 'ثي', 'ﱼ' => 'فى', 'ﱽ' => 'في', 'ﱾ' => 'قى', 'ﱿ' => 'قي', 'ﲀ' => 'كا', 'ﲁ' => 'كل', 'ﲂ' => 'كم', 'ﲃ' => 'كى', 'ﲄ' => 'كي', 'ﲅ' => 'لم', 'ﲆ' => 'لى', 'ﲇ' => 'لي', 'ﲈ' => 'ما', 'ﲉ' => 'مم', 'ﲊ' => 'نر', 'ﲋ' => 'نز', 'ﲌ' => 'نم', 'ﲍ' => 'نن', 'ﲎ' => 'نى', 'ﲏ' => 'ني', 'ﲐ' => 'ىٰ', 'ﲑ' => 'ير', 'ﲒ' => 'يز', 'ﲓ' => 'يم', 'ﲔ' => 'ين', 'ﲕ' => 'يى', 'ﲖ' => 'يي', 'ﲗ' => 'ئج', 'ﲘ' => 'ئح', 'ﲙ' => 'ئخ', 'ﲚ' => 'ئم', 'ﲛ' => 'ئه', 'ﲜ' => 'بج', 'ﲝ' => 'بح', 'ﲞ' => 'بخ', 'ﲟ' => 'بم', 'ﲠ' => 'به', 'ﲡ' => 'تج', 'ﲢ' => 'تح', 'ﲣ' => 'تخ', 'ﲤ' => 'تم', 'ﲥ' => 'ته', 'ﲦ' => 'ثم', 'ﲧ' => 'جح', 'ﲨ' => 'جم', 'ﲩ' => 'حج', 'ﲪ' => 'حم', 'ﲫ' => 'خج', 'ﲬ' => 'خم', 'ﲭ' => 'سج', 'ﲮ' => 'سح', 'ﲯ' => 'سخ', 'ﲰ' => 'سم', 'ﲱ' => 'صح', 'ﲲ' => 'صخ', 'ﲳ' => 'صم', 'ﲴ' => 'ضج', 'ﲵ' => 'ضح', 'ﲶ' => 'ضخ', 'ﲷ' => 'ضم', 'ﲸ' => 'طح', 'ﲹ' => 'ظم', 'ﲺ' => 'عج', 'ﲻ' => 'عم', 'ﲼ' => 'غج', 'ﲽ' => 'غم', 'ﲾ' => 'فج', 'ﲿ' => 'فح', 'ﳀ' => 'فخ', 'ﳁ' => 'فم', 'ﳂ' => 'قح', 'ﳃ' => 'قم', 'ﳄ' => 'كج', 'ﳅ' => 'كح', 'ﳆ' => 'كخ', 'ﳇ' => 'كل', 'ﳈ' => 'كم', 'ﳉ' => 'لج', 'ﳊ' => 'لح', 'ﳋ' => 'لخ', 'ﳌ' => 'لم', 'ﳍ' => 'له', 'ﳎ' => 'مج', 'ﳏ' => 'مح', 'ﳐ' => 'مخ', 'ﳑ' => 'مم', 'ﳒ' => 'نج', 'ﳓ' => 'نح', 'ﳔ' => 'نخ', 'ﳕ' => 'نم', 'ﳖ' => 'نه', 'ﳗ' => 'هج', 'ﳘ' => 'هم', 'ﳙ' => 'هٰ', 'ﳚ' => 'يج', 'ﳛ' => 'يح', 'ﳜ' => 'يخ', 'ﳝ' => 'يم', 'ﳞ' => 'يه', 'ﳟ' => 'ئم', 'ﳠ' => 'ئه', 'ﳡ' => 'بم', 'ﳢ' => 'به', 'ﳣ' => 'تم', 'ﳤ' => 'ته', 'ﳥ' => 'ثم', 'ﳦ' => 'ثه', 'ﳧ' => 'سم', 'ﳨ' => 'سه', 'ﳩ' => 'شم', 'ﳪ' => 'شه', 'ﳫ' => 'كل', 'ﳬ' => 'كم', 'ﳭ' => 'لم', 'ﳮ' => 'نم', 'ﳯ' => 'نه', 'ﳰ' => 'يم', 'ﳱ' => 'يه', 'ﳲ' => 'ـَّ', 'ﳳ' => 'ـُّ', 'ﳴ' => 'ـِّ', 'ﳵ' => 'طى', 'ﳶ' => 'طي', 'ﳷ' => 'عى', 'ﳸ' => 'عي', 'ﳹ' => 'غى', 'ﳺ' => 'غي', 'ﳻ' => 'سى', 'ﳼ' => 'سي', 'ﳽ' => 'شى', 'ﳾ' => 'شي', 'ﳿ' => 'حى', 'ﴀ' => 'حي', 'ﴁ' => 'جى', 'ﴂ' => 'جي', 'ﴃ' => 'خى', 'ﴄ' => 'خي', 'ﴅ' => 'صى', 'ﴆ' => 'صي', 'ﴇ' => 'ضى', 'ﴈ' => 'ضي', 'ﴉ' => 'شج', 'ﴊ' => 'شح', 'ﴋ' => 'شخ', 'ﴌ' => 'شم', 'ﴍ' => 'شر', 'ﴎ' => 'سر', 'ﴏ' => 'صر', 'ﴐ' => 'ضر', 'ﴑ' => 'طى', 'ﴒ' => 'طي', 'ﴓ' => 'عى', 'ﴔ' => 'عي', 'ﴕ' => 'غى', 'ﴖ' => 'غي', 'ﴗ' => 'سى', 'ﴘ' => 'سي', 'ﴙ' => 'شى', 'ﴚ' => 'شي', 'ﴛ' => 'حى', 'ﴜ' => 'حي', 'ﴝ' => 'جى', 'ﴞ' => 'جي', 'ﴟ' => 'خى', 'ﴠ' => 'خي', 'ﴡ' => 'صى', 'ﴢ' => 'صي', 'ﴣ' => 'ضى', 'ﴤ' => 'ضي', 'ﴥ' => 'شج', 'ﴦ' => 'شح', 'ﴧ' => 'شخ', 'ﴨ' => 'شم', 'ﴩ' => 'شر', 'ﴪ' => 'سر', 'ﴫ' => 'صر', 'ﴬ' => 'ضر', 'ﴭ' => 'شج', 'ﴮ' => 'شح', 'ﴯ' => 'شخ', 'ﴰ' => 'شم', 'ﴱ' => 'سه', 'ﴲ' => 'شه', 'ﴳ' => 'طم', 'ﴴ' => 'سج', 'ﴵ' => 'سح', 'ﴶ' => 'سخ', 'ﴷ' => 'شج', 'ﴸ' => 'شح', 'ﴹ' => 'شخ', 'ﴺ' => 'طم', 'ﴻ' => 'ظم', 'ﴼ' => 'اً', 'ﴽ' => 'اً', 'ﵐ' => 'تجم', 'ﵑ' => 'تحج', 'ﵒ' => 'تحج', 'ﵓ' => 'تحم', 'ﵔ' => 'تخم', 'ﵕ' => 'تمج', 'ﵖ' => 'تمح', 'ﵗ' => 'تمخ', 'ﵘ' => 'جمح', 'ﵙ' => 'جمح', 'ﵚ' => 'حمي', 'ﵛ' => 'حمى', 'ﵜ' => 'سحج', 'ﵝ' => 'سجح', 'ﵞ' => 'سجى', 'ﵟ' => 'سمح', 'ﵠ' => 'سمح', 'ﵡ' => 'سمج', 'ﵢ' => 'سمم', 'ﵣ' => 'سمم', 'ﵤ' => 'صحح', 'ﵥ' => 'صحح', 'ﵦ' => 'صمم', 'ﵧ' => 'شحم', 'ﵨ' => 'شحم', 'ﵩ' => 'شجي', 'ﵪ' => 'شمخ', 'ﵫ' => 'شمخ', 'ﵬ' => 'شمم', 'ﵭ' => 'شمم', 'ﵮ' => 'ضحى', 'ﵯ' => 'ضخم', 'ﵰ' => 'ضخم', 'ﵱ' => 'طمح', 'ﵲ' => 'طمح', 'ﵳ' => 'طمم', 'ﵴ' => 'طمي', 'ﵵ' => 'عجم', 'ﵶ' => 'عمم', 'ﵷ' => 'عمم', 'ﵸ' => 'عمى', 'ﵹ' => 'غمم', 'ﵺ' => 'غمي', 'ﵻ' => 'غمى', 'ﵼ' => 'فخم', 'ﵽ' => 'فخم', 'ﵾ' => 'قمح', 'ﵿ' => 'قمم', 'ﶀ' => 'لحم', 'ﶁ' => 'لحي', 'ﶂ' => 'لحى', 'ﶃ' => 'لجج', 'ﶄ' => 'لجج', 'ﶅ' => 'لخم', 'ﶆ' => 'لخم', 'ﶇ' => 'لمح', 'ﶈ' => 'لمح', 'ﶉ' => 'محج', 'ﶊ' => 'محم', 'ﶋ' => 'محي', 'ﶌ' => 'مجح', 'ﶍ' => 'مجم', 'ﶎ' => 'مخج', 'ﶏ' => 'مخم', 'ﶒ' => 'مجخ', 'ﶓ' => 'همج', 'ﶔ' => 'همم', 'ﶕ' => 'نحم', 'ﶖ' => 'نحى', 'ﶗ' => 'نجم', 'ﶘ' => 'نجم', 'ﶙ' => 'نجى', 'ﶚ' => 'نمي', 'ﶛ' => 'نمى', 'ﶜ' => 'يمم', 'ﶝ' => 'يمم', 'ﶞ' => 'بخي', 'ﶟ' => 'تجي', 'ﶠ' => 'تجى', 'ﶡ' => 'تخي', 'ﶢ' => 'تخى', 'ﶣ' => 'تمي', 'ﶤ' => 'تمى', 'ﶥ' => 'جمي', 'ﶦ' => 'جحى', 'ﶧ' => 'جمى', 'ﶨ' => 'سخى', 'ﶩ' => 'صحي', 'ﶪ' => 'شحي', 'ﶫ' => 'ضحي', 'ﶬ' => 'لجي', 'ﶭ' => 'لمي', 'ﶮ' => 'يحي', 'ﶯ' => 'يجي', 'ﶰ' => 'يمي', 'ﶱ' => 'ممي', 'ﶲ' => 'قمي', 'ﶳ' => 'نحي', 'ﶴ' => 'قمح', 'ﶵ' => 'لحم', 'ﶶ' => 'عمي', 'ﶷ' => 'كمي', 'ﶸ' => 'نجح', 'ﶹ' => 'مخي', 'ﶺ' => 'لجم', 'ﶻ' => 'كمم', 'ﶼ' => 'لجم', 'ﶽ' => 'نجح', 'ﶾ' => 'جحي', 'ﶿ' => 'حجي', 'ﷀ' => 'مجي', 'ﷁ' => 'فمي', 'ﷂ' => 'بحي', 'ﷃ' => 'كمم', 'ﷄ' => 'عجم', 'ﷅ' => 'صمم', 'ﷆ' => 'سخي', 'ﷇ' => 'نجي', 'ﷰ' => 'صلے', 'ﷱ' => 'قلے', 'ﷲ' => 'الله', 'ﷳ' => 'اكبر', 'ﷴ' => 'محمد', 'ﷵ' => 'صلعم', 'ﷶ' => 'رسول', 'ﷷ' => 'عليه', 'ﷸ' => 'وسلم', 'ﷹ' => 'صلى', 'ﷺ' => 'صلى الله عليه وسلم', 'ﷻ' => 'جل جلاله', '﷼' => 'ریال', '︐' => ',', '︑' => '、', '︒' => '。', '︓' => ':', '︔' => ';', '︕' => '!', '︖' => '?', '︗' => '〖', '︘' => '〗', '︙' => '...', '︰' => '..', '︱' => '—', '︲' => '–', '︳' => '_', '︴' => '_', '︵' => '(', '︶' => ')', '︷' => '{', '︸' => '}', '︹' => '〔', '︺' => '〕', '︻' => '【', '︼' => '】', '︽' => '《', '︾' => '》', '︿' => '〈', '﹀' => '〉', '﹁' => '「', '﹂' => '」', '﹃' => '『', '﹄' => '』', '﹇' => '[', '﹈' => ']', '﹉' => ' ̅', '﹊' => ' ̅', '﹋' => ' ̅', '﹌' => ' ̅', '﹍' => '_', '﹎' => '_', '﹏' => '_', '﹐' => ',', '﹑' => '、', '﹒' => '.', '﹔' => ';', '﹕' => ':', '﹖' => '?', '﹗' => '!', '﹘' => '—', '﹙' => '(', '﹚' => ')', '﹛' => '{', '﹜' => '}', '﹝' => '〔', '﹞' => '〕', '﹟' => '#', '﹠' => '&', '﹡' => '*', '﹢' => '+', '﹣' => '-', '﹤' => '<', '﹥' => '>', '﹦' => '=', '﹨' => '\\', '﹩' => '$', '﹪' => '%', '﹫' => '@', 'ﹰ' => ' ً', 'ﹱ' => 'ـً', 'ﹲ' => ' ٌ', 'ﹴ' => ' ٍ', 'ﹶ' => ' َ', 'ﹷ' => 'ـَ', 'ﹸ' => ' ُ', 'ﹹ' => 'ـُ', 'ﹺ' => ' ِ', 'ﹻ' => 'ـِ', 'ﹼ' => ' ّ', 'ﹽ' => 'ـّ', 'ﹾ' => ' ْ', 'ﹿ' => 'ـْ', 'ﺀ' => 'ء', 'ﺁ' => 'آ', 'ﺂ' => 'آ', 'ﺃ' => 'أ', 'ﺄ' => 'أ', 'ﺅ' => 'ؤ', 'ﺆ' => 'ؤ', 'ﺇ' => 'إ', 'ﺈ' => 'إ', 'ﺉ' => 'ئ', 'ﺊ' => 'ئ', 'ﺋ' => 'ئ', 'ﺌ' => 'ئ', 'ﺍ' => 'ا', 'ﺎ' => 'ا', 'ﺏ' => 'ب', 'ﺐ' => 'ب', 'ﺑ' => 'ب', 'ﺒ' => 'ب', 'ﺓ' => 'ة', 'ﺔ' => 'ة', 'ﺕ' => 'ت', 'ﺖ' => 'ت', 'ﺗ' => 'ت', 'ﺘ' => 'ت', 'ﺙ' => 'ث', 'ﺚ' => 'ث', 'ﺛ' => 'ث', 'ﺜ' => 'ث', 'ﺝ' => 'ج', 'ﺞ' => 'ج', 'ﺟ' => 'ج', 'ﺠ' => 'ج', 'ﺡ' => 'ح', 'ﺢ' => 'ح', 'ﺣ' => 'ح', 'ﺤ' => 'ح', 'ﺥ' => 'خ', 'ﺦ' => 'خ', 'ﺧ' => 'خ', 'ﺨ' => 'خ', 'ﺩ' => 'د', 'ﺪ' => 'د', 'ﺫ' => 'ذ', 'ﺬ' => 'ذ', 'ﺭ' => 'ر', 'ﺮ' => 'ر', 'ﺯ' => 'ز', 'ﺰ' => 'ز', 'ﺱ' => 'س', 'ﺲ' => 'س', 'ﺳ' => 'س', 'ﺴ' => 'س', 'ﺵ' => 'ش', 'ﺶ' => 'ش', 'ﺷ' => 'ش', 'ﺸ' => 'ش', 'ﺹ' => 'ص', 'ﺺ' => 'ص', 'ﺻ' => 'ص', 'ﺼ' => 'ص', 'ﺽ' => 'ض', 'ﺾ' => 'ض', 'ﺿ' => 'ض', 'ﻀ' => 'ض', 'ﻁ' => 'ط', 'ﻂ' => 'ط', 'ﻃ' => 'ط', 'ﻄ' => 'ط', 'ﻅ' => 'ظ', 'ﻆ' => 'ظ', 'ﻇ' => 'ظ', 'ﻈ' => 'ظ', 'ﻉ' => 'ع', 'ﻊ' => 'ع', 'ﻋ' => 'ع', 'ﻌ' => 'ع', 'ﻍ' => 'غ', 'ﻎ' => 'غ', 'ﻏ' => 'غ', 'ﻐ' => 'غ', 'ﻑ' => 'ف', 'ﻒ' => 'ف', 'ﻓ' => 'ف', 'ﻔ' => 'ف', 'ﻕ' => 'ق', 'ﻖ' => 'ق', 'ﻗ' => 'ق', 'ﻘ' => 'ق', 'ﻙ' => 'ك', 'ﻚ' => 'ك', 'ﻛ' => 'ك', 'ﻜ' => 'ك', 'ﻝ' => 'ل', 'ﻞ' => 'ل', 'ﻟ' => 'ل', 'ﻠ' => 'ل', 'ﻡ' => 'م', 'ﻢ' => 'م', 'ﻣ' => 'م', 'ﻤ' => 'م', 'ﻥ' => 'ن', 'ﻦ' => 'ن', 'ﻧ' => 'ن', 'ﻨ' => 'ن', 'ﻩ' => 'ه', 'ﻪ' => 'ه', 'ﻫ' => 'ه', 'ﻬ' => 'ه', 'ﻭ' => 'و', 'ﻮ' => 'و', 'ﻯ' => 'ى', 'ﻰ' => 'ى', 'ﻱ' => 'ي', 'ﻲ' => 'ي', 'ﻳ' => 'ي', 'ﻴ' => 'ي', 'ﻵ' => 'لآ', 'ﻶ' => 'لآ', 'ﻷ' => 'لأ', 'ﻸ' => 'لأ', 'ﻹ' => 'لإ', 'ﻺ' => 'لإ', 'ﻻ' => 'لا', 'ﻼ' => 'لا', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', ''' => '\'', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '-' => '-', '.' => '.', '/' => '/', '0' => '0', '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9', ':' => ':', ';' => ';', '<' => '<', '=' => '=', '>' => '>', '?' => '?', '@' => '@', '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', '[' => '[', '\' => '\\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', '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', '{' => '{', '|' => '|', '}' => '}', '~' => '~', '⦅' => '⦅', '⦆' => '⦆', '。' => '。', '「' => '「', '」' => '」', '、' => '、', '・' => '・', 'ヲ' => 'ヲ', 'ァ' => 'ァ', 'ィ' => 'ィ', 'ゥ' => 'ゥ', 'ェ' => 'ェ', 'ォ' => 'ォ', 'ャ' => 'ャ', 'ュ' => 'ュ', 'ョ' => 'ョ', 'ッ' => 'ッ', 'ー' => 'ー', 'ア' => 'ア', 'イ' => 'イ', 'ウ' => 'ウ', 'エ' => 'エ', 'オ' => 'オ', 'カ' => 'カ', 'キ' => 'キ', 'ク' => 'ク', 'ケ' => 'ケ', 'コ' => 'コ', 'サ' => 'サ', 'シ' => 'シ', 'ス' => 'ス', 'セ' => 'セ', 'ソ' => 'ソ', 'タ' => 'タ', 'チ' => 'チ', 'ツ' => 'ツ', 'テ' => 'テ', 'ト' => 'ト', 'ナ' => 'ナ', 'ニ' => 'ニ', 'ヌ' => 'ヌ', 'ネ' => 'ネ', 'ノ' => 'ノ', 'ハ' => 'ハ', 'ヒ' => 'ヒ', 'フ' => 'フ', 'ヘ' => 'ヘ', 'ホ' => 'ホ', 'マ' => 'マ', 'ミ' => 'ミ', 'ム' => 'ム', 'メ' => 'メ', 'モ' => 'モ', 'ヤ' => 'ヤ', 'ユ' => 'ユ', 'ヨ' => 'ヨ', 'ラ' => 'ラ', 'リ' => 'リ', 'ル' => 'ル', 'レ' => 'レ', 'ロ' => 'ロ', 'ワ' => 'ワ', 'ン' => 'ン', '゙' => '゙', '゚' => '゚', 'ᅠ' => 'ᅠ', 'ᄀ' => 'ᄀ', 'ᄁ' => 'ᄁ', 'ᆪ' => 'ᆪ', 'ᄂ' => 'ᄂ', 'ᆬ' => 'ᆬ', 'ᆭ' => 'ᆭ', 'ᄃ' => 'ᄃ', 'ᄄ' => 'ᄄ', 'ᄅ' => 'ᄅ', 'ᆰ' => 'ᆰ', 'ᆱ' => 'ᆱ', 'ᆲ' => 'ᆲ', 'ᆳ' => 'ᆳ', 'ᆴ' => 'ᆴ', 'ᆵ' => 'ᆵ', 'ᄚ' => 'ᄚ', 'ᄆ' => 'ᄆ', 'ᄇ' => 'ᄇ', 'ᄈ' => 'ᄈ', 'ᄡ' => 'ᄡ', 'ᄉ' => 'ᄉ', 'ᄊ' => 'ᄊ', 'ᄋ' => 'ᄋ', 'ᄌ' => 'ᄌ', 'ᄍ' => 'ᄍ', 'ᄎ' => 'ᄎ', 'ᄏ' => 'ᄏ', 'ᄐ' => 'ᄐ', 'ᄑ' => 'ᄑ', 'ᄒ' => 'ᄒ', 'ᅡ' => 'ᅡ', 'ᅢ' => 'ᅢ', 'ᅣ' => 'ᅣ', 'ᅤ' => 'ᅤ', 'ᅥ' => 'ᅥ', 'ᅦ' => 'ᅦ', 'ᅧ' => 'ᅧ', 'ᅨ' => 'ᅨ', 'ᅩ' => 'ᅩ', 'ᅪ' => 'ᅪ', 'ᅫ' => 'ᅫ', 'ᅬ' => 'ᅬ', 'ᅭ' => 'ᅭ', 'ᅮ' => 'ᅮ', 'ᅯ' => 'ᅯ', 'ᅰ' => 'ᅰ', 'ᅱ' => 'ᅱ', 'ᅲ' => 'ᅲ', 'ᅳ' => 'ᅳ', 'ᅴ' => 'ᅴ', 'ᅵ' => 'ᅵ', '¢' => '¢', '£' => '£', '¬' => '¬', ' ̄' => ' ̄', '¦' => '¦', '¥' => '¥', '₩' => '₩', '│' => '│', '←' => '←', '↑' => '↑', '→' => '→', '↓' => '↓', '■' => '■', '○' => '○', '𝐀' => 'A', '𝐁' => 'B', '𝐂' => 'C', '𝐃' => 'D', '𝐄' => 'E', '𝐅' => 'F', '𝐆' => 'G', '𝐇' => 'H', '𝐈' => 'I', '𝐉' => 'J', '𝐊' => 'K', '𝐋' => 'L', '𝐌' => 'M', '𝐍' => 'N', '𝐎' => 'O', '𝐏' => 'P', '𝐐' => 'Q', '𝐑' => 'R', '𝐒' => 'S', '𝐓' => 'T', '𝐔' => 'U', '𝐕' => 'V', '𝐖' => 'W', '𝐗' => 'X', '𝐘' => 'Y', '𝐙' => 'Z', '𝐚' => 'a', '𝐛' => 'b', '𝐜' => 'c', '𝐝' => 'd', '𝐞' => 'e', '𝐟' => 'f', '𝐠' => 'g', '𝐡' => 'h', '𝐢' => 'i', '𝐣' => 'j', '𝐤' => 'k', '𝐥' => 'l', '𝐦' => 'm', '𝐧' => 'n', '𝐨' => 'o', '𝐩' => 'p', '𝐪' => 'q', '𝐫' => 'r', '𝐬' => 's', '𝐭' => 't', '𝐮' => 'u', '𝐯' => 'v', '𝐰' => 'w', '𝐱' => 'x', '𝐲' => 'y', '𝐳' => 'z', '𝐴' => 'A', '𝐵' => 'B', '𝐶' => 'C', '𝐷' => 'D', '𝐸' => 'E', '𝐹' => 'F', '𝐺' => 'G', '𝐻' => 'H', '𝐼' => 'I', '𝐽' => 'J', '𝐾' => 'K', '𝐿' => 'L', '𝑀' => 'M', '𝑁' => 'N', '𝑂' => 'O', '𝑃' => 'P', '𝑄' => 'Q', '𝑅' => 'R', '𝑆' => 'S', '𝑇' => 'T', '𝑈' => 'U', '𝑉' => 'V', '𝑊' => 'W', '𝑋' => 'X', '𝑌' => 'Y', '𝑍' => 'Z', '𝑎' => 'a', '𝑏' => 'b', '𝑐' => 'c', '𝑑' => 'd', '𝑒' => 'e', '𝑓' => 'f', '𝑔' => 'g', '𝑖' => 'i', '𝑗' => 'j', '𝑘' => 'k', '𝑙' => 'l', '𝑚' => 'm', '𝑛' => 'n', '𝑜' => 'o', '𝑝' => 'p', '𝑞' => 'q', '𝑟' => 'r', '𝑠' => 's', '𝑡' => 't', '𝑢' => 'u', '𝑣' => 'v', '𝑤' => 'w', '𝑥' => 'x', '𝑦' => 'y', '𝑧' => 'z', '𝑨' => 'A', '𝑩' => 'B', '𝑪' => 'C', '𝑫' => 'D', '𝑬' => 'E', '𝑭' => 'F', '𝑮' => 'G', '𝑯' => 'H', '𝑰' => 'I', '𝑱' => 'J', '𝑲' => 'K', '𝑳' => 'L', '𝑴' => 'M', '𝑵' => 'N', '𝑶' => 'O', '𝑷' => 'P', '𝑸' => 'Q', '𝑹' => 'R', '𝑺' => 'S', '𝑻' => 'T', '𝑼' => 'U', '𝑽' => 'V', '𝑾' => 'W', '𝑿' => 'X', '𝒀' => 'Y', '𝒁' => 'Z', '𝒂' => 'a', '𝒃' => 'b', '𝒄' => 'c', '𝒅' => 'd', '𝒆' => 'e', '𝒇' => 'f', '𝒈' => 'g', '𝒉' => 'h', '𝒊' => 'i', '𝒋' => 'j', '𝒌' => 'k', '𝒍' => 'l', '𝒎' => 'm', '𝒏' => 'n', '𝒐' => 'o', '𝒑' => 'p', '𝒒' => 'q', '𝒓' => 'r', '𝒔' => 's', '𝒕' => 't', '𝒖' => 'u', '𝒗' => 'v', '𝒘' => 'w', '𝒙' => 'x', '𝒚' => 'y', '𝒛' => 'z', '𝒜' => 'A', '𝒞' => 'C', '𝒟' => 'D', '𝒢' => 'G', '𝒥' => 'J', '𝒦' => 'K', '𝒩' => 'N', '𝒪' => 'O', '𝒫' => 'P', '𝒬' => 'Q', '𝒮' => 'S', '𝒯' => 'T', '𝒰' => 'U', '𝒱' => 'V', '𝒲' => 'W', '𝒳' => 'X', '𝒴' => 'Y', '𝒵' => 'Z', '𝒶' => 'a', '𝒷' => 'b', '𝒸' => 'c', '𝒹' => 'd', '𝒻' => 'f', '𝒽' => 'h', '𝒾' => 'i', '𝒿' => 'j', '𝓀' => 'k', '𝓁' => 'l', '𝓂' => 'm', '𝓃' => 'n', '𝓅' => 'p', '𝓆' => 'q', '𝓇' => 'r', '𝓈' => 's', '𝓉' => 't', '𝓊' => 'u', '𝓋' => 'v', '𝓌' => 'w', '𝓍' => 'x', '𝓎' => 'y', '𝓏' => 'z', '𝓐' => 'A', '𝓑' => 'B', '𝓒' => 'C', '𝓓' => 'D', '𝓔' => 'E', '𝓕' => 'F', '𝓖' => 'G', '𝓗' => 'H', '𝓘' => 'I', '𝓙' => 'J', '𝓚' => 'K', '𝓛' => 'L', '𝓜' => 'M', '𝓝' => 'N', '𝓞' => 'O', '𝓟' => 'P', '𝓠' => 'Q', '𝓡' => 'R', '𝓢' => 'S', '𝓣' => 'T', '𝓤' => 'U', '𝓥' => 'V', '𝓦' => 'W', '𝓧' => 'X', '𝓨' => 'Y', '𝓩' => 'Z', '𝓪' => 'a', '𝓫' => 'b', '𝓬' => 'c', '𝓭' => 'd', '𝓮' => 'e', '𝓯' => 'f', '𝓰' => 'g', '𝓱' => 'h', '𝓲' => 'i', '𝓳' => 'j', '𝓴' => 'k', '𝓵' => 'l', '𝓶' => 'm', '𝓷' => 'n', '𝓸' => 'o', '𝓹' => 'p', '𝓺' => 'q', '𝓻' => 'r', '𝓼' => 's', '𝓽' => 't', '𝓾' => 'u', '𝓿' => 'v', '𝔀' => 'w', '𝔁' => 'x', '𝔂' => 'y', '𝔃' => 'z', '𝔄' => 'A', '𝔅' => 'B', '𝔇' => 'D', '𝔈' => 'E', '𝔉' => 'F', '𝔊' => 'G', '𝔍' => 'J', '𝔎' => 'K', '𝔏' => 'L', '𝔐' => 'M', '𝔑' => 'N', '𝔒' => 'O', '𝔓' => 'P', '𝔔' => 'Q', '𝔖' => 'S', '𝔗' => 'T', '𝔘' => 'U', '𝔙' => 'V', '𝔚' => 'W', '𝔛' => 'X', '𝔜' => 'Y', '𝔞' => 'a', '𝔟' => 'b', '𝔠' => 'c', '𝔡' => 'd', '𝔢' => 'e', '𝔣' => 'f', '𝔤' => 'g', '𝔥' => 'h', '𝔦' => 'i', '𝔧' => 'j', '𝔨' => 'k', '𝔩' => 'l', '𝔪' => 'm', '𝔫' => 'n', '𝔬' => 'o', '𝔭' => 'p', '𝔮' => 'q', '𝔯' => 'r', '𝔰' => 's', '𝔱' => 't', '𝔲' => 'u', '𝔳' => 'v', '𝔴' => 'w', '𝔵' => 'x', '𝔶' => 'y', '𝔷' => 'z', '𝔸' => 'A', '𝔹' => 'B', '𝔻' => 'D', '𝔼' => 'E', '𝔽' => 'F', '𝔾' => 'G', '𝕀' => 'I', '𝕁' => 'J', '𝕂' => 'K', '𝕃' => 'L', '𝕄' => 'M', '𝕆' => 'O', '𝕊' => 'S', '𝕋' => 'T', '𝕌' => 'U', '𝕍' => 'V', '𝕎' => 'W', '𝕏' => 'X', '𝕐' => 'Y', '𝕒' => 'a', '𝕓' => 'b', '𝕔' => 'c', '𝕕' => 'd', '𝕖' => 'e', '𝕗' => 'f', '𝕘' => 'g', '𝕙' => 'h', '𝕚' => 'i', '𝕛' => 'j', '𝕜' => 'k', '𝕝' => 'l', '𝕞' => 'm', '𝕟' => 'n', '𝕠' => 'o', '𝕡' => 'p', '𝕢' => 'q', '𝕣' => 'r', '𝕤' => 's', '𝕥' => 't', '𝕦' => 'u', '𝕧' => 'v', '𝕨' => 'w', '𝕩' => 'x', '𝕪' => 'y', '𝕫' => 'z', '𝕬' => 'A', '𝕭' => 'B', '𝕮' => 'C', '𝕯' => 'D', '𝕰' => 'E', '𝕱' => 'F', '𝕲' => 'G', '𝕳' => 'H', '𝕴' => 'I', '𝕵' => 'J', '𝕶' => 'K', '𝕷' => 'L', '𝕸' => 'M', '𝕹' => 'N', '𝕺' => 'O', '𝕻' => 'P', '𝕼' => 'Q', '𝕽' => 'R', '𝕾' => 'S', '𝕿' => 'T', '𝖀' => 'U', '𝖁' => 'V', '𝖂' => 'W', '𝖃' => 'X', '𝖄' => 'Y', '𝖅' => 'Z', '𝖆' => 'a', '𝖇' => 'b', '𝖈' => 'c', '𝖉' => 'd', '𝖊' => 'e', '𝖋' => 'f', '𝖌' => 'g', '𝖍' => 'h', '𝖎' => 'i', '𝖏' => 'j', '𝖐' => 'k', '𝖑' => 'l', '𝖒' => 'm', '𝖓' => 'n', '𝖔' => 'o', '𝖕' => 'p', '𝖖' => 'q', '𝖗' => 'r', '𝖘' => 's', '𝖙' => 't', '𝖚' => 'u', '𝖛' => 'v', '𝖜' => 'w', '𝖝' => 'x', '𝖞' => 'y', '𝖟' => 'z', '𝖠' => 'A', '𝖡' => 'B', '𝖢' => 'C', '𝖣' => 'D', '𝖤' => 'E', '𝖥' => 'F', '𝖦' => 'G', '𝖧' => 'H', '𝖨' => 'I', '𝖩' => 'J', '𝖪' => 'K', '𝖫' => 'L', '𝖬' => 'M', '𝖭' => 'N', '𝖮' => 'O', '𝖯' => 'P', '𝖰' => 'Q', '𝖱' => 'R', '𝖲' => 'S', '𝖳' => 'T', '𝖴' => 'U', '𝖵' => 'V', '𝖶' => 'W', '𝖷' => 'X', '𝖸' => 'Y', '𝖹' => 'Z', '𝖺' => 'a', '𝖻' => 'b', '𝖼' => 'c', '𝖽' => 'd', '𝖾' => 'e', '𝖿' => 'f', '𝗀' => 'g', '𝗁' => 'h', '𝗂' => 'i', '𝗃' => 'j', '𝗄' => 'k', '𝗅' => 'l', '𝗆' => 'm', '𝗇' => 'n', '𝗈' => 'o', '𝗉' => 'p', '𝗊' => 'q', '𝗋' => 'r', '𝗌' => 's', '𝗍' => 't', '𝗎' => 'u', '𝗏' => 'v', '𝗐' => 'w', '𝗑' => 'x', '𝗒' => 'y', '𝗓' => 'z', '𝗔' => 'A', '𝗕' => 'B', '𝗖' => 'C', '𝗗' => 'D', '𝗘' => 'E', '𝗙' => 'F', '𝗚' => 'G', '𝗛' => 'H', '𝗜' => 'I', '𝗝' => 'J', '𝗞' => 'K', '𝗟' => 'L', '𝗠' => 'M', '𝗡' => 'N', '𝗢' => 'O', '𝗣' => 'P', '𝗤' => 'Q', '𝗥' => 'R', '𝗦' => 'S', '𝗧' => 'T', '𝗨' => 'U', '𝗩' => 'V', '𝗪' => 'W', '𝗫' => 'X', '𝗬' => 'Y', '𝗭' => 'Z', '𝗮' => 'a', '𝗯' => 'b', '𝗰' => 'c', '𝗱' => 'd', '𝗲' => 'e', '𝗳' => 'f', '𝗴' => 'g', '𝗵' => 'h', '𝗶' => 'i', '𝗷' => 'j', '𝗸' => 'k', '𝗹' => 'l', '𝗺' => 'm', '𝗻' => 'n', '𝗼' => 'o', '𝗽' => 'p', '𝗾' => 'q', '𝗿' => 'r', '𝘀' => 's', '𝘁' => 't', '𝘂' => 'u', '𝘃' => 'v', '𝘄' => 'w', '𝘅' => 'x', '𝘆' => 'y', '𝘇' => 'z', '𝘈' => 'A', '𝘉' => 'B', '𝘊' => 'C', '𝘋' => 'D', '𝘌' => 'E', '𝘍' => 'F', '𝘎' => 'G', '𝘏' => 'H', '𝘐' => 'I', '𝘑' => 'J', '𝘒' => 'K', '𝘓' => 'L', '𝘔' => 'M', '𝘕' => 'N', '𝘖' => 'O', '𝘗' => 'P', '𝘘' => 'Q', '𝘙' => 'R', '𝘚' => 'S', '𝘛' => 'T', '𝘜' => 'U', '𝘝' => 'V', '𝘞' => 'W', '𝘟' => 'X', '𝘠' => 'Y', '𝘡' => 'Z', '𝘢' => 'a', '𝘣' => 'b', '𝘤' => 'c', '𝘥' => 'd', '𝘦' => 'e', '𝘧' => 'f', '𝘨' => 'g', '𝘩' => 'h', '𝘪' => 'i', '𝘫' => 'j', '𝘬' => 'k', '𝘭' => 'l', '𝘮' => 'm', '𝘯' => 'n', '𝘰' => 'o', '𝘱' => 'p', '𝘲' => 'q', '𝘳' => 'r', '𝘴' => 's', '𝘵' => 't', '𝘶' => 'u', '𝘷' => 'v', '𝘸' => 'w', '𝘹' => 'x', '𝘺' => 'y', '𝘻' => 'z', '𝘼' => 'A', '𝘽' => 'B', '𝘾' => 'C', '𝘿' => 'D', '𝙀' => 'E', '𝙁' => 'F', '𝙂' => 'G', '𝙃' => 'H', '𝙄' => 'I', '𝙅' => 'J', '𝙆' => 'K', '𝙇' => 'L', '𝙈' => 'M', '𝙉' => 'N', '𝙊' => 'O', '𝙋' => 'P', '𝙌' => 'Q', '𝙍' => 'R', '𝙎' => 'S', '𝙏' => 'T', '𝙐' => 'U', '𝙑' => 'V', '𝙒' => 'W', '𝙓' => 'X', '𝙔' => 'Y', '𝙕' => 'Z', '𝙖' => 'a', '𝙗' => 'b', '𝙘' => 'c', '𝙙' => 'd', '𝙚' => 'e', '𝙛' => 'f', '𝙜' => 'g', '𝙝' => 'h', '𝙞' => 'i', '𝙟' => 'j', '𝙠' => 'k', '𝙡' => 'l', '𝙢' => 'm', '𝙣' => 'n', '𝙤' => 'o', '𝙥' => 'p', '𝙦' => 'q', '𝙧' => 'r', '𝙨' => 's', '𝙩' => 't', '𝙪' => 'u', '𝙫' => 'v', '𝙬' => 'w', '𝙭' => 'x', '𝙮' => 'y', '𝙯' => 'z', '𝙰' => 'A', '𝙱' => 'B', '𝙲' => 'C', '𝙳' => 'D', '𝙴' => 'E', '𝙵' => 'F', '𝙶' => 'G', '𝙷' => 'H', '𝙸' => 'I', '𝙹' => 'J', '𝙺' => 'K', '𝙻' => 'L', '𝙼' => 'M', '𝙽' => 'N', '𝙾' => 'O', '𝙿' => 'P', '𝚀' => 'Q', '𝚁' => 'R', '𝚂' => 'S', '𝚃' => 'T', '𝚄' => 'U', '𝚅' => 'V', '𝚆' => 'W', '𝚇' => 'X', '𝚈' => 'Y', '𝚉' => 'Z', '𝚊' => 'a', '𝚋' => 'b', '𝚌' => 'c', '𝚍' => 'd', '𝚎' => 'e', '𝚏' => 'f', '𝚐' => 'g', '𝚑' => 'h', '𝚒' => 'i', '𝚓' => 'j', '𝚔' => 'k', '𝚕' => 'l', '𝚖' => 'm', '𝚗' => 'n', '𝚘' => 'o', '𝚙' => 'p', '𝚚' => 'q', '𝚛' => 'r', '𝚜' => 's', '𝚝' => 't', '𝚞' => 'u', '𝚟' => 'v', '𝚠' => 'w', '𝚡' => 'x', '𝚢' => 'y', '𝚣' => 'z', '𝚤' => 'ı', '𝚥' => 'ȷ', '𝚨' => 'Α', '𝚩' => 'Β', '𝚪' => 'Γ', '𝚫' => 'Δ', '𝚬' => 'Ε', '𝚭' => 'Ζ', '𝚮' => 'Η', '𝚯' => 'Θ', '𝚰' => 'Ι', '𝚱' => 'Κ', '𝚲' => 'Λ', '𝚳' => 'Μ', '𝚴' => 'Ν', '𝚵' => 'Ξ', '𝚶' => 'Ο', '𝚷' => 'Π', '𝚸' => 'Ρ', '𝚹' => 'Θ', '𝚺' => 'Σ', '𝚻' => 'Τ', '𝚼' => 'Υ', '𝚽' => 'Φ', '𝚾' => 'Χ', '𝚿' => 'Ψ', '𝛀' => 'Ω', '𝛁' => '∇', '𝛂' => 'α', '𝛃' => 'β', '𝛄' => 'γ', '𝛅' => 'δ', '𝛆' => 'ε', '𝛇' => 'ζ', '𝛈' => 'η', '𝛉' => 'θ', '𝛊' => 'ι', '𝛋' => 'κ', '𝛌' => 'λ', '𝛍' => 'μ', '𝛎' => 'ν', '𝛏' => 'ξ', '𝛐' => 'ο', '𝛑' => 'π', '𝛒' => 'ρ', '𝛓' => 'ς', '𝛔' => 'σ', '𝛕' => 'τ', '𝛖' => 'υ', '𝛗' => 'φ', '𝛘' => 'χ', '𝛙' => 'ψ', '𝛚' => 'ω', '𝛛' => '∂', '𝛜' => 'ε', '𝛝' => 'θ', '𝛞' => 'κ', '𝛟' => 'φ', '𝛠' => 'ρ', '𝛡' => 'π', '𝛢' => 'Α', '𝛣' => 'Β', '𝛤' => 'Γ', '𝛥' => 'Δ', '𝛦' => 'Ε', '𝛧' => 'Ζ', '𝛨' => 'Η', '𝛩' => 'Θ', '𝛪' => 'Ι', '𝛫' => 'Κ', '𝛬' => 'Λ', '𝛭' => 'Μ', '𝛮' => 'Ν', '𝛯' => 'Ξ', '𝛰' => 'Ο', '𝛱' => 'Π', '𝛲' => 'Ρ', '𝛳' => 'Θ', '𝛴' => 'Σ', '𝛵' => 'Τ', '𝛶' => 'Υ', '𝛷' => 'Φ', '𝛸' => 'Χ', '𝛹' => 'Ψ', '𝛺' => 'Ω', '𝛻' => '∇', '𝛼' => 'α', '𝛽' => 'β', '𝛾' => 'γ', '𝛿' => 'δ', '𝜀' => 'ε', '𝜁' => 'ζ', '𝜂' => 'η', '𝜃' => 'θ', '𝜄' => 'ι', '𝜅' => 'κ', '𝜆' => 'λ', '𝜇' => 'μ', '𝜈' => 'ν', '𝜉' => 'ξ', '𝜊' => 'ο', '𝜋' => 'π', '𝜌' => 'ρ', '𝜍' => 'ς', '𝜎' => 'σ', '𝜏' => 'τ', '𝜐' => 'υ', '𝜑' => 'φ', '𝜒' => 'χ', '𝜓' => 'ψ', '𝜔' => 'ω', '𝜕' => '∂', '𝜖' => 'ε', '𝜗' => 'θ', '𝜘' => 'κ', '𝜙' => 'φ', '𝜚' => 'ρ', '𝜛' => 'π', '𝜜' => 'Α', '𝜝' => 'Β', '𝜞' => 'Γ', '𝜟' => 'Δ', '𝜠' => 'Ε', '𝜡' => 'Ζ', '𝜢' => 'Η', '𝜣' => 'Θ', '𝜤' => 'Ι', '𝜥' => 'Κ', '𝜦' => 'Λ', '𝜧' => 'Μ', '𝜨' => 'Ν', '𝜩' => 'Ξ', '𝜪' => 'Ο', '𝜫' => 'Π', '𝜬' => 'Ρ', '𝜭' => 'Θ', '𝜮' => 'Σ', '𝜯' => 'Τ', '𝜰' => 'Υ', '𝜱' => 'Φ', '𝜲' => 'Χ', '𝜳' => 'Ψ', '𝜴' => 'Ω', '𝜵' => '∇', '𝜶' => 'α', '𝜷' => 'β', '𝜸' => 'γ', '𝜹' => 'δ', '𝜺' => 'ε', '𝜻' => 'ζ', '𝜼' => 'η', '𝜽' => 'θ', '𝜾' => 'ι', '𝜿' => 'κ', '𝝀' => 'λ', '𝝁' => 'μ', '𝝂' => 'ν', '𝝃' => 'ξ', '𝝄' => 'ο', '𝝅' => 'π', '𝝆' => 'ρ', '𝝇' => 'ς', '𝝈' => 'σ', '𝝉' => 'τ', '𝝊' => 'υ', '𝝋' => 'φ', '𝝌' => 'χ', '𝝍' => 'ψ', '𝝎' => 'ω', '𝝏' => '∂', '𝝐' => 'ε', '𝝑' => 'θ', '𝝒' => 'κ', '𝝓' => 'φ', '𝝔' => 'ρ', '𝝕' => 'π', '𝝖' => 'Α', '𝝗' => 'Β', '𝝘' => 'Γ', '𝝙' => 'Δ', '𝝚' => 'Ε', '𝝛' => 'Ζ', '𝝜' => 'Η', '𝝝' => 'Θ', '𝝞' => 'Ι', '𝝟' => 'Κ', '𝝠' => 'Λ', '𝝡' => 'Μ', '𝝢' => 'Ν', '𝝣' => 'Ξ', '𝝤' => 'Ο', '𝝥' => 'Π', '𝝦' => 'Ρ', '𝝧' => 'Θ', '𝝨' => 'Σ', '𝝩' => 'Τ', '𝝪' => 'Υ', '𝝫' => 'Φ', '𝝬' => 'Χ', '𝝭' => 'Ψ', '𝝮' => 'Ω', '𝝯' => '∇', '𝝰' => 'α', '𝝱' => 'β', '𝝲' => 'γ', '𝝳' => 'δ', '𝝴' => 'ε', '𝝵' => 'ζ', '𝝶' => 'η', '𝝷' => 'θ', '𝝸' => 'ι', '𝝹' => 'κ', '𝝺' => 'λ', '𝝻' => 'μ', '𝝼' => 'ν', '𝝽' => 'ξ', '𝝾' => 'ο', '𝝿' => 'π', '𝞀' => 'ρ', '𝞁' => 'ς', '𝞂' => 'σ', '𝞃' => 'τ', '𝞄' => 'υ', '𝞅' => 'φ', '𝞆' => 'χ', '𝞇' => 'ψ', '𝞈' => 'ω', '𝞉' => '∂', '𝞊' => 'ε', '𝞋' => 'θ', '𝞌' => 'κ', '𝞍' => 'φ', '𝞎' => 'ρ', '𝞏' => 'π', '𝞐' => 'Α', '𝞑' => 'Β', '𝞒' => 'Γ', '𝞓' => 'Δ', '𝞔' => 'Ε', '𝞕' => 'Ζ', '𝞖' => 'Η', '𝞗' => 'Θ', '𝞘' => 'Ι', '𝞙' => 'Κ', '𝞚' => 'Λ', '𝞛' => 'Μ', '𝞜' => 'Ν', '𝞝' => 'Ξ', '𝞞' => 'Ο', '𝞟' => 'Π', '𝞠' => 'Ρ', '𝞡' => 'Θ', '𝞢' => 'Σ', '𝞣' => 'Τ', '𝞤' => 'Υ', '𝞥' => 'Φ', '𝞦' => 'Χ', '𝞧' => 'Ψ', '𝞨' => 'Ω', '𝞩' => '∇', '𝞪' => 'α', '𝞫' => 'β', '𝞬' => 'γ', '𝞭' => 'δ', '𝞮' => 'ε', '𝞯' => 'ζ', '𝞰' => 'η', '𝞱' => 'θ', '𝞲' => 'ι', '𝞳' => 'κ', '𝞴' => 'λ', '𝞵' => 'μ', '𝞶' => 'ν', '𝞷' => 'ξ', '𝞸' => 'ο', '𝞹' => 'π', '𝞺' => 'ρ', '𝞻' => 'ς', '𝞼' => 'σ', '𝞽' => 'τ', '𝞾' => 'υ', '𝞿' => 'φ', '𝟀' => 'χ', '𝟁' => 'ψ', '𝟂' => 'ω', '𝟃' => '∂', '𝟄' => 'ε', '𝟅' => 'θ', '𝟆' => 'κ', '𝟇' => 'φ', '𝟈' => 'ρ', '𝟉' => 'π', '𝟊' => 'Ϝ', '𝟋' => 'ϝ', '𝟎' => '0', '𝟏' => '1', '𝟐' => '2', '𝟑' => '3', '𝟒' => '4', '𝟓' => '5', '𝟔' => '6', '𝟕' => '7', '𝟖' => '8', '𝟗' => '9', '𝟘' => '0', '𝟙' => '1', '𝟚' => '2', '𝟛' => '3', '𝟜' => '4', '𝟝' => '5', '𝟞' => '6', '𝟟' => '7', '𝟠' => '8', '𝟡' => '9', '𝟢' => '0', '𝟣' => '1', '𝟤' => '2', '𝟥' => '3', '𝟦' => '4', '𝟧' => '5', '𝟨' => '6', '𝟩' => '7', '𝟪' => '8', '𝟫' => '9', '𝟬' => '0', '𝟭' => '1', '𝟮' => '2', '𝟯' => '3', '𝟰' => '4', '𝟱' => '5', '𝟲' => '6', '𝟳' => '7', '𝟴' => '8', '𝟵' => '9', '𝟶' => '0', '𝟷' => '1', '𝟸' => '2', '𝟹' => '3', '𝟺' => '4', '𝟻' => '5', '𝟼' => '6', '𝟽' => '7', '𝟾' => '8', '𝟿' => '9', '𞸀' => 'ا', '𞸁' => 'ب', '𞸂' => 'ج', '𞸃' => 'د', '𞸅' => 'و', '𞸆' => 'ز', '𞸇' => 'ح', '𞸈' => 'ط', '𞸉' => 'ي', '𞸊' => 'ك', '𞸋' => 'ل', '𞸌' => 'م', '𞸍' => 'ن', '𞸎' => 'س', '𞸏' => 'ع', '𞸐' => 'ف', '𞸑' => 'ص', '𞸒' => 'ق', '𞸓' => 'ر', '𞸔' => 'ش', '𞸕' => 'ت', '𞸖' => 'ث', '𞸗' => 'خ', '𞸘' => 'ذ', '𞸙' => 'ض', '𞸚' => 'ظ', '𞸛' => 'غ', '𞸜' => 'ٮ', '𞸝' => 'ں', '𞸞' => 'ڡ', '𞸟' => 'ٯ', '𞸡' => 'ب', '𞸢' => 'ج', '𞸤' => 'ه', '𞸧' => 'ح', '𞸩' => 'ي', '𞸪' => 'ك', '𞸫' => 'ل', '𞸬' => 'م', '𞸭' => 'ن', '𞸮' => 'س', '𞸯' => 'ع', '𞸰' => 'ف', '𞸱' => 'ص', '𞸲' => 'ق', '𞸴' => 'ش', '𞸵' => 'ت', '𞸶' => 'ث', '𞸷' => 'خ', '𞸹' => 'ض', '𞸻' => 'غ', '𞹂' => 'ج', '𞹇' => 'ح', '𞹉' => 'ي', '𞹋' => 'ل', '𞹍' => 'ن', '𞹎' => 'س', '𞹏' => 'ع', '𞹑' => 'ص', '𞹒' => 'ق', '𞹔' => 'ش', '𞹗' => 'خ', '𞹙' => 'ض', '𞹛' => 'غ', '𞹝' => 'ں', '𞹟' => 'ٯ', '𞹡' => 'ب', '𞹢' => 'ج', '𞹤' => 'ه', '𞹧' => 'ح', '𞹨' => 'ط', '𞹩' => 'ي', '𞹪' => 'ك', '𞹬' => 'م', '𞹭' => 'ن', '𞹮' => 'س', '𞹯' => 'ع', '𞹰' => 'ف', '𞹱' => 'ص', '𞹲' => 'ق', '𞹴' => 'ش', '𞹵' => 'ت', '𞹶' => 'ث', '𞹷' => 'خ', '𞹹' => 'ض', '𞹺' => 'ظ', '𞹻' => 'غ', '𞹼' => 'ٮ', '𞹾' => 'ڡ', '𞺀' => 'ا', '𞺁' => 'ب', '𞺂' => 'ج', '𞺃' => 'د', '𞺄' => 'ه', '𞺅' => 'و', '𞺆' => 'ز', '𞺇' => 'ح', '𞺈' => 'ط', '𞺉' => 'ي', '𞺋' => 'ل', '𞺌' => 'م', '𞺍' => 'ن', '𞺎' => 'س', '𞺏' => 'ع', '𞺐' => 'ف', '𞺑' => 'ص', '𞺒' => 'ق', '𞺓' => 'ر', '𞺔' => 'ش', '𞺕' => 'ت', '𞺖' => 'ث', '𞺗' => 'خ', '𞺘' => 'ذ', '𞺙' => 'ض', '𞺚' => 'ظ', '𞺛' => 'غ', '𞺡' => 'ب', '𞺢' => 'ج', '𞺣' => 'د', '𞺥' => 'و', '𞺦' => 'ز', '𞺧' => 'ح', '𞺨' => 'ط', '𞺩' => 'ي', '𞺫' => 'ل', '𞺬' => 'م', '𞺭' => 'ن', '𞺮' => 'س', '𞺯' => 'ع', '𞺰' => 'ف', '𞺱' => 'ص', '𞺲' => 'ق', '𞺳' => 'ر', '𞺴' => 'ش', '𞺵' => 'ت', '𞺶' => 'ث', '𞺷' => 'خ', '𞺸' => 'ذ', '𞺹' => 'ض', '𞺺' => 'ظ', '𞺻' => 'غ', '🄀' => '0.', '🄁' => '0,', '🄂' => '1,', '🄃' => '2,', '🄄' => '3,', '🄅' => '4,', '🄆' => '5,', '🄇' => '6,', '🄈' => '7,', '🄉' => '8,', '🄊' => '9,', '🄐' => '(A)', '🄑' => '(B)', '🄒' => '(C)', '🄓' => '(D)', '🄔' => '(E)', '🄕' => '(F)', '🄖' => '(G)', '🄗' => '(H)', '🄘' => '(I)', '🄙' => '(J)', '🄚' => '(K)', '🄛' => '(L)', '🄜' => '(M)', '🄝' => '(N)', '🄞' => '(O)', '🄟' => '(P)', '🄠' => '(Q)', '🄡' => '(R)', '🄢' => '(S)', '🄣' => '(T)', '🄤' => '(U)', '🄥' => '(V)', '🄦' => '(W)', '🄧' => '(X)', '🄨' => '(Y)', '🄩' => '(Z)', '🄪' => '〔S〕', '🄫' => 'C', '🄬' => 'R', '🄭' => 'CD', '🄮' => 'WZ', '🄰' => 'A', '🄱' => 'B', '🄲' => 'C', '🄳' => 'D', '🄴' => 'E', '🄵' => 'F', '🄶' => 'G', '🄷' => 'H', '🄸' => 'I', '🄹' => 'J', '🄺' => 'K', '🄻' => 'L', '🄼' => 'M', '🄽' => 'N', '🄾' => 'O', '🄿' => 'P', '🅀' => 'Q', '🅁' => 'R', '🅂' => 'S', '🅃' => 'T', '🅄' => 'U', '🅅' => 'V', '🅆' => 'W', '🅇' => 'X', '🅈' => 'Y', '🅉' => 'Z', '🅊' => 'HV', '🅋' => 'MV', '🅌' => 'SD', '🅍' => 'SS', '🅎' => 'PPV', '🅏' => 'WC', '🅪' => 'MC', '🅫' => 'MD', '🅬' => 'MR', '🆐' => 'DJ', '🈀' => 'ほか', '🈁' => 'ココ', '🈂' => 'サ', '🈐' => '手', '🈑' => '字', '🈒' => '双', '🈓' => 'デ', '🈔' => '二', '🈕' => '多', '🈖' => '解', '🈗' => '天', '🈘' => '交', '🈙' => '映', '🈚' => '無', '🈛' => '料', '🈜' => '前', '🈝' => '後', '🈞' => '再', '🈟' => '新', '🈠' => '初', '🈡' => '終', '🈢' => '生', '🈣' => '販', '🈤' => '声', '🈥' => '吹', '🈦' => '演', '🈧' => '投', '🈨' => '捕', '🈩' => '一', '🈪' => '三', '🈫' => '遊', '🈬' => '左', '🈭' => '中', '🈮' => '右', '🈯' => '指', '🈰' => '走', '🈱' => '打', '🈲' => '禁', '🈳' => '空', '🈴' => '合', '🈵' => '満', '🈶' => '有', '🈷' => '月', '🈸' => '申', '🈹' => '割', '🈺' => '営', '🈻' => '配', '🉀' => '〔本〕', '🉁' => '〔三〕', '🉂' => '〔二〕', '🉃' => '〔安〕', '🉄' => '〔点〕', '🉅' => '〔打〕', '🉆' => '〔盗〕', '🉇' => '〔勝〕', '🉈' => '〔敗〕', '🉐' => '得', '🉑' => '可', '🯰' => '0', '🯱' => '1', '🯲' => '2', '🯳' => '3', '🯴' => '4', '🯵' => '5', '🯶' => '6', '🯷' => '7', '🯸' => '8', '🯹' => '9', ); 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Ç' => 'Ç', 'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'ç' => 'ç', 'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'ÿ' => 'ÿ', 'Ā' => 'Ā', 'ā' => 'ā', 'Ă' => 'Ă', 'ă' => 'ă', 'Ą' => 'Ą', 'ą' => 'ą', 'Ć' => 'Ć', 'ć' => 'ć', 'Ĉ' => 'Ĉ', 'ĉ' => 'ĉ', 'Ċ' => 'Ċ', 'ċ' => 'ċ', 'Č' => 'Č', 'č' => 'č', 'Ď' => 'Ď', 'ď' => 'ď', 'Ē' => 'Ē', 'ē' => 'ē', 'Ĕ' => 'Ĕ', 'ĕ' => 'ĕ', 'Ė' => 'Ė', 'ė' => 'ė', 'Ę' => 'Ę', 'ę' => 'ę', 'Ě' => 'Ě', 'ě' => 'ě', 'Ĝ' => 'Ĝ', 'ĝ' => 'ĝ', 'Ğ' => 'Ğ', 'ğ' => 'ğ', 'Ġ' => 'Ġ', 'ġ' => 'ġ', 'Ģ' => 'Ģ', 'ģ' => 'ģ', 'Ĥ' => 'Ĥ', 'ĥ' => 'ĥ', 'Ĩ' => 'Ĩ', 'ĩ' => 'ĩ', 'Ī' => 'Ī', 'ī' => 'ī', 'Ĭ' => 'Ĭ', 'ĭ' => 'ĭ', 'Į' => 'Į', 'į' => 'į', 'İ' => 'İ', 'Ĵ' => 'Ĵ', 'ĵ' => 'ĵ', 'Ķ' => 'Ķ', 'ķ' => 'ķ', 'Ĺ' => 'Ĺ', 'ĺ' => 'ĺ', 'Ļ' => 'Ļ', 'ļ' => 'ļ', 'Ľ' => 'Ľ', 'ľ' => 'ľ', 'Ń' => 'Ń', 'ń' => 'ń', 'Ņ' => 'Ņ', 'ņ' => 'ņ', 'Ň' => 'Ň', 'ň' => 'ň', 'Ō' => 'Ō', 'ō' => 'ō', 'Ŏ' => 'Ŏ', 'ŏ' => 'ŏ', 'Ő' => 'Ő', 'ő' => 'ő', 'Ŕ' => 'Ŕ', 'ŕ' => 'ŕ', 'Ŗ' => 'Ŗ', 'ŗ' => 'ŗ', 'Ř' => 'Ř', 'ř' => 'ř', 'Ś' => 'Ś', 'ś' => 'ś', 'Ŝ' => 'Ŝ', 'ŝ' => 'ŝ', 'Ş' => 'Ş', 'ş' => 'ş', 'Š' => 'Š', 'š' => 'š', 'Ţ' => 'Ţ', 'ţ' => 'ţ', 'Ť' => 'Ť', 'ť' => 'ť', 'Ũ' => 'Ũ', 'ũ' => 'ũ', 'Ū' => 'Ū', 'ū' => 'ū', 'Ŭ' => 'Ŭ', 'ŭ' => 'ŭ', 'Ů' => 'Ů', 'ů' => 'ů', 'Ű' => 'Ű', 'ű' => 'ű', 'Ų' => 'Ų', 'ų' => 'ų', 'Ŵ' => 'Ŵ', 'ŵ' => 'ŵ', 'Ŷ' => 'Ŷ', 'ŷ' => 'ŷ', 'Ÿ' => 'Ÿ', 'Ź' => 'Ź', 'ź' => 'ź', 'Ż' => 'Ż', 'ż' => 'ż', 'Ž' => 'Ž', 'ž' => 'ž', 'Ơ' => 'Ơ', 'ơ' => 'ơ', 'Ư' => 'Ư', 'ư' => 'ư', 'Ǎ' => 'Ǎ', 'ǎ' => 'ǎ', 'Ǐ' => 'Ǐ', 'ǐ' => 'ǐ', 'Ǒ' => 'Ǒ', 'ǒ' => 'ǒ', 'Ǔ' => 'Ǔ', 'ǔ' => 'ǔ', 'Ǖ' => 'Ǖ', 'ǖ' => 'ǖ', 'Ǘ' => 'Ǘ', 'ǘ' => 'ǘ', 'Ǚ' => 'Ǚ', 'ǚ' => 'ǚ', 'Ǜ' => 'Ǜ', 'ǜ' => 'ǜ', 'Ǟ' => 'Ǟ', 'ǟ' => 'ǟ', 'Ǡ' => 'Ǡ', 'ǡ' => 'ǡ', 'Ǣ' => 'Ǣ', 'ǣ' => 'ǣ', 'Ǧ' => 'Ǧ', 'ǧ' => 'ǧ', 'Ǩ' => 'Ǩ', 'ǩ' => 'ǩ', 'Ǫ' => 'Ǫ', 'ǫ' => 'ǫ', 'Ǭ' => 'Ǭ', 'ǭ' => 'ǭ', 'Ǯ' => 'Ǯ', 'ǯ' => 'ǯ', 'ǰ' => 'ǰ', 'Ǵ' => 'Ǵ', 'ǵ' => 'ǵ', 'Ǹ' => 'Ǹ', 'ǹ' => 'ǹ', 'Ǻ' => 'Ǻ', 'ǻ' => 'ǻ', 'Ǽ' => 'Ǽ', 'ǽ' => 'ǽ', 'Ǿ' => 'Ǿ', 'ǿ' => 'ǿ', 'Ȁ' => 'Ȁ', 'ȁ' => 'ȁ', 'Ȃ' => 'Ȃ', 'ȃ' => 'ȃ', 'Ȅ' => 'Ȅ', 'ȅ' => 'ȅ', 'Ȇ' => 'Ȇ', 'ȇ' => 'ȇ', 'Ȉ' => 'Ȉ', 'ȉ' => 'ȉ', 'Ȋ' => 'Ȋ', 'ȋ' => 'ȋ', 'Ȍ' => 'Ȍ', 'ȍ' => 'ȍ', 'Ȏ' => 'Ȏ', 'ȏ' => 'ȏ', 'Ȑ' => 'Ȑ', 'ȑ' => 'ȑ', 'Ȓ' => 'Ȓ', 'ȓ' => 'ȓ', 'Ȕ' => 'Ȕ', 'ȕ' => 'ȕ', 'Ȗ' => 'Ȗ', 'ȗ' => 'ȗ', 'Ș' => 'Ș', 'ș' => 'ș', 'Ț' => 'Ț', 'ț' => 'ț', 'Ȟ' => 'Ȟ', 'ȟ' => 'ȟ', 'Ȧ' => 'Ȧ', 'ȧ' => 'ȧ', 'Ȩ' => 'Ȩ', 'ȩ' => 'ȩ', 'Ȫ' => 'Ȫ', 'ȫ' => 'ȫ', 'Ȭ' => 'Ȭ', 'ȭ' => 'ȭ', 'Ȯ' => 'Ȯ', 'ȯ' => 'ȯ', 'Ȱ' => 'Ȱ', 'ȱ' => 'ȱ', 'Ȳ' => 'Ȳ', 'ȳ' => 'ȳ', '΅' => '΅', 'Ά' => 'Ά', 'Έ' => 'Έ', 'Ή' => 'Ή', 'Ί' => 'Ί', 'Ό' => 'Ό', 'Ύ' => 'Ύ', 'Ώ' => 'Ώ', 'ΐ' => 'ΐ', 'Ϊ' => 'Ϊ', 'Ϋ' => 'Ϋ', 'ά' => 'ά', 'έ' => 'έ', 'ή' => 'ή', 'ί' => 'ί', 'ΰ' => 'ΰ', 'ϊ' => 'ϊ', 'ϋ' => 'ϋ', 'ό' => 'ό', 'ύ' => 'ύ', 'ώ' => 'ώ', 'ϓ' => 'ϓ', 'ϔ' => 'ϔ', 'Ѐ' => 'Ѐ', 'Ё' => 'Ё', 'Ѓ' => 'Ѓ', 'Ї' => 'Ї', 'Ќ' => 'Ќ', 'Ѝ' => 'Ѝ', 'Ў' => 'Ў', 'Й' => 'Й', 'й' => 'й', 'ѐ' => 'ѐ', 'ё' => 'ё', 'ѓ' => 'ѓ', 'ї' => 'ї', 'ќ' => 'ќ', 'ѝ' => 'ѝ', 'ў' => 'ў', 'Ѷ' => 'Ѷ', 'ѷ' => 'ѷ', 'Ӂ' => 'Ӂ', 'ӂ' => 'ӂ', 'Ӑ' => 'Ӑ', 'ӑ' => 'ӑ', 'Ӓ' => 'Ӓ', 'ӓ' => 'ӓ', 'Ӗ' => 'Ӗ', 'ӗ' => 'ӗ', 'Ӛ' => 'Ӛ', 'ӛ' => 'ӛ', 'Ӝ' => 'Ӝ', 'ӝ' => 'ӝ', 'Ӟ' => 'Ӟ', 'ӟ' => 'ӟ', 'Ӣ' => 'Ӣ', 'ӣ' => 'ӣ', 'Ӥ' => 'Ӥ', 'ӥ' => 'ӥ', 'Ӧ' => 'Ӧ', 'ӧ' => 'ӧ', 'Ӫ' => 'Ӫ', 'ӫ' => 'ӫ', 'Ӭ' => 'Ӭ', 'ӭ' => 'ӭ', 'Ӯ' => 'Ӯ', 'ӯ' => 'ӯ', 'Ӱ' => 'Ӱ', 'ӱ' => 'ӱ', 'Ӳ' => 'Ӳ', 'ӳ' => 'ӳ', 'Ӵ' => 'Ӵ', 'ӵ' => 'ӵ', 'Ӹ' => 'Ӹ', 'ӹ' => 'ӹ', 'آ' => 'آ', 'أ' => 'أ', 'ؤ' => 'ؤ', 'إ' => 'إ', 'ئ' => 'ئ', 'ۀ' => 'ۀ', 'ۂ' => 'ۂ', 'ۓ' => 'ۓ', 'ऩ' => 'ऩ', 'ऱ' => 'ऱ', 'ऴ' => 'ऴ', 'ো' => 'ো', 'ৌ' => 'ৌ', 'ୈ' => 'ୈ', 'ୋ' => 'ୋ', 'ୌ' => 'ୌ', 'ஔ' => 'ஔ', 'ொ' => 'ொ', 'ோ' => 'ோ', 'ௌ' => 'ௌ', 'ై' => 'ై', 'ೀ' => 'ೀ', 'ೇ' => 'ೇ', 'ೈ' => 'ೈ', 'ೊ' => 'ೊ', 'ೋ' => 'ೋ', 'ൊ' => 'ൊ', 'ോ' => 'ോ', 'ൌ' => 'ൌ', 'ේ' => 'ේ', 'ො' => 'ො', 'ෝ' => 'ෝ', 'ෞ' => 'ෞ', 'ဦ' => 'ဦ', 'ᬆ' => 'ᬆ', 'ᬈ' => 'ᬈ', 'ᬊ' => 'ᬊ', 'ᬌ' => 'ᬌ', 'ᬎ' => 'ᬎ', 'ᬒ' => 'ᬒ', 'ᬻ' => 'ᬻ', 'ᬽ' => 'ᬽ', 'ᭀ' => 'ᭀ', 'ᭁ' => 'ᭁ', 'ᭃ' => 'ᭃ', 'Ḁ' => 'Ḁ', 'ḁ' => 'ḁ', 'Ḃ' => 'Ḃ', 'ḃ' => 'ḃ', 'Ḅ' => 'Ḅ', 'ḅ' => 'ḅ', 'Ḇ' => 'Ḇ', 'ḇ' => 'ḇ', 'Ḉ' => 'Ḉ', 'ḉ' => 'ḉ', 'Ḋ' => 'Ḋ', 'ḋ' => 'ḋ', 'Ḍ' => 'Ḍ', 'ḍ' => 'ḍ', 'Ḏ' => 'Ḏ', 'ḏ' => 'ḏ', 'Ḑ' => 'Ḑ', 'ḑ' => 'ḑ', 'Ḓ' => 'Ḓ', 'ḓ' => 'ḓ', 'Ḕ' => 'Ḕ', 'ḕ' => 'ḕ', 'Ḗ' => 'Ḗ', 'ḗ' => 'ḗ', 'Ḙ' => 'Ḙ', 'ḙ' => 'ḙ', 'Ḛ' => 'Ḛ', 'ḛ' => 'ḛ', 'Ḝ' => 'Ḝ', 'ḝ' => 'ḝ', 'Ḟ' => 'Ḟ', 'ḟ' => 'ḟ', 'Ḡ' => 'Ḡ', 'ḡ' => 'ḡ', 'Ḣ' => 'Ḣ', 'ḣ' => 'ḣ', 'Ḥ' => 'Ḥ', 'ḥ' => 'ḥ', 'Ḧ' => 'Ḧ', 'ḧ' => 'ḧ', 'Ḩ' => 'Ḩ', 'ḩ' => 'ḩ', 'Ḫ' => 'Ḫ', 'ḫ' => 'ḫ', 'Ḭ' => 'Ḭ', 'ḭ' => 'ḭ', 'Ḯ' => 'Ḯ', 'ḯ' => 'ḯ', 'Ḱ' => 'Ḱ', 'ḱ' => 'ḱ', 'Ḳ' => 'Ḳ', 'ḳ' => 'ḳ', 'Ḵ' => 'Ḵ', 'ḵ' => 'ḵ', 'Ḷ' => 'Ḷ', 'ḷ' => 'ḷ', 'Ḹ' => 'Ḹ', 'ḹ' => 'ḹ', 'Ḻ' => 'Ḻ', 'ḻ' => 'ḻ', 'Ḽ' => 'Ḽ', 'ḽ' => 'ḽ', 'Ḿ' => 'Ḿ', 'ḿ' => 'ḿ', 'Ṁ' => 'Ṁ', 'ṁ' => 'ṁ', 'Ṃ' => 'Ṃ', 'ṃ' => 'ṃ', 'Ṅ' => 'Ṅ', 'ṅ' => 'ṅ', 'Ṇ' => 'Ṇ', 'ṇ' => 'ṇ', 'Ṉ' => 'Ṉ', 'ṉ' => 'ṉ', 'Ṋ' => 'Ṋ', 'ṋ' => 'ṋ', 'Ṍ' => 'Ṍ', 'ṍ' => 'ṍ', 'Ṏ' => 'Ṏ', 'ṏ' => 'ṏ', 'Ṑ' => 'Ṑ', 'ṑ' => 'ṑ', 'Ṓ' => 'Ṓ', 'ṓ' => 'ṓ', 'Ṕ' => 'Ṕ', 'ṕ' => 'ṕ', 'Ṗ' => 'Ṗ', 'ṗ' => 'ṗ', 'Ṙ' => 'Ṙ', 'ṙ' => 'ṙ', 'Ṛ' => 'Ṛ', 'ṛ' => 'ṛ', 'Ṝ' => 'Ṝ', 'ṝ' => 'ṝ', 'Ṟ' => 'Ṟ', 'ṟ' => 'ṟ', 'Ṡ' => 'Ṡ', 'ṡ' => 'ṡ', 'Ṣ' => 'Ṣ', 'ṣ' => 'ṣ', 'Ṥ' => 'Ṥ', 'ṥ' => 'ṥ', 'Ṧ' => 'Ṧ', 'ṧ' => 'ṧ', 'Ṩ' => 'Ṩ', 'ṩ' => 'ṩ', 'Ṫ' => 'Ṫ', 'ṫ' => 'ṫ', 'Ṭ' => 'Ṭ', 'ṭ' => 'ṭ', 'Ṯ' => 'Ṯ', 'ṯ' => 'ṯ', 'Ṱ' => 'Ṱ', 'ṱ' => 'ṱ', 'Ṳ' => 'Ṳ', 'ṳ' => 'ṳ', 'Ṵ' => 'Ṵ', 'ṵ' => 'ṵ', 'Ṷ' => 'Ṷ', 'ṷ' => 'ṷ', 'Ṹ' => 'Ṹ', 'ṹ' => 'ṹ', 'Ṻ' => 'Ṻ', 'ṻ' => 'ṻ', 'Ṽ' => 'Ṽ', 'ṽ' => 'ṽ', 'Ṿ' => 'Ṿ', 'ṿ' => 'ṿ', 'Ẁ' => 'Ẁ', 'ẁ' => 'ẁ', 'Ẃ' => 'Ẃ', 'ẃ' => 'ẃ', 'Ẅ' => 'Ẅ', 'ẅ' => 'ẅ', 'Ẇ' => 'Ẇ', 'ẇ' => 'ẇ', 'Ẉ' => 'Ẉ', 'ẉ' => 'ẉ', 'Ẋ' => 'Ẋ', 'ẋ' => 'ẋ', 'Ẍ' => 'Ẍ', 'ẍ' => 'ẍ', 'Ẏ' => 'Ẏ', 'ẏ' => 'ẏ', 'Ẑ' => 'Ẑ', 'ẑ' => 'ẑ', 'Ẓ' => 'Ẓ', 'ẓ' => 'ẓ', 'Ẕ' => 'Ẕ', 'ẕ' => 'ẕ', 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'ẘ', 'ẙ' => 'ẙ', 'ẛ' => 'ẛ', 'Ạ' => 'Ạ', 'ạ' => 'ạ', 'Ả' => 'Ả', 'ả' => 'ả', 'Ấ' => 'Ấ', 'ấ' => 'ấ', 'Ầ' => 'Ầ', 'ầ' => 'ầ', 'Ẩ' => 'Ẩ', 'ẩ' => 'ẩ', 'Ẫ' => 'Ẫ', 'ẫ' => 'ẫ', 'Ậ' => 'Ậ', 'ậ' => 'ậ', 'Ắ' => 'Ắ', 'ắ' => 'ắ', 'Ằ' => 'Ằ', 'ằ' => 'ằ', 'Ẳ' => 'Ẳ', 'ẳ' => 'ẳ', 'Ẵ' => 'Ẵ', 'ẵ' => 'ẵ', 'Ặ' => 'Ặ', 'ặ' => 'ặ', 'Ẹ' => 'Ẹ', 'ẹ' => 'ẹ', 'Ẻ' => 'Ẻ', 'ẻ' => 'ẻ', 'Ẽ' => 'Ẽ', 'ẽ' => 'ẽ', 'Ế' => 'Ế', 'ế' => 'ế', 'Ề' => 'Ề', 'ề' => 'ề', 'Ể' => 'Ể', 'ể' => 'ể', 'Ễ' => 'Ễ', 'ễ' => 'ễ', 'Ệ' => 'Ệ', 'ệ' => 'ệ', 'Ỉ' => 'Ỉ', 'ỉ' => 'ỉ', 'Ị' => 'Ị', 'ị' => 'ị', 'Ọ' => 'Ọ', 'ọ' => 'ọ', 'Ỏ' => 'Ỏ', 'ỏ' => 'ỏ', 'Ố' => 'Ố', 'ố' => 'ố', 'Ồ' => 'Ồ', 'ồ' => 'ồ', 'Ổ' => 'Ổ', 'ổ' => 'ổ', 'Ỗ' => 'Ỗ', 'ỗ' => 'ỗ', 'Ộ' => 'Ộ', 'ộ' => 'ộ', 'Ớ' => 'Ớ', 'ớ' => 'ớ', 'Ờ' => 'Ờ', 'ờ' => 'ờ', 'Ở' => 'Ở', 'ở' => 'ở', 'Ỡ' => 'Ỡ', 'ỡ' => 'ỡ', 'Ợ' => 'Ợ', 'ợ' => 'ợ', 'Ụ' => 'Ụ', 'ụ' => 'ụ', 'Ủ' => 'Ủ', 'ủ' => 'ủ', 'Ứ' => 'Ứ', 'ứ' => 'ứ', 'Ừ' => 'Ừ', 'ừ' => 'ừ', 'Ử' => 'Ử', 'ử' => 'ử', 'Ữ' => 'Ữ', 'ữ' => 'ữ', 'Ự' => 'Ự', 'ự' => 'ự', 'Ỳ' => 'Ỳ', 'ỳ' => 'ỳ', 'Ỵ' => 'Ỵ', 'ỵ' => 'ỵ', 'Ỷ' => 'Ỷ', 'ỷ' => 'ỷ', 'Ỹ' => 'Ỹ', 'ỹ' => 'ỹ', 'ἀ' => 'ἀ', 'ἁ' => 'ἁ', 'ἂ' => 'ἂ', 'ἃ' => 'ἃ', 'ἄ' => 'ἄ', 'ἅ' => 'ἅ', 'ἆ' => 'ἆ', 'ἇ' => 'ἇ', 'Ἀ' => 'Ἀ', 'Ἁ' => 'Ἁ', 'Ἂ' => 'Ἂ', 'Ἃ' => 'Ἃ', 'Ἄ' => 'Ἄ', 'Ἅ' => 'Ἅ', 'Ἆ' => 'Ἆ', 'Ἇ' => 'Ἇ', 'ἐ' => 'ἐ', 'ἑ' => 'ἑ', 'ἒ' => 'ἒ', 'ἓ' => 'ἓ', 'ἔ' => 'ἔ', 'ἕ' => 'ἕ', 'Ἐ' => 'Ἐ', 'Ἑ' => 'Ἑ', 'Ἒ' => 'Ἒ', 'Ἓ' => 'Ἓ', 'Ἔ' => 'Ἔ', 'Ἕ' => 'Ἕ', 'ἠ' => 'ἠ', 'ἡ' => 'ἡ', 'ἢ' => 'ἢ', 'ἣ' => 'ἣ', 'ἤ' => 'ἤ', 'ἥ' => 'ἥ', 'ἦ' => 'ἦ', 'ἧ' => 'ἧ', 'Ἠ' => 'Ἠ', 'Ἡ' => 'Ἡ', 'Ἢ' => 'Ἢ', 'Ἣ' => 'Ἣ', 'Ἤ' => 'Ἤ', 'Ἥ' => 'Ἥ', 'Ἦ' => 'Ἦ', 'Ἧ' => 'Ἧ', 'ἰ' => 'ἰ', 'ἱ' => 'ἱ', 'ἲ' => 'ἲ', 'ἳ' => 'ἳ', 'ἴ' => 'ἴ', 'ἵ' => 'ἵ', 'ἶ' => 'ἶ', 'ἷ' => 'ἷ', 'Ἰ' => 'Ἰ', 'Ἱ' => 'Ἱ', 'Ἲ' => 'Ἲ', 'Ἳ' => 'Ἳ', 'Ἴ' => 'Ἴ', 'Ἵ' => 'Ἵ', 'Ἶ' => 'Ἶ', 'Ἷ' => 'Ἷ', 'ὀ' => 'ὀ', 'ὁ' => 'ὁ', 'ὂ' => 'ὂ', 'ὃ' => 'ὃ', 'ὄ' => 'ὄ', 'ὅ' => 'ὅ', 'Ὀ' => 'Ὀ', 'Ὁ' => 'Ὁ', 'Ὂ' => 'Ὂ', 'Ὃ' => 'Ὃ', 'Ὄ' => 'Ὄ', 'Ὅ' => 'Ὅ', 'ὐ' => 'ὐ', 'ὑ' => 'ὑ', 'ὒ' => 'ὒ', 'ὓ' => 'ὓ', 'ὔ' => 'ὔ', 'ὕ' => 'ὕ', 'ὖ' => 'ὖ', 'ὗ' => 'ὗ', 'Ὑ' => 'Ὑ', 'Ὓ' => 'Ὓ', 'Ὕ' => 'Ὕ', 'Ὗ' => 'Ὗ', 'ὠ' => 'ὠ', 'ὡ' => 'ὡ', 'ὢ' => 'ὢ', 'ὣ' => 'ὣ', 'ὤ' => 'ὤ', 'ὥ' => 'ὥ', 'ὦ' => 'ὦ', 'ὧ' => 'ὧ', 'Ὠ' => 'Ὠ', 'Ὡ' => 'Ὡ', 'Ὢ' => 'Ὢ', 'Ὣ' => 'Ὣ', 'Ὤ' => 'Ὤ', 'Ὥ' => 'Ὥ', 'Ὦ' => 'Ὦ', 'Ὧ' => 'Ὧ', 'ὰ' => 'ὰ', 'ὲ' => 'ὲ', 'ὴ' => 'ὴ', 'ὶ' => 'ὶ', 'ὸ' => 'ὸ', 'ὺ' => 'ὺ', 'ὼ' => 'ὼ', 'ᾀ' => 'ᾀ', 'ᾁ' => 'ᾁ', 'ᾂ' => 'ᾂ', 'ᾃ' => 'ᾃ', 'ᾄ' => 'ᾄ', 'ᾅ' => 'ᾅ', 'ᾆ' => 'ᾆ', 'ᾇ' => 'ᾇ', 'ᾈ' => 'ᾈ', 'ᾉ' => 'ᾉ', 'ᾊ' => 'ᾊ', 'ᾋ' => 'ᾋ', 'ᾌ' => 'ᾌ', 'ᾍ' => 'ᾍ', 'ᾎ' => 'ᾎ', 'ᾏ' => 'ᾏ', 'ᾐ' => 'ᾐ', 'ᾑ' => 'ᾑ', 'ᾒ' => 'ᾒ', 'ᾓ' => 'ᾓ', 'ᾔ' => 'ᾔ', 'ᾕ' => 'ᾕ', 'ᾖ' => 'ᾖ', 'ᾗ' => 'ᾗ', 'ᾘ' => 'ᾘ', 'ᾙ' => 'ᾙ', 'ᾚ' => 'ᾚ', 'ᾛ' => 'ᾛ', 'ᾜ' => 'ᾜ', 'ᾝ' => 'ᾝ', 'ᾞ' => 'ᾞ', 'ᾟ' => 'ᾟ', 'ᾠ' => 'ᾠ', 'ᾡ' => 'ᾡ', 'ᾢ' => 'ᾢ', 'ᾣ' => 'ᾣ', 'ᾤ' => 'ᾤ', 'ᾥ' => 'ᾥ', 'ᾦ' => 'ᾦ', 'ᾧ' => 'ᾧ', 'ᾨ' => 'ᾨ', 'ᾩ' => 'ᾩ', 'ᾪ' => 'ᾪ', 'ᾫ' => 'ᾫ', 'ᾬ' => 'ᾬ', 'ᾭ' => 'ᾭ', 'ᾮ' => 'ᾮ', 'ᾯ' => 'ᾯ', 'ᾰ' => 'ᾰ', 'ᾱ' => 'ᾱ', 'ᾲ' => 'ᾲ', 'ᾳ' => 'ᾳ', 'ᾴ' => 'ᾴ', 'ᾶ' => 'ᾶ', 'ᾷ' => 'ᾷ', 'Ᾰ' => 'Ᾰ', 'Ᾱ' => 'Ᾱ', 'Ὰ' => 'Ὰ', 'ᾼ' => 'ᾼ', '῁' => '῁', 'ῂ' => 'ῂ', 'ῃ' => 'ῃ', 'ῄ' => 'ῄ', 'ῆ' => 'ῆ', 'ῇ' => 'ῇ', 'Ὲ' => 'Ὲ', 'Ὴ' => 'Ὴ', 'ῌ' => 'ῌ', '῍' => '῍', '῎' => '῎', '῏' => '῏', 'ῐ' => 'ῐ', 'ῑ' => 'ῑ', 'ῒ' => 'ῒ', 'ῖ' => 'ῖ', 'ῗ' => 'ῗ', 'Ῐ' => 'Ῐ', 'Ῑ' => 'Ῑ', 'Ὶ' => 'Ὶ', '῝' => '῝', '῞' => '῞', '῟' => '῟', 'ῠ' => 'ῠ', 'ῡ' => 'ῡ', 'ῢ' => 'ῢ', 'ῤ' => 'ῤ', 'ῥ' => 'ῥ', 'ῦ' => 'ῦ', 'ῧ' => 'ῧ', 'Ῠ' => 'Ῠ', 'Ῡ' => 'Ῡ', 'Ὺ' => 'Ὺ', 'Ῥ' => 'Ῥ', '῭' => '῭', 'ῲ' => 'ῲ', 'ῳ' => 'ῳ', 'ῴ' => 'ῴ', 'ῶ' => 'ῶ', 'ῷ' => 'ῷ', 'Ὸ' => 'Ὸ', 'Ὼ' => 'Ὼ', 'ῼ' => 'ῼ', '↚' => '↚', '↛' => '↛', '↮' => '↮', '⇍' => '⇍', '⇎' => '⇎', '⇏' => '⇏', '∄' => '∄', '∉' => '∉', '∌' => '∌', '∤' => '∤', '∦' => '∦', '≁' => '≁', '≄' => '≄', '≇' => '≇', '≉' => '≉', '≠' => '≠', '≢' => '≢', '≭' => '≭', '≮' => '≮', '≯' => '≯', '≰' => '≰', '≱' => '≱', '≴' => '≴', '≵' => '≵', '≸' => '≸', '≹' => '≹', '⊀' => '⊀', '⊁' => '⊁', '⊄' => '⊄', '⊅' => '⊅', '⊈' => '⊈', '⊉' => '⊉', '⊬' => '⊬', '⊭' => '⊭', '⊮' => '⊮', '⊯' => '⊯', '⋠' => '⋠', '⋡' => '⋡', '⋢' => '⋢', '⋣' => '⋣', '⋪' => '⋪', '⋫' => '⋫', '⋬' => '⋬', '⋭' => '⋭', 'が' => 'が', 'ぎ' => 'ぎ', 'ぐ' => 'ぐ', 'げ' => 'げ', 'ご' => 'ご', 'ざ' => 'ざ', 'じ' => 'じ', 'ず' => 'ず', 'ぜ' => 'ぜ', 'ぞ' => 'ぞ', 'だ' => 'だ', 'ぢ' => 'ぢ', 'づ' => 'づ', 'で' => 'で', 'ど' => 'ど', 'ば' => 'ば', 'ぱ' => 'ぱ', 'び' => 'び', 'ぴ' => 'ぴ', 'ぶ' => 'ぶ', 'ぷ' => 'ぷ', 'べ' => 'べ', 'ぺ' => 'ぺ', 'ぼ' => 'ぼ', 'ぽ' => 'ぽ', 'ゔ' => 'ゔ', 'ゞ' => 'ゞ', 'ガ' => 'ガ', 'ギ' => 'ギ', 'グ' => 'グ', 'ゲ' => 'ゲ', 'ゴ' => 'ゴ', 'ザ' => 'ザ', 'ジ' => 'ジ', 'ズ' => 'ズ', 'ゼ' => 'ゼ', 'ゾ' => 'ゾ', 'ダ' => 'ダ', 'ヂ' => 'ヂ', 'ヅ' => 'ヅ', 'デ' => 'デ', 'ド' => 'ド', 'バ' => 'バ', 'パ' => 'パ', 'ビ' => 'ビ', 'ピ' => 'ピ', 'ブ' => 'ブ', 'プ' => 'プ', 'ベ' => 'ベ', 'ペ' => 'ペ', 'ボ' => 'ボ', 'ポ' => 'ポ', 'ヴ' => 'ヴ', 'ヷ' => 'ヷ', 'ヸ' => 'ヸ', 'ヹ' => 'ヹ', 'ヺ' => 'ヺ', 'ヾ' => 'ヾ', '𑂚' => '𑂚', '𑂜' => '𑂜', '𑂫' => '𑂫', '𑄮' => '𑄮', '𑄯' => '𑄯', '𑍋' => '𑍋', '𑍌' => '𑍌', '𑒻' => '𑒻', '𑒼' => '𑒼', '𑒾' => '𑒾', '𑖺' => '𑖺', '𑖻' => '𑖻', '𑤸' => '𑤸', ); 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Ç' => 'Ç', 'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'ç' => 'ç', 'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'ÿ' => 'ÿ', 'Ā' => 'Ā', 'ā' => 'ā', 'Ă' => 'Ă', 'ă' => 'ă', 'Ą' => 'Ą', 'ą' => 'ą', 'Ć' => 'Ć', 'ć' => 'ć', 'Ĉ' => 'Ĉ', 'ĉ' => 'ĉ', 'Ċ' => 'Ċ', 'ċ' => 'ċ', 'Č' => 'Č', 'č' => 'č', 'Ď' => 'Ď', 'ď' => 'ď', 'Ē' => 'Ē', 'ē' => 'ē', 'Ĕ' => 'Ĕ', 'ĕ' => 'ĕ', 'Ė' => 'Ė', 'ė' => 'ė', 'Ę' => 'Ę', 'ę' => 'ę', 'Ě' => 'Ě', 'ě' => 'ě', 'Ĝ' => 'Ĝ', 'ĝ' => 'ĝ', 'Ğ' => 'Ğ', 'ğ' => 'ğ', 'Ġ' => 'Ġ', 'ġ' => 'ġ', 'Ģ' => 'Ģ', 'ģ' => 'ģ', 'Ĥ' => 'Ĥ', 'ĥ' => 'ĥ', 'Ĩ' => 'Ĩ', 'ĩ' => 'ĩ', 'Ī' => 'Ī', 'ī' => 'ī', 'Ĭ' => 'Ĭ', 'ĭ' => 'ĭ', 'Į' => 'Į', 'į' => 'į', 'İ' => 'İ', 'Ĵ' => 'Ĵ', 'ĵ' => 'ĵ', 'Ķ' => 'Ķ', 'ķ' => 'ķ', 'Ĺ' => 'Ĺ', 'ĺ' => 'ĺ', 'Ļ' => 'Ļ', 'ļ' => 'ļ', 'Ľ' => 'Ľ', 'ľ' => 'ľ', 'Ń' => 'Ń', 'ń' => 'ń', 'Ņ' => 'Ņ', 'ņ' => 'ņ', 'Ň' => 'Ň', 'ň' => 'ň', 'Ō' => 'Ō', 'ō' => 'ō', 'Ŏ' => 'Ŏ', 'ŏ' => 'ŏ', 'Ő' => 'Ő', 'ő' => 'ő', 'Ŕ' => 'Ŕ', 'ŕ' => 'ŕ', 'Ŗ' => 'Ŗ', 'ŗ' => 'ŗ', 'Ř' => 'Ř', 'ř' => 'ř', 'Ś' => 'Ś', 'ś' => 'ś', 'Ŝ' => 'Ŝ', 'ŝ' => 'ŝ', 'Ş' => 'Ş', 'ş' => 'ş', 'Š' => 'Š', 'š' => 'š', 'Ţ' => 'Ţ', 'ţ' => 'ţ', 'Ť' => 'Ť', 'ť' => 'ť', 'Ũ' => 'Ũ', 'ũ' => 'ũ', 'Ū' => 'Ū', 'ū' => 'ū', 'Ŭ' => 'Ŭ', 'ŭ' => 'ŭ', 'Ů' => 'Ů', 'ů' => 'ů', 'Ű' => 'Ű', 'ű' => 'ű', 'Ų' => 'Ų', 'ų' => 'ų', 'Ŵ' => 'Ŵ', 'ŵ' => 'ŵ', 'Ŷ' => 'Ŷ', 'ŷ' => 'ŷ', 'Ÿ' => 'Ÿ', 'Ź' => 'Ź', 'ź' => 'ź', 'Ż' => 'Ż', 'ż' => 'ż', 'Ž' => 'Ž', 'ž' => 'ž', 'Ơ' => 'Ơ', 'ơ' => 'ơ', 'Ư' => 'Ư', 'ư' => 'ư', 'Ǎ' => 'Ǎ', 'ǎ' => 'ǎ', 'Ǐ' => 'Ǐ', 'ǐ' => 'ǐ', 'Ǒ' => 'Ǒ', 'ǒ' => 'ǒ', 'Ǔ' => 'Ǔ', 'ǔ' => 'ǔ', 'Ǖ' => 'Ǖ', 'ǖ' => 'ǖ', 'Ǘ' => 'Ǘ', 'ǘ' => 'ǘ', 'Ǚ' => 'Ǚ', 'ǚ' => 'ǚ', 'Ǜ' => 'Ǜ', 'ǜ' => 'ǜ', 'Ǟ' => 'Ǟ', 'ǟ' => 'ǟ', 'Ǡ' => 'Ǡ', 'ǡ' => 'ǡ', 'Ǣ' => 'Ǣ', 'ǣ' => 'ǣ', 'Ǧ' => 'Ǧ', 'ǧ' => 'ǧ', 'Ǩ' => 'Ǩ', 'ǩ' => 'ǩ', 'Ǫ' => 'Ǫ', 'ǫ' => 'ǫ', 'Ǭ' => 'Ǭ', 'ǭ' => 'ǭ', 'Ǯ' => 'Ǯ', 'ǯ' => 'ǯ', 'ǰ' => 'ǰ', 'Ǵ' => 'Ǵ', 'ǵ' => 'ǵ', 'Ǹ' => 'Ǹ', 'ǹ' => 'ǹ', 'Ǻ' => 'Ǻ', 'ǻ' => 'ǻ', 'Ǽ' => 'Ǽ', 'ǽ' => 'ǽ', 'Ǿ' => 'Ǿ', 'ǿ' => 'ǿ', 'Ȁ' => 'Ȁ', 'ȁ' => 'ȁ', 'Ȃ' => 'Ȃ', 'ȃ' => 'ȃ', 'Ȅ' => 'Ȅ', 'ȅ' => 'ȅ', 'Ȇ' => 'Ȇ', 'ȇ' => 'ȇ', 'Ȉ' => 'Ȉ', 'ȉ' => 'ȉ', 'Ȋ' => 'Ȋ', 'ȋ' => 'ȋ', 'Ȍ' => 'Ȍ', 'ȍ' => 'ȍ', 'Ȏ' => 'Ȏ', 'ȏ' => 'ȏ', 'Ȑ' => 'Ȑ', 'ȑ' => 'ȑ', 'Ȓ' => 'Ȓ', 'ȓ' => 'ȓ', 'Ȕ' => 'Ȕ', 'ȕ' => 'ȕ', 'Ȗ' => 'Ȗ', 'ȗ' => 'ȗ', 'Ș' => 'Ș', 'ș' => 'ș', 'Ț' => 'Ț', 'ț' => 'ț', 'Ȟ' => 'Ȟ', 'ȟ' => 'ȟ', 'Ȧ' => 'Ȧ', 'ȧ' => 'ȧ', 'Ȩ' => 'Ȩ', 'ȩ' => 'ȩ', 'Ȫ' => 'Ȫ', 'ȫ' => 'ȫ', 'Ȭ' => 'Ȭ', 'ȭ' => 'ȭ', 'Ȯ' => 'Ȯ', 'ȯ' => 'ȯ', 'Ȱ' => 'Ȱ', 'ȱ' => 'ȱ', 'Ȳ' => 'Ȳ', 'ȳ' => 'ȳ', '̀' => '̀', '́' => '́', '̓' => '̓', '̈́' => '̈́', 'ʹ' => 'ʹ', ';' => ';', '΅' => '΅', 'Ά' => 'Ά', '·' => '·', 'Έ' => 'Έ', 'Ή' => 'Ή', 'Ί' => 'Ί', 'Ό' => 'Ό', 'Ύ' => 'Ύ', 'Ώ' => 'Ώ', 'ΐ' => 'ΐ', 'Ϊ' => 'Ϊ', 'Ϋ' => 'Ϋ', 'ά' => 'ά', 'έ' => 'έ', 'ή' => 'ή', 'ί' => 'ί', 'ΰ' => 'ΰ', 'ϊ' => 'ϊ', 'ϋ' => 'ϋ', 'ό' => 'ό', 'ύ' => 'ύ', 'ώ' => 'ώ', 'ϓ' => 'ϓ', 'ϔ' => 'ϔ', 'Ѐ' => 'Ѐ', 'Ё' => 'Ё', 'Ѓ' => 'Ѓ', 'Ї' => 'Ї', 'Ќ' => 'Ќ', 'Ѝ' => 'Ѝ', 'Ў' => 'Ў', 'Й' => 'Й', 'й' => 'й', 'ѐ' => 'ѐ', 'ё' => 'ё', 'ѓ' => 'ѓ', 'ї' => 'ї', 'ќ' => 'ќ', 'ѝ' => 'ѝ', 'ў' => 'ў', 'Ѷ' => 'Ѷ', 'ѷ' => 'ѷ', 'Ӂ' => 'Ӂ', 'ӂ' => 'ӂ', 'Ӑ' => 'Ӑ', 'ӑ' => 'ӑ', 'Ӓ' => 'Ӓ', 'ӓ' => 'ӓ', 'Ӗ' => 'Ӗ', 'ӗ' => 'ӗ', 'Ӛ' => 'Ӛ', 'ӛ' => 'ӛ', 'Ӝ' => 'Ӝ', 'ӝ' => 'ӝ', 'Ӟ' => 'Ӟ', 'ӟ' => 'ӟ', 'Ӣ' => 'Ӣ', 'ӣ' => 'ӣ', 'Ӥ' => 'Ӥ', 'ӥ' => 'ӥ', 'Ӧ' => 'Ӧ', 'ӧ' => 'ӧ', 'Ӫ' => 'Ӫ', 'ӫ' => 'ӫ', 'Ӭ' => 'Ӭ', 'ӭ' => 'ӭ', 'Ӯ' => 'Ӯ', 'ӯ' => 'ӯ', 'Ӱ' => 'Ӱ', 'ӱ' => 'ӱ', 'Ӳ' => 'Ӳ', 'ӳ' => 'ӳ', 'Ӵ' => 'Ӵ', 'ӵ' => 'ӵ', 'Ӹ' => 'Ӹ', 'ӹ' => 'ӹ', 'آ' => 'آ', 'أ' => 'أ', 'ؤ' => 'ؤ', 'إ' => 'إ', 'ئ' => 'ئ', 'ۀ' => 'ۀ', 'ۂ' => 'ۂ', 'ۓ' => 'ۓ', 'ऩ' => 'ऩ', 'ऱ' => 'ऱ', 'ऴ' => 'ऴ', 'क़' => 'क़', 'ख़' => 'ख़', 'ग़' => 'ग़', 'ज़' => 'ज़', 'ड़' => 'ड़', 'ढ़' => 'ढ़', 'फ़' => 'फ़', 'य़' => 'य़', 'ো' => 'ো', 'ৌ' => 'ৌ', 'ড়' => 'ড়', 'ঢ়' => 'ঢ়', 'য়' => 'য়', 'ਲ਼' => 'ਲ਼', 'ਸ਼' => 'ਸ਼', 'ਖ਼' => 'ਖ਼', 'ਗ਼' => 'ਗ਼', 'ਜ਼' => 'ਜ਼', 'ਫ਼' => 'ਫ਼', 'ୈ' => 'ୈ', 'ୋ' => 'ୋ', 'ୌ' => 'ୌ', 'ଡ଼' => 'ଡ଼', 'ଢ଼' => 'ଢ଼', 'ஔ' => 'ஔ', 'ொ' => 'ொ', 'ோ' => 'ோ', 'ௌ' => 'ௌ', 'ై' => 'ై', 'ೀ' => 'ೀ', 'ೇ' => 'ೇ', 'ೈ' => 'ೈ', 'ೊ' => 'ೊ', 'ೋ' => 'ೋ', 'ൊ' => 'ൊ', 'ോ' => 'ോ', 'ൌ' => 'ൌ', 'ේ' => 'ේ', 'ො' => 'ො', 'ෝ' => 'ෝ', 'ෞ' => 'ෞ', 'གྷ' => 'གྷ', 'ཌྷ' => 'ཌྷ', 'དྷ' => 'དྷ', 'བྷ' => 'བྷ', 'ཛྷ' => 'ཛྷ', 'ཀྵ' => 'ཀྵ', 'ཱི' => 'ཱི', 'ཱུ' => 'ཱུ', 'ྲྀ' => 'ྲྀ', 'ླྀ' => 'ླྀ', 'ཱྀ' => 'ཱྀ', 'ྒྷ' => 'ྒྷ', 'ྜྷ' => 'ྜྷ', 'ྡྷ' => 'ྡྷ', 'ྦྷ' => 'ྦྷ', 'ྫྷ' => 'ྫྷ', 'ྐྵ' => 'ྐྵ', 'ဦ' => 'ဦ', 'ᬆ' => 'ᬆ', 'ᬈ' => 'ᬈ', 'ᬊ' => 'ᬊ', 'ᬌ' => 'ᬌ', 'ᬎ' => 'ᬎ', 'ᬒ' => 'ᬒ', 'ᬻ' => 'ᬻ', 'ᬽ' => 'ᬽ', 'ᭀ' => 'ᭀ', 'ᭁ' => 'ᭁ', 'ᭃ' => 'ᭃ', 'Ḁ' => 'Ḁ', 'ḁ' => 'ḁ', 'Ḃ' => 'Ḃ', 'ḃ' => 'ḃ', 'Ḅ' => 'Ḅ', 'ḅ' => 'ḅ', 'Ḇ' => 'Ḇ', 'ḇ' => 'ḇ', 'Ḉ' => 'Ḉ', 'ḉ' => 'ḉ', 'Ḋ' => 'Ḋ', 'ḋ' => 'ḋ', 'Ḍ' => 'Ḍ', 'ḍ' => 'ḍ', 'Ḏ' => 'Ḏ', 'ḏ' => 'ḏ', 'Ḑ' => 'Ḑ', 'ḑ' => 'ḑ', 'Ḓ' => 'Ḓ', 'ḓ' => 'ḓ', 'Ḕ' => 'Ḕ', 'ḕ' => 'ḕ', 'Ḗ' => 'Ḗ', 'ḗ' => 'ḗ', 'Ḙ' => 'Ḙ', 'ḙ' => 'ḙ', 'Ḛ' => 'Ḛ', 'ḛ' => 'ḛ', 'Ḝ' => 'Ḝ', 'ḝ' => 'ḝ', 'Ḟ' => 'Ḟ', 'ḟ' => 'ḟ', 'Ḡ' => 'Ḡ', 'ḡ' => 'ḡ', 'Ḣ' => 'Ḣ', 'ḣ' => 'ḣ', 'Ḥ' => 'Ḥ', 'ḥ' => 'ḥ', 'Ḧ' => 'Ḧ', 'ḧ' => 'ḧ', 'Ḩ' => 'Ḩ', 'ḩ' => 'ḩ', 'Ḫ' => 'Ḫ', 'ḫ' => 'ḫ', 'Ḭ' => 'Ḭ', 'ḭ' => 'ḭ', 'Ḯ' => 'Ḯ', 'ḯ' => 'ḯ', 'Ḱ' => 'Ḱ', 'ḱ' => 'ḱ', 'Ḳ' => 'Ḳ', 'ḳ' => 'ḳ', 'Ḵ' => 'Ḵ', 'ḵ' => 'ḵ', 'Ḷ' => 'Ḷ', 'ḷ' => 'ḷ', 'Ḹ' => 'Ḹ', 'ḹ' => 'ḹ', 'Ḻ' => 'Ḻ', 'ḻ' => 'ḻ', 'Ḽ' => 'Ḽ', 'ḽ' => 'ḽ', 'Ḿ' => 'Ḿ', 'ḿ' => 'ḿ', 'Ṁ' => 'Ṁ', 'ṁ' => 'ṁ', 'Ṃ' => 'Ṃ', 'ṃ' => 'ṃ', 'Ṅ' => 'Ṅ', 'ṅ' => 'ṅ', 'Ṇ' => 'Ṇ', 'ṇ' => 'ṇ', 'Ṉ' => 'Ṉ', 'ṉ' => 'ṉ', 'Ṋ' => 'Ṋ', 'ṋ' => 'ṋ', 'Ṍ' => 'Ṍ', 'ṍ' => 'ṍ', 'Ṏ' => 'Ṏ', 'ṏ' => 'ṏ', 'Ṑ' => 'Ṑ', 'ṑ' => 'ṑ', 'Ṓ' => 'Ṓ', 'ṓ' => 'ṓ', 'Ṕ' => 'Ṕ', 'ṕ' => 'ṕ', 'Ṗ' => 'Ṗ', 'ṗ' => 'ṗ', 'Ṙ' => 'Ṙ', 'ṙ' => 'ṙ', 'Ṛ' => 'Ṛ', 'ṛ' => 'ṛ', 'Ṝ' => 'Ṝ', 'ṝ' => 'ṝ', 'Ṟ' => 'Ṟ', 'ṟ' => 'ṟ', 'Ṡ' => 'Ṡ', 'ṡ' => 'ṡ', 'Ṣ' => 'Ṣ', 'ṣ' => 'ṣ', 'Ṥ' => 'Ṥ', 'ṥ' => 'ṥ', 'Ṧ' => 'Ṧ', 'ṧ' => 'ṧ', 'Ṩ' => 'Ṩ', 'ṩ' => 'ṩ', 'Ṫ' => 'Ṫ', 'ṫ' => 'ṫ', 'Ṭ' => 'Ṭ', 'ṭ' => 'ṭ', 'Ṯ' => 'Ṯ', 'ṯ' => 'ṯ', 'Ṱ' => 'Ṱ', 'ṱ' => 'ṱ', 'Ṳ' => 'Ṳ', 'ṳ' => 'ṳ', 'Ṵ' => 'Ṵ', 'ṵ' => 'ṵ', 'Ṷ' => 'Ṷ', 'ṷ' => 'ṷ', 'Ṹ' => 'Ṹ', 'ṹ' => 'ṹ', 'Ṻ' => 'Ṻ', 'ṻ' => 'ṻ', 'Ṽ' => 'Ṽ', 'ṽ' => 'ṽ', 'Ṿ' => 'Ṿ', 'ṿ' => 'ṿ', 'Ẁ' => 'Ẁ', 'ẁ' => 'ẁ', 'Ẃ' => 'Ẃ', 'ẃ' => 'ẃ', 'Ẅ' => 'Ẅ', 'ẅ' => 'ẅ', 'Ẇ' => 'Ẇ', 'ẇ' => 'ẇ', 'Ẉ' => 'Ẉ', 'ẉ' => 'ẉ', 'Ẋ' => 'Ẋ', 'ẋ' => 'ẋ', 'Ẍ' => 'Ẍ', 'ẍ' => 'ẍ', 'Ẏ' => 'Ẏ', 'ẏ' => 'ẏ', 'Ẑ' => 'Ẑ', 'ẑ' => 'ẑ', 'Ẓ' => 'Ẓ', 'ẓ' => 'ẓ', 'Ẕ' => 'Ẕ', 'ẕ' => 'ẕ', 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'ẘ', 'ẙ' => 'ẙ', 'ẛ' => 'ẛ', 'Ạ' => 'Ạ', 'ạ' => 'ạ', 'Ả' => 'Ả', 'ả' => 'ả', 'Ấ' => 'Ấ', 'ấ' => 'ấ', 'Ầ' => 'Ầ', 'ầ' => 'ầ', 'Ẩ' => 'Ẩ', 'ẩ' => 'ẩ', 'Ẫ' => 'Ẫ', 'ẫ' => 'ẫ', 'Ậ' => 'Ậ', 'ậ' => 'ậ', 'Ắ' => 'Ắ', 'ắ' => 'ắ', 'Ằ' => 'Ằ', 'ằ' => 'ằ', 'Ẳ' => 'Ẳ', 'ẳ' => 'ẳ', 'Ẵ' => 'Ẵ', 'ẵ' => 'ẵ', 'Ặ' => 'Ặ', 'ặ' => 'ặ', 'Ẹ' => 'Ẹ', 'ẹ' => 'ẹ', 'Ẻ' => 'Ẻ', 'ẻ' => 'ẻ', 'Ẽ' => 'Ẽ', 'ẽ' => 'ẽ', 'Ế' => 'Ế', 'ế' => 'ế', 'Ề' => 'Ề', 'ề' => 'ề', 'Ể' => 'Ể', 'ể' => 'ể', 'Ễ' => 'Ễ', 'ễ' => 'ễ', 'Ệ' => 'Ệ', 'ệ' => 'ệ', 'Ỉ' => 'Ỉ', 'ỉ' => 'ỉ', 'Ị' => 'Ị', 'ị' => 'ị', 'Ọ' => 'Ọ', 'ọ' => 'ọ', 'Ỏ' => 'Ỏ', 'ỏ' => 'ỏ', 'Ố' => 'Ố', 'ố' => 'ố', 'Ồ' => 'Ồ', 'ồ' => 'ồ', 'Ổ' => 'Ổ', 'ổ' => 'ổ', 'Ỗ' => 'Ỗ', 'ỗ' => 'ỗ', 'Ộ' => 'Ộ', 'ộ' => 'ộ', 'Ớ' => 'Ớ', 'ớ' => 'ớ', 'Ờ' => 'Ờ', 'ờ' => 'ờ', 'Ở' => 'Ở', 'ở' => 'ở', 'Ỡ' => 'Ỡ', 'ỡ' => 'ỡ', 'Ợ' => 'Ợ', 'ợ' => 'ợ', 'Ụ' => 'Ụ', 'ụ' => 'ụ', 'Ủ' => 'Ủ', 'ủ' => 'ủ', 'Ứ' => 'Ứ', 'ứ' => 'ứ', 'Ừ' => 'Ừ', 'ừ' => 'ừ', 'Ử' => 'Ử', 'ử' => 'ử', 'Ữ' => 'Ữ', 'ữ' => 'ữ', 'Ự' => 'Ự', 'ự' => 'ự', 'Ỳ' => 'Ỳ', 'ỳ' => 'ỳ', 'Ỵ' => 'Ỵ', 'ỵ' => 'ỵ', 'Ỷ' => 'Ỷ', 'ỷ' => 'ỷ', 'Ỹ' => 'Ỹ', 'ỹ' => 'ỹ', 'ἀ' => 'ἀ', 'ἁ' => 'ἁ', 'ἂ' => 'ἂ', 'ἃ' => 'ἃ', 'ἄ' => 'ἄ', 'ἅ' => 'ἅ', 'ἆ' => 'ἆ', 'ἇ' => 'ἇ', 'Ἀ' => 'Ἀ', 'Ἁ' => 'Ἁ', 'Ἂ' => 'Ἂ', 'Ἃ' => 'Ἃ', 'Ἄ' => 'Ἄ', 'Ἅ' => 'Ἅ', 'Ἆ' => 'Ἆ', 'Ἇ' => 'Ἇ', 'ἐ' => 'ἐ', 'ἑ' => 'ἑ', 'ἒ' => 'ἒ', 'ἓ' => 'ἓ', 'ἔ' => 'ἔ', 'ἕ' => 'ἕ', 'Ἐ' => 'Ἐ', 'Ἑ' => 'Ἑ', 'Ἒ' => 'Ἒ', 'Ἓ' => 'Ἓ', 'Ἔ' => 'Ἔ', 'Ἕ' => 'Ἕ', 'ἠ' => 'ἠ', 'ἡ' => 'ἡ', 'ἢ' => 'ἢ', 'ἣ' => 'ἣ', 'ἤ' => 'ἤ', 'ἥ' => 'ἥ', 'ἦ' => 'ἦ', 'ἧ' => 'ἧ', 'Ἠ' => 'Ἠ', 'Ἡ' => 'Ἡ', 'Ἢ' => 'Ἢ', 'Ἣ' => 'Ἣ', 'Ἤ' => 'Ἤ', 'Ἥ' => 'Ἥ', 'Ἦ' => 'Ἦ', 'Ἧ' => 'Ἧ', 'ἰ' => 'ἰ', 'ἱ' => 'ἱ', 'ἲ' => 'ἲ', 'ἳ' => 'ἳ', 'ἴ' => 'ἴ', 'ἵ' => 'ἵ', 'ἶ' => 'ἶ', 'ἷ' => 'ἷ', 'Ἰ' => 'Ἰ', 'Ἱ' => 'Ἱ', 'Ἲ' => 'Ἲ', 'Ἳ' => 'Ἳ', 'Ἴ' => 'Ἴ', 'Ἵ' => 'Ἵ', 'Ἶ' => 'Ἶ', 'Ἷ' => 'Ἷ', 'ὀ' => 'ὀ', 'ὁ' => 'ὁ', 'ὂ' => 'ὂ', 'ὃ' => 'ὃ', 'ὄ' => 'ὄ', 'ὅ' => 'ὅ', 'Ὀ' => 'Ὀ', 'Ὁ' => 'Ὁ', 'Ὂ' => 'Ὂ', 'Ὃ' => 'Ὃ', 'Ὄ' => 'Ὄ', 'Ὅ' => 'Ὅ', 'ὐ' => 'ὐ', 'ὑ' => 'ὑ', 'ὒ' => 'ὒ', 'ὓ' => 'ὓ', 'ὔ' => 'ὔ', 'ὕ' => 'ὕ', 'ὖ' => 'ὖ', 'ὗ' => 'ὗ', 'Ὑ' => 'Ὑ', 'Ὓ' => 'Ὓ', 'Ὕ' => 'Ὕ', 'Ὗ' => 'Ὗ', 'ὠ' => 'ὠ', 'ὡ' => 'ὡ', 'ὢ' => 'ὢ', 'ὣ' => 'ὣ', 'ὤ' => 'ὤ', 'ὥ' => 'ὥ', 'ὦ' => 'ὦ', 'ὧ' => 'ὧ', 'Ὠ' => 'Ὠ', 'Ὡ' => 'Ὡ', 'Ὢ' => 'Ὢ', 'Ὣ' => 'Ὣ', 'Ὤ' => 'Ὤ', 'Ὥ' => 'Ὥ', 'Ὦ' => 'Ὦ', 'Ὧ' => 'Ὧ', 'ὰ' => 'ὰ', 'ά' => 'ά', 'ὲ' => 'ὲ', 'έ' => 'έ', 'ὴ' => 'ὴ', 'ή' => 'ή', 'ὶ' => 'ὶ', 'ί' => 'ί', 'ὸ' => 'ὸ', 'ό' => 'ό', 'ὺ' => 'ὺ', 'ύ' => 'ύ', 'ὼ' => 'ὼ', 'ώ' => 'ώ', 'ᾀ' => 'ᾀ', 'ᾁ' => 'ᾁ', 'ᾂ' => 'ᾂ', 'ᾃ' => 'ᾃ', 'ᾄ' => 'ᾄ', 'ᾅ' => 'ᾅ', 'ᾆ' => 'ᾆ', 'ᾇ' => 'ᾇ', 'ᾈ' => 'ᾈ', 'ᾉ' => 'ᾉ', 'ᾊ' => 'ᾊ', 'ᾋ' => 'ᾋ', 'ᾌ' => 'ᾌ', 'ᾍ' => 'ᾍ', 'ᾎ' => 'ᾎ', 'ᾏ' => 'ᾏ', 'ᾐ' => 'ᾐ', 'ᾑ' => 'ᾑ', 'ᾒ' => 'ᾒ', 'ᾓ' => 'ᾓ', 'ᾔ' => 'ᾔ', 'ᾕ' => 'ᾕ', 'ᾖ' => 'ᾖ', 'ᾗ' => 'ᾗ', 'ᾘ' => 'ᾘ', 'ᾙ' => 'ᾙ', 'ᾚ' => 'ᾚ', 'ᾛ' => 'ᾛ', 'ᾜ' => 'ᾜ', 'ᾝ' => 'ᾝ', 'ᾞ' => 'ᾞ', 'ᾟ' => 'ᾟ', 'ᾠ' => 'ᾠ', 'ᾡ' => 'ᾡ', 'ᾢ' => 'ᾢ', 'ᾣ' => 'ᾣ', 'ᾤ' => 'ᾤ', 'ᾥ' => 'ᾥ', 'ᾦ' => 'ᾦ', 'ᾧ' => 'ᾧ', 'ᾨ' => 'ᾨ', 'ᾩ' => 'ᾩ', 'ᾪ' => 'ᾪ', 'ᾫ' => 'ᾫ', 'ᾬ' => 'ᾬ', 'ᾭ' => 'ᾭ', 'ᾮ' => 'ᾮ', 'ᾯ' => 'ᾯ', 'ᾰ' => 'ᾰ', 'ᾱ' => 'ᾱ', 'ᾲ' => 'ᾲ', 'ᾳ' => 'ᾳ', 'ᾴ' => 'ᾴ', 'ᾶ' => 'ᾶ', 'ᾷ' => 'ᾷ', 'Ᾰ' => 'Ᾰ', 'Ᾱ' => 'Ᾱ', 'Ὰ' => 'Ὰ', 'Ά' => 'Ά', 'ᾼ' => 'ᾼ', 'ι' => 'ι', '῁' => '῁', 'ῂ' => 'ῂ', 'ῃ' => 'ῃ', 'ῄ' => 'ῄ', 'ῆ' => 'ῆ', 'ῇ' => 'ῇ', 'Ὲ' => 'Ὲ', 'Έ' => 'Έ', 'Ὴ' => 'Ὴ', 'Ή' => 'Ή', 'ῌ' => 'ῌ', '῍' => '῍', '῎' => '῎', '῏' => '῏', 'ῐ' => 'ῐ', 'ῑ' => 'ῑ', 'ῒ' => 'ῒ', 'ΐ' => 'ΐ', 'ῖ' => 'ῖ', 'ῗ' => 'ῗ', 'Ῐ' => 'Ῐ', 'Ῑ' => 'Ῑ', 'Ὶ' => 'Ὶ', 'Ί' => 'Ί', '῝' => '῝', '῞' => '῞', '῟' => '῟', 'ῠ' => 'ῠ', 'ῡ' => 'ῡ', 'ῢ' => 'ῢ', 'ΰ' => 'ΰ', 'ῤ' => 'ῤ', 'ῥ' => 'ῥ', 'ῦ' => 'ῦ', 'ῧ' => 'ῧ', 'Ῠ' => 'Ῠ', 'Ῡ' => 'Ῡ', 'Ὺ' => 'Ὺ', 'Ύ' => 'Ύ', 'Ῥ' => 'Ῥ', '῭' => '῭', '΅' => '΅', '`' => '`', 'ῲ' => 'ῲ', 'ῳ' => 'ῳ', 'ῴ' => 'ῴ', 'ῶ' => 'ῶ', 'ῷ' => 'ῷ', 'Ὸ' => 'Ὸ', 'Ό' => 'Ό', 'Ὼ' => 'Ὼ', 'Ώ' => 'Ώ', 'ῼ' => 'ῼ', '´' => '´', ' ' => ' ', ' ' => ' ', 'Ω' => 'Ω', 'K' => 'K', 'Å' => 'Å', '↚' => '↚', '↛' => '↛', '↮' => '↮', '⇍' => '⇍', '⇎' => '⇎', '⇏' => '⇏', '∄' => '∄', '∉' => '∉', '∌' => '∌', '∤' => '∤', '∦' => '∦', '≁' => '≁', '≄' => '≄', '≇' => '≇', '≉' => '≉', '≠' => '≠', '≢' => '≢', '≭' => '≭', '≮' => '≮', '≯' => '≯', '≰' => '≰', '≱' => '≱', '≴' => '≴', '≵' => '≵', '≸' => '≸', '≹' => '≹', '⊀' => '⊀', '⊁' => '⊁', '⊄' => '⊄', '⊅' => '⊅', '⊈' => '⊈', '⊉' => '⊉', '⊬' => '⊬', '⊭' => '⊭', '⊮' => '⊮', '⊯' => '⊯', '⋠' => '⋠', '⋡' => '⋡', '⋢' => '⋢', '⋣' => '⋣', '⋪' => '⋪', '⋫' => '⋫', '⋬' => '⋬', '⋭' => '⋭', '〈' => '〈', '〉' => '〉', '⫝̸' => '⫝̸', 'が' => 'が', 'ぎ' => 'ぎ', 'ぐ' => 'ぐ', 'げ' => 'げ', 'ご' => 'ご', 'ざ' => 'ざ', 'じ' => 'じ', 'ず' => 'ず', 'ぜ' => 'ぜ', 'ぞ' => 'ぞ', 'だ' => 'だ', 'ぢ' => 'ぢ', 'づ' => 'づ', 'で' => 'で', 'ど' => 'ど', 'ば' => 'ば', 'ぱ' => 'ぱ', 'び' => 'び', 'ぴ' => 'ぴ', 'ぶ' => 'ぶ', 'ぷ' => 'ぷ', 'べ' => 'べ', 'ぺ' => 'ぺ', 'ぼ' => 'ぼ', 'ぽ' => 'ぽ', 'ゔ' => 'ゔ', 'ゞ' => 'ゞ', 'ガ' => 'ガ', 'ギ' => 'ギ', 'グ' => 'グ', 'ゲ' => 'ゲ', 'ゴ' => 'ゴ', 'ザ' => 'ザ', 'ジ' => 'ジ', 'ズ' => 'ズ', 'ゼ' => 'ゼ', 'ゾ' => 'ゾ', 'ダ' => 'ダ', 'ヂ' => 'ヂ', 'ヅ' => 'ヅ', 'デ' => 'デ', 'ド' => 'ド', 'バ' => 'バ', 'パ' => 'パ', 'ビ' => 'ビ', 'ピ' => 'ピ', 'ブ' => 'ブ', 'プ' => 'プ', 'ベ' => 'ベ', 'ペ' => 'ペ', 'ボ' => 'ボ', 'ポ' => 'ポ', 'ヴ' => 'ヴ', 'ヷ' => 'ヷ', 'ヸ' => 'ヸ', 'ヹ' => 'ヹ', 'ヺ' => 'ヺ', 'ヾ' => 'ヾ', '豈' => '豈', '更' => '更', '車' => '車', '賈' => '賈', '滑' => '滑', '串' => '串', '句' => '句', '龜' => '龜', '龜' => '龜', '契' => '契', '金' => '金', '喇' => '喇', '奈' => '奈', '懶' => '懶', '癩' => '癩', '羅' => '羅', '蘿' => '蘿', '螺' => '螺', '裸' => '裸', '邏' => '邏', '樂' => '樂', '洛' => '洛', '烙' => '烙', '珞' => '珞', '落' => '落', '酪' => '酪', '駱' => '駱', '亂' => '亂', '卵' => '卵', '欄' => '欄', '爛' => '爛', '蘭' => '蘭', '鸞' => '鸞', '嵐' => '嵐', '濫' => '濫', '藍' => '藍', '襤' => '襤', '拉' => '拉', '臘' => '臘', '蠟' => '蠟', '廊' => '廊', '朗' => '朗', '浪' => '浪', '狼' => '狼', '郎' => '郎', '來' => '來', '冷' => '冷', '勞' => '勞', '擄' => '擄', '櫓' => '櫓', '爐' => '爐', '盧' => '盧', '老' => '老', '蘆' => '蘆', '虜' => '虜', '路' => '路', '露' => '露', '魯' => '魯', '鷺' => '鷺', '碌' => '碌', '祿' => '祿', '綠' => '綠', '菉' => '菉', '錄' => '錄', '鹿' => '鹿', '論' => '論', '壟' => '壟', '弄' => '弄', '籠' => '籠', '聾' => '聾', '牢' => '牢', '磊' => '磊', '賂' => '賂', '雷' => '雷', '壘' => '壘', '屢' => '屢', '樓' => '樓', '淚' => '淚', '漏' => '漏', '累' => '累', '縷' => '縷', '陋' => '陋', '勒' => '勒', '肋' => '肋', '凜' => '凜', '凌' => '凌', '稜' => '稜', '綾' => '綾', '菱' => '菱', '陵' => '陵', '讀' => '讀', '拏' => '拏', '樂' => '樂', '諾' => '諾', '丹' => '丹', '寧' => '寧', '怒' => '怒', '率' => '率', '異' => '異', '北' => '北', '磻' => '磻', '便' => '便', '復' => '復', '不' => '不', '泌' => '泌', '數' => '數', '索' => '索', '參' => '參', '塞' => '塞', '省' => '省', '葉' => '葉', '說' => '說', '殺' => '殺', '辰' => '辰', '沈' => '沈', '拾' => '拾', '若' => '若', '掠' => '掠', '略' => '略', '亮' => '亮', '兩' => '兩', '凉' => '凉', '梁' => '梁', '糧' => '糧', '良' => '良', '諒' => '諒', '量' => '量', '勵' => '勵', '呂' => '呂', '女' => '女', '廬' => '廬', '旅' => '旅', '濾' => '濾', '礪' => '礪', '閭' => '閭', '驪' => '驪', '麗' => '麗', '黎' => '黎', '力' => '力', '曆' => '曆', '歷' => '歷', '轢' => '轢', '年' => '年', '憐' => '憐', '戀' => '戀', '撚' => '撚', '漣' => '漣', '煉' => '煉', '璉' => '璉', '秊' => '秊', '練' => '練', '聯' => '聯', '輦' => '輦', '蓮' => '蓮', '連' => '連', '鍊' => '鍊', '列' => '列', '劣' => '劣', '咽' => '咽', '烈' => '烈', '裂' => '裂', '說' => '說', '廉' => '廉', '念' => '念', '捻' => '捻', '殮' => '殮', '簾' => '簾', '獵' => '獵', '令' => '令', '囹' => '囹', '寧' => '寧', '嶺' => '嶺', '怜' => '怜', '玲' => '玲', '瑩' => '瑩', '羚' => '羚', '聆' => '聆', '鈴' => '鈴', '零' => '零', '靈' => '靈', '領' => '領', '例' => '例', '禮' => '禮', '醴' => '醴', '隸' => '隸', '惡' => '惡', '了' => '了', '僚' => '僚', '寮' => '寮', '尿' => '尿', '料' => '料', '樂' => '樂', '燎' => '燎', '療' => '療', '蓼' => '蓼', '遼' => '遼', '龍' => '龍', '暈' => '暈', '阮' => '阮', '劉' => '劉', '杻' => '杻', '柳' => '柳', '流' => '流', '溜' => '溜', '琉' => '琉', '留' => '留', '硫' => '硫', '紐' => '紐', '類' => '類', '六' => '六', '戮' => '戮', '陸' => '陸', '倫' => '倫', '崙' => '崙', '淪' => '淪', '輪' => '輪', '律' => '律', '慄' => '慄', '栗' => '栗', '率' => '率', '隆' => '隆', '利' => '利', '吏' => '吏', '履' => '履', '易' => '易', '李' => '李', '梨' => '梨', '泥' => '泥', '理' => '理', '痢' => '痢', '罹' => '罹', '裏' => '裏', '裡' => '裡', '里' => '里', '離' => '離', '匿' => '匿', '溺' => '溺', '吝' => '吝', '燐' => '燐', '璘' => '璘', '藺' => '藺', '隣' => '隣', '鱗' => '鱗', '麟' => '麟', '林' => '林', '淋' => '淋', '臨' => '臨', '立' => '立', '笠' => '笠', '粒' => '粒', '狀' => '狀', '炙' => '炙', '識' => '識', '什' => '什', '茶' => '茶', '刺' => '刺', '切' => '切', '度' => '度', '拓' => '拓', '糖' => '糖', '宅' => '宅', '洞' => '洞', '暴' => '暴', '輻' => '輻', '行' => '行', '降' => '降', '見' => '見', '廓' => '廓', '兀' => '兀', '嗀' => '嗀', '塚' => '塚', '晴' => '晴', '凞' => '凞', '猪' => '猪', '益' => '益', '礼' => '礼', '神' => '神', '祥' => '祥', '福' => '福', '靖' => '靖', '精' => '精', '羽' => '羽', '蘒' => '蘒', '諸' => '諸', '逸' => '逸', '都' => '都', '飯' => '飯', '飼' => '飼', '館' => '館', '鶴' => '鶴', '郞' => '郞', '隷' => '隷', '侮' => '侮', '僧' => '僧', '免' => '免', '勉' => '勉', '勤' => '勤', '卑' => '卑', '喝' => '喝', '嘆' => '嘆', '器' => '器', '塀' => '塀', '墨' => '墨', '層' => '層', '屮' => '屮', '悔' => '悔', '慨' => '慨', '憎' => '憎', '懲' => '懲', '敏' => '敏', '既' => '既', '暑' => '暑', '梅' => '梅', '海' => '海', '渚' => '渚', '漢' => '漢', '煮' => '煮', '爫' => '爫', '琢' => '琢', '碑' => '碑', '社' => '社', '祉' => '祉', '祈' => '祈', '祐' => '祐', '祖' => '祖', '祝' => '祝', '禍' => '禍', '禎' => '禎', '穀' => '穀', '突' => '突', '節' => '節', '練' => '練', '縉' => '縉', '繁' => '繁', '署' => '署', '者' => '者', '臭' => '臭', '艹' => '艹', '艹' => '艹', '著' => '著', '褐' => '褐', '視' => '視', '謁' => '謁', '謹' => '謹', '賓' => '賓', '贈' => '贈', '辶' => '辶', '逸' => '逸', '難' => '難', '響' => '響', '頻' => '頻', '恵' => '恵', '𤋮' => '𤋮', '舘' => '舘', '並' => '並', '况' => '况', '全' => '全', '侀' => '侀', '充' => '充', '冀' => '冀', '勇' => '勇', '勺' => '勺', '喝' => '喝', '啕' => '啕', '喙' => '喙', '嗢' => '嗢', '塚' => '塚', '墳' => '墳', '奄' => '奄', '奔' => '奔', '婢' => '婢', '嬨' => '嬨', '廒' => '廒', '廙' => '廙', '彩' => '彩', '徭' => '徭', '惘' => '惘', '慎' => '慎', '愈' => '愈', '憎' => '憎', '慠' => '慠', '懲' => '懲', '戴' => '戴', '揄' => '揄', '搜' => '搜', '摒' => '摒', '敖' => '敖', '晴' => '晴', '朗' => '朗', '望' => '望', '杖' => '杖', '歹' => '歹', '殺' => '殺', '流' => '流', '滛' => '滛', '滋' => '滋', '漢' => '漢', '瀞' => '瀞', '煮' => '煮', '瞧' => '瞧', '爵' => '爵', '犯' => '犯', '猪' => '猪', '瑱' => '瑱', '甆' => '甆', '画' => '画', '瘝' => '瘝', '瘟' => '瘟', '益' => '益', '盛' => '盛', '直' => '直', '睊' => '睊', '着' => '着', '磌' => '磌', '窱' => '窱', '節' => '節', '类' => '类', '絛' => '絛', '練' => '練', '缾' => '缾', '者' => '者', '荒' => '荒', '華' => '華', '蝹' => '蝹', '襁' => '襁', '覆' => '覆', '視' => '視', '調' => '調', '諸' => '諸', '請' => '請', '謁' => '謁', '諾' => '諾', '諭' => '諭', '謹' => '謹', '變' => '變', '贈' => '贈', '輸' => '輸', '遲' => '遲', '醙' => '醙', '鉶' => '鉶', '陼' => '陼', '難' => '難', '靖' => '靖', '韛' => '韛', '響' => '響', '頋' => '頋', '頻' => '頻', '鬒' => '鬒', '龜' => '龜', '𢡊' => '𢡊', '𢡄' => '𢡄', '𣏕' => '𣏕', '㮝' => '㮝', '䀘' => '䀘', '䀹' => '䀹', '𥉉' => '𥉉', '𥳐' => '𥳐', '𧻓' => '𧻓', '齃' => '齃', '龎' => '龎', 'יִ' => 'יִ', 'ײַ' => 'ײַ', 'שׁ' => 'שׁ', 'שׂ' => 'שׂ', 'שּׁ' => 'שּׁ', 'שּׂ' => 'שּׂ', 'אַ' => 'אַ', 'אָ' => 'אָ', 'אּ' => 'אּ', 'בּ' => 'בּ', 'גּ' => 'גּ', 'דּ' => 'דּ', 'הּ' => 'הּ', 'וּ' => 'וּ', 'זּ' => 'זּ', 'טּ' => 'טּ', 'יּ' => 'יּ', 'ךּ' => 'ךּ', 'כּ' => 'כּ', 'לּ' => 'לּ', 'מּ' => 'מּ', 'נּ' => 'נּ', 'סּ' => 'סּ', 'ףּ' => 'ףּ', 'פּ' => 'פּ', 'צּ' => 'צּ', 'קּ' => 'קּ', 'רּ' => 'רּ', 'שּ' => 'שּ', 'תּ' => 'תּ', 'וֹ' => 'וֹ', 'בֿ' => 'בֿ', 'כֿ' => 'כֿ', 'פֿ' => 'פֿ', '𑂚' => '𑂚', '𑂜' => '𑂜', '𑂫' => '𑂫', '𑄮' => '𑄮', '𑄯' => '𑄯', '𑍋' => '𑍋', '𑍌' => '𑍌', '𑒻' => '𑒻', '𑒼' => '𑒼', '𑒾' => '𑒾', '𑖺' => '𑖺', '𑖻' => '𑖻', '𑤸' => '𑤸', '𝅗𝅥' => '𝅗𝅥', '𝅘𝅥' => '𝅘𝅥', '𝅘𝅥𝅮' => '𝅘𝅥𝅮', '𝅘𝅥𝅯' => '𝅘𝅥𝅯', '𝅘𝅥𝅰' => '𝅘𝅥𝅰', '𝅘𝅥𝅱' => '𝅘𝅥𝅱', '𝅘𝅥𝅲' => '𝅘𝅥𝅲', '𝆹𝅥' => '𝆹𝅥', '𝆺𝅥' => '𝆺𝅥', '𝆹𝅥𝅮' => '𝆹𝅥𝅮', '𝆺𝅥𝅮' => '𝆺𝅥𝅮', '𝆹𝅥𝅯' => '𝆹𝅥𝅯', '𝆺𝅥𝅯' => '𝆺𝅥𝅯', '丽' => '丽', '丸' => '丸', '乁' => '乁', '𠄢' => '𠄢', '你' => '你', '侮' => '侮', '侻' => '侻', '倂' => '倂', '偺' => '偺', '備' => '備', '僧' => '僧', '像' => '像', '㒞' => '㒞', '𠘺' => '𠘺', '免' => '免', '兔' => '兔', '兤' => '兤', '具' => '具', '𠔜' => '𠔜', '㒹' => '㒹', '內' => '內', '再' => '再', '𠕋' => '𠕋', '冗' => '冗', '冤' => '冤', '仌' => '仌', '冬' => '冬', '况' => '况', '𩇟' => '𩇟', '凵' => '凵', '刃' => '刃', '㓟' => '㓟', '刻' => '刻', '剆' => '剆', '割' => '割', '剷' => '剷', '㔕' => '㔕', '勇' => '勇', '勉' => '勉', '勤' => '勤', '勺' => '勺', '包' => '包', '匆' => '匆', '北' => '北', '卉' => '卉', '卑' => '卑', '博' => '博', '即' => '即', '卽' => '卽', '卿' => '卿', '卿' => '卿', '卿' => '卿', '𠨬' => '𠨬', '灰' => '灰', '及' => '及', '叟' => '叟', '𠭣' => '𠭣', '叫' => '叫', '叱' => '叱', '吆' => '吆', '咞' => '咞', '吸' => '吸', '呈' => '呈', '周' => '周', '咢' => '咢', '哶' => '哶', '唐' => '唐', '啓' => '啓', '啣' => '啣', '善' => '善', '善' => '善', '喙' => '喙', '喫' => '喫', '喳' => '喳', '嗂' => '嗂', '圖' => '圖', '嘆' => '嘆', '圗' => '圗', '噑' => '噑', '噴' => '噴', '切' => '切', '壮' => '壮', '城' => '城', '埴' => '埴', '堍' => '堍', '型' => '型', '堲' => '堲', '報' => '報', '墬' => '墬', '𡓤' => '𡓤', '売' => '売', '壷' => '壷', '夆' => '夆', '多' => '多', '夢' => '夢', '奢' => '奢', '𡚨' => '𡚨', '𡛪' => '𡛪', '姬' => '姬', '娛' => '娛', '娧' => '娧', '姘' => '姘', '婦' => '婦', '㛮' => '㛮', '㛼' => '㛼', '嬈' => '嬈', '嬾' => '嬾', '嬾' => '嬾', '𡧈' => '𡧈', '寃' => '寃', '寘' => '寘', '寧' => '寧', '寳' => '寳', '𡬘' => '𡬘', '寿' => '寿', '将' => '将', '当' => '当', '尢' => '尢', '㞁' => '㞁', '屠' => '屠', '屮' => '屮', '峀' => '峀', '岍' => '岍', '𡷤' => '𡷤', '嵃' => '嵃', '𡷦' => '𡷦', '嵮' => '嵮', '嵫' => '嵫', '嵼' => '嵼', '巡' => '巡', '巢' => '巢', '㠯' => '㠯', '巽' => '巽', '帨' => '帨', '帽' => '帽', '幩' => '幩', '㡢' => '㡢', '𢆃' => '𢆃', '㡼' => '㡼', '庰' => '庰', '庳' => '庳', '庶' => '庶', '廊' => '廊', '𪎒' => '𪎒', '廾' => '廾', '𢌱' => '𢌱', '𢌱' => '𢌱', '舁' => '舁', '弢' => '弢', '弢' => '弢', '㣇' => '㣇', '𣊸' => '𣊸', '𦇚' => '𦇚', '形' => '形', '彫' => '彫', '㣣' => '㣣', '徚' => '徚', '忍' => '忍', '志' => '志', '忹' => '忹', '悁' => '悁', '㤺' => '㤺', '㤜' => '㤜', '悔' => '悔', '𢛔' => '𢛔', '惇' => '惇', '慈' => '慈', '慌' => '慌', '慎' => '慎', '慌' => '慌', '慺' => '慺', '憎' => '憎', '憲' => '憲', '憤' => '憤', '憯' => '憯', '懞' => '懞', '懲' => '懲', '懶' => '懶', '成' => '成', '戛' => '戛', '扝' => '扝', '抱' => '抱', '拔' => '拔', '捐' => '捐', '𢬌' => '𢬌', '挽' => '挽', '拼' => '拼', '捨' => '捨', '掃' => '掃', '揤' => '揤', '𢯱' => '𢯱', '搢' => '搢', '揅' => '揅', '掩' => '掩', '㨮' => '㨮', '摩' => '摩', '摾' => '摾', '撝' => '撝', '摷' => '摷', '㩬' => '㩬', '敏' => '敏', '敬' => '敬', '𣀊' => '𣀊', '旣' => '旣', '書' => '書', '晉' => '晉', '㬙' => '㬙', '暑' => '暑', '㬈' => '㬈', '㫤' => '㫤', '冒' => '冒', '冕' => '冕', '最' => '最', '暜' => '暜', '肭' => '肭', '䏙' => '䏙', '朗' => '朗', '望' => '望', '朡' => '朡', '杞' => '杞', '杓' => '杓', '𣏃' => '𣏃', '㭉' => '㭉', '柺' => '柺', '枅' => '枅', '桒' => '桒', '梅' => '梅', '𣑭' => '𣑭', '梎' => '梎', '栟' => '栟', '椔' => '椔', '㮝' => '㮝', '楂' => '楂', '榣' => '榣', '槪' => '槪', '檨' => '檨', '𣚣' => '𣚣', '櫛' => '櫛', '㰘' => '㰘', '次' => '次', '𣢧' => '𣢧', '歔' => '歔', '㱎' => '㱎', '歲' => '歲', '殟' => '殟', '殺' => '殺', '殻' => '殻', '𣪍' => '𣪍', '𡴋' => '𡴋', '𣫺' => '𣫺', '汎' => '汎', '𣲼' => '𣲼', '沿' => '沿', '泍' => '泍', '汧' => '汧', '洖' => '洖', '派' => '派', '海' => '海', '流' => '流', '浩' => '浩', '浸' => '浸', '涅' => '涅', '𣴞' => '𣴞', '洴' => '洴', '港' => '港', '湮' => '湮', '㴳' => '㴳', '滋' => '滋', '滇' => '滇', '𣻑' => '𣻑', '淹' => '淹', '潮' => '潮', '𣽞' => '𣽞', '𣾎' => '𣾎', '濆' => '濆', '瀹' => '瀹', '瀞' => '瀞', '瀛' => '瀛', '㶖' => '㶖', '灊' => '灊', '災' => '災', '灷' => '灷', '炭' => '炭', '𠔥' => '𠔥', '煅' => '煅', '𤉣' => '𤉣', '熜' => '熜', '𤎫' => '𤎫', '爨' => '爨', '爵' => '爵', '牐' => '牐', '𤘈' => '𤘈', '犀' => '犀', '犕' => '犕', '𤜵' => '𤜵', '𤠔' => '𤠔', '獺' => '獺', '王' => '王', '㺬' => '㺬', '玥' => '玥', '㺸' => '㺸', '㺸' => '㺸', '瑇' => '瑇', '瑜' => '瑜', '瑱' => '瑱', '璅' => '璅', '瓊' => '瓊', '㼛' => '㼛', '甤' => '甤', '𤰶' => '𤰶', '甾' => '甾', '𤲒' => '𤲒', '異' => '異', '𢆟' => '𢆟', '瘐' => '瘐', '𤾡' => '𤾡', '𤾸' => '𤾸', '𥁄' => '𥁄', '㿼' => '㿼', '䀈' => '䀈', '直' => '直', '𥃳' => '𥃳', '𥃲' => '𥃲', '𥄙' => '𥄙', '𥄳' => '𥄳', '眞' => '眞', '真' => '真', '真' => '真', '睊' => '睊', '䀹' => '䀹', '瞋' => '瞋', '䁆' => '䁆', '䂖' => '䂖', '𥐝' => '𥐝', '硎' => '硎', '碌' => '碌', '磌' => '磌', '䃣' => '䃣', '𥘦' => '𥘦', '祖' => '祖', '𥚚' => '𥚚', '𥛅' => '𥛅', '福' => '福', '秫' => '秫', '䄯' => '䄯', '穀' => '穀', '穊' => '穊', '穏' => '穏', '𥥼' => '𥥼', '𥪧' => '𥪧', '𥪧' => '𥪧', '竮' => '竮', '䈂' => '䈂', '𥮫' => '𥮫', '篆' => '篆', '築' => '築', '䈧' => '䈧', '𥲀' => '𥲀', '糒' => '糒', '䊠' => '䊠', '糨' => '糨', '糣' => '糣', '紀' => '紀', '𥾆' => '𥾆', '絣' => '絣', '䌁' => '䌁', '緇' => '緇', '縂' => '縂', '繅' => '繅', '䌴' => '䌴', '𦈨' => '𦈨', '𦉇' => '𦉇', '䍙' => '䍙', '𦋙' => '𦋙', '罺' => '罺', '𦌾' => '𦌾', '羕' => '羕', '翺' => '翺', '者' => '者', '𦓚' => '𦓚', '𦔣' => '𦔣', '聠' => '聠', '𦖨' => '𦖨', '聰' => '聰', '𣍟' => '𣍟', '䏕' => '䏕', '育' => '育', '脃' => '脃', '䐋' => '䐋', '脾' => '脾', '媵' => '媵', '𦞧' => '𦞧', '𦞵' => '𦞵', '𣎓' => '𣎓', '𣎜' => '𣎜', '舁' => '舁', '舄' => '舄', '辞' => '辞', '䑫' => '䑫', '芑' => '芑', '芋' => '芋', '芝' => '芝', '劳' => '劳', '花' => '花', '芳' => '芳', '芽' => '芽', '苦' => '苦', '𦬼' => '𦬼', '若' => '若', '茝' => '茝', '荣' => '荣', '莭' => '莭', '茣' => '茣', '莽' => '莽', '菧' => '菧', '著' => '著', '荓' => '荓', '菊' => '菊', '菌' => '菌', '菜' => '菜', '𦰶' => '𦰶', '𦵫' => '𦵫', '𦳕' => '𦳕', '䔫' => '䔫', '蓱' => '蓱', '蓳' => '蓳', '蔖' => '蔖', '𧏊' => '𧏊', '蕤' => '蕤', '𦼬' => '𦼬', '䕝' => '䕝', '䕡' => '䕡', '𦾱' => '𦾱', '𧃒' => '𧃒', '䕫' => '䕫', '虐' => '虐', '虜' => '虜', '虧' => '虧', '虩' => '虩', '蚩' => '蚩', '蚈' => '蚈', '蜎' => '蜎', '蛢' => '蛢', '蝹' => '蝹', '蜨' => '蜨', '蝫' => '蝫', '螆' => '螆', '䗗' => '䗗', '蟡' => '蟡', '蠁' => '蠁', '䗹' => '䗹', '衠' => '衠', '衣' => '衣', '𧙧' => '𧙧', '裗' => '裗', '裞' => '裞', '䘵' => '䘵', '裺' => '裺', '㒻' => '㒻', '𧢮' => '𧢮', '𧥦' => '𧥦', '䚾' => '䚾', '䛇' => '䛇', '誠' => '誠', '諭' => '諭', '變' => '變', '豕' => '豕', '𧲨' => '𧲨', '貫' => '貫', '賁' => '賁', '贛' => '贛', '起' => '起', '𧼯' => '𧼯', '𠠄' => '𠠄', '跋' => '跋', '趼' => '趼', '跰' => '跰', '𠣞' => '𠣞', '軔' => '軔', '輸' => '輸', '𨗒' => '𨗒', '𨗭' => '𨗭', '邔' => '邔', '郱' => '郱', '鄑' => '鄑', '𨜮' => '𨜮', '鄛' => '鄛', '鈸' => '鈸', '鋗' => '鋗', '鋘' => '鋘', '鉼' => '鉼', '鏹' => '鏹', '鐕' => '鐕', '𨯺' => '𨯺', '開' => '開', '䦕' => '䦕', '閷' => '閷', '𨵷' => '𨵷', '䧦' => '䧦', '雃' => '雃', '嶲' => '嶲', '霣' => '霣', '𩅅' => '𩅅', '𩈚' => '𩈚', '䩮' => '䩮', '䩶' => '䩶', '韠' => '韠', '𩐊' => '𩐊', '䪲' => '䪲', '𩒖' => '𩒖', '頋' => '頋', '頋' => '頋', '頩' => '頩', '𩖶' => '𩖶', '飢' => '飢', '䬳' => '䬳', '餩' => '餩', '馧' => '馧', '駂' => '駂', '駾' => '駾', '䯎' => '䯎', '𩬰' => '𩬰', '鬒' => '鬒', '鱀' => '鱀', '鳽' => '鳽', '䳎' => '䳎', '䳭' => '䳭', '鵧' => '鵧', '𪃎' => '𪃎', '䳸' => '䳸', '𪄅' => '𪄅', '𪈎' => '𪈎', '𪊑' => '𪊑', '麻' => '麻', '䵖' => '䵖', '黹' => '黹', '黾' => '黾', '鼅' => '鼅', '鼏' => '鼏', '鼖' => '鼖', '鼻' => '鼻', '𪘀' => '𪘀', ); = 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) { @trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED); } $options = array( 'CheckHyphens' => true, 'CheckBidi' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 !== ($options & self::IDNA_CHECK_BIDI), 'CheckJoiners' => self::INTL_IDNA_VARIANT_UTS46 === $variant && 0 !== ($options & self::IDNA_CHECK_CONTEXTJ), 'UseSTD3ASCIIRules' => 0 !== ($options & self::IDNA_USE_STD3_RULES), 'Transitional_Processing' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 === ($options & self::IDNA_NONTRANSITIONAL_TO_ASCII), 'VerifyDnsLength' => true, ); $info = new Info(); $labels = self::process((string) $domainName, $options, $info); foreach ($labels as $i => $label) { if (1 === preg_match('/[^\x00-\x7F]/', $label)) { try { $label = 'xn--'.self::punycodeEncode($label); } catch (Exception $e) { $info->errors |= self::ERROR_PUNYCODE; } $labels[$i] = $label; } } if ($options['VerifyDnsLength']) { self::validateDomainAndLabelLength($labels, $info); } $idna_info = array( 'result' => implode('.', $labels), 'isTransitionalDifferent' => $info->transitionalDifferent, 'errors' => $info->errors, ); return 0 === $info->errors ? $idna_info['result'] : false; } public static function idn_to_utf8($domainName, $options = self::IDNA_DEFAULT, $variant = self::INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) { @trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED); } $info = new Info(); $labels = self::process((string) $domainName, array( 'CheckHyphens' => true, 'CheckBidi' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 !== ($options & self::IDNA_CHECK_BIDI), 'CheckJoiners' => self::INTL_IDNA_VARIANT_UTS46 === $variant && 0 !== ($options & self::IDNA_CHECK_CONTEXTJ), 'UseSTD3ASCIIRules' => 0 !== ($options & self::IDNA_USE_STD3_RULES), 'Transitional_Processing' => self::INTL_IDNA_VARIANT_2003 === $variant || 0 === ($options & self::IDNA_NONTRANSITIONAL_TO_UNICODE), ), $info); $idna_info = array( 'result' => implode('.', $labels), 'isTransitionalDifferent' => $info->transitionalDifferent, 'errors' => $info->errors, ); return 0 === $info->errors ? $idna_info['result'] : false; } private static function isValidContextJ(array $codePoints, $label) { if (!isset(self::$virama)) { self::$virama = require __DIR__.\DIRECTORY_SEPARATOR.'Resources'.\DIRECTORY_SEPARATOR.'unidata'.\DIRECTORY_SEPARATOR.'virama.php'; } $offset = 0; foreach ($codePoints as $i => $codePoint) { if (0x200C !== $codePoint && 0x200D !== $codePoint) { continue; } if (!isset($codePoints[$i - 1])) { return false; } if (isset(self::$virama[$codePoints[$i - 1]])) { continue; } if (0x200C === $codePoint && 1 === preg_match(Regex::ZWNJ, $label, $matches, PREG_OFFSET_CAPTURE, $offset)) { $offset += \strlen($matches[1][0]); continue; } return false; } return true; } private static function mapCodePoints($input, array $options, Info $info) { $str = ''; $useSTD3ASCIIRules = $options['UseSTD3ASCIIRules']; $transitional = $options['Transitional_Processing']; foreach (self::utf8Decode($input) as $codePoint) { $data = self::lookupCodePointStatus($codePoint, $useSTD3ASCIIRules); switch ($data['status']) { case 'disallowed': $info->errors |= self::ERROR_DISALLOWED; case 'valid': $str .= mb_chr($codePoint, 'utf-8'); break; case 'ignored': break; case 'mapped': $str .= $data['mapping']; break; case 'deviation': $info->transitionalDifferent = true; $str .= ($transitional ? $data['mapping'] : mb_chr($codePoint, 'utf-8')); break; } } return $str; } private static function process($domain, array $options, Info $info) { $checkForEmptyLabels = !isset($options['VerifyDnsLength']) || $options['VerifyDnsLength']; if ($checkForEmptyLabels && '' === $domain) { $info->errors |= self::ERROR_EMPTY_LABEL; return array($domain); } $domain = self::mapCodePoints($domain, $options, $info); if (!Normalizer::isNormalized($domain, Normalizer::FORM_C)) { $domain = Normalizer::normalize($domain, Normalizer::FORM_C); } $labels = explode('.', $domain); $lastLabelIndex = \count($labels) - 1; foreach ($labels as $i => $label) { $validationOptions = $options; if ('xn--' === substr($label, 0, 4)) { try { $label = self::punycodeDecode(substr($label, 4)); } catch (Exception $e) { $info->errors |= self::ERROR_PUNYCODE; continue; } $validationOptions['Transitional_Processing'] = false; $labels[$i] = $label; } self::validateLabel($label, $info, $validationOptions, $i > 0 && $i === $lastLabelIndex); } if ($info->bidiDomain && !$info->validBidiDomain) { $info->errors |= self::ERROR_BIDI; } return $labels; } private static function validateBidiLabel($label, Info $info) { if (1 === preg_match(Regex::RTL_LABEL, $label)) { $info->bidiDomain = true; if (1 !== preg_match(Regex::BIDI_STEP_1_RTL, $label)) { $info->validBidiDomain = false; return; } if (1 === preg_match(Regex::BIDI_STEP_2, $label)) { $info->validBidiDomain = false; return; } if (1 !== preg_match(Regex::BIDI_STEP_3, $label)) { $info->validBidiDomain = false; return; } if (1 === preg_match(Regex::BIDI_STEP_4_AN, $label) && 1 === preg_match(Regex::BIDI_STEP_4_EN, $label)) { $info->validBidiDomain = false; return; } return; } if (1 !== preg_match(Regex::BIDI_STEP_1_LTR, $label)) { $info->validBidiDomain = false; return; } if (1 === preg_match(Regex::BIDI_STEP_5, $label)) { $info->validBidiDomain = false; return; } if (1 !== preg_match(Regex::BIDI_STEP_6, $label)) { $info->validBidiDomain = false; return; } } private static function validateDomainAndLabelLength(array $labels, Info $info) { $maxDomainSize = self::MAX_DOMAIN_SIZE; $length = \count($labels); $domainLength = $length - 1; if ($length > 1 && '' === $labels[$length - 1]) { ++$maxDomainSize; --$length; } for ($i = 0; $i < $length; ++$i) { $bytes = \strlen($labels[$i]); $domainLength += $bytes; if ($bytes > self::MAX_LABEL_SIZE) { $info->errors |= self::ERROR_LABEL_TOO_LONG; } } if ($domainLength > $maxDomainSize) { $info->errors |= self::ERROR_DOMAIN_NAME_TOO_LONG; } } private static function validateLabel($label, Info $info, array $options, $canBeEmpty) { if ('' === $label) { if (!$canBeEmpty && (!isset($options['VerifyDnsLength']) || $options['VerifyDnsLength'])) { $info->errors |= self::ERROR_EMPTY_LABEL; } return; } if (!Normalizer::isNormalized($label, Normalizer::FORM_C)) { $info->errors |= self::ERROR_INVALID_ACE_LABEL; } $codePoints = self::utf8Decode($label); if ($options['CheckHyphens']) { if (isset($codePoints[2], $codePoints[3]) && 0x002D === $codePoints[2] && 0x002D === $codePoints[3]) { $info->errors |= self::ERROR_HYPHEN_3_4; } if ('-' === substr($label, 0, 1)) { $info->errors |= self::ERROR_LEADING_HYPHEN; } if ('-' === substr($label, -1, 1)) { $info->errors |= self::ERROR_TRAILING_HYPHEN; } } if (false !== strpos($label, '.')) { $info->errors |= self::ERROR_LABEL_HAS_DOT; } if (1 === preg_match(Regex::COMBINING_MARK, $label)) { $info->errors |= self::ERROR_LEADING_COMBINING_MARK; } $transitional = $options['Transitional_Processing']; $useSTD3ASCIIRules = $options['UseSTD3ASCIIRules']; foreach ($codePoints as $codePoint) { $data = self::lookupCodePointStatus($codePoint, $useSTD3ASCIIRules); $status = $data['status']; if ('valid' === $status || (!$transitional && 'deviation' === $status)) { continue; } $info->errors |= self::ERROR_DISALLOWED; break; } if ($options['CheckJoiners'] && !self::isValidContextJ($codePoints, $label)) { $info->errors |= self::ERROR_CONTEXTJ; } if ($options['CheckBidi'] && (!$info->bidiDomain || $info->validBidiDomain)) { self::validateBidiLabel($label, $info); } } private static function punycodeDecode($input) { $n = self::INITIAL_N; $out = 0; $i = 0; $bias = self::INITIAL_BIAS; $lastDelimIndex = strrpos($input, self::DELIMITER); $b = false === $lastDelimIndex ? 0 : $lastDelimIndex; $inputLength = \strlen($input); $output = array(); $bytes = array_map('ord', str_split($input)); for ($j = 0; $j < $b; ++$j) { if ($bytes[$j] > 0x7F) { throw new Exception('Invalid input'); } $output[$out++] = $input[$j]; } if ($b > 0) { ++$b; } for ($in = $b; $in < $inputLength; ++$out) { $oldi = $i; $w = 1; for ($k = self::BASE; ; $k += self::BASE) { if ($in >= $inputLength) { throw new Exception('Invalid input'); } $digit = self::$basicToDigit[$bytes[$in++] & 0xFF]; if ($digit < 0) { throw new Exception('Invalid input'); } if ($digit > intdiv(self::MAX_INT - $i, $w)) { throw new Exception('Integer overflow'); } $i += $digit * $w; if ($k <= $bias) { $t = self::TMIN; } elseif ($k >= $bias + self::TMAX) { $t = self::TMAX; } else { $t = $k - $bias; } if ($digit < $t) { break; } $baseMinusT = self::BASE - $t; if ($w > intdiv(self::MAX_INT, $baseMinusT)) { throw new Exception('Integer overflow'); } $w *= $baseMinusT; } $outPlusOne = $out + 1; $bias = self::adaptBias($i - $oldi, $outPlusOne, 0 === $oldi); if (intdiv($i, $outPlusOne) > self::MAX_INT - $n) { throw new Exception('Integer overflow'); } $n += intdiv($i, $outPlusOne); $i %= $outPlusOne; array_splice($output, $i++, 0, array(mb_chr($n, 'utf-8'))); } return implode('', $output); } private static function punycodeEncode($input) { $n = self::INITIAL_N; $delta = 0; $out = 0; $bias = self::INITIAL_BIAS; $inputLength = 0; $output = ''; $iter = self::utf8Decode($input); foreach ($iter as $codePoint) { ++$inputLength; if ($codePoint < 0x80) { $output .= \chr($codePoint); ++$out; } } $h = $out; $b = $out; if ($b > 0) { $output .= self::DELIMITER; ++$out; } while ($h < $inputLength) { $m = self::MAX_INT; foreach ($iter as $codePoint) { if ($codePoint >= $n && $codePoint < $m) { $m = $codePoint; } } if ($m - $n > intdiv(self::MAX_INT - $delta, $h + 1)) { throw new Exception('Integer overflow'); } $delta += ($m - $n) * ($h + 1); $n = $m; foreach ($iter as $codePoint) { if ($codePoint < $n && 0 === ++$delta) { throw new Exception('Integer overflow'); } elseif ($codePoint === $n) { $q = $delta; for ($k = self::BASE; ; $k += self::BASE) { if ($k <= $bias) { $t = self::TMIN; } elseif ($k >= $bias + self::TMAX) { $t = self::TMAX; } else { $t = $k - $bias; } if ($q < $t) { break; } $qMinusT = $q - $t; $baseMinusT = self::BASE - $t; $output .= self::encodeDigit($t + ($qMinusT) % ($baseMinusT), false); ++$out; $q = intdiv($qMinusT, $baseMinusT); } $output .= self::encodeDigit($q, false); ++$out; $bias = self::adaptBias($delta, $h + 1, $h === $b); $delta = 0; ++$h; } } ++$delta; ++$n; } return $output; } private static function adaptBias($delta, $numPoints, $firstTime) { $delta = $firstTime ? intdiv($delta, self::DAMP) : $delta >> 1; $delta += intdiv($delta, $numPoints); $k = 0; while ($delta > ((self::BASE - self::TMIN) * self::TMAX) >> 1) { $delta = intdiv($delta, self::BASE - self::TMIN); $k += self::BASE; } return $k + intdiv((self::BASE - self::TMIN + 1) * $delta, $delta + self::SKEW); } private static function encodeDigit($d, $flag) { return \chr($d + 22 + 75 * ($d < 26 ? 1 : 0) - (($flag ? 1 : 0) << 5)); } private static function utf8Decode($input) { $bytesSeen = 0; $bytesNeeded = 0; $lowerBoundary = 0x80; $upperBoundary = 0xBF; $codePoint = 0; $codePoints = array(); $length = \strlen($input); for ($i = 0; $i < $length; ++$i) { $byte = \ord($input[$i]); if (0 === $bytesNeeded) { if ($byte >= 0x00 && $byte <= 0x7F) { $codePoints[] = $byte; continue; } if ($byte >= 0xC2 && $byte <= 0xDF) { $bytesNeeded = 1; $codePoint = $byte & 0x1F; } elseif ($byte >= 0xE0 && $byte <= 0xEF) { if (0xE0 === $byte) { $lowerBoundary = 0xA0; } elseif (0xED === $byte) { $upperBoundary = 0x9F; } $bytesNeeded = 2; $codePoint = $byte & 0xF; } elseif ($byte >= 0xF0 && $byte <= 0xF4) { if (0xF0 === $byte) { $lowerBoundary = 0x90; } elseif (0xF4 === $byte) { $upperBoundary = 0x8F; } $bytesNeeded = 3; $codePoint = $byte & 0x7; } else { $codePoints[] = 0xFFFD; } continue; } if ($byte < $lowerBoundary || $byte > $upperBoundary) { $codePoint = 0; $bytesNeeded = 0; $bytesSeen = 0; $lowerBoundary = 0x80; $upperBoundary = 0xBF; --$i; $codePoints[] = 0xFFFD; continue; } $lowerBoundary = 0x80; $upperBoundary = 0xBF; $codePoint = ($codePoint << 6) | ($byte & 0x3F); if (++$bytesSeen !== $bytesNeeded) { continue; } $codePoints[] = $codePoint; $codePoint = 0; $bytesNeeded = 0; $bytesSeen = 0; } if (0 !== $bytesNeeded) { $codePoints[] = 0xFFFD; } return $codePoints; } private static function lookupCodePointStatus($codePoint, $useSTD3ASCIIRules) { if (!self::$mappingTableLoaded) { self::$mappingTableLoaded = true; self::$mapped = require __DIR__.'/Resources/unidata/mapped.php'; self::$ignored = require __DIR__.'/Resources/unidata/ignored.php'; self::$deviation = require __DIR__.'/Resources/unidata/deviation.php'; self::$disallowed = require __DIR__.'/Resources/unidata/disallowed.php'; self::$disallowed_STD3_mapped = require __DIR__.'/Resources/unidata/disallowed_STD3_mapped.php'; self::$disallowed_STD3_valid = require __DIR__.'/Resources/unidata/disallowed_STD3_valid.php'; } if (isset(self::$mapped[$codePoint])) { return array('status' => 'mapped', 'mapping' => self::$mapped[$codePoint]); } if (isset(self::$ignored[$codePoint])) { return array('status' => 'ignored'); } if (isset(self::$deviation[$codePoint])) { return array('status' => 'deviation', 'mapping' => self::$deviation[$codePoint]); } if (isset(self::$disallowed[$codePoint]) || DisallowedRanges::inRange($codePoint)) { return array('status' => 'disallowed'); } $isDisallowedMapped = isset(self::$disallowed_STD3_mapped[$codePoint]); if ($isDisallowedMapped || isset(self::$disallowed_STD3_valid[$codePoint])) { $status = 'disallowed'; if (!$useSTD3ASCIIRules) { $status = $isDisallowedMapped ? 'mapped' : 'valid'; } if ($isDisallowedMapped) { return array('status' => $status, 'mapping' => self::$disallowed_STD3_mapped[$codePoint]); } return array('status' => $status); } return array('status' => 'valid'); } } true, 1 => true, 2 => true, 3 => true, 4 => true, 5 => true, 6 => true, 7 => true, 8 => true, 9 => true, 10 => true, 11 => true, 12 => true, 13 => true, 14 => true, 15 => true, 16 => true, 17 => true, 18 => true, 19 => true, 20 => true, 21 => true, 22 => true, 23 => true, 24 => true, 25 => true, 26 => true, 27 => true, 28 => true, 29 => true, 30 => true, 31 => true, 32 => true, 33 => true, 34 => true, 35 => true, 36 => true, 37 => true, 38 => true, 39 => true, 40 => true, 41 => true, 42 => true, 43 => true, 44 => true, 47 => true, 58 => true, 59 => true, 60 => true, 61 => true, 62 => true, 63 => true, 64 => true, 91 => true, 92 => true, 93 => true, 94 => true, 95 => true, 96 => true, 123 => true, 124 => true, 125 => true, 126 => true, 127 => true, 8800 => true, 8814 => true, 8815 => true, ); = 128 && $codePoint <= 159) { return true; } if ($codePoint >= 2155 && $codePoint <= 2207) { return true; } if ($codePoint >= 3676 && $codePoint <= 3712) { return true; } if ($codePoint >= 3808 && $codePoint <= 3839) { return true; } if ($codePoint >= 4059 && $codePoint <= 4095) { return true; } if ($codePoint >= 4256 && $codePoint <= 4293) { return true; } if ($codePoint >= 6849 && $codePoint <= 6911) { return true; } if ($codePoint >= 11859 && $codePoint <= 11903) { return true; } if ($codePoint >= 42955 && $codePoint <= 42996) { return true; } if ($codePoint >= 55296 && $codePoint <= 57343) { return true; } if ($codePoint >= 57344 && $codePoint <= 63743) { return true; } if ($codePoint >= 64218 && $codePoint <= 64255) { return true; } if ($codePoint >= 64976 && $codePoint <= 65007) { return true; } if ($codePoint >= 65630 && $codePoint <= 65663) { return true; } if ($codePoint >= 65953 && $codePoint <= 65999) { return true; } if ($codePoint >= 66046 && $codePoint <= 66175) { return true; } if ($codePoint >= 66518 && $codePoint <= 66559) { return true; } if ($codePoint >= 66928 && $codePoint <= 67071) { return true; } if ($codePoint >= 67432 && $codePoint <= 67583) { return true; } if ($codePoint >= 67760 && $codePoint <= 67807) { return true; } if ($codePoint >= 67904 && $codePoint <= 67967) { return true; } if ($codePoint >= 68256 && $codePoint <= 68287) { return true; } if ($codePoint >= 68528 && $codePoint <= 68607) { return true; } if ($codePoint >= 68681 && $codePoint <= 68735) { return true; } if ($codePoint >= 68922 && $codePoint <= 69215) { return true; } if ($codePoint >= 69298 && $codePoint <= 69375) { return true; } if ($codePoint >= 69466 && $codePoint <= 69551) { return true; } if ($codePoint >= 70207 && $codePoint <= 70271) { return true; } if ($codePoint >= 70517 && $codePoint <= 70655) { return true; } if ($codePoint >= 70874 && $codePoint <= 71039) { return true; } if ($codePoint >= 71134 && $codePoint <= 71167) { return true; } if ($codePoint >= 71370 && $codePoint <= 71423) { return true; } if ($codePoint >= 71488 && $codePoint <= 71679) { return true; } if ($codePoint >= 71740 && $codePoint <= 71839) { return true; } if ($codePoint >= 72026 && $codePoint <= 72095) { return true; } if ($codePoint >= 72441 && $codePoint <= 72703) { return true; } if ($codePoint >= 72887 && $codePoint <= 72959) { return true; } if ($codePoint >= 73130 && $codePoint <= 73439) { return true; } if ($codePoint >= 73465 && $codePoint <= 73647) { return true; } if ($codePoint >= 74650 && $codePoint <= 74751) { return true; } if ($codePoint >= 75076 && $codePoint <= 77823) { return true; } if ($codePoint >= 78905 && $codePoint <= 82943) { return true; } if ($codePoint >= 83527 && $codePoint <= 92159) { return true; } if ($codePoint >= 92784 && $codePoint <= 92879) { return true; } if ($codePoint >= 93072 && $codePoint <= 93759) { return true; } if ($codePoint >= 93851 && $codePoint <= 93951) { return true; } if ($codePoint >= 94112 && $codePoint <= 94175) { return true; } if ($codePoint >= 101590 && $codePoint <= 101631) { return true; } if ($codePoint >= 101641 && $codePoint <= 110591) { return true; } if ($codePoint >= 110879 && $codePoint <= 110927) { return true; } if ($codePoint >= 111356 && $codePoint <= 113663) { return true; } if ($codePoint >= 113828 && $codePoint <= 118783) { return true; } if ($codePoint >= 119366 && $codePoint <= 119519) { return true; } if ($codePoint >= 119673 && $codePoint <= 119807) { return true; } if ($codePoint >= 121520 && $codePoint <= 122879) { return true; } if ($codePoint >= 122923 && $codePoint <= 123135) { return true; } if ($codePoint >= 123216 && $codePoint <= 123583) { return true; } if ($codePoint >= 123648 && $codePoint <= 124927) { return true; } if ($codePoint >= 125143 && $codePoint <= 125183) { return true; } if ($codePoint >= 125280 && $codePoint <= 126064) { return true; } if ($codePoint >= 126133 && $codePoint <= 126208) { return true; } if ($codePoint >= 126270 && $codePoint <= 126463) { return true; } if ($codePoint >= 126652 && $codePoint <= 126703) { return true; } if ($codePoint >= 126706 && $codePoint <= 126975) { return true; } if ($codePoint >= 127406 && $codePoint <= 127461) { return true; } if ($codePoint >= 127590 && $codePoint <= 127743) { return true; } if ($codePoint >= 129202 && $codePoint <= 129279) { return true; } if ($codePoint >= 129751 && $codePoint <= 129791) { return true; } if ($codePoint >= 129995 && $codePoint <= 130031) { return true; } if ($codePoint >= 130042 && $codePoint <= 131069) { return true; } if ($codePoint >= 173790 && $codePoint <= 173823) { return true; } if ($codePoint >= 191457 && $codePoint <= 194559) { return true; } if ($codePoint >= 195102 && $codePoint <= 196605) { return true; } if ($codePoint >= 201547 && $codePoint <= 262141) { return true; } if ($codePoint >= 262144 && $codePoint <= 327677) { return true; } if ($codePoint >= 327680 && $codePoint <= 393213) { return true; } if ($codePoint >= 393216 && $codePoint <= 458749) { return true; } if ($codePoint >= 458752 && $codePoint <= 524285) { return true; } if ($codePoint >= 524288 && $codePoint <= 589821) { return true; } if ($codePoint >= 589824 && $codePoint <= 655357) { return true; } if ($codePoint >= 655360 && $codePoint <= 720893) { return true; } if ($codePoint >= 720896 && $codePoint <= 786429) { return true; } if ($codePoint >= 786432 && $codePoint <= 851965) { return true; } if ($codePoint >= 851968 && $codePoint <= 917501) { return true; } if ($codePoint >= 917536 && $codePoint <= 917631) { return true; } if ($codePoint >= 917632 && $codePoint <= 917759) { return true; } if ($codePoint >= 918000 && $codePoint <= 983037) { return true; } if ($codePoint >= 983040 && $codePoint <= 1048573) { return true; } if ($codePoint >= 1048576 && $codePoint <= 1114109) { return true; } return false; } } true, 889 => true, 896 => true, 897 => true, 898 => true, 899 => true, 907 => true, 909 => true, 930 => true, 1216 => true, 1328 => true, 1367 => true, 1368 => true, 1419 => true, 1420 => true, 1424 => true, 1480 => true, 1481 => true, 1482 => true, 1483 => true, 1484 => true, 1485 => true, 1486 => true, 1487 => true, 1515 => true, 1516 => true, 1517 => true, 1518 => true, 1525 => true, 1526 => true, 1527 => true, 1528 => true, 1529 => true, 1530 => true, 1531 => true, 1532 => true, 1533 => true, 1534 => true, 1535 => true, 1536 => true, 1537 => true, 1538 => true, 1539 => true, 1540 => true, 1541 => true, 1564 => true, 1565 => true, 1757 => true, 1806 => true, 1807 => true, 1867 => true, 1868 => true, 1970 => true, 1971 => true, 1972 => true, 1973 => true, 1974 => true, 1975 => true, 1976 => true, 1977 => true, 1978 => true, 1979 => true, 1980 => true, 1981 => true, 1982 => true, 1983 => true, 2043 => true, 2044 => true, 2094 => true, 2095 => true, 2111 => true, 2140 => true, 2141 => true, 2143 => true, 2229 => true, 2248 => true, 2249 => true, 2250 => true, 2251 => true, 2252 => true, 2253 => true, 2254 => true, 2255 => true, 2256 => true, 2257 => true, 2258 => true, 2274 => true, 2436 => true, 2445 => true, 2446 => true, 2449 => true, 2450 => true, 2473 => true, 2481 => true, 2483 => true, 2484 => true, 2485 => true, 2490 => true, 2491 => true, 2501 => true, 2502 => true, 2505 => true, 2506 => true, 2511 => true, 2512 => true, 2513 => true, 2514 => true, 2515 => true, 2516 => true, 2517 => true, 2518 => true, 2520 => true, 2521 => true, 2522 => true, 2523 => true, 2526 => true, 2532 => true, 2533 => true, 2559 => true, 2560 => true, 2564 => true, 2571 => true, 2572 => true, 2573 => true, 2574 => true, 2577 => true, 2578 => true, 2601 => true, 2609 => true, 2612 => true, 2615 => true, 2618 => true, 2619 => true, 2621 => true, 2627 => true, 2628 => true, 2629 => true, 2630 => true, 2633 => true, 2634 => true, 2638 => true, 2639 => true, 2640 => true, 2642 => true, 2643 => true, 2644 => true, 2645 => true, 2646 => true, 2647 => true, 2648 => true, 2653 => true, 2655 => true, 2656 => true, 2657 => true, 2658 => true, 2659 => true, 2660 => true, 2661 => true, 2679 => true, 2680 => true, 2681 => true, 2682 => true, 2683 => true, 2684 => true, 2685 => true, 2686 => true, 2687 => true, 2688 => true, 2692 => true, 2702 => true, 2706 => true, 2729 => true, 2737 => true, 2740 => true, 2746 => true, 2747 => true, 2758 => true, 2762 => true, 2766 => true, 2767 => true, 2769 => true, 2770 => true, 2771 => true, 2772 => true, 2773 => true, 2774 => true, 2775 => true, 2776 => true, 2777 => true, 2778 => true, 2779 => true, 2780 => true, 2781 => true, 2782 => true, 2783 => true, 2788 => true, 2789 => true, 2802 => true, 2803 => true, 2804 => true, 2805 => true, 2806 => true, 2807 => true, 2808 => true, 2816 => true, 2820 => true, 2829 => true, 2830 => true, 2833 => true, 2834 => true, 2857 => true, 2865 => true, 2868 => true, 2874 => true, 2875 => true, 2885 => true, 2886 => true, 2889 => true, 2890 => true, 2894 => true, 2895 => true, 2896 => true, 2897 => true, 2898 => true, 2899 => true, 2900 => true, 2904 => true, 2905 => true, 2906 => true, 2907 => true, 2910 => true, 2916 => true, 2917 => true, 2936 => true, 2937 => true, 2938 => true, 2939 => true, 2940 => true, 2941 => true, 2942 => true, 2943 => true, 2944 => true, 2945 => true, 2948 => true, 2955 => true, 2956 => true, 2957 => true, 2961 => true, 2966 => true, 2967 => true, 2968 => true, 2971 => true, 2973 => true, 2976 => true, 2977 => true, 2978 => true, 2981 => true, 2982 => true, 2983 => true, 2987 => true, 2988 => true, 2989 => true, 3002 => true, 3003 => true, 3004 => true, 3005 => true, 3011 => true, 3012 => true, 3013 => true, 3017 => true, 3022 => true, 3023 => true, 3025 => true, 3026 => true, 3027 => true, 3028 => true, 3029 => true, 3030 => true, 3032 => true, 3033 => true, 3034 => true, 3035 => true, 3036 => true, 3037 => true, 3038 => true, 3039 => true, 3040 => true, 3041 => true, 3042 => true, 3043 => true, 3044 => true, 3045 => true, 3067 => true, 3068 => true, 3069 => true, 3070 => true, 3071 => true, 3085 => true, 3089 => true, 3113 => true, 3130 => true, 3131 => true, 3132 => true, 3141 => true, 3145 => true, 3150 => true, 3151 => true, 3152 => true, 3153 => true, 3154 => true, 3155 => true, 3156 => true, 3159 => true, 3163 => true, 3164 => true, 3165 => true, 3166 => true, 3167 => true, 3172 => true, 3173 => true, 3184 => true, 3185 => true, 3186 => true, 3187 => true, 3188 => true, 3189 => true, 3190 => true, 3213 => true, 3217 => true, 3241 => true, 3252 => true, 3258 => true, 3259 => true, 3269 => true, 3273 => true, 3278 => true, 3279 => true, 3280 => true, 3281 => true, 3282 => true, 3283 => true, 3284 => true, 3287 => true, 3288 => true, 3289 => true, 3290 => true, 3291 => true, 3292 => true, 3293 => true, 3295 => true, 3300 => true, 3301 => true, 3312 => true, 3315 => true, 3316 => true, 3317 => true, 3318 => true, 3319 => true, 3320 => true, 3321 => true, 3322 => true, 3323 => true, 3324 => true, 3325 => true, 3326 => true, 3327 => true, 3341 => true, 3345 => true, 3397 => true, 3401 => true, 3408 => true, 3409 => true, 3410 => true, 3411 => true, 3428 => true, 3429 => true, 3456 => true, 3460 => true, 3479 => true, 3480 => true, 3481 => true, 3506 => true, 3516 => true, 3518 => true, 3519 => true, 3527 => true, 3528 => true, 3529 => true, 3531 => true, 3532 => true, 3533 => true, 3534 => true, 3541 => true, 3543 => true, 3552 => true, 3553 => true, 3554 => true, 3555 => true, 3556 => true, 3557 => true, 3568 => true, 3569 => true, 3573 => true, 3574 => true, 3575 => true, 3576 => true, 3577 => true, 3578 => true, 3579 => true, 3580 => true, 3581 => true, 3582 => true, 3583 => true, 3584 => true, 3643 => true, 3644 => true, 3645 => true, 3646 => true, 3715 => true, 3717 => true, 3723 => true, 3748 => true, 3750 => true, 3774 => true, 3775 => true, 3781 => true, 3783 => true, 3790 => true, 3791 => true, 3802 => true, 3803 => true, 3912 => true, 3949 => true, 3950 => true, 3951 => true, 3952 => true, 3992 => true, 4029 => true, 4045 => true, 4294 => true, 4296 => true, 4297 => true, 4298 => true, 4299 => true, 4300 => true, 4302 => true, 4303 => true, 4447 => true, 4448 => true, 4681 => true, 4686 => true, 4687 => true, 4695 => true, 4697 => true, 4702 => true, 4703 => true, 4745 => true, 4750 => true, 4751 => true, 4785 => true, 4790 => true, 4791 => true, 4799 => true, 4801 => true, 4806 => true, 4807 => true, 4823 => true, 4881 => true, 4886 => true, 4887 => true, 4955 => true, 4956 => true, 4989 => true, 4990 => true, 4991 => true, 5018 => true, 5019 => true, 5020 => true, 5021 => true, 5022 => true, 5023 => true, 5110 => true, 5111 => true, 5118 => true, 5119 => true, 5760 => true, 5789 => true, 5790 => true, 5791 => true, 5881 => true, 5882 => true, 5883 => true, 5884 => true, 5885 => true, 5886 => true, 5887 => true, 5901 => true, 5909 => true, 5910 => true, 5911 => true, 5912 => true, 5913 => true, 5914 => true, 5915 => true, 5916 => true, 5917 => true, 5918 => true, 5919 => true, 5943 => true, 5944 => true, 5945 => true, 5946 => true, 5947 => true, 5948 => true, 5949 => true, 5950 => true, 5951 => true, 5972 => true, 5973 => true, 5974 => true, 5975 => true, 5976 => true, 5977 => true, 5978 => true, 5979 => true, 5980 => true, 5981 => true, 5982 => true, 5983 => true, 5997 => true, 6001 => true, 6004 => true, 6005 => true, 6006 => true, 6007 => true, 6008 => true, 6009 => true, 6010 => true, 6011 => true, 6012 => true, 6013 => true, 6014 => true, 6015 => true, 6068 => true, 6069 => true, 6110 => true, 6111 => true, 6122 => true, 6123 => true, 6124 => true, 6125 => true, 6126 => true, 6127 => true, 6138 => true, 6139 => true, 6140 => true, 6141 => true, 6142 => true, 6143 => true, 6150 => true, 6158 => true, 6159 => true, 6170 => true, 6171 => true, 6172 => true, 6173 => true, 6174 => true, 6175 => true, 6265 => true, 6266 => true, 6267 => true, 6268 => true, 6269 => true, 6270 => true, 6271 => true, 6315 => true, 6316 => true, 6317 => true, 6318 => true, 6319 => true, 6390 => true, 6391 => true, 6392 => true, 6393 => true, 6394 => true, 6395 => true, 6396 => true, 6397 => true, 6398 => true, 6399 => true, 6431 => true, 6444 => true, 6445 => true, 6446 => true, 6447 => true, 6460 => true, 6461 => true, 6462 => true, 6463 => true, 6465 => true, 6466 => true, 6467 => true, 6510 => true, 6511 => true, 6517 => true, 6518 => true, 6519 => true, 6520 => true, 6521 => true, 6522 => true, 6523 => true, 6524 => true, 6525 => true, 6526 => true, 6527 => true, 6572 => true, 6573 => true, 6574 => true, 6575 => true, 6602 => true, 6603 => true, 6604 => true, 6605 => true, 6606 => true, 6607 => true, 6619 => true, 6620 => true, 6621 => true, 6684 => true, 6685 => true, 6751 => true, 6781 => true, 6782 => true, 6794 => true, 6795 => true, 6796 => true, 6797 => true, 6798 => true, 6799 => true, 6810 => true, 6811 => true, 6812 => true, 6813 => true, 6814 => true, 6815 => true, 6830 => true, 6831 => true, 6988 => true, 6989 => true, 6990 => true, 6991 => true, 7037 => true, 7038 => true, 7039 => true, 7156 => true, 7157 => true, 7158 => true, 7159 => true, 7160 => true, 7161 => true, 7162 => true, 7163 => true, 7224 => true, 7225 => true, 7226 => true, 7242 => true, 7243 => true, 7244 => true, 7305 => true, 7306 => true, 7307 => true, 7308 => true, 7309 => true, 7310 => true, 7311 => true, 7355 => true, 7356 => true, 7368 => true, 7369 => true, 7370 => true, 7371 => true, 7372 => true, 7373 => true, 7374 => true, 7375 => true, 7419 => true, 7420 => true, 7421 => true, 7422 => true, 7423 => true, 7674 => true, 7958 => true, 7959 => true, 7966 => true, 7967 => true, 8006 => true, 8007 => true, 8014 => true, 8015 => true, 8024 => true, 8026 => true, 8028 => true, 8030 => true, 8062 => true, 8063 => true, 8117 => true, 8133 => true, 8148 => true, 8149 => true, 8156 => true, 8176 => true, 8177 => true, 8181 => true, 8191 => true, 8206 => true, 8207 => true, 8228 => true, 8229 => true, 8230 => true, 8232 => true, 8233 => true, 8234 => true, 8235 => true, 8236 => true, 8237 => true, 8238 => true, 8289 => true, 8290 => true, 8291 => true, 8293 => true, 8294 => true, 8295 => true, 8296 => true, 8297 => true, 8298 => true, 8299 => true, 8300 => true, 8301 => true, 8302 => true, 8303 => true, 8306 => true, 8307 => true, 8335 => true, 8349 => true, 8350 => true, 8351 => true, 8384 => true, 8385 => true, 8386 => true, 8387 => true, 8388 => true, 8389 => true, 8390 => true, 8391 => true, 8392 => true, 8393 => true, 8394 => true, 8395 => true, 8396 => true, 8397 => true, 8398 => true, 8399 => true, 8433 => true, 8434 => true, 8435 => true, 8436 => true, 8437 => true, 8438 => true, 8439 => true, 8440 => true, 8441 => true, 8442 => true, 8443 => true, 8444 => true, 8445 => true, 8446 => true, 8447 => true, 8498 => true, 8579 => true, 8588 => true, 8589 => true, 8590 => true, 8591 => true, 9255 => true, 9256 => true, 9257 => true, 9258 => true, 9259 => true, 9260 => true, 9261 => true, 9262 => true, 9263 => true, 9264 => true, 9265 => true, 9266 => true, 9267 => true, 9268 => true, 9269 => true, 9270 => true, 9271 => true, 9272 => true, 9273 => true, 9274 => true, 9275 => true, 9276 => true, 9277 => true, 9278 => true, 9279 => true, 9291 => true, 9292 => true, 9293 => true, 9294 => true, 9295 => true, 9296 => true, 9297 => true, 9298 => true, 9299 => true, 9300 => true, 9301 => true, 9302 => true, 9303 => true, 9304 => true, 9305 => true, 9306 => true, 9307 => true, 9308 => true, 9309 => true, 9310 => true, 9311 => true, 9352 => true, 9353 => true, 9354 => true, 9355 => true, 9356 => true, 9357 => true, 9358 => true, 9359 => true, 9360 => true, 9361 => true, 9362 => true, 9363 => true, 9364 => true, 9365 => true, 9366 => true, 9367 => true, 9368 => true, 9369 => true, 9370 => true, 9371 => true, 11124 => true, 11125 => true, 11158 => true, 11311 => true, 11359 => true, 11508 => true, 11509 => true, 11510 => true, 11511 => true, 11512 => true, 11558 => true, 11560 => true, 11561 => true, 11562 => true, 11563 => true, 11564 => true, 11566 => true, 11567 => true, 11624 => true, 11625 => true, 11626 => true, 11627 => true, 11628 => true, 11629 => true, 11630 => true, 11633 => true, 11634 => true, 11635 => true, 11636 => true, 11637 => true, 11638 => true, 11639 => true, 11640 => true, 11641 => true, 11642 => true, 11643 => true, 11644 => true, 11645 => true, 11646 => true, 11671 => true, 11672 => true, 11673 => true, 11674 => true, 11675 => true, 11676 => true, 11677 => true, 11678 => true, 11679 => true, 11687 => true, 11695 => true, 11703 => true, 11711 => true, 11719 => true, 11727 => true, 11735 => true, 11743 => true, 11930 => true, 12020 => true, 12021 => true, 12022 => true, 12023 => true, 12024 => true, 12025 => true, 12026 => true, 12027 => true, 12028 => true, 12029 => true, 12030 => true, 12031 => true, 12246 => true, 12247 => true, 12248 => true, 12249 => true, 12250 => true, 12251 => true, 12252 => true, 12253 => true, 12254 => true, 12255 => true, 12256 => true, 12257 => true, 12258 => true, 12259 => true, 12260 => true, 12261 => true, 12262 => true, 12263 => true, 12264 => true, 12265 => true, 12266 => true, 12267 => true, 12268 => true, 12269 => true, 12270 => true, 12271 => true, 12272 => true, 12273 => true, 12274 => true, 12275 => true, 12276 => true, 12277 => true, 12278 => true, 12279 => true, 12280 => true, 12281 => true, 12282 => true, 12283 => true, 12284 => true, 12285 => true, 12286 => true, 12287 => true, 12352 => true, 12439 => true, 12440 => true, 12544 => true, 12545 => true, 12546 => true, 12547 => true, 12548 => true, 12592 => true, 12644 => true, 12687 => true, 12772 => true, 12773 => true, 12774 => true, 12775 => true, 12776 => true, 12777 => true, 12778 => true, 12779 => true, 12780 => true, 12781 => true, 12782 => true, 12783 => true, 12831 => true, 13250 => true, 13255 => true, 13272 => true, 40957 => true, 40958 => true, 40959 => true, 42125 => true, 42126 => true, 42127 => true, 42183 => true, 42184 => true, 42185 => true, 42186 => true, 42187 => true, 42188 => true, 42189 => true, 42190 => true, 42191 => true, 42540 => true, 42541 => true, 42542 => true, 42543 => true, 42544 => true, 42545 => true, 42546 => true, 42547 => true, 42548 => true, 42549 => true, 42550 => true, 42551 => true, 42552 => true, 42553 => true, 42554 => true, 42555 => true, 42556 => true, 42557 => true, 42558 => true, 42559 => true, 42744 => true, 42745 => true, 42746 => true, 42747 => true, 42748 => true, 42749 => true, 42750 => true, 42751 => true, 42944 => true, 42945 => true, 43053 => true, 43054 => true, 43055 => true, 43066 => true, 43067 => true, 43068 => true, 43069 => true, 43070 => true, 43071 => true, 43128 => true, 43129 => true, 43130 => true, 43131 => true, 43132 => true, 43133 => true, 43134 => true, 43135 => true, 43206 => true, 43207 => true, 43208 => true, 43209 => true, 43210 => true, 43211 => true, 43212 => true, 43213 => true, 43226 => true, 43227 => true, 43228 => true, 43229 => true, 43230 => true, 43231 => true, 43348 => true, 43349 => true, 43350 => true, 43351 => true, 43352 => true, 43353 => true, 43354 => true, 43355 => true, 43356 => true, 43357 => true, 43358 => true, 43389 => true, 43390 => true, 43391 => true, 43470 => true, 43482 => true, 43483 => true, 43484 => true, 43485 => true, 43519 => true, 43575 => true, 43576 => true, 43577 => true, 43578 => true, 43579 => true, 43580 => true, 43581 => true, 43582 => true, 43583 => true, 43598 => true, 43599 => true, 43610 => true, 43611 => true, 43715 => true, 43716 => true, 43717 => true, 43718 => true, 43719 => true, 43720 => true, 43721 => true, 43722 => true, 43723 => true, 43724 => true, 43725 => true, 43726 => true, 43727 => true, 43728 => true, 43729 => true, 43730 => true, 43731 => true, 43732 => true, 43733 => true, 43734 => true, 43735 => true, 43736 => true, 43737 => true, 43738 => true, 43767 => true, 43768 => true, 43769 => true, 43770 => true, 43771 => true, 43772 => true, 43773 => true, 43774 => true, 43775 => true, 43776 => true, 43783 => true, 43784 => true, 43791 => true, 43792 => true, 43799 => true, 43800 => true, 43801 => true, 43802 => true, 43803 => true, 43804 => true, 43805 => true, 43806 => true, 43807 => true, 43815 => true, 43823 => true, 43884 => true, 43885 => true, 43886 => true, 43887 => true, 44014 => true, 44015 => true, 44026 => true, 44027 => true, 44028 => true, 44029 => true, 44030 => true, 44031 => true, 55204 => true, 55205 => true, 55206 => true, 55207 => true, 55208 => true, 55209 => true, 55210 => true, 55211 => true, 55212 => true, 55213 => true, 55214 => true, 55215 => true, 55239 => true, 55240 => true, 55241 => true, 55242 => true, 55292 => true, 55293 => true, 55294 => true, 55295 => true, 64110 => true, 64111 => true, 64263 => true, 64264 => true, 64265 => true, 64266 => true, 64267 => true, 64268 => true, 64269 => true, 64270 => true, 64271 => true, 64272 => true, 64273 => true, 64274 => true, 64280 => true, 64281 => true, 64282 => true, 64283 => true, 64284 => true, 64311 => true, 64317 => true, 64319 => true, 64322 => true, 64325 => true, 64450 => true, 64451 => true, 64452 => true, 64453 => true, 64454 => true, 64455 => true, 64456 => true, 64457 => true, 64458 => true, 64459 => true, 64460 => true, 64461 => true, 64462 => true, 64463 => true, 64464 => true, 64465 => true, 64466 => true, 64832 => true, 64833 => true, 64834 => true, 64835 => true, 64836 => true, 64837 => true, 64838 => true, 64839 => true, 64840 => true, 64841 => true, 64842 => true, 64843 => true, 64844 => true, 64845 => true, 64846 => true, 64847 => true, 64912 => true, 64913 => true, 64968 => true, 64969 => true, 64970 => true, 64971 => true, 64972 => true, 64973 => true, 64974 => true, 64975 => true, 65022 => true, 65023 => true, 65042 => true, 65049 => true, 65050 => true, 65051 => true, 65052 => true, 65053 => true, 65054 => true, 65055 => true, 65072 => true, 65106 => true, 65107 => true, 65127 => true, 65132 => true, 65133 => true, 65134 => true, 65135 => true, 65141 => true, 65277 => true, 65278 => true, 65280 => true, 65440 => true, 65471 => true, 65472 => true, 65473 => true, 65480 => true, 65481 => true, 65488 => true, 65489 => true, 65496 => true, 65497 => true, 65501 => true, 65502 => true, 65503 => true, 65511 => true, 65519 => true, 65520 => true, 65521 => true, 65522 => true, 65523 => true, 65524 => true, 65525 => true, 65526 => true, 65527 => true, 65528 => true, 65529 => true, 65530 => true, 65531 => true, 65532 => true, 65533 => true, 65534 => true, 65535 => true, 65548 => true, 65575 => true, 65595 => true, 65598 => true, 65614 => true, 65615 => true, 65787 => true, 65788 => true, 65789 => true, 65790 => true, 65791 => true, 65795 => true, 65796 => true, 65797 => true, 65798 => true, 65844 => true, 65845 => true, 65846 => true, 65935 => true, 65949 => true, 65950 => true, 65951 => true, 66205 => true, 66206 => true, 66207 => true, 66257 => true, 66258 => true, 66259 => true, 66260 => true, 66261 => true, 66262 => true, 66263 => true, 66264 => true, 66265 => true, 66266 => true, 66267 => true, 66268 => true, 66269 => true, 66270 => true, 66271 => true, 66300 => true, 66301 => true, 66302 => true, 66303 => true, 66340 => true, 66341 => true, 66342 => true, 66343 => true, 66344 => true, 66345 => true, 66346 => true, 66347 => true, 66348 => true, 66379 => true, 66380 => true, 66381 => true, 66382 => true, 66383 => true, 66427 => true, 66428 => true, 66429 => true, 66430 => true, 66431 => true, 66462 => true, 66500 => true, 66501 => true, 66502 => true, 66503 => true, 66718 => true, 66719 => true, 66730 => true, 66731 => true, 66732 => true, 66733 => true, 66734 => true, 66735 => true, 66772 => true, 66773 => true, 66774 => true, 66775 => true, 66812 => true, 66813 => true, 66814 => true, 66815 => true, 66856 => true, 66857 => true, 66858 => true, 66859 => true, 66860 => true, 66861 => true, 66862 => true, 66863 => true, 66916 => true, 66917 => true, 66918 => true, 66919 => true, 66920 => true, 66921 => true, 66922 => true, 66923 => true, 66924 => true, 66925 => true, 66926 => true, 67383 => true, 67384 => true, 67385 => true, 67386 => true, 67387 => true, 67388 => true, 67389 => true, 67390 => true, 67391 => true, 67414 => true, 67415 => true, 67416 => true, 67417 => true, 67418 => true, 67419 => true, 67420 => true, 67421 => true, 67422 => true, 67423 => true, 67590 => true, 67591 => true, 67593 => true, 67638 => true, 67641 => true, 67642 => true, 67643 => true, 67645 => true, 67646 => true, 67670 => true, 67743 => true, 67744 => true, 67745 => true, 67746 => true, 67747 => true, 67748 => true, 67749 => true, 67750 => true, 67827 => true, 67830 => true, 67831 => true, 67832 => true, 67833 => true, 67834 => true, 67868 => true, 67869 => true, 67870 => true, 67898 => true, 67899 => true, 67900 => true, 67901 => true, 67902 => true, 68024 => true, 68025 => true, 68026 => true, 68027 => true, 68048 => true, 68049 => true, 68100 => true, 68103 => true, 68104 => true, 68105 => true, 68106 => true, 68107 => true, 68116 => true, 68120 => true, 68150 => true, 68151 => true, 68155 => true, 68156 => true, 68157 => true, 68158 => true, 68169 => true, 68170 => true, 68171 => true, 68172 => true, 68173 => true, 68174 => true, 68175 => true, 68185 => true, 68186 => true, 68187 => true, 68188 => true, 68189 => true, 68190 => true, 68191 => true, 68327 => true, 68328 => true, 68329 => true, 68330 => true, 68343 => true, 68344 => true, 68345 => true, 68346 => true, 68347 => true, 68348 => true, 68349 => true, 68350 => true, 68351 => true, 68406 => true, 68407 => true, 68408 => true, 68438 => true, 68439 => true, 68467 => true, 68468 => true, 68469 => true, 68470 => true, 68471 => true, 68498 => true, 68499 => true, 68500 => true, 68501 => true, 68502 => true, 68503 => true, 68504 => true, 68509 => true, 68510 => true, 68511 => true, 68512 => true, 68513 => true, 68514 => true, 68515 => true, 68516 => true, 68517 => true, 68518 => true, 68519 => true, 68520 => true, 68787 => true, 68788 => true, 68789 => true, 68790 => true, 68791 => true, 68792 => true, 68793 => true, 68794 => true, 68795 => true, 68796 => true, 68797 => true, 68798 => true, 68799 => true, 68851 => true, 68852 => true, 68853 => true, 68854 => true, 68855 => true, 68856 => true, 68857 => true, 68904 => true, 68905 => true, 68906 => true, 68907 => true, 68908 => true, 68909 => true, 68910 => true, 68911 => true, 69247 => true, 69290 => true, 69294 => true, 69295 => true, 69416 => true, 69417 => true, 69418 => true, 69419 => true, 69420 => true, 69421 => true, 69422 => true, 69423 => true, 69580 => true, 69581 => true, 69582 => true, 69583 => true, 69584 => true, 69585 => true, 69586 => true, 69587 => true, 69588 => true, 69589 => true, 69590 => true, 69591 => true, 69592 => true, 69593 => true, 69594 => true, 69595 => true, 69596 => true, 69597 => true, 69598 => true, 69599 => true, 69623 => true, 69624 => true, 69625 => true, 69626 => true, 69627 => true, 69628 => true, 69629 => true, 69630 => true, 69631 => true, 69710 => true, 69711 => true, 69712 => true, 69713 => true, 69744 => true, 69745 => true, 69746 => true, 69747 => true, 69748 => true, 69749 => true, 69750 => true, 69751 => true, 69752 => true, 69753 => true, 69754 => true, 69755 => true, 69756 => true, 69757 => true, 69758 => true, 69821 => true, 69826 => true, 69827 => true, 69828 => true, 69829 => true, 69830 => true, 69831 => true, 69832 => true, 69833 => true, 69834 => true, 69835 => true, 69836 => true, 69837 => true, 69838 => true, 69839 => true, 69865 => true, 69866 => true, 69867 => true, 69868 => true, 69869 => true, 69870 => true, 69871 => true, 69882 => true, 69883 => true, 69884 => true, 69885 => true, 69886 => true, 69887 => true, 69941 => true, 69960 => true, 69961 => true, 69962 => true, 69963 => true, 69964 => true, 69965 => true, 69966 => true, 69967 => true, 70007 => true, 70008 => true, 70009 => true, 70010 => true, 70011 => true, 70012 => true, 70013 => true, 70014 => true, 70015 => true, 70112 => true, 70133 => true, 70134 => true, 70135 => true, 70136 => true, 70137 => true, 70138 => true, 70139 => true, 70140 => true, 70141 => true, 70142 => true, 70143 => true, 70162 => true, 70279 => true, 70281 => true, 70286 => true, 70302 => true, 70314 => true, 70315 => true, 70316 => true, 70317 => true, 70318 => true, 70319 => true, 70379 => true, 70380 => true, 70381 => true, 70382 => true, 70383 => true, 70394 => true, 70395 => true, 70396 => true, 70397 => true, 70398 => true, 70399 => true, 70404 => true, 70413 => true, 70414 => true, 70417 => true, 70418 => true, 70441 => true, 70449 => true, 70452 => true, 70458 => true, 70469 => true, 70470 => true, 70473 => true, 70474 => true, 70478 => true, 70479 => true, 70481 => true, 70482 => true, 70483 => true, 70484 => true, 70485 => true, 70486 => true, 70488 => true, 70489 => true, 70490 => true, 70491 => true, 70492 => true, 70500 => true, 70501 => true, 70509 => true, 70510 => true, 70511 => true, 70748 => true, 70754 => true, 70755 => true, 70756 => true, 70757 => true, 70758 => true, 70759 => true, 70760 => true, 70761 => true, 70762 => true, 70763 => true, 70764 => true, 70765 => true, 70766 => true, 70767 => true, 70768 => true, 70769 => true, 70770 => true, 70771 => true, 70772 => true, 70773 => true, 70774 => true, 70775 => true, 70776 => true, 70777 => true, 70778 => true, 70779 => true, 70780 => true, 70781 => true, 70782 => true, 70783 => true, 70856 => true, 70857 => true, 70858 => true, 70859 => true, 70860 => true, 70861 => true, 70862 => true, 70863 => true, 71094 => true, 71095 => true, 71237 => true, 71238 => true, 71239 => true, 71240 => true, 71241 => true, 71242 => true, 71243 => true, 71244 => true, 71245 => true, 71246 => true, 71247 => true, 71258 => true, 71259 => true, 71260 => true, 71261 => true, 71262 => true, 71263 => true, 71277 => true, 71278 => true, 71279 => true, 71280 => true, 71281 => true, 71282 => true, 71283 => true, 71284 => true, 71285 => true, 71286 => true, 71287 => true, 71288 => true, 71289 => true, 71290 => true, 71291 => true, 71292 => true, 71293 => true, 71294 => true, 71295 => true, 71353 => true, 71354 => true, 71355 => true, 71356 => true, 71357 => true, 71358 => true, 71359 => true, 71451 => true, 71452 => true, 71468 => true, 71469 => true, 71470 => true, 71471 => true, 71923 => true, 71924 => true, 71925 => true, 71926 => true, 71927 => true, 71928 => true, 71929 => true, 71930 => true, 71931 => true, 71932 => true, 71933 => true, 71934 => true, 71943 => true, 71944 => true, 71946 => true, 71947 => true, 71956 => true, 71959 => true, 71990 => true, 71993 => true, 71994 => true, 72007 => true, 72008 => true, 72009 => true, 72010 => true, 72011 => true, 72012 => true, 72013 => true, 72014 => true, 72015 => true, 72104 => true, 72105 => true, 72152 => true, 72153 => true, 72165 => true, 72166 => true, 72167 => true, 72168 => true, 72169 => true, 72170 => true, 72171 => true, 72172 => true, 72173 => true, 72174 => true, 72175 => true, 72176 => true, 72177 => true, 72178 => true, 72179 => true, 72180 => true, 72181 => true, 72182 => true, 72183 => true, 72184 => true, 72185 => true, 72186 => true, 72187 => true, 72188 => true, 72189 => true, 72190 => true, 72191 => true, 72264 => true, 72265 => true, 72266 => true, 72267 => true, 72268 => true, 72269 => true, 72270 => true, 72271 => true, 72355 => true, 72356 => true, 72357 => true, 72358 => true, 72359 => true, 72360 => true, 72361 => true, 72362 => true, 72363 => true, 72364 => true, 72365 => true, 72366 => true, 72367 => true, 72368 => true, 72369 => true, 72370 => true, 72371 => true, 72372 => true, 72373 => true, 72374 => true, 72375 => true, 72376 => true, 72377 => true, 72378 => true, 72379 => true, 72380 => true, 72381 => true, 72382 => true, 72383 => true, 72713 => true, 72759 => true, 72774 => true, 72775 => true, 72776 => true, 72777 => true, 72778 => true, 72779 => true, 72780 => true, 72781 => true, 72782 => true, 72783 => true, 72813 => true, 72814 => true, 72815 => true, 72848 => true, 72849 => true, 72872 => true, 72967 => true, 72970 => true, 73015 => true, 73016 => true, 73017 => true, 73019 => true, 73022 => true, 73032 => true, 73033 => true, 73034 => true, 73035 => true, 73036 => true, 73037 => true, 73038 => true, 73039 => true, 73050 => true, 73051 => true, 73052 => true, 73053 => true, 73054 => true, 73055 => true, 73062 => true, 73065 => true, 73103 => true, 73106 => true, 73113 => true, 73114 => true, 73115 => true, 73116 => true, 73117 => true, 73118 => true, 73119 => true, 73649 => true, 73650 => true, 73651 => true, 73652 => true, 73653 => true, 73654 => true, 73655 => true, 73656 => true, 73657 => true, 73658 => true, 73659 => true, 73660 => true, 73661 => true, 73662 => true, 73663 => true, 73714 => true, 73715 => true, 73716 => true, 73717 => true, 73718 => true, 73719 => true, 73720 => true, 73721 => true, 73722 => true, 73723 => true, 73724 => true, 73725 => true, 73726 => true, 74863 => true, 74869 => true, 74870 => true, 74871 => true, 74872 => true, 74873 => true, 74874 => true, 74875 => true, 74876 => true, 74877 => true, 74878 => true, 74879 => true, 78895 => true, 78896 => true, 78897 => true, 78898 => true, 78899 => true, 78900 => true, 78901 => true, 78902 => true, 78903 => true, 78904 => true, 92729 => true, 92730 => true, 92731 => true, 92732 => true, 92733 => true, 92734 => true, 92735 => true, 92767 => true, 92778 => true, 92779 => true, 92780 => true, 92781 => true, 92910 => true, 92911 => true, 92918 => true, 92919 => true, 92920 => true, 92921 => true, 92922 => true, 92923 => true, 92924 => true, 92925 => true, 92926 => true, 92927 => true, 92998 => true, 92999 => true, 93000 => true, 93001 => true, 93002 => true, 93003 => true, 93004 => true, 93005 => true, 93006 => true, 93007 => true, 93018 => true, 93026 => true, 93048 => true, 93049 => true, 93050 => true, 93051 => true, 93052 => true, 94027 => true, 94028 => true, 94029 => true, 94030 => true, 94088 => true, 94089 => true, 94090 => true, 94091 => true, 94092 => true, 94093 => true, 94094 => true, 94181 => true, 94182 => true, 94183 => true, 94184 => true, 94185 => true, 94186 => true, 94187 => true, 94188 => true, 94189 => true, 94190 => true, 94191 => true, 94194 => true, 94195 => true, 94196 => true, 94197 => true, 94198 => true, 94199 => true, 94200 => true, 94201 => true, 94202 => true, 94203 => true, 94204 => true, 94205 => true, 94206 => true, 94207 => true, 100344 => true, 100345 => true, 100346 => true, 100347 => true, 100348 => true, 100349 => true, 100350 => true, 100351 => true, 110931 => true, 110932 => true, 110933 => true, 110934 => true, 110935 => true, 110936 => true, 110937 => true, 110938 => true, 110939 => true, 110940 => true, 110941 => true, 110942 => true, 110943 => true, 110944 => true, 110945 => true, 110946 => true, 110947 => true, 110952 => true, 110953 => true, 110954 => true, 110955 => true, 110956 => true, 110957 => true, 110958 => true, 110959 => true, 113771 => true, 113772 => true, 113773 => true, 113774 => true, 113775 => true, 113789 => true, 113790 => true, 113791 => true, 113801 => true, 113802 => true, 113803 => true, 113804 => true, 113805 => true, 113806 => true, 113807 => true, 113818 => true, 113819 => true, 119030 => true, 119031 => true, 119032 => true, 119033 => true, 119034 => true, 119035 => true, 119036 => true, 119037 => true, 119038 => true, 119039 => true, 119079 => true, 119080 => true, 119155 => true, 119156 => true, 119157 => true, 119158 => true, 119159 => true, 119160 => true, 119161 => true, 119162 => true, 119273 => true, 119274 => true, 119275 => true, 119276 => true, 119277 => true, 119278 => true, 119279 => true, 119280 => true, 119281 => true, 119282 => true, 119283 => true, 119284 => true, 119285 => true, 119286 => true, 119287 => true, 119288 => true, 119289 => true, 119290 => true, 119291 => true, 119292 => true, 119293 => true, 119294 => true, 119295 => true, 119540 => true, 119541 => true, 119542 => true, 119543 => true, 119544 => true, 119545 => true, 119546 => true, 119547 => true, 119548 => true, 119549 => true, 119550 => true, 119551 => true, 119639 => true, 119640 => true, 119641 => true, 119642 => true, 119643 => true, 119644 => true, 119645 => true, 119646 => true, 119647 => true, 119893 => true, 119965 => true, 119968 => true, 119969 => true, 119971 => true, 119972 => true, 119975 => true, 119976 => true, 119981 => true, 119994 => true, 119996 => true, 120004 => true, 120070 => true, 120075 => true, 120076 => true, 120085 => true, 120093 => true, 120122 => true, 120127 => true, 120133 => true, 120135 => true, 120136 => true, 120137 => true, 120145 => true, 120486 => true, 120487 => true, 120780 => true, 120781 => true, 121484 => true, 121485 => true, 121486 => true, 121487 => true, 121488 => true, 121489 => true, 121490 => true, 121491 => true, 121492 => true, 121493 => true, 121494 => true, 121495 => true, 121496 => true, 121497 => true, 121498 => true, 121504 => true, 122887 => true, 122905 => true, 122906 => true, 122914 => true, 122917 => true, 123181 => true, 123182 => true, 123183 => true, 123198 => true, 123199 => true, 123210 => true, 123211 => true, 123212 => true, 123213 => true, 123642 => true, 123643 => true, 123644 => true, 123645 => true, 123646 => true, 125125 => true, 125126 => true, 125260 => true, 125261 => true, 125262 => true, 125263 => true, 125274 => true, 125275 => true, 125276 => true, 125277 => true, 126468 => true, 126496 => true, 126499 => true, 126501 => true, 126502 => true, 126504 => true, 126515 => true, 126520 => true, 126522 => true, 126524 => true, 126525 => true, 126526 => true, 126527 => true, 126528 => true, 126529 => true, 126531 => true, 126532 => true, 126533 => true, 126534 => true, 126536 => true, 126538 => true, 126540 => true, 126544 => true, 126547 => true, 126549 => true, 126550 => true, 126552 => true, 126554 => true, 126556 => true, 126558 => true, 126560 => true, 126563 => true, 126565 => true, 126566 => true, 126571 => true, 126579 => true, 126584 => true, 126589 => true, 126591 => true, 126602 => true, 126620 => true, 126621 => true, 126622 => true, 126623 => true, 126624 => true, 126628 => true, 126634 => true, 127020 => true, 127021 => true, 127022 => true, 127023 => true, 127124 => true, 127125 => true, 127126 => true, 127127 => true, 127128 => true, 127129 => true, 127130 => true, 127131 => true, 127132 => true, 127133 => true, 127134 => true, 127135 => true, 127151 => true, 127152 => true, 127168 => true, 127184 => true, 127222 => true, 127223 => true, 127224 => true, 127225 => true, 127226 => true, 127227 => true, 127228 => true, 127229 => true, 127230 => true, 127231 => true, 127232 => true, 127491 => true, 127492 => true, 127493 => true, 127494 => true, 127495 => true, 127496 => true, 127497 => true, 127498 => true, 127499 => true, 127500 => true, 127501 => true, 127502 => true, 127503 => true, 127548 => true, 127549 => true, 127550 => true, 127551 => true, 127561 => true, 127562 => true, 127563 => true, 127564 => true, 127565 => true, 127566 => true, 127567 => true, 127570 => true, 127571 => true, 127572 => true, 127573 => true, 127574 => true, 127575 => true, 127576 => true, 127577 => true, 127578 => true, 127579 => true, 127580 => true, 127581 => true, 127582 => true, 127583 => true, 128728 => true, 128729 => true, 128730 => true, 128731 => true, 128732 => true, 128733 => true, 128734 => true, 128735 => true, 128749 => true, 128750 => true, 128751 => true, 128765 => true, 128766 => true, 128767 => true, 128884 => true, 128885 => true, 128886 => true, 128887 => true, 128888 => true, 128889 => true, 128890 => true, 128891 => true, 128892 => true, 128893 => true, 128894 => true, 128895 => true, 128985 => true, 128986 => true, 128987 => true, 128988 => true, 128989 => true, 128990 => true, 128991 => true, 129004 => true, 129005 => true, 129006 => true, 129007 => true, 129008 => true, 129009 => true, 129010 => true, 129011 => true, 129012 => true, 129013 => true, 129014 => true, 129015 => true, 129016 => true, 129017 => true, 129018 => true, 129019 => true, 129020 => true, 129021 => true, 129022 => true, 129023 => true, 129036 => true, 129037 => true, 129038 => true, 129039 => true, 129096 => true, 129097 => true, 129098 => true, 129099 => true, 129100 => true, 129101 => true, 129102 => true, 129103 => true, 129114 => true, 129115 => true, 129116 => true, 129117 => true, 129118 => true, 129119 => true, 129160 => true, 129161 => true, 129162 => true, 129163 => true, 129164 => true, 129165 => true, 129166 => true, 129167 => true, 129198 => true, 129199 => true, 129401 => true, 129484 => true, 129620 => true, 129621 => true, 129622 => true, 129623 => true, 129624 => true, 129625 => true, 129626 => true, 129627 => true, 129628 => true, 129629 => true, 129630 => true, 129631 => true, 129646 => true, 129647 => true, 129653 => true, 129654 => true, 129655 => true, 129659 => true, 129660 => true, 129661 => true, 129662 => true, 129663 => true, 129671 => true, 129672 => true, 129673 => true, 129674 => true, 129675 => true, 129676 => true, 129677 => true, 129678 => true, 129679 => true, 129705 => true, 129706 => true, 129707 => true, 129708 => true, 129709 => true, 129710 => true, 129711 => true, 129719 => true, 129720 => true, 129721 => true, 129722 => true, 129723 => true, 129724 => true, 129725 => true, 129726 => true, 129727 => true, 129731 => true, 129732 => true, 129733 => true, 129734 => true, 129735 => true, 129736 => true, 129737 => true, 129738 => true, 129739 => true, 129740 => true, 129741 => true, 129742 => true, 129743 => true, 129939 => true, 131070 => true, 131071 => true, 177973 => true, 177974 => true, 177975 => true, 177976 => true, 177977 => true, 177978 => true, 177979 => true, 177980 => true, 177981 => true, 177982 => true, 177983 => true, 178206 => true, 178207 => true, 183970 => true, 183971 => true, 183972 => true, 183973 => true, 183974 => true, 183975 => true, 183976 => true, 183977 => true, 183978 => true, 183979 => true, 183980 => true, 183981 => true, 183982 => true, 183983 => true, 194664 => true, 194676 => true, 194847 => true, 194911 => true, 195007 => true, 196606 => true, 196607 => true, 262142 => true, 262143 => true, 327678 => true, 327679 => true, 393214 => true, 393215 => true, 458750 => true, 458751 => true, 524286 => true, 524287 => true, 589822 => true, 589823 => true, 655358 => true, 655359 => true, 720894 => true, 720895 => true, 786430 => true, 786431 => true, 851966 => true, 851967 => true, 917502 => true, 917503 => true, 917504 => true, 917505 => true, 917506 => true, 917507 => true, 917508 => true, 917509 => true, 917510 => true, 917511 => true, 917512 => true, 917513 => true, 917514 => true, 917515 => true, 917516 => true, 917517 => true, 917518 => true, 917519 => true, 917520 => true, 917521 => true, 917522 => true, 917523 => true, 917524 => true, 917525 => true, 917526 => true, 917527 => true, 917528 => true, 917529 => true, 917530 => true, 917531 => true, 917532 => true, 917533 => true, 917534 => true, 917535 => true, 983038 => true, 983039 => true, 1048574 => true, 1048575 => true, 1114110 => true, 1114111 => true, ); ' ', 168 => ' ̈', 175 => ' ̄', 180 => ' ́', 184 => ' ̧', 728 => ' ̆', 729 => ' ̇', 730 => ' ̊', 731 => ' ̨', 732 => ' ̃', 733 => ' ̋', 890 => ' ι', 894 => ';', 900 => ' ́', 901 => ' ̈́', 8125 => ' ̓', 8127 => ' ̓', 8128 => ' ͂', 8129 => ' ̈͂', 8141 => ' ̓̀', 8142 => ' ̓́', 8143 => ' ̓͂', 8157 => ' ̔̀', 8158 => ' ̔́', 8159 => ' ̔͂', 8173 => ' ̈̀', 8174 => ' ̈́', 8175 => '`', 8189 => ' ́', 8190 => ' ̔', 8192 => ' ', 8193 => ' ', 8194 => ' ', 8195 => ' ', 8196 => ' ', 8197 => ' ', 8198 => ' ', 8199 => ' ', 8200 => ' ', 8201 => ' ', 8202 => ' ', 8215 => ' ̳', 8239 => ' ', 8252 => '!!', 8254 => ' ̅', 8263 => '??', 8264 => '?!', 8265 => '!?', 8287 => ' ', 8314 => '+', 8316 => '=', 8317 => '(', 8318 => ')', 8330 => '+', 8332 => '=', 8333 => '(', 8334 => ')', 8448 => 'a/c', 8449 => 'a/s', 8453 => 'c/o', 8454 => 'c/u', 9332 => '(1)', 9333 => '(2)', 9334 => '(3)', 9335 => '(4)', 9336 => '(5)', 9337 => '(6)', 9338 => '(7)', 9339 => '(8)', 9340 => '(9)', 9341 => '(10)', 9342 => '(11)', 9343 => '(12)', 9344 => '(13)', 9345 => '(14)', 9346 => '(15)', 9347 => '(16)', 9348 => '(17)', 9349 => '(18)', 9350 => '(19)', 9351 => '(20)', 9372 => '(a)', 9373 => '(b)', 9374 => '(c)', 9375 => '(d)', 9376 => '(e)', 9377 => '(f)', 9378 => '(g)', 9379 => '(h)', 9380 => '(i)', 9381 => '(j)', 9382 => '(k)', 9383 => '(l)', 9384 => '(m)', 9385 => '(n)', 9386 => '(o)', 9387 => '(p)', 9388 => '(q)', 9389 => '(r)', 9390 => '(s)', 9391 => '(t)', 9392 => '(u)', 9393 => '(v)', 9394 => '(w)', 9395 => '(x)', 9396 => '(y)', 9397 => '(z)', 10868 => '::=', 10869 => '==', 10870 => '===', 12288 => ' ', 12443 => ' ゙', 12444 => ' ゚', 12800 => '(ᄀ)', 12801 => '(ᄂ)', 12802 => '(ᄃ)', 12803 => '(ᄅ)', 12804 => '(ᄆ)', 12805 => '(ᄇ)', 12806 => '(ᄉ)', 12807 => '(ᄋ)', 12808 => '(ᄌ)', 12809 => '(ᄎ)', 12810 => '(ᄏ)', 12811 => '(ᄐ)', 12812 => '(ᄑ)', 12813 => '(ᄒ)', 12814 => '(가)', 12815 => '(나)', 12816 => '(다)', 12817 => '(라)', 12818 => '(마)', 12819 => '(바)', 12820 => '(사)', 12821 => '(아)', 12822 => '(자)', 12823 => '(차)', 12824 => '(카)', 12825 => '(타)', 12826 => '(파)', 12827 => '(하)', 12828 => '(주)', 12829 => '(오전)', 12830 => '(오후)', 12832 => '(一)', 12833 => '(二)', 12834 => '(三)', 12835 => '(四)', 12836 => '(五)', 12837 => '(六)', 12838 => '(七)', 12839 => '(八)', 12840 => '(九)', 12841 => '(十)', 12842 => '(月)', 12843 => '(火)', 12844 => '(水)', 12845 => '(木)', 12846 => '(金)', 12847 => '(土)', 12848 => '(日)', 12849 => '(株)', 12850 => '(有)', 12851 => '(社)', 12852 => '(名)', 12853 => '(特)', 12854 => '(財)', 12855 => '(祝)', 12856 => '(労)', 12857 => '(代)', 12858 => '(呼)', 12859 => '(学)', 12860 => '(監)', 12861 => '(企)', 12862 => '(資)', 12863 => '(協)', 12864 => '(祭)', 12865 => '(休)', 12866 => '(自)', 12867 => '(至)', 64297 => '+', 64606 => ' ٌّ', 64607 => ' ٍّ', 64608 => ' َّ', 64609 => ' ُّ', 64610 => ' ِّ', 64611 => ' ّٰ', 65018 => 'صلى الله عليه وسلم', 65019 => 'جل جلاله', 65040 => ',', 65043 => ':', 65044 => ';', 65045 => '!', 65046 => '?', 65075 => '_', 65076 => '_', 65077 => '(', 65078 => ')', 65079 => '{', 65080 => '}', 65095 => '[', 65096 => ']', 65097 => ' ̅', 65098 => ' ̅', 65099 => ' ̅', 65100 => ' ̅', 65101 => '_', 65102 => '_', 65103 => '_', 65104 => ',', 65108 => ';', 65109 => ':', 65110 => '?', 65111 => '!', 65113 => '(', 65114 => ')', 65115 => '{', 65116 => '}', 65119 => '#', 65120 => '&', 65121 => '*', 65122 => '+', 65124 => '<', 65125 => '>', 65126 => '=', 65128 => '\\', 65129 => '$', 65130 => '%', 65131 => '@', 65136 => ' ً', 65138 => ' ٌ', 65140 => ' ٍ', 65142 => ' َ', 65144 => ' ُ', 65146 => ' ِ', 65148 => ' ّ', 65150 => ' ْ', 65281 => '!', 65282 => '"', 65283 => '#', 65284 => '$', 65285 => '%', 65286 => '&', 65287 => '\'', 65288 => '(', 65289 => ')', 65290 => '*', 65291 => '+', 65292 => ',', 65295 => '/', 65306 => ':', 65307 => ';', 65308 => '<', 65309 => '=', 65310 => '>', 65311 => '?', 65312 => '@', 65339 => '[', 65340 => '\\', 65341 => ']', 65342 => '^', 65343 => '_', 65344 => '`', 65371 => '{', 65372 => '|', 65373 => '}', 65374 => '~', 65507 => ' ̄', 127233 => '0,', 127234 => '1,', 127235 => '2,', 127236 => '3,', 127237 => '4,', 127238 => '5,', 127239 => '6,', 127240 => '7,', 127241 => '8,', 127242 => '9,', 127248 => '(a)', 127249 => '(b)', 127250 => '(c)', 127251 => '(d)', 127252 => '(e)', 127253 => '(f)', 127254 => '(g)', 127255 => '(h)', 127256 => '(i)', 127257 => '(j)', 127258 => '(k)', 127259 => '(l)', 127260 => '(m)', 127261 => '(n)', 127262 => '(o)', 127263 => '(p)', 127264 => '(q)', 127265 => '(r)', 127266 => '(s)', 127267 => '(t)', 127268 => '(u)', 127269 => '(v)', 127270 => '(w)', 127271 => '(x)', 127272 => '(y)', 127273 => '(z)', ); 9, 2509 => 9, 2637 => 9, 2765 => 9, 2893 => 9, 3021 => 9, 3149 => 9, 3277 => 9, 3387 => 9, 3388 => 9, 3405 => 9, 3530 => 9, 3642 => 9, 3770 => 9, 3972 => 9, 4153 => 9, 4154 => 9, 5908 => 9, 5940 => 9, 6098 => 9, 6752 => 9, 6980 => 9, 7082 => 9, 7083 => 9, 7154 => 9, 7155 => 9, 11647 => 9, 43014 => 9, 43052 => 9, 43204 => 9, 43347 => 9, 43456 => 9, 43766 => 9, 44013 => 9, 68159 => 9, 69702 => 9, 69759 => 9, 69817 => 9, 69939 => 9, 69940 => 9, 70080 => 9, 70197 => 9, 70378 => 9, 70477 => 9, 70722 => 9, 70850 => 9, 71103 => 9, 71231 => 9, 71350 => 9, 71467 => 9, 71737 => 9, 71997 => 9, 71998 => 9, 72160 => 9, 72244 => 9, 72263 => 9, 72345 => 9, 72767 => 9, 73028 => 9, 73029 => 9, 73111 => 9, ); 'a', 66 => 'b', 67 => 'c', 68 => 'd', 69 => 'e', 70 => 'f', 71 => 'g', 72 => 'h', 73 => 'i', 74 => 'j', 75 => 'k', 76 => 'l', 77 => 'm', 78 => 'n', 79 => 'o', 80 => 'p', 81 => 'q', 82 => 'r', 83 => 's', 84 => 't', 85 => 'u', 86 => 'v', 87 => 'w', 88 => 'x', 89 => 'y', 90 => 'z', 170 => 'a', 178 => '2', 179 => '3', 181 => 'μ', 185 => '1', 186 => 'o', 188 => '1⁄4', 189 => '1⁄2', 190 => '3⁄4', 192 => 'à', 193 => 'á', 194 => 'â', 195 => 'ã', 196 => 'ä', 197 => 'å', 198 => 'æ', 199 => 'ç', 200 => 'è', 201 => 'é', 202 => 'ê', 203 => 'ë', 204 => 'ì', 205 => 'í', 206 => 'î', 207 => 'ï', 208 => 'ð', 209 => 'ñ', 210 => 'ò', 211 => 'ó', 212 => 'ô', 213 => 'õ', 214 => 'ö', 216 => 'ø', 217 => 'ù', 218 => 'ú', 219 => 'û', 220 => 'ü', 221 => 'ý', 222 => 'þ', 256 => 'ā', 258 => 'ă', 260 => 'ą', 262 => 'ć', 264 => 'ĉ', 266 => 'ċ', 268 => 'č', 270 => 'ď', 272 => 'đ', 274 => 'ē', 276 => 'ĕ', 278 => 'ė', 280 => 'ę', 282 => 'ě', 284 => 'ĝ', 286 => 'ğ', 288 => 'ġ', 290 => 'ģ', 292 => 'ĥ', 294 => 'ħ', 296 => 'ĩ', 298 => 'ī', 300 => 'ĭ', 302 => 'į', 304 => 'i̇', 306 => 'ij', 307 => 'ij', 308 => 'ĵ', 310 => 'ķ', 313 => 'ĺ', 315 => 'ļ', 317 => 'ľ', 319 => 'l·', 320 => 'l·', 321 => 'ł', 323 => 'ń', 325 => 'ņ', 327 => 'ň', 329 => 'ʼn', 330 => 'ŋ', 332 => 'ō', 334 => 'ŏ', 336 => 'ő', 338 => 'œ', 340 => 'ŕ', 342 => 'ŗ', 344 => 'ř', 346 => 'ś', 348 => 'ŝ', 350 => 'ş', 352 => 'š', 354 => 'ţ', 356 => 'ť', 358 => 'ŧ', 360 => 'ũ', 362 => 'ū', 364 => 'ŭ', 366 => 'ů', 368 => 'ű', 370 => 'ų', 372 => 'ŵ', 374 => 'ŷ', 376 => 'ÿ', 377 => 'ź', 379 => 'ż', 381 => 'ž', 383 => 's', 385 => 'ɓ', 386 => 'ƃ', 388 => 'ƅ', 390 => 'ɔ', 391 => 'ƈ', 393 => 'ɖ', 394 => 'ɗ', 395 => 'ƌ', 398 => 'ǝ', 399 => 'ə', 400 => 'ɛ', 401 => 'ƒ', 403 => 'ɠ', 404 => 'ɣ', 406 => 'ɩ', 407 => 'ɨ', 408 => 'ƙ', 412 => 'ɯ', 413 => 'ɲ', 415 => 'ɵ', 416 => 'ơ', 418 => 'ƣ', 420 => 'ƥ', 422 => 'ʀ', 423 => 'ƨ', 425 => 'ʃ', 428 => 'ƭ', 430 => 'ʈ', 431 => 'ư', 433 => 'ʊ', 434 => 'ʋ', 435 => 'ƴ', 437 => 'ƶ', 439 => 'ʒ', 440 => 'ƹ', 444 => 'ƽ', 452 => 'dž', 453 => 'dž', 454 => 'dž', 455 => 'lj', 456 => 'lj', 457 => 'lj', 458 => 'nj', 459 => 'nj', 460 => 'nj', 461 => 'ǎ', 463 => 'ǐ', 465 => 'ǒ', 467 => 'ǔ', 469 => 'ǖ', 471 => 'ǘ', 473 => 'ǚ', 475 => 'ǜ', 478 => 'ǟ', 480 => 'ǡ', 482 => 'ǣ', 484 => 'ǥ', 486 => 'ǧ', 488 => 'ǩ', 490 => 'ǫ', 492 => 'ǭ', 494 => 'ǯ', 497 => 'dz', 498 => 'dz', 499 => 'dz', 500 => 'ǵ', 502 => 'ƕ', 503 => 'ƿ', 504 => 'ǹ', 506 => 'ǻ', 508 => 'ǽ', 510 => 'ǿ', 512 => 'ȁ', 514 => 'ȃ', 516 => 'ȅ', 518 => 'ȇ', 520 => 'ȉ', 522 => 'ȋ', 524 => 'ȍ', 526 => 'ȏ', 528 => 'ȑ', 530 => 'ȓ', 532 => 'ȕ', 534 => 'ȗ', 536 => 'ș', 538 => 'ț', 540 => 'ȝ', 542 => 'ȟ', 544 => 'ƞ', 546 => 'ȣ', 548 => 'ȥ', 550 => 'ȧ', 552 => 'ȩ', 554 => 'ȫ', 556 => 'ȭ', 558 => 'ȯ', 560 => 'ȱ', 562 => 'ȳ', 570 => 'ⱥ', 571 => 'ȼ', 573 => 'ƚ', 574 => 'ⱦ', 577 => 'ɂ', 579 => 'ƀ', 580 => 'ʉ', 581 => 'ʌ', 582 => 'ɇ', 584 => 'ɉ', 586 => 'ɋ', 588 => 'ɍ', 590 => 'ɏ', 688 => 'h', 689 => 'ɦ', 690 => 'j', 691 => 'r', 692 => 'ɹ', 693 => 'ɻ', 694 => 'ʁ', 695 => 'w', 696 => 'y', 736 => 'ɣ', 737 => 'l', 738 => 's', 739 => 'x', 740 => 'ʕ', 832 => '̀', 833 => '́', 835 => '̓', 836 => '̈́', 837 => 'ι', 880 => 'ͱ', 882 => 'ͳ', 884 => 'ʹ', 886 => 'ͷ', 895 => 'ϳ', 902 => 'ά', 903 => '·', 904 => 'έ', 905 => 'ή', 906 => 'ί', 908 => 'ό', 910 => 'ύ', 911 => 'ώ', 913 => 'α', 914 => 'β', 915 => 'γ', 916 => 'δ', 917 => 'ε', 918 => 'ζ', 919 => 'η', 920 => 'θ', 921 => 'ι', 922 => 'κ', 923 => 'λ', 924 => 'μ', 925 => 'ν', 926 => 'ξ', 927 => 'ο', 928 => 'π', 929 => 'ρ', 931 => 'σ', 932 => 'τ', 933 => 'υ', 934 => 'φ', 935 => 'χ', 936 => 'ψ', 937 => 'ω', 938 => 'ϊ', 939 => 'ϋ', 975 => 'ϗ', 976 => 'β', 977 => 'θ', 978 => 'υ', 979 => 'ύ', 980 => 'ϋ', 981 => 'φ', 982 => 'π', 984 => 'ϙ', 986 => 'ϛ', 988 => 'ϝ', 990 => 'ϟ', 992 => 'ϡ', 994 => 'ϣ', 996 => 'ϥ', 998 => 'ϧ', 1000 => 'ϩ', 1002 => 'ϫ', 1004 => 'ϭ', 1006 => 'ϯ', 1008 => 'κ', 1009 => 'ρ', 1010 => 'σ', 1012 => 'θ', 1013 => 'ε', 1015 => 'ϸ', 1017 => 'σ', 1018 => 'ϻ', 1021 => 'ͻ', 1022 => 'ͼ', 1023 => 'ͽ', 1024 => 'ѐ', 1025 => 'ё', 1026 => 'ђ', 1027 => 'ѓ', 1028 => 'є', 1029 => 'ѕ', 1030 => 'і', 1031 => 'ї', 1032 => 'ј', 1033 => 'љ', 1034 => 'њ', 1035 => 'ћ', 1036 => 'ќ', 1037 => 'ѝ', 1038 => 'ў', 1039 => 'џ', 1040 => 'а', 1041 => 'б', 1042 => 'в', 1043 => 'г', 1044 => 'д', 1045 => 'е', 1046 => 'ж', 1047 => 'з', 1048 => 'и', 1049 => 'й', 1050 => 'к', 1051 => 'л', 1052 => 'м', 1053 => 'н', 1054 => 'о', 1055 => 'п', 1056 => 'р', 1057 => 'с', 1058 => 'т', 1059 => 'у', 1060 => 'ф', 1061 => 'х', 1062 => 'ц', 1063 => 'ч', 1064 => 'ш', 1065 => 'щ', 1066 => 'ъ', 1067 => 'ы', 1068 => 'ь', 1069 => 'э', 1070 => 'ю', 1071 => 'я', 1120 => 'ѡ', 1122 => 'ѣ', 1124 => 'ѥ', 1126 => 'ѧ', 1128 => 'ѩ', 1130 => 'ѫ', 1132 => 'ѭ', 1134 => 'ѯ', 1136 => 'ѱ', 1138 => 'ѳ', 1140 => 'ѵ', 1142 => 'ѷ', 1144 => 'ѹ', 1146 => 'ѻ', 1148 => 'ѽ', 1150 => 'ѿ', 1152 => 'ҁ', 1162 => 'ҋ', 1164 => 'ҍ', 1166 => 'ҏ', 1168 => 'ґ', 1170 => 'ғ', 1172 => 'ҕ', 1174 => 'җ', 1176 => 'ҙ', 1178 => 'қ', 1180 => 'ҝ', 1182 => 'ҟ', 1184 => 'ҡ', 1186 => 'ң', 1188 => 'ҥ', 1190 => 'ҧ', 1192 => 'ҩ', 1194 => 'ҫ', 1196 => 'ҭ', 1198 => 'ү', 1200 => 'ұ', 1202 => 'ҳ', 1204 => 'ҵ', 1206 => 'ҷ', 1208 => 'ҹ', 1210 => 'һ', 1212 => 'ҽ', 1214 => 'ҿ', 1217 => 'ӂ', 1219 => 'ӄ', 1221 => 'ӆ', 1223 => 'ӈ', 1225 => 'ӊ', 1227 => 'ӌ', 1229 => 'ӎ', 1232 => 'ӑ', 1234 => 'ӓ', 1236 => 'ӕ', 1238 => 'ӗ', 1240 => 'ә', 1242 => 'ӛ', 1244 => 'ӝ', 1246 => 'ӟ', 1248 => 'ӡ', 1250 => 'ӣ', 1252 => 'ӥ', 1254 => 'ӧ', 1256 => 'ө', 1258 => 'ӫ', 1260 => 'ӭ', 1262 => 'ӯ', 1264 => 'ӱ', 1266 => 'ӳ', 1268 => 'ӵ', 1270 => 'ӷ', 1272 => 'ӹ', 1274 => 'ӻ', 1276 => 'ӽ', 1278 => 'ӿ', 1280 => 'ԁ', 1282 => 'ԃ', 1284 => 'ԅ', 1286 => 'ԇ', 1288 => 'ԉ', 1290 => 'ԋ', 1292 => 'ԍ', 1294 => 'ԏ', 1296 => 'ԑ', 1298 => 'ԓ', 1300 => 'ԕ', 1302 => 'ԗ', 1304 => 'ԙ', 1306 => 'ԛ', 1308 => 'ԝ', 1310 => 'ԟ', 1312 => 'ԡ', 1314 => 'ԣ', 1316 => 'ԥ', 1318 => 'ԧ', 1320 => 'ԩ', 1322 => 'ԫ', 1324 => 'ԭ', 1326 => 'ԯ', 1329 => 'ա', 1330 => 'բ', 1331 => 'գ', 1332 => 'դ', 1333 => 'ե', 1334 => 'զ', 1335 => 'է', 1336 => 'ը', 1337 => 'թ', 1338 => 'ժ', 1339 => 'ի', 1340 => 'լ', 1341 => 'խ', 1342 => 'ծ', 1343 => 'կ', 1344 => 'հ', 1345 => 'ձ', 1346 => 'ղ', 1347 => 'ճ', 1348 => 'մ', 1349 => 'յ', 1350 => 'ն', 1351 => 'շ', 1352 => 'ո', 1353 => 'չ', 1354 => 'պ', 1355 => 'ջ', 1356 => 'ռ', 1357 => 'ս', 1358 => 'վ', 1359 => 'տ', 1360 => 'ր', 1361 => 'ց', 1362 => 'ւ', 1363 => 'փ', 1364 => 'ք', 1365 => 'օ', 1366 => 'ֆ', 1415 => 'եւ', 1653 => 'اٴ', 1654 => 'وٴ', 1655 => 'ۇٴ', 1656 => 'يٴ', 2392 => 'क़', 2393 => 'ख़', 2394 => 'ग़', 2395 => 'ज़', 2396 => 'ड़', 2397 => 'ढ़', 2398 => 'फ़', 2399 => 'य़', 2524 => 'ড়', 2525 => 'ঢ়', 2527 => 'য়', 2611 => 'ਲ਼', 2614 => 'ਸ਼', 2649 => 'ਖ਼', 2650 => 'ਗ਼', 2651 => 'ਜ਼', 2654 => 'ਫ਼', 2908 => 'ଡ଼', 2909 => 'ଢ଼', 3635 => 'ํา', 3763 => 'ໍາ', 3804 => 'ຫນ', 3805 => 'ຫມ', 3852 => '་', 3907 => 'གྷ', 3917 => 'ཌྷ', 3922 => 'དྷ', 3927 => 'བྷ', 3932 => 'ཛྷ', 3945 => 'ཀྵ', 3955 => 'ཱི', 3957 => 'ཱུ', 3958 => 'ྲྀ', 3959 => 'ྲཱྀ', 3960 => 'ླྀ', 3961 => 'ླཱྀ', 3969 => 'ཱྀ', 3987 => 'ྒྷ', 3997 => 'ྜྷ', 4002 => 'ྡྷ', 4007 => 'ྦྷ', 4012 => 'ྫྷ', 4025 => 'ྐྵ', 4295 => 'ⴧ', 4301 => 'ⴭ', 4348 => 'ნ', 5112 => 'Ᏸ', 5113 => 'Ᏹ', 5114 => 'Ᏺ', 5115 => 'Ᏻ', 5116 => 'Ᏼ', 5117 => 'Ᏽ', 7296 => 'в', 7297 => 'д', 7298 => 'о', 7299 => 'с', 7300 => 'т', 7301 => 'т', 7302 => 'ъ', 7303 => 'ѣ', 7304 => 'ꙋ', 7312 => 'ა', 7313 => 'ბ', 7314 => 'გ', 7315 => 'დ', 7316 => 'ე', 7317 => 'ვ', 7318 => 'ზ', 7319 => 'თ', 7320 => 'ი', 7321 => 'კ', 7322 => 'ლ', 7323 => 'მ', 7324 => 'ნ', 7325 => 'ო', 7326 => 'პ', 7327 => 'ჟ', 7328 => 'რ', 7329 => 'ს', 7330 => 'ტ', 7331 => 'უ', 7332 => 'ფ', 7333 => 'ქ', 7334 => 'ღ', 7335 => 'ყ', 7336 => 'შ', 7337 => 'ჩ', 7338 => 'ც', 7339 => 'ძ', 7340 => 'წ', 7341 => 'ჭ', 7342 => 'ხ', 7343 => 'ჯ', 7344 => 'ჰ', 7345 => 'ჱ', 7346 => 'ჲ', 7347 => 'ჳ', 7348 => 'ჴ', 7349 => 'ჵ', 7350 => 'ჶ', 7351 => 'ჷ', 7352 => 'ჸ', 7353 => 'ჹ', 7354 => 'ჺ', 7357 => 'ჽ', 7358 => 'ჾ', 7359 => 'ჿ', 7468 => 'a', 7469 => 'æ', 7470 => 'b', 7472 => 'd', 7473 => 'e', 7474 => 'ǝ', 7475 => 'g', 7476 => 'h', 7477 => 'i', 7478 => 'j', 7479 => 'k', 7480 => 'l', 7481 => 'm', 7482 => 'n', 7484 => 'o', 7485 => 'ȣ', 7486 => 'p', 7487 => 'r', 7488 => 't', 7489 => 'u', 7490 => 'w', 7491 => 'a', 7492 => 'ɐ', 7493 => 'ɑ', 7494 => 'ᴂ', 7495 => 'b', 7496 => 'd', 7497 => 'e', 7498 => 'ə', 7499 => 'ɛ', 7500 => 'ɜ', 7501 => 'g', 7503 => 'k', 7504 => 'm', 7505 => 'ŋ', 7506 => 'o', 7507 => 'ɔ', 7508 => 'ᴖ', 7509 => 'ᴗ', 7510 => 'p', 7511 => 't', 7512 => 'u', 7513 => 'ᴝ', 7514 => 'ɯ', 7515 => 'v', 7516 => 'ᴥ', 7517 => 'β', 7518 => 'γ', 7519 => 'δ', 7520 => 'φ', 7521 => 'χ', 7522 => 'i', 7523 => 'r', 7524 => 'u', 7525 => 'v', 7526 => 'β', 7527 => 'γ', 7528 => 'ρ', 7529 => 'φ', 7530 => 'χ', 7544 => 'н', 7579 => 'ɒ', 7580 => 'c', 7581 => 'ɕ', 7582 => 'ð', 7583 => 'ɜ', 7584 => 'f', 7585 => 'ɟ', 7586 => 'ɡ', 7587 => 'ɥ', 7588 => 'ɨ', 7589 => 'ɩ', 7590 => 'ɪ', 7591 => 'ᵻ', 7592 => 'ʝ', 7593 => 'ɭ', 7594 => 'ᶅ', 7595 => 'ʟ', 7596 => 'ɱ', 7597 => 'ɰ', 7598 => 'ɲ', 7599 => 'ɳ', 7600 => 'ɴ', 7601 => 'ɵ', 7602 => 'ɸ', 7603 => 'ʂ', 7604 => 'ʃ', 7605 => 'ƫ', 7606 => 'ʉ', 7607 => 'ʊ', 7608 => 'ᴜ', 7609 => 'ʋ', 7610 => 'ʌ', 7611 => 'z', 7612 => 'ʐ', 7613 => 'ʑ', 7614 => 'ʒ', 7615 => 'θ', 7680 => 'ḁ', 7682 => 'ḃ', 7684 => 'ḅ', 7686 => 'ḇ', 7688 => 'ḉ', 7690 => 'ḋ', 7692 => 'ḍ', 7694 => 'ḏ', 7696 => 'ḑ', 7698 => 'ḓ', 7700 => 'ḕ', 7702 => 'ḗ', 7704 => 'ḙ', 7706 => 'ḛ', 7708 => 'ḝ', 7710 => 'ḟ', 7712 => 'ḡ', 7714 => 'ḣ', 7716 => 'ḥ', 7718 => 'ḧ', 7720 => 'ḩ', 7722 => 'ḫ', 7724 => 'ḭ', 7726 => 'ḯ', 7728 => 'ḱ', 7730 => 'ḳ', 7732 => 'ḵ', 7734 => 'ḷ', 7736 => 'ḹ', 7738 => 'ḻ', 7740 => 'ḽ', 7742 => 'ḿ', 7744 => 'ṁ', 7746 => 'ṃ', 7748 => 'ṅ', 7750 => 'ṇ', 7752 => 'ṉ', 7754 => 'ṋ', 7756 => 'ṍ', 7758 => 'ṏ', 7760 => 'ṑ', 7762 => 'ṓ', 7764 => 'ṕ', 7766 => 'ṗ', 7768 => 'ṙ', 7770 => 'ṛ', 7772 => 'ṝ', 7774 => 'ṟ', 7776 => 'ṡ', 7778 => 'ṣ', 7780 => 'ṥ', 7782 => 'ṧ', 7784 => 'ṩ', 7786 => 'ṫ', 7788 => 'ṭ', 7790 => 'ṯ', 7792 => 'ṱ', 7794 => 'ṳ', 7796 => 'ṵ', 7798 => 'ṷ', 7800 => 'ṹ', 7802 => 'ṻ', 7804 => 'ṽ', 7806 => 'ṿ', 7808 => 'ẁ', 7810 => 'ẃ', 7812 => 'ẅ', 7814 => 'ẇ', 7816 => 'ẉ', 7818 => 'ẋ', 7820 => 'ẍ', 7822 => 'ẏ', 7824 => 'ẑ', 7826 => 'ẓ', 7828 => 'ẕ', 7834 => 'aʾ', 7835 => 'ṡ', 7838 => 'ss', 7840 => 'ạ', 7842 => 'ả', 7844 => 'ấ', 7846 => 'ầ', 7848 => 'ẩ', 7850 => 'ẫ', 7852 => 'ậ', 7854 => 'ắ', 7856 => 'ằ', 7858 => 'ẳ', 7860 => 'ẵ', 7862 => 'ặ', 7864 => 'ẹ', 7866 => 'ẻ', 7868 => 'ẽ', 7870 => 'ế', 7872 => 'ề', 7874 => 'ể', 7876 => 'ễ', 7878 => 'ệ', 7880 => 'ỉ', 7882 => 'ị', 7884 => 'ọ', 7886 => 'ỏ', 7888 => 'ố', 7890 => 'ồ', 7892 => 'ổ', 7894 => 'ỗ', 7896 => 'ộ', 7898 => 'ớ', 7900 => 'ờ', 7902 => 'ở', 7904 => 'ỡ', 7906 => 'ợ', 7908 => 'ụ', 7910 => 'ủ', 7912 => 'ứ', 7914 => 'ừ', 7916 => 'ử', 7918 => 'ữ', 7920 => 'ự', 7922 => 'ỳ', 7924 => 'ỵ', 7926 => 'ỷ', 7928 => 'ỹ', 7930 => 'ỻ', 7932 => 'ỽ', 7934 => 'ỿ', 7944 => 'ἀ', 7945 => 'ἁ', 7946 => 'ἂ', 7947 => 'ἃ', 7948 => 'ἄ', 7949 => 'ἅ', 7950 => 'ἆ', 7951 => 'ἇ', 7960 => 'ἐ', 7961 => 'ἑ', 7962 => 'ἒ', 7963 => 'ἓ', 7964 => 'ἔ', 7965 => 'ἕ', 7976 => 'ἠ', 7977 => 'ἡ', 7978 => 'ἢ', 7979 => 'ἣ', 7980 => 'ἤ', 7981 => 'ἥ', 7982 => 'ἦ', 7983 => 'ἧ', 7992 => 'ἰ', 7993 => 'ἱ', 7994 => 'ἲ', 7995 => 'ἳ', 7996 => 'ἴ', 7997 => 'ἵ', 7998 => 'ἶ', 7999 => 'ἷ', 8008 => 'ὀ', 8009 => 'ὁ', 8010 => 'ὂ', 8011 => 'ὃ', 8012 => 'ὄ', 8013 => 'ὅ', 8025 => 'ὑ', 8027 => 'ὓ', 8029 => 'ὕ', 8031 => 'ὗ', 8040 => 'ὠ', 8041 => 'ὡ', 8042 => 'ὢ', 8043 => 'ὣ', 8044 => 'ὤ', 8045 => 'ὥ', 8046 => 'ὦ', 8047 => 'ὧ', 8049 => 'ά', 8051 => 'έ', 8053 => 'ή', 8055 => 'ί', 8057 => 'ό', 8059 => 'ύ', 8061 => 'ώ', 8064 => 'ἀι', 8065 => 'ἁι', 8066 => 'ἂι', 8067 => 'ἃι', 8068 => 'ἄι', 8069 => 'ἅι', 8070 => 'ἆι', 8071 => 'ἇι', 8072 => 'ἀι', 8073 => 'ἁι', 8074 => 'ἂι', 8075 => 'ἃι', 8076 => 'ἄι', 8077 => 'ἅι', 8078 => 'ἆι', 8079 => 'ἇι', 8080 => 'ἠι', 8081 => 'ἡι', 8082 => 'ἢι', 8083 => 'ἣι', 8084 => 'ἤι', 8085 => 'ἥι', 8086 => 'ἦι', 8087 => 'ἧι', 8088 => 'ἠι', 8089 => 'ἡι', 8090 => 'ἢι', 8091 => 'ἣι', 8092 => 'ἤι', 8093 => 'ἥι', 8094 => 'ἦι', 8095 => 'ἧι', 8096 => 'ὠι', 8097 => 'ὡι', 8098 => 'ὢι', 8099 => 'ὣι', 8100 => 'ὤι', 8101 => 'ὥι', 8102 => 'ὦι', 8103 => 'ὧι', 8104 => 'ὠι', 8105 => 'ὡι', 8106 => 'ὢι', 8107 => 'ὣι', 8108 => 'ὤι', 8109 => 'ὥι', 8110 => 'ὦι', 8111 => 'ὧι', 8114 => 'ὰι', 8115 => 'αι', 8116 => 'άι', 8119 => 'ᾶι', 8120 => 'ᾰ', 8121 => 'ᾱ', 8122 => 'ὰ', 8123 => 'ά', 8124 => 'αι', 8126 => 'ι', 8130 => 'ὴι', 8131 => 'ηι', 8132 => 'ήι', 8135 => 'ῆι', 8136 => 'ὲ', 8137 => 'έ', 8138 => 'ὴ', 8139 => 'ή', 8140 => 'ηι', 8147 => 'ΐ', 8152 => 'ῐ', 8153 => 'ῑ', 8154 => 'ὶ', 8155 => 'ί', 8163 => 'ΰ', 8168 => 'ῠ', 8169 => 'ῡ', 8170 => 'ὺ', 8171 => 'ύ', 8172 => 'ῥ', 8178 => 'ὼι', 8179 => 'ωι', 8180 => 'ώι', 8183 => 'ῶι', 8184 => 'ὸ', 8185 => 'ό', 8186 => 'ὼ', 8187 => 'ώ', 8188 => 'ωι', 8209 => '‐', 8243 => '′′', 8244 => '′′′', 8246 => '‵‵', 8247 => '‵‵‵', 8279 => '′′′′', 8304 => '0', 8305 => 'i', 8308 => '4', 8309 => '5', 8310 => '6', 8311 => '7', 8312 => '8', 8313 => '9', 8315 => '−', 8319 => 'n', 8320 => '0', 8321 => '1', 8322 => '2', 8323 => '3', 8324 => '4', 8325 => '5', 8326 => '6', 8327 => '7', 8328 => '8', 8329 => '9', 8331 => '−', 8336 => 'a', 8337 => 'e', 8338 => 'o', 8339 => 'x', 8340 => 'ə', 8341 => 'h', 8342 => 'k', 8343 => 'l', 8344 => 'm', 8345 => 'n', 8346 => 'p', 8347 => 's', 8348 => 't', 8360 => 'rs', 8450 => 'c', 8451 => '°c', 8455 => 'ɛ', 8457 => '°f', 8458 => 'g', 8459 => 'h', 8460 => 'h', 8461 => 'h', 8462 => 'h', 8463 => 'ħ', 8464 => 'i', 8465 => 'i', 8466 => 'l', 8467 => 'l', 8469 => 'n', 8470 => 'no', 8473 => 'p', 8474 => 'q', 8475 => 'r', 8476 => 'r', 8477 => 'r', 8480 => 'sm', 8481 => 'tel', 8482 => 'tm', 8484 => 'z', 8486 => 'ω', 8488 => 'z', 8490 => 'k', 8491 => 'å', 8492 => 'b', 8493 => 'c', 8495 => 'e', 8496 => 'e', 8497 => 'f', 8499 => 'm', 8500 => 'o', 8501 => 'א', 8502 => 'ב', 8503 => 'ג', 8504 => 'ד', 8505 => 'i', 8507 => 'fax', 8508 => 'π', 8509 => 'γ', 8510 => 'γ', 8511 => 'π', 8512 => '∑', 8517 => 'd', 8518 => 'd', 8519 => 'e', 8520 => 'i', 8521 => 'j', 8528 => '1⁄7', 8529 => '1⁄9', 8530 => '1⁄10', 8531 => '1⁄3', 8532 => '2⁄3', 8533 => '1⁄5', 8534 => '2⁄5', 8535 => '3⁄5', 8536 => '4⁄5', 8537 => '1⁄6', 8538 => '5⁄6', 8539 => '1⁄8', 8540 => '3⁄8', 8541 => '5⁄8', 8542 => '7⁄8', 8543 => '1⁄', 8544 => 'i', 8545 => 'ii', 8546 => 'iii', 8547 => 'iv', 8548 => 'v', 8549 => 'vi', 8550 => 'vii', 8551 => 'viii', 8552 => 'ix', 8553 => 'x', 8554 => 'xi', 8555 => 'xii', 8556 => 'l', 8557 => 'c', 8558 => 'd', 8559 => 'm', 8560 => 'i', 8561 => 'ii', 8562 => 'iii', 8563 => 'iv', 8564 => 'v', 8565 => 'vi', 8566 => 'vii', 8567 => 'viii', 8568 => 'ix', 8569 => 'x', 8570 => 'xi', 8571 => 'xii', 8572 => 'l', 8573 => 'c', 8574 => 'd', 8575 => 'm', 8585 => '0⁄3', 8748 => '∫∫', 8749 => '∫∫∫', 8751 => '∮∮', 8752 => '∮∮∮', 9001 => '〈', 9002 => '〉', 9312 => '1', 9313 => '2', 9314 => '3', 9315 => '4', 9316 => '5', 9317 => '6', 9318 => '7', 9319 => '8', 9320 => '9', 9321 => '10', 9322 => '11', 9323 => '12', 9324 => '13', 9325 => '14', 9326 => '15', 9327 => '16', 9328 => '17', 9329 => '18', 9330 => '19', 9331 => '20', 9398 => 'a', 9399 => 'b', 9400 => 'c', 9401 => 'd', 9402 => 'e', 9403 => 'f', 9404 => 'g', 9405 => 'h', 9406 => 'i', 9407 => 'j', 9408 => 'k', 9409 => 'l', 9410 => 'm', 9411 => 'n', 9412 => 'o', 9413 => 'p', 9414 => 'q', 9415 => 'r', 9416 => 's', 9417 => 't', 9418 => 'u', 9419 => 'v', 9420 => 'w', 9421 => 'x', 9422 => 'y', 9423 => 'z', 9424 => 'a', 9425 => 'b', 9426 => 'c', 9427 => 'd', 9428 => 'e', 9429 => 'f', 9430 => 'g', 9431 => 'h', 9432 => 'i', 9433 => 'j', 9434 => 'k', 9435 => 'l', 9436 => 'm', 9437 => 'n', 9438 => 'o', 9439 => 'p', 9440 => 'q', 9441 => 'r', 9442 => 's', 9443 => 't', 9444 => 'u', 9445 => 'v', 9446 => 'w', 9447 => 'x', 9448 => 'y', 9449 => 'z', 9450 => '0', 10764 => '∫∫∫∫', 10972 => '⫝̸', 11264 => 'ⰰ', 11265 => 'ⰱ', 11266 => 'ⰲ', 11267 => 'ⰳ', 11268 => 'ⰴ', 11269 => 'ⰵ', 11270 => 'ⰶ', 11271 => 'ⰷ', 11272 => 'ⰸ', 11273 => 'ⰹ', 11274 => 'ⰺ', 11275 => 'ⰻ', 11276 => 'ⰼ', 11277 => 'ⰽ', 11278 => 'ⰾ', 11279 => 'ⰿ', 11280 => 'ⱀ', 11281 => 'ⱁ', 11282 => 'ⱂ', 11283 => 'ⱃ', 11284 => 'ⱄ', 11285 => 'ⱅ', 11286 => 'ⱆ', 11287 => 'ⱇ', 11288 => 'ⱈ', 11289 => 'ⱉ', 11290 => 'ⱊ', 11291 => 'ⱋ', 11292 => 'ⱌ', 11293 => 'ⱍ', 11294 => 'ⱎ', 11295 => 'ⱏ', 11296 => 'ⱐ', 11297 => 'ⱑ', 11298 => 'ⱒ', 11299 => 'ⱓ', 11300 => 'ⱔ', 11301 => 'ⱕ', 11302 => 'ⱖ', 11303 => 'ⱗ', 11304 => 'ⱘ', 11305 => 'ⱙ', 11306 => 'ⱚ', 11307 => 'ⱛ', 11308 => 'ⱜ', 11309 => 'ⱝ', 11310 => 'ⱞ', 11360 => 'ⱡ', 11362 => 'ɫ', 11363 => 'ᵽ', 11364 => 'ɽ', 11367 => 'ⱨ', 11369 => 'ⱪ', 11371 => 'ⱬ', 11373 => 'ɑ', 11374 => 'ɱ', 11375 => 'ɐ', 11376 => 'ɒ', 11378 => 'ⱳ', 11381 => 'ⱶ', 11388 => 'j', 11389 => 'v', 11390 => 'ȿ', 11391 => 'ɀ', 11392 => 'ⲁ', 11394 => 'ⲃ', 11396 => 'ⲅ', 11398 => 'ⲇ', 11400 => 'ⲉ', 11402 => 'ⲋ', 11404 => 'ⲍ', 11406 => 'ⲏ', 11408 => 'ⲑ', 11410 => 'ⲓ', 11412 => 'ⲕ', 11414 => 'ⲗ', 11416 => 'ⲙ', 11418 => 'ⲛ', 11420 => 'ⲝ', 11422 => 'ⲟ', 11424 => 'ⲡ', 11426 => 'ⲣ', 11428 => 'ⲥ', 11430 => 'ⲧ', 11432 => 'ⲩ', 11434 => 'ⲫ', 11436 => 'ⲭ', 11438 => 'ⲯ', 11440 => 'ⲱ', 11442 => 'ⲳ', 11444 => 'ⲵ', 11446 => 'ⲷ', 11448 => 'ⲹ', 11450 => 'ⲻ', 11452 => 'ⲽ', 11454 => 'ⲿ', 11456 => 'ⳁ', 11458 => 'ⳃ', 11460 => 'ⳅ', 11462 => 'ⳇ', 11464 => 'ⳉ', 11466 => 'ⳋ', 11468 => 'ⳍ', 11470 => 'ⳏ', 11472 => 'ⳑ', 11474 => 'ⳓ', 11476 => 'ⳕ', 11478 => 'ⳗ', 11480 => 'ⳙ', 11482 => 'ⳛ', 11484 => 'ⳝ', 11486 => 'ⳟ', 11488 => 'ⳡ', 11490 => 'ⳣ', 11499 => 'ⳬ', 11501 => 'ⳮ', 11506 => 'ⳳ', 11631 => 'ⵡ', 11935 => '母', 12019 => '龟', 12032 => '一', 12033 => '丨', 12034 => '丶', 12035 => '丿', 12036 => '乙', 12037 => '亅', 12038 => '二', 12039 => '亠', 12040 => '人', 12041 => '儿', 12042 => '入', 12043 => '八', 12044 => '冂', 12045 => '冖', 12046 => '冫', 12047 => '几', 12048 => '凵', 12049 => '刀', 12050 => '力', 12051 => '勹', 12052 => '匕', 12053 => '匚', 12054 => '匸', 12055 => '十', 12056 => '卜', 12057 => '卩', 12058 => '厂', 12059 => '厶', 12060 => '又', 12061 => '口', 12062 => '囗', 12063 => '土', 12064 => '士', 12065 => '夂', 12066 => '夊', 12067 => '夕', 12068 => '大', 12069 => '女', 12070 => '子', 12071 => '宀', 12072 => '寸', 12073 => '小', 12074 => '尢', 12075 => '尸', 12076 => '屮', 12077 => '山', 12078 => '巛', 12079 => '工', 12080 => '己', 12081 => '巾', 12082 => '干', 12083 => '幺', 12084 => '广', 12085 => '廴', 12086 => '廾', 12087 => '弋', 12088 => '弓', 12089 => '彐', 12090 => '彡', 12091 => '彳', 12092 => '心', 12093 => '戈', 12094 => '戶', 12095 => '手', 12096 => '支', 12097 => '攴', 12098 => '文', 12099 => '斗', 12100 => '斤', 12101 => '方', 12102 => '无', 12103 => '日', 12104 => '曰', 12105 => '月', 12106 => '木', 12107 => '欠', 12108 => '止', 12109 => '歹', 12110 => '殳', 12111 => '毋', 12112 => '比', 12113 => '毛', 12114 => '氏', 12115 => '气', 12116 => '水', 12117 => '火', 12118 => '爪', 12119 => '父', 12120 => '爻', 12121 => '爿', 12122 => '片', 12123 => '牙', 12124 => '牛', 12125 => '犬', 12126 => '玄', 12127 => '玉', 12128 => '瓜', 12129 => '瓦', 12130 => '甘', 12131 => '生', 12132 => '用', 12133 => '田', 12134 => '疋', 12135 => '疒', 12136 => '癶', 12137 => '白', 12138 => '皮', 12139 => '皿', 12140 => '目', 12141 => '矛', 12142 => '矢', 12143 => '石', 12144 => '示', 12145 => '禸', 12146 => '禾', 12147 => '穴', 12148 => '立', 12149 => '竹', 12150 => '米', 12151 => '糸', 12152 => '缶', 12153 => '网', 12154 => '羊', 12155 => '羽', 12156 => '老', 12157 => '而', 12158 => '耒', 12159 => '耳', 12160 => '聿', 12161 => '肉', 12162 => '臣', 12163 => '自', 12164 => '至', 12165 => '臼', 12166 => '舌', 12167 => '舛', 12168 => '舟', 12169 => '艮', 12170 => '色', 12171 => '艸', 12172 => '虍', 12173 => '虫', 12174 => '血', 12175 => '行', 12176 => '衣', 12177 => '襾', 12178 => '見', 12179 => '角', 12180 => '言', 12181 => '谷', 12182 => '豆', 12183 => '豕', 12184 => '豸', 12185 => '貝', 12186 => '赤', 12187 => '走', 12188 => '足', 12189 => '身', 12190 => '車', 12191 => '辛', 12192 => '辰', 12193 => '辵', 12194 => '邑', 12195 => '酉', 12196 => '釆', 12197 => '里', 12198 => '金', 12199 => '長', 12200 => '門', 12201 => '阜', 12202 => '隶', 12203 => '隹', 12204 => '雨', 12205 => '靑', 12206 => '非', 12207 => '面', 12208 => '革', 12209 => '韋', 12210 => '韭', 12211 => '音', 12212 => '頁', 12213 => '風', 12214 => '飛', 12215 => '食', 12216 => '首', 12217 => '香', 12218 => '馬', 12219 => '骨', 12220 => '高', 12221 => '髟', 12222 => '鬥', 12223 => '鬯', 12224 => '鬲', 12225 => '鬼', 12226 => '魚', 12227 => '鳥', 12228 => '鹵', 12229 => '鹿', 12230 => '麥', 12231 => '麻', 12232 => '黃', 12233 => '黍', 12234 => '黑', 12235 => '黹', 12236 => '黽', 12237 => '鼎', 12238 => '鼓', 12239 => '鼠', 12240 => '鼻', 12241 => '齊', 12242 => '齒', 12243 => '龍', 12244 => '龜', 12245 => '龠', 12290 => '.', 12342 => '〒', 12344 => '十', 12345 => '卄', 12346 => '卅', 12447 => 'より', 12543 => 'コト', 12593 => 'ᄀ', 12594 => 'ᄁ', 12595 => 'ᆪ', 12596 => 'ᄂ', 12597 => 'ᆬ', 12598 => 'ᆭ', 12599 => 'ᄃ', 12600 => 'ᄄ', 12601 => 'ᄅ', 12602 => 'ᆰ', 12603 => 'ᆱ', 12604 => 'ᆲ', 12605 => 'ᆳ', 12606 => 'ᆴ', 12607 => 'ᆵ', 12608 => 'ᄚ', 12609 => 'ᄆ', 12610 => 'ᄇ', 12611 => 'ᄈ', 12612 => 'ᄡ', 12613 => 'ᄉ', 12614 => 'ᄊ', 12615 => 'ᄋ', 12616 => 'ᄌ', 12617 => 'ᄍ', 12618 => 'ᄎ', 12619 => 'ᄏ', 12620 => 'ᄐ', 12621 => 'ᄑ', 12622 => 'ᄒ', 12623 => 'ᅡ', 12624 => 'ᅢ', 12625 => 'ᅣ', 12626 => 'ᅤ', 12627 => 'ᅥ', 12628 => 'ᅦ', 12629 => 'ᅧ', 12630 => 'ᅨ', 12631 => 'ᅩ', 12632 => 'ᅪ', 12633 => 'ᅫ', 12634 => 'ᅬ', 12635 => 'ᅭ', 12636 => 'ᅮ', 12637 => 'ᅯ', 12638 => 'ᅰ', 12639 => 'ᅱ', 12640 => 'ᅲ', 12641 => 'ᅳ', 12642 => 'ᅴ', 12643 => 'ᅵ', 12645 => 'ᄔ', 12646 => 'ᄕ', 12647 => 'ᇇ', 12648 => 'ᇈ', 12649 => 'ᇌ', 12650 => 'ᇎ', 12651 => 'ᇓ', 12652 => 'ᇗ', 12653 => 'ᇙ', 12654 => 'ᄜ', 12655 => 'ᇝ', 12656 => 'ᇟ', 12657 => 'ᄝ', 12658 => 'ᄞ', 12659 => 'ᄠ', 12660 => 'ᄢ', 12661 => 'ᄣ', 12662 => 'ᄧ', 12663 => 'ᄩ', 12664 => 'ᄫ', 12665 => 'ᄬ', 12666 => 'ᄭ', 12667 => 'ᄮ', 12668 => 'ᄯ', 12669 => 'ᄲ', 12670 => 'ᄶ', 12671 => 'ᅀ', 12672 => 'ᅇ', 12673 => 'ᅌ', 12674 => 'ᇱ', 12675 => 'ᇲ', 12676 => 'ᅗ', 12677 => 'ᅘ', 12678 => 'ᅙ', 12679 => 'ᆄ', 12680 => 'ᆅ', 12681 => 'ᆈ', 12682 => 'ᆑ', 12683 => 'ᆒ', 12684 => 'ᆔ', 12685 => 'ᆞ', 12686 => 'ᆡ', 12690 => '一', 12691 => '二', 12692 => '三', 12693 => '四', 12694 => '上', 12695 => '中', 12696 => '下', 12697 => '甲', 12698 => '乙', 12699 => '丙', 12700 => '丁', 12701 => '天', 12702 => '地', 12703 => '人', 12868 => '問', 12869 => '幼', 12870 => '文', 12871 => '箏', 12880 => 'pte', 12881 => '21', 12882 => '22', 12883 => '23', 12884 => '24', 12885 => '25', 12886 => '26', 12887 => '27', 12888 => '28', 12889 => '29', 12890 => '30', 12891 => '31', 12892 => '32', 12893 => '33', 12894 => '34', 12895 => '35', 12896 => 'ᄀ', 12897 => 'ᄂ', 12898 => 'ᄃ', 12899 => 'ᄅ', 12900 => 'ᄆ', 12901 => 'ᄇ', 12902 => 'ᄉ', 12903 => 'ᄋ', 12904 => 'ᄌ', 12905 => 'ᄎ', 12906 => 'ᄏ', 12907 => 'ᄐ', 12908 => 'ᄑ', 12909 => 'ᄒ', 12910 => '가', 12911 => '나', 12912 => '다', 12913 => '라', 12914 => '마', 12915 => '바', 12916 => '사', 12917 => '아', 12918 => '자', 12919 => '차', 12920 => '카', 12921 => '타', 12922 => '파', 12923 => '하', 12924 => '참고', 12925 => '주의', 12926 => '우', 12928 => '一', 12929 => '二', 12930 => '三', 12931 => '四', 12932 => '五', 12933 => '六', 12934 => '七', 12935 => '八', 12936 => '九', 12937 => '十', 12938 => '月', 12939 => '火', 12940 => '水', 12941 => '木', 12942 => '金', 12943 => '土', 12944 => '日', 12945 => '株', 12946 => '有', 12947 => '社', 12948 => '名', 12949 => '特', 12950 => '財', 12951 => '祝', 12952 => '労', 12953 => '秘', 12954 => '男', 12955 => '女', 12956 => '適', 12957 => '優', 12958 => '印', 12959 => '注', 12960 => '項', 12961 => '休', 12962 => '写', 12963 => '正', 12964 => '上', 12965 => '中', 12966 => '下', 12967 => '左', 12968 => '右', 12969 => '医', 12970 => '宗', 12971 => '学', 12972 => '監', 12973 => '企', 12974 => '資', 12975 => '協', 12976 => '夜', 12977 => '36', 12978 => '37', 12979 => '38', 12980 => '39', 12981 => '40', 12982 => '41', 12983 => '42', 12984 => '43', 12985 => '44', 12986 => '45', 12987 => '46', 12988 => '47', 12989 => '48', 12990 => '49', 12991 => '50', 12992 => '1月', 12993 => '2月', 12994 => '3月', 12995 => '4月', 12996 => '5月', 12997 => '6月', 12998 => '7月', 12999 => '8月', 13000 => '9月', 13001 => '10月', 13002 => '11月', 13003 => '12月', 13004 => 'hg', 13005 => 'erg', 13006 => 'ev', 13007 => 'ltd', 13008 => 'ア', 13009 => 'イ', 13010 => 'ウ', 13011 => 'エ', 13012 => 'オ', 13013 => 'カ', 13014 => 'キ', 13015 => 'ク', 13016 => 'ケ', 13017 => 'コ', 13018 => 'サ', 13019 => 'シ', 13020 => 'ス', 13021 => 'セ', 13022 => 'ソ', 13023 => 'タ', 13024 => 'チ', 13025 => 'ツ', 13026 => 'テ', 13027 => 'ト', 13028 => 'ナ', 13029 => 'ニ', 13030 => 'ヌ', 13031 => 'ネ', 13032 => 'ノ', 13033 => 'ハ', 13034 => 'ヒ', 13035 => 'フ', 13036 => 'ヘ', 13037 => 'ホ', 13038 => 'マ', 13039 => 'ミ', 13040 => 'ム', 13041 => 'メ', 13042 => 'モ', 13043 => 'ヤ', 13044 => 'ユ', 13045 => 'ヨ', 13046 => 'ラ', 13047 => 'リ', 13048 => 'ル', 13049 => 'レ', 13050 => 'ロ', 13051 => 'ワ', 13052 => 'ヰ', 13053 => 'ヱ', 13054 => 'ヲ', 13055 => '令和', 13056 => 'アパート', 13057 => 'アルファ', 13058 => 'アンペア', 13059 => 'アール', 13060 => 'イニング', 13061 => 'インチ', 13062 => 'ウォン', 13063 => 'エスクード', 13064 => 'エーカー', 13065 => 'オンス', 13066 => 'オーム', 13067 => 'カイリ', 13068 => 'カラット', 13069 => 'カロリー', 13070 => 'ガロン', 13071 => 'ガンマ', 13072 => 'ギガ', 13073 => 'ギニー', 13074 => 'キュリー', 13075 => 'ギルダー', 13076 => 'キロ', 13077 => 'キログラム', 13078 => 'キロメートル', 13079 => 'キロワット', 13080 => 'グラム', 13081 => 'グラムトン', 13082 => 'クルゼイロ', 13083 => 'クローネ', 13084 => 'ケース', 13085 => 'コルナ', 13086 => 'コーポ', 13087 => 'サイクル', 13088 => 'サンチーム', 13089 => 'シリング', 13090 => 'センチ', 13091 => 'セント', 13092 => 'ダース', 13093 => 'デシ', 13094 => 'ドル', 13095 => 'トン', 13096 => 'ナノ', 13097 => 'ノット', 13098 => 'ハイツ', 13099 => 'パーセント', 13100 => 'パーツ', 13101 => 'バーレル', 13102 => 'ピアストル', 13103 => 'ピクル', 13104 => 'ピコ', 13105 => 'ビル', 13106 => 'ファラッド', 13107 => 'フィート', 13108 => 'ブッシェル', 13109 => 'フラン', 13110 => 'ヘクタール', 13111 => 'ペソ', 13112 => 'ペニヒ', 13113 => 'ヘルツ', 13114 => 'ペンス', 13115 => 'ページ', 13116 => 'ベータ', 13117 => 'ポイント', 13118 => 'ボルト', 13119 => 'ホン', 13120 => 'ポンド', 13121 => 'ホール', 13122 => 'ホーン', 13123 => 'マイクロ', 13124 => 'マイル', 13125 => 'マッハ', 13126 => 'マルク', 13127 => 'マンション', 13128 => 'ミクロン', 13129 => 'ミリ', 13130 => 'ミリバール', 13131 => 'メガ', 13132 => 'メガトン', 13133 => 'メートル', 13134 => 'ヤード', 13135 => 'ヤール', 13136 => 'ユアン', 13137 => 'リットル', 13138 => 'リラ', 13139 => 'ルピー', 13140 => 'ルーブル', 13141 => 'レム', 13142 => 'レントゲン', 13143 => 'ワット', 13144 => '0点', 13145 => '1点', 13146 => '2点', 13147 => '3点', 13148 => '4点', 13149 => '5点', 13150 => '6点', 13151 => '7点', 13152 => '8点', 13153 => '9点', 13154 => '10点', 13155 => '11点', 13156 => '12点', 13157 => '13点', 13158 => '14点', 13159 => '15点', 13160 => '16点', 13161 => '17点', 13162 => '18点', 13163 => '19点', 13164 => '20点', 13165 => '21点', 13166 => '22点', 13167 => '23点', 13168 => '24点', 13169 => 'hpa', 13170 => 'da', 13171 => 'au', 13172 => 'bar', 13173 => 'ov', 13174 => 'pc', 13175 => 'dm', 13176 => 'dm2', 13177 => 'dm3', 13178 => 'iu', 13179 => '平成', 13180 => '昭和', 13181 => '大正', 13182 => '明治', 13183 => '株式会社', 13184 => 'pa', 13185 => 'na', 13186 => 'μa', 13187 => 'ma', 13188 => 'ka', 13189 => 'kb', 13190 => 'mb', 13191 => 'gb', 13192 => 'cal', 13193 => 'kcal', 13194 => 'pf', 13195 => 'nf', 13196 => 'μf', 13197 => 'μg', 13198 => 'mg', 13199 => 'kg', 13200 => 'hz', 13201 => 'khz', 13202 => 'mhz', 13203 => 'ghz', 13204 => 'thz', 13205 => 'μl', 13206 => 'ml', 13207 => 'dl', 13208 => 'kl', 13209 => 'fm', 13210 => 'nm', 13211 => 'μm', 13212 => 'mm', 13213 => 'cm', 13214 => 'km', 13215 => 'mm2', 13216 => 'cm2', 13217 => 'm2', 13218 => 'km2', 13219 => 'mm3', 13220 => 'cm3', 13221 => 'm3', 13222 => 'km3', 13223 => 'm∕s', 13224 => 'm∕s2', 13225 => 'pa', 13226 => 'kpa', 13227 => 'mpa', 13228 => 'gpa', 13229 => 'rad', 13230 => 'rad∕s', 13231 => 'rad∕s2', 13232 => 'ps', 13233 => 'ns', 13234 => 'μs', 13235 => 'ms', 13236 => 'pv', 13237 => 'nv', 13238 => 'μv', 13239 => 'mv', 13240 => 'kv', 13241 => 'mv', 13242 => 'pw', 13243 => 'nw', 13244 => 'μw', 13245 => 'mw', 13246 => 'kw', 13247 => 'mw', 13248 => 'kω', 13249 => 'mω', 13251 => 'bq', 13252 => 'cc', 13253 => 'cd', 13254 => 'c∕kg', 13256 => 'db', 13257 => 'gy', 13258 => 'ha', 13259 => 'hp', 13260 => 'in', 13261 => 'kk', 13262 => 'km', 13263 => 'kt', 13264 => 'lm', 13265 => 'ln', 13266 => 'log', 13267 => 'lx', 13268 => 'mb', 13269 => 'mil', 13270 => 'mol', 13271 => 'ph', 13273 => 'ppm', 13274 => 'pr', 13275 => 'sr', 13276 => 'sv', 13277 => 'wb', 13278 => 'v∕m', 13279 => 'a∕m', 13280 => '1日', 13281 => '2日', 13282 => '3日', 13283 => '4日', 13284 => '5日', 13285 => '6日', 13286 => '7日', 13287 => '8日', 13288 => '9日', 13289 => '10日', 13290 => '11日', 13291 => '12日', 13292 => '13日', 13293 => '14日', 13294 => '15日', 13295 => '16日', 13296 => '17日', 13297 => '18日', 13298 => '19日', 13299 => '20日', 13300 => '21日', 13301 => '22日', 13302 => '23日', 13303 => '24日', 13304 => '25日', 13305 => '26日', 13306 => '27日', 13307 => '28日', 13308 => '29日', 13309 => '30日', 13310 => '31日', 13311 => 'gal', 42560 => 'ꙁ', 42562 => 'ꙃ', 42564 => 'ꙅ', 42566 => 'ꙇ', 42568 => 'ꙉ', 42570 => 'ꙋ', 42572 => 'ꙍ', 42574 => 'ꙏ', 42576 => 'ꙑ', 42578 => 'ꙓ', 42580 => 'ꙕ', 42582 => 'ꙗ', 42584 => 'ꙙ', 42586 => 'ꙛ', 42588 => 'ꙝ', 42590 => 'ꙟ', 42592 => 'ꙡ', 42594 => 'ꙣ', 42596 => 'ꙥ', 42598 => 'ꙧ', 42600 => 'ꙩ', 42602 => 'ꙫ', 42604 => 'ꙭ', 42624 => 'ꚁ', 42626 => 'ꚃ', 42628 => 'ꚅ', 42630 => 'ꚇ', 42632 => 'ꚉ', 42634 => 'ꚋ', 42636 => 'ꚍ', 42638 => 'ꚏ', 42640 => 'ꚑ', 42642 => 'ꚓ', 42644 => 'ꚕ', 42646 => 'ꚗ', 42648 => 'ꚙ', 42650 => 'ꚛ', 42652 => 'ъ', 42653 => 'ь', 42786 => 'ꜣ', 42788 => 'ꜥ', 42790 => 'ꜧ', 42792 => 'ꜩ', 42794 => 'ꜫ', 42796 => 'ꜭ', 42798 => 'ꜯ', 42802 => 'ꜳ', 42804 => 'ꜵ', 42806 => 'ꜷ', 42808 => 'ꜹ', 42810 => 'ꜻ', 42812 => 'ꜽ', 42814 => 'ꜿ', 42816 => 'ꝁ', 42818 => 'ꝃ', 42820 => 'ꝅ', 42822 => 'ꝇ', 42824 => 'ꝉ', 42826 => 'ꝋ', 42828 => 'ꝍ', 42830 => 'ꝏ', 42832 => 'ꝑ', 42834 => 'ꝓ', 42836 => 'ꝕ', 42838 => 'ꝗ', 42840 => 'ꝙ', 42842 => 'ꝛ', 42844 => 'ꝝ', 42846 => 'ꝟ', 42848 => 'ꝡ', 42850 => 'ꝣ', 42852 => 'ꝥ', 42854 => 'ꝧ', 42856 => 'ꝩ', 42858 => 'ꝫ', 42860 => 'ꝭ', 42862 => 'ꝯ', 42864 => 'ꝯ', 42873 => 'ꝺ', 42875 => 'ꝼ', 42877 => 'ᵹ', 42878 => 'ꝿ', 42880 => 'ꞁ', 42882 => 'ꞃ', 42884 => 'ꞅ', 42886 => 'ꞇ', 42891 => 'ꞌ', 42893 => 'ɥ', 42896 => 'ꞑ', 42898 => 'ꞓ', 42902 => 'ꞗ', 42904 => 'ꞙ', 42906 => 'ꞛ', 42908 => 'ꞝ', 42910 => 'ꞟ', 42912 => 'ꞡ', 42914 => 'ꞣ', 42916 => 'ꞥ', 42918 => 'ꞧ', 42920 => 'ꞩ', 42922 => 'ɦ', 42923 => 'ɜ', 42924 => 'ɡ', 42925 => 'ɬ', 42926 => 'ɪ', 42928 => 'ʞ', 42929 => 'ʇ', 42930 => 'ʝ', 42931 => 'ꭓ', 42932 => 'ꞵ', 42934 => 'ꞷ', 42936 => 'ꞹ', 42938 => 'ꞻ', 42940 => 'ꞽ', 42942 => 'ꞿ', 42946 => 'ꟃ', 42948 => 'ꞔ', 42949 => 'ʂ', 42950 => 'ᶎ', 42951 => 'ꟈ', 42953 => 'ꟊ', 42997 => 'ꟶ', 43000 => 'ħ', 43001 => 'œ', 43868 => 'ꜧ', 43869 => 'ꬷ', 43870 => 'ɫ', 43871 => 'ꭒ', 43881 => 'ʍ', 43888 => 'Ꭰ', 43889 => 'Ꭱ', 43890 => 'Ꭲ', 43891 => 'Ꭳ', 43892 => 'Ꭴ', 43893 => 'Ꭵ', 43894 => 'Ꭶ', 43895 => 'Ꭷ', 43896 => 'Ꭸ', 43897 => 'Ꭹ', 43898 => 'Ꭺ', 43899 => 'Ꭻ', 43900 => 'Ꭼ', 43901 => 'Ꭽ', 43902 => 'Ꭾ', 43903 => 'Ꭿ', 43904 => 'Ꮀ', 43905 => 'Ꮁ', 43906 => 'Ꮂ', 43907 => 'Ꮃ', 43908 => 'Ꮄ', 43909 => 'Ꮅ', 43910 => 'Ꮆ', 43911 => 'Ꮇ', 43912 => 'Ꮈ', 43913 => 'Ꮉ', 43914 => 'Ꮊ', 43915 => 'Ꮋ', 43916 => 'Ꮌ', 43917 => 'Ꮍ', 43918 => 'Ꮎ', 43919 => 'Ꮏ', 43920 => 'Ꮐ', 43921 => 'Ꮑ', 43922 => 'Ꮒ', 43923 => 'Ꮓ', 43924 => 'Ꮔ', 43925 => 'Ꮕ', 43926 => 'Ꮖ', 43927 => 'Ꮗ', 43928 => 'Ꮘ', 43929 => 'Ꮙ', 43930 => 'Ꮚ', 43931 => 'Ꮛ', 43932 => 'Ꮜ', 43933 => 'Ꮝ', 43934 => 'Ꮞ', 43935 => 'Ꮟ', 43936 => 'Ꮠ', 43937 => 'Ꮡ', 43938 => 'Ꮢ', 43939 => 'Ꮣ', 43940 => 'Ꮤ', 43941 => 'Ꮥ', 43942 => 'Ꮦ', 43943 => 'Ꮧ', 43944 => 'Ꮨ', 43945 => 'Ꮩ', 43946 => 'Ꮪ', 43947 => 'Ꮫ', 43948 => 'Ꮬ', 43949 => 'Ꮭ', 43950 => 'Ꮮ', 43951 => 'Ꮯ', 43952 => 'Ꮰ', 43953 => 'Ꮱ', 43954 => 'Ꮲ', 43955 => 'Ꮳ', 43956 => 'Ꮴ', 43957 => 'Ꮵ', 43958 => 'Ꮶ', 43959 => 'Ꮷ', 43960 => 'Ꮸ', 43961 => 'Ꮹ', 43962 => 'Ꮺ', 43963 => 'Ꮻ', 43964 => 'Ꮼ', 43965 => 'Ꮽ', 43966 => 'Ꮾ', 43967 => 'Ꮿ', 63744 => '豈', 63745 => '更', 63746 => '車', 63747 => '賈', 63748 => '滑', 63749 => '串', 63750 => '句', 63751 => '龜', 63752 => '龜', 63753 => '契', 63754 => '金', 63755 => '喇', 63756 => '奈', 63757 => '懶', 63758 => '癩', 63759 => '羅', 63760 => '蘿', 63761 => '螺', 63762 => '裸', 63763 => '邏', 63764 => '樂', 63765 => '洛', 63766 => '烙', 63767 => '珞', 63768 => '落', 63769 => '酪', 63770 => '駱', 63771 => '亂', 63772 => '卵', 63773 => '欄', 63774 => '爛', 63775 => '蘭', 63776 => '鸞', 63777 => '嵐', 63778 => '濫', 63779 => '藍', 63780 => '襤', 63781 => '拉', 63782 => '臘', 63783 => '蠟', 63784 => '廊', 63785 => '朗', 63786 => '浪', 63787 => '狼', 63788 => '郎', 63789 => '來', 63790 => '冷', 63791 => '勞', 63792 => '擄', 63793 => '櫓', 63794 => '爐', 63795 => '盧', 63796 => '老', 63797 => '蘆', 63798 => '虜', 63799 => '路', 63800 => '露', 63801 => '魯', 63802 => '鷺', 63803 => '碌', 63804 => '祿', 63805 => '綠', 63806 => '菉', 63807 => '錄', 63808 => '鹿', 63809 => '論', 63810 => '壟', 63811 => '弄', 63812 => '籠', 63813 => '聾', 63814 => '牢', 63815 => '磊', 63816 => '賂', 63817 => '雷', 63818 => '壘', 63819 => '屢', 63820 => '樓', 63821 => '淚', 63822 => '漏', 63823 => '累', 63824 => '縷', 63825 => '陋', 63826 => '勒', 63827 => '肋', 63828 => '凜', 63829 => '凌', 63830 => '稜', 63831 => '綾', 63832 => '菱', 63833 => '陵', 63834 => '讀', 63835 => '拏', 63836 => '樂', 63837 => '諾', 63838 => '丹', 63839 => '寧', 63840 => '怒', 63841 => '率', 63842 => '異', 63843 => '北', 63844 => '磻', 63845 => '便', 63846 => '復', 63847 => '不', 63848 => '泌', 63849 => '數', 63850 => '索', 63851 => '參', 63852 => '塞', 63853 => '省', 63854 => '葉', 63855 => '說', 63856 => '殺', 63857 => '辰', 63858 => '沈', 63859 => '拾', 63860 => '若', 63861 => '掠', 63862 => '略', 63863 => '亮', 63864 => '兩', 63865 => '凉', 63866 => '梁', 63867 => '糧', 63868 => '良', 63869 => '諒', 63870 => '量', 63871 => '勵', 63872 => '呂', 63873 => '女', 63874 => '廬', 63875 => '旅', 63876 => '濾', 63877 => '礪', 63878 => '閭', 63879 => '驪', 63880 => '麗', 63881 => '黎', 63882 => '力', 63883 => '曆', 63884 => '歷', 63885 => '轢', 63886 => '年', 63887 => '憐', 63888 => '戀', 63889 => '撚', 63890 => '漣', 63891 => '煉', 63892 => '璉', 63893 => '秊', 63894 => '練', 63895 => '聯', 63896 => '輦', 63897 => '蓮', 63898 => '連', 63899 => '鍊', 63900 => '列', 63901 => '劣', 63902 => '咽', 63903 => '烈', 63904 => '裂', 63905 => '說', 63906 => '廉', 63907 => '念', 63908 => '捻', 63909 => '殮', 63910 => '簾', 63911 => '獵', 63912 => '令', 63913 => '囹', 63914 => '寧', 63915 => '嶺', 63916 => '怜', 63917 => '玲', 63918 => '瑩', 63919 => '羚', 63920 => '聆', 63921 => '鈴', 63922 => '零', 63923 => '靈', 63924 => '領', 63925 => '例', 63926 => '禮', 63927 => '醴', 63928 => '隸', 63929 => '惡', 63930 => '了', 63931 => '僚', 63932 => '寮', 63933 => '尿', 63934 => '料', 63935 => '樂', 63936 => '燎', 63937 => '療', 63938 => '蓼', 63939 => '遼', 63940 => '龍', 63941 => '暈', 63942 => '阮', 63943 => '劉', 63944 => '杻', 63945 => '柳', 63946 => '流', 63947 => '溜', 63948 => '琉', 63949 => '留', 63950 => '硫', 63951 => '紐', 63952 => '類', 63953 => '六', 63954 => '戮', 63955 => '陸', 63956 => '倫', 63957 => '崙', 63958 => '淪', 63959 => '輪', 63960 => '律', 63961 => '慄', 63962 => '栗', 63963 => '率', 63964 => '隆', 63965 => '利', 63966 => '吏', 63967 => '履', 63968 => '易', 63969 => '李', 63970 => '梨', 63971 => '泥', 63972 => '理', 63973 => '痢', 63974 => '罹', 63975 => '裏', 63976 => '裡', 63977 => '里', 63978 => '離', 63979 => '匿', 63980 => '溺', 63981 => '吝', 63982 => '燐', 63983 => '璘', 63984 => '藺', 63985 => '隣', 63986 => '鱗', 63987 => '麟', 63988 => '林', 63989 => '淋', 63990 => '臨', 63991 => '立', 63992 => '笠', 63993 => '粒', 63994 => '狀', 63995 => '炙', 63996 => '識', 63997 => '什', 63998 => '茶', 63999 => '刺', 64000 => '切', 64001 => '度', 64002 => '拓', 64003 => '糖', 64004 => '宅', 64005 => '洞', 64006 => '暴', 64007 => '輻', 64008 => '行', 64009 => '降', 64010 => '見', 64011 => '廓', 64012 => '兀', 64013 => '嗀', 64016 => '塚', 64018 => '晴', 64021 => '凞', 64022 => '猪', 64023 => '益', 64024 => '礼', 64025 => '神', 64026 => '祥', 64027 => '福', 64028 => '靖', 64029 => '精', 64030 => '羽', 64032 => '蘒', 64034 => '諸', 64037 => '逸', 64038 => '都', 64042 => '飯', 64043 => '飼', 64044 => '館', 64045 => '鶴', 64046 => '郞', 64047 => '隷', 64048 => '侮', 64049 => '僧', 64050 => '免', 64051 => '勉', 64052 => '勤', 64053 => '卑', 64054 => '喝', 64055 => '嘆', 64056 => '器', 64057 => '塀', 64058 => '墨', 64059 => '層', 64060 => '屮', 64061 => '悔', 64062 => '慨', 64063 => '憎', 64064 => '懲', 64065 => '敏', 64066 => '既', 64067 => '暑', 64068 => '梅', 64069 => '海', 64070 => '渚', 64071 => '漢', 64072 => '煮', 64073 => '爫', 64074 => '琢', 64075 => '碑', 64076 => '社', 64077 => '祉', 64078 => '祈', 64079 => '祐', 64080 => '祖', 64081 => '祝', 64082 => '禍', 64083 => '禎', 64084 => '穀', 64085 => '突', 64086 => '節', 64087 => '練', 64088 => '縉', 64089 => '繁', 64090 => '署', 64091 => '者', 64092 => '臭', 64093 => '艹', 64094 => '艹', 64095 => '著', 64096 => '褐', 64097 => '視', 64098 => '謁', 64099 => '謹', 64100 => '賓', 64101 => '贈', 64102 => '辶', 64103 => '逸', 64104 => '難', 64105 => '響', 64106 => '頻', 64107 => '恵', 64108 => '𤋮', 64109 => '舘', 64112 => '並', 64113 => '况', 64114 => '全', 64115 => '侀', 64116 => '充', 64117 => '冀', 64118 => '勇', 64119 => '勺', 64120 => '喝', 64121 => '啕', 64122 => '喙', 64123 => '嗢', 64124 => '塚', 64125 => '墳', 64126 => '奄', 64127 => '奔', 64128 => '婢', 64129 => '嬨', 64130 => '廒', 64131 => '廙', 64132 => '彩', 64133 => '徭', 64134 => '惘', 64135 => '慎', 64136 => '愈', 64137 => '憎', 64138 => '慠', 64139 => '懲', 64140 => '戴', 64141 => '揄', 64142 => '搜', 64143 => '摒', 64144 => '敖', 64145 => '晴', 64146 => '朗', 64147 => '望', 64148 => '杖', 64149 => '歹', 64150 => '殺', 64151 => '流', 64152 => '滛', 64153 => '滋', 64154 => '漢', 64155 => '瀞', 64156 => '煮', 64157 => '瞧', 64158 => '爵', 64159 => '犯', 64160 => '猪', 64161 => '瑱', 64162 => '甆', 64163 => '画', 64164 => '瘝', 64165 => '瘟', 64166 => '益', 64167 => '盛', 64168 => '直', 64169 => '睊', 64170 => '着', 64171 => '磌', 64172 => '窱', 64173 => '節', 64174 => '类', 64175 => '絛', 64176 => '練', 64177 => '缾', 64178 => '者', 64179 => '荒', 64180 => '華', 64181 => '蝹', 64182 => '襁', 64183 => '覆', 64184 => '視', 64185 => '調', 64186 => '諸', 64187 => '請', 64188 => '謁', 64189 => '諾', 64190 => '諭', 64191 => '謹', 64192 => '變', 64193 => '贈', 64194 => '輸', 64195 => '遲', 64196 => '醙', 64197 => '鉶', 64198 => '陼', 64199 => '難', 64200 => '靖', 64201 => '韛', 64202 => '響', 64203 => '頋', 64204 => '頻', 64205 => '鬒', 64206 => '龜', 64207 => '𢡊', 64208 => '𢡄', 64209 => '𣏕', 64210 => '㮝', 64211 => '䀘', 64212 => '䀹', 64213 => '𥉉', 64214 => '𥳐', 64215 => '𧻓', 64216 => '齃', 64217 => '龎', 64256 => 'ff', 64257 => 'fi', 64258 => 'fl', 64259 => 'ffi', 64260 => 'ffl', 64261 => 'st', 64262 => 'st', 64275 => 'մն', 64276 => 'մե', 64277 => 'մի', 64278 => 'վն', 64279 => 'մխ', 64285 => 'יִ', 64287 => 'ײַ', 64288 => 'ע', 64289 => 'א', 64290 => 'ד', 64291 => 'ה', 64292 => 'כ', 64293 => 'ל', 64294 => 'ם', 64295 => 'ר', 64296 => 'ת', 64298 => 'שׁ', 64299 => 'שׂ', 64300 => 'שּׁ', 64301 => 'שּׂ', 64302 => 'אַ', 64303 => 'אָ', 64304 => 'אּ', 64305 => 'בּ', 64306 => 'גּ', 64307 => 'דּ', 64308 => 'הּ', 64309 => 'וּ', 64310 => 'זּ', 64312 => 'טּ', 64313 => 'יּ', 64314 => 'ךּ', 64315 => 'כּ', 64316 => 'לּ', 64318 => 'מּ', 64320 => 'נּ', 64321 => 'סּ', 64323 => 'ףּ', 64324 => 'פּ', 64326 => 'צּ', 64327 => 'קּ', 64328 => 'רּ', 64329 => 'שּ', 64330 => 'תּ', 64331 => 'וֹ', 64332 => 'בֿ', 64333 => 'כֿ', 64334 => 'פֿ', 64335 => 'אל', 64336 => 'ٱ', 64337 => 'ٱ', 64338 => 'ٻ', 64339 => 'ٻ', 64340 => 'ٻ', 64341 => 'ٻ', 64342 => 'پ', 64343 => 'پ', 64344 => 'پ', 64345 => 'پ', 64346 => 'ڀ', 64347 => 'ڀ', 64348 => 'ڀ', 64349 => 'ڀ', 64350 => 'ٺ', 64351 => 'ٺ', 64352 => 'ٺ', 64353 => 'ٺ', 64354 => 'ٿ', 64355 => 'ٿ', 64356 => 'ٿ', 64357 => 'ٿ', 64358 => 'ٹ', 64359 => 'ٹ', 64360 => 'ٹ', 64361 => 'ٹ', 64362 => 'ڤ', 64363 => 'ڤ', 64364 => 'ڤ', 64365 => 'ڤ', 64366 => 'ڦ', 64367 => 'ڦ', 64368 => 'ڦ', 64369 => 'ڦ', 64370 => 'ڄ', 64371 => 'ڄ', 64372 => 'ڄ', 64373 => 'ڄ', 64374 => 'ڃ', 64375 => 'ڃ', 64376 => 'ڃ', 64377 => 'ڃ', 64378 => 'چ', 64379 => 'چ', 64380 => 'چ', 64381 => 'چ', 64382 => 'ڇ', 64383 => 'ڇ', 64384 => 'ڇ', 64385 => 'ڇ', 64386 => 'ڍ', 64387 => 'ڍ', 64388 => 'ڌ', 64389 => 'ڌ', 64390 => 'ڎ', 64391 => 'ڎ', 64392 => 'ڈ', 64393 => 'ڈ', 64394 => 'ژ', 64395 => 'ژ', 64396 => 'ڑ', 64397 => 'ڑ', 64398 => 'ک', 64399 => 'ک', 64400 => 'ک', 64401 => 'ک', 64402 => 'گ', 64403 => 'گ', 64404 => 'گ', 64405 => 'گ', 64406 => 'ڳ', 64407 => 'ڳ', 64408 => 'ڳ', 64409 => 'ڳ', 64410 => 'ڱ', 64411 => 'ڱ', 64412 => 'ڱ', 64413 => 'ڱ', 64414 => 'ں', 64415 => 'ں', 64416 => 'ڻ', 64417 => 'ڻ', 64418 => 'ڻ', 64419 => 'ڻ', 64420 => 'ۀ', 64421 => 'ۀ', 64422 => 'ہ', 64423 => 'ہ', 64424 => 'ہ', 64425 => 'ہ', 64426 => 'ھ', 64427 => 'ھ', 64428 => 'ھ', 64429 => 'ھ', 64430 => 'ے', 64431 => 'ے', 64432 => 'ۓ', 64433 => 'ۓ', 64467 => 'ڭ', 64468 => 'ڭ', 64469 => 'ڭ', 64470 => 'ڭ', 64471 => 'ۇ', 64472 => 'ۇ', 64473 => 'ۆ', 64474 => 'ۆ', 64475 => 'ۈ', 64476 => 'ۈ', 64477 => 'ۇٴ', 64478 => 'ۋ', 64479 => 'ۋ', 64480 => 'ۅ', 64481 => 'ۅ', 64482 => 'ۉ', 64483 => 'ۉ', 64484 => 'ې', 64485 => 'ې', 64486 => 'ې', 64487 => 'ې', 64488 => 'ى', 64489 => 'ى', 64490 => 'ئا', 64491 => 'ئا', 64492 => 'ئە', 64493 => 'ئە', 64494 => 'ئو', 64495 => 'ئو', 64496 => 'ئۇ', 64497 => 'ئۇ', 64498 => 'ئۆ', 64499 => 'ئۆ', 64500 => 'ئۈ', 64501 => 'ئۈ', 64502 => 'ئې', 64503 => 'ئې', 64504 => 'ئې', 64505 => 'ئى', 64506 => 'ئى', 64507 => 'ئى', 64508 => 'ی', 64509 => 'ی', 64510 => 'ی', 64511 => 'ی', 64512 => 'ئج', 64513 => 'ئح', 64514 => 'ئم', 64515 => 'ئى', 64516 => 'ئي', 64517 => 'بج', 64518 => 'بح', 64519 => 'بخ', 64520 => 'بم', 64521 => 'بى', 64522 => 'بي', 64523 => 'تج', 64524 => 'تح', 64525 => 'تخ', 64526 => 'تم', 64527 => 'تى', 64528 => 'تي', 64529 => 'ثج', 64530 => 'ثم', 64531 => 'ثى', 64532 => 'ثي', 64533 => 'جح', 64534 => 'جم', 64535 => 'حج', 64536 => 'حم', 64537 => 'خج', 64538 => 'خح', 64539 => 'خم', 64540 => 'سج', 64541 => 'سح', 64542 => 'سخ', 64543 => 'سم', 64544 => 'صح', 64545 => 'صم', 64546 => 'ضج', 64547 => 'ضح', 64548 => 'ضخ', 64549 => 'ضم', 64550 => 'طح', 64551 => 'طم', 64552 => 'ظم', 64553 => 'عج', 64554 => 'عم', 64555 => 'غج', 64556 => 'غم', 64557 => 'فج', 64558 => 'فح', 64559 => 'فخ', 64560 => 'فم', 64561 => 'فى', 64562 => 'في', 64563 => 'قح', 64564 => 'قم', 64565 => 'قى', 64566 => 'قي', 64567 => 'كا', 64568 => 'كج', 64569 => 'كح', 64570 => 'كخ', 64571 => 'كل', 64572 => 'كم', 64573 => 'كى', 64574 => 'كي', 64575 => 'لج', 64576 => 'لح', 64577 => 'لخ', 64578 => 'لم', 64579 => 'لى', 64580 => 'لي', 64581 => 'مج', 64582 => 'مح', 64583 => 'مخ', 64584 => 'مم', 64585 => 'مى', 64586 => 'مي', 64587 => 'نج', 64588 => 'نح', 64589 => 'نخ', 64590 => 'نم', 64591 => 'نى', 64592 => 'ني', 64593 => 'هج', 64594 => 'هم', 64595 => 'هى', 64596 => 'هي', 64597 => 'يج', 64598 => 'يح', 64599 => 'يخ', 64600 => 'يم', 64601 => 'يى', 64602 => 'يي', 64603 => 'ذٰ', 64604 => 'رٰ', 64605 => 'ىٰ', 64612 => 'ئر', 64613 => 'ئز', 64614 => 'ئم', 64615 => 'ئن', 64616 => 'ئى', 64617 => 'ئي', 64618 => 'بر', 64619 => 'بز', 64620 => 'بم', 64621 => 'بن', 64622 => 'بى', 64623 => 'بي', 64624 => 'تر', 64625 => 'تز', 64626 => 'تم', 64627 => 'تن', 64628 => 'تى', 64629 => 'تي', 64630 => 'ثر', 64631 => 'ثز', 64632 => 'ثم', 64633 => 'ثن', 64634 => 'ثى', 64635 => 'ثي', 64636 => 'فى', 64637 => 'في', 64638 => 'قى', 64639 => 'قي', 64640 => 'كا', 64641 => 'كل', 64642 => 'كم', 64643 => 'كى', 64644 => 'كي', 64645 => 'لم', 64646 => 'لى', 64647 => 'لي', 64648 => 'ما', 64649 => 'مم', 64650 => 'نر', 64651 => 'نز', 64652 => 'نم', 64653 => 'نن', 64654 => 'نى', 64655 => 'ني', 64656 => 'ىٰ', 64657 => 'ير', 64658 => 'يز', 64659 => 'يم', 64660 => 'ين', 64661 => 'يى', 64662 => 'يي', 64663 => 'ئج', 64664 => 'ئح', 64665 => 'ئخ', 64666 => 'ئم', 64667 => 'ئه', 64668 => 'بج', 64669 => 'بح', 64670 => 'بخ', 64671 => 'بم', 64672 => 'به', 64673 => 'تج', 64674 => 'تح', 64675 => 'تخ', 64676 => 'تم', 64677 => 'ته', 64678 => 'ثم', 64679 => 'جح', 64680 => 'جم', 64681 => 'حج', 64682 => 'حم', 64683 => 'خج', 64684 => 'خم', 64685 => 'سج', 64686 => 'سح', 64687 => 'سخ', 64688 => 'سم', 64689 => 'صح', 64690 => 'صخ', 64691 => 'صم', 64692 => 'ضج', 64693 => 'ضح', 64694 => 'ضخ', 64695 => 'ضم', 64696 => 'طح', 64697 => 'ظم', 64698 => 'عج', 64699 => 'عم', 64700 => 'غج', 64701 => 'غم', 64702 => 'فج', 64703 => 'فح', 64704 => 'فخ', 64705 => 'فم', 64706 => 'قح', 64707 => 'قم', 64708 => 'كج', 64709 => 'كح', 64710 => 'كخ', 64711 => 'كل', 64712 => 'كم', 64713 => 'لج', 64714 => 'لح', 64715 => 'لخ', 64716 => 'لم', 64717 => 'له', 64718 => 'مج', 64719 => 'مح', 64720 => 'مخ', 64721 => 'مم', 64722 => 'نج', 64723 => 'نح', 64724 => 'نخ', 64725 => 'نم', 64726 => 'نه', 64727 => 'هج', 64728 => 'هم', 64729 => 'هٰ', 64730 => 'يج', 64731 => 'يح', 64732 => 'يخ', 64733 => 'يم', 64734 => 'يه', 64735 => 'ئم', 64736 => 'ئه', 64737 => 'بم', 64738 => 'به', 64739 => 'تم', 64740 => 'ته', 64741 => 'ثم', 64742 => 'ثه', 64743 => 'سم', 64744 => 'سه', 64745 => 'شم', 64746 => 'شه', 64747 => 'كل', 64748 => 'كم', 64749 => 'لم', 64750 => 'نم', 64751 => 'نه', 64752 => 'يم', 64753 => 'يه', 64754 => 'ـَّ', 64755 => 'ـُّ', 64756 => 'ـِّ', 64757 => 'طى', 64758 => 'طي', 64759 => 'عى', 64760 => 'عي', 64761 => 'غى', 64762 => 'غي', 64763 => 'سى', 64764 => 'سي', 64765 => 'شى', 64766 => 'شي', 64767 => 'حى', 64768 => 'حي', 64769 => 'جى', 64770 => 'جي', 64771 => 'خى', 64772 => 'خي', 64773 => 'صى', 64774 => 'صي', 64775 => 'ضى', 64776 => 'ضي', 64777 => 'شج', 64778 => 'شح', 64779 => 'شخ', 64780 => 'شم', 64781 => 'شر', 64782 => 'سر', 64783 => 'صر', 64784 => 'ضر', 64785 => 'طى', 64786 => 'طي', 64787 => 'عى', 64788 => 'عي', 64789 => 'غى', 64790 => 'غي', 64791 => 'سى', 64792 => 'سي', 64793 => 'شى', 64794 => 'شي', 64795 => 'حى', 64796 => 'حي', 64797 => 'جى', 64798 => 'جي', 64799 => 'خى', 64800 => 'خي', 64801 => 'صى', 64802 => 'صي', 64803 => 'ضى', 64804 => 'ضي', 64805 => 'شج', 64806 => 'شح', 64807 => 'شخ', 64808 => 'شم', 64809 => 'شر', 64810 => 'سر', 64811 => 'صر', 64812 => 'ضر', 64813 => 'شج', 64814 => 'شح', 64815 => 'شخ', 64816 => 'شم', 64817 => 'سه', 64818 => 'شه', 64819 => 'طم', 64820 => 'سج', 64821 => 'سح', 64822 => 'سخ', 64823 => 'شج', 64824 => 'شح', 64825 => 'شخ', 64826 => 'طم', 64827 => 'ظم', 64828 => 'اً', 64829 => 'اً', 64848 => 'تجم', 64849 => 'تحج', 64850 => 'تحج', 64851 => 'تحم', 64852 => 'تخم', 64853 => 'تمج', 64854 => 'تمح', 64855 => 'تمخ', 64856 => 'جمح', 64857 => 'جمح', 64858 => 'حمي', 64859 => 'حمى', 64860 => 'سحج', 64861 => 'سجح', 64862 => 'سجى', 64863 => 'سمح', 64864 => 'سمح', 64865 => 'سمج', 64866 => 'سمم', 64867 => 'سمم', 64868 => 'صحح', 64869 => 'صحح', 64870 => 'صمم', 64871 => 'شحم', 64872 => 'شحم', 64873 => 'شجي', 64874 => 'شمخ', 64875 => 'شمخ', 64876 => 'شمم', 64877 => 'شمم', 64878 => 'ضحى', 64879 => 'ضخم', 64880 => 'ضخم', 64881 => 'طمح', 64882 => 'طمح', 64883 => 'طمم', 64884 => 'طمي', 64885 => 'عجم', 64886 => 'عمم', 64887 => 'عمم', 64888 => 'عمى', 64889 => 'غمم', 64890 => 'غمي', 64891 => 'غمى', 64892 => 'فخم', 64893 => 'فخم', 64894 => 'قمح', 64895 => 'قمم', 64896 => 'لحم', 64897 => 'لحي', 64898 => 'لحى', 64899 => 'لجج', 64900 => 'لجج', 64901 => 'لخم', 64902 => 'لخم', 64903 => 'لمح', 64904 => 'لمح', 64905 => 'محج', 64906 => 'محم', 64907 => 'محي', 64908 => 'مجح', 64909 => 'مجم', 64910 => 'مخج', 64911 => 'مخم', 64914 => 'مجخ', 64915 => 'همج', 64916 => 'همم', 64917 => 'نحم', 64918 => 'نحى', 64919 => 'نجم', 64920 => 'نجم', 64921 => 'نجى', 64922 => 'نمي', 64923 => 'نمى', 64924 => 'يمم', 64925 => 'يمم', 64926 => 'بخي', 64927 => 'تجي', 64928 => 'تجى', 64929 => 'تخي', 64930 => 'تخى', 64931 => 'تمي', 64932 => 'تمى', 64933 => 'جمي', 64934 => 'جحى', 64935 => 'جمى', 64936 => 'سخى', 64937 => 'صحي', 64938 => 'شحي', 64939 => 'ضحي', 64940 => 'لجي', 64941 => 'لمي', 64942 => 'يحي', 64943 => 'يجي', 64944 => 'يمي', 64945 => 'ممي', 64946 => 'قمي', 64947 => 'نحي', 64948 => 'قمح', 64949 => 'لحم', 64950 => 'عمي', 64951 => 'كمي', 64952 => 'نجح', 64953 => 'مخي', 64954 => 'لجم', 64955 => 'كمم', 64956 => 'لجم', 64957 => 'نجح', 64958 => 'جحي', 64959 => 'حجي', 64960 => 'مجي', 64961 => 'فمي', 64962 => 'بحي', 64963 => 'كمم', 64964 => 'عجم', 64965 => 'صمم', 64966 => 'سخي', 64967 => 'نجي', 65008 => 'صلے', 65009 => 'قلے', 65010 => 'الله', 65011 => 'اكبر', 65012 => 'محمد', 65013 => 'صلعم', 65014 => 'رسول', 65015 => 'عليه', 65016 => 'وسلم', 65017 => 'صلى', 65020 => 'ریال', 65041 => '、', 65047 => '〖', 65048 => '〗', 65073 => '—', 65074 => '–', 65081 => '〔', 65082 => '〕', 65083 => '【', 65084 => '】', 65085 => '《', 65086 => '》', 65087 => '〈', 65088 => '〉', 65089 => '「', 65090 => '」', 65091 => '『', 65092 => '』', 65105 => '、', 65112 => '—', 65117 => '〔', 65118 => '〕', 65123 => '-', 65137 => 'ـً', 65143 => 'ـَ', 65145 => 'ـُ', 65147 => 'ـِ', 65149 => 'ـّ', 65151 => 'ـْ', 65152 => 'ء', 65153 => 'آ', 65154 => 'آ', 65155 => 'أ', 65156 => 'أ', 65157 => 'ؤ', 65158 => 'ؤ', 65159 => 'إ', 65160 => 'إ', 65161 => 'ئ', 65162 => 'ئ', 65163 => 'ئ', 65164 => 'ئ', 65165 => 'ا', 65166 => 'ا', 65167 => 'ب', 65168 => 'ب', 65169 => 'ب', 65170 => 'ب', 65171 => 'ة', 65172 => 'ة', 65173 => 'ت', 65174 => 'ت', 65175 => 'ت', 65176 => 'ت', 65177 => 'ث', 65178 => 'ث', 65179 => 'ث', 65180 => 'ث', 65181 => 'ج', 65182 => 'ج', 65183 => 'ج', 65184 => 'ج', 65185 => 'ح', 65186 => 'ح', 65187 => 'ح', 65188 => 'ح', 65189 => 'خ', 65190 => 'خ', 65191 => 'خ', 65192 => 'خ', 65193 => 'د', 65194 => 'د', 65195 => 'ذ', 65196 => 'ذ', 65197 => 'ر', 65198 => 'ر', 65199 => 'ز', 65200 => 'ز', 65201 => 'س', 65202 => 'س', 65203 => 'س', 65204 => 'س', 65205 => 'ش', 65206 => 'ش', 65207 => 'ش', 65208 => 'ش', 65209 => 'ص', 65210 => 'ص', 65211 => 'ص', 65212 => 'ص', 65213 => 'ض', 65214 => 'ض', 65215 => 'ض', 65216 => 'ض', 65217 => 'ط', 65218 => 'ط', 65219 => 'ط', 65220 => 'ط', 65221 => 'ظ', 65222 => 'ظ', 65223 => 'ظ', 65224 => 'ظ', 65225 => 'ع', 65226 => 'ع', 65227 => 'ع', 65228 => 'ع', 65229 => 'غ', 65230 => 'غ', 65231 => 'غ', 65232 => 'غ', 65233 => 'ف', 65234 => 'ف', 65235 => 'ف', 65236 => 'ف', 65237 => 'ق', 65238 => 'ق', 65239 => 'ق', 65240 => 'ق', 65241 => 'ك', 65242 => 'ك', 65243 => 'ك', 65244 => 'ك', 65245 => 'ل', 65246 => 'ل', 65247 => 'ل', 65248 => 'ل', 65249 => 'م', 65250 => 'م', 65251 => 'م', 65252 => 'م', 65253 => 'ن', 65254 => 'ن', 65255 => 'ن', 65256 => 'ن', 65257 => 'ه', 65258 => 'ه', 65259 => 'ه', 65260 => 'ه', 65261 => 'و', 65262 => 'و', 65263 => 'ى', 65264 => 'ى', 65265 => 'ي', 65266 => 'ي', 65267 => 'ي', 65268 => 'ي', 65269 => 'لآ', 65270 => 'لآ', 65271 => 'لأ', 65272 => 'لأ', 65273 => 'لإ', 65274 => 'لإ', 65275 => 'لا', 65276 => 'لا', 65293 => '-', 65294 => '.', 65296 => '0', 65297 => '1', 65298 => '2', 65299 => '3', 65300 => '4', 65301 => '5', 65302 => '6', 65303 => '7', 65304 => '8', 65305 => '9', 65313 => 'a', 65314 => 'b', 65315 => 'c', 65316 => 'd', 65317 => 'e', 65318 => 'f', 65319 => 'g', 65320 => 'h', 65321 => 'i', 65322 => 'j', 65323 => 'k', 65324 => 'l', 65325 => 'm', 65326 => 'n', 65327 => 'o', 65328 => 'p', 65329 => 'q', 65330 => 'r', 65331 => 's', 65332 => 't', 65333 => 'u', 65334 => 'v', 65335 => 'w', 65336 => 'x', 65337 => 'y', 65338 => 'z', 65345 => 'a', 65346 => 'b', 65347 => 'c', 65348 => 'd', 65349 => 'e', 65350 => 'f', 65351 => 'g', 65352 => 'h', 65353 => 'i', 65354 => 'j', 65355 => 'k', 65356 => 'l', 65357 => 'm', 65358 => 'n', 65359 => 'o', 65360 => 'p', 65361 => 'q', 65362 => 'r', 65363 => 's', 65364 => 't', 65365 => 'u', 65366 => 'v', 65367 => 'w', 65368 => 'x', 65369 => 'y', 65370 => 'z', 65375 => '⦅', 65376 => '⦆', 65377 => '.', 65378 => '「', 65379 => '」', 65380 => '、', 65381 => '・', 65382 => 'ヲ', 65383 => 'ァ', 65384 => 'ィ', 65385 => 'ゥ', 65386 => 'ェ', 65387 => 'ォ', 65388 => 'ャ', 65389 => 'ュ', 65390 => 'ョ', 65391 => 'ッ', 65392 => 'ー', 65393 => 'ア', 65394 => 'イ', 65395 => 'ウ', 65396 => 'エ', 65397 => 'オ', 65398 => 'カ', 65399 => 'キ', 65400 => 'ク', 65401 => 'ケ', 65402 => 'コ', 65403 => 'サ', 65404 => 'シ', 65405 => 'ス', 65406 => 'セ', 65407 => 'ソ', 65408 => 'タ', 65409 => 'チ', 65410 => 'ツ', 65411 => 'テ', 65412 => 'ト', 65413 => 'ナ', 65414 => 'ニ', 65415 => 'ヌ', 65416 => 'ネ', 65417 => 'ノ', 65418 => 'ハ', 65419 => 'ヒ', 65420 => 'フ', 65421 => 'ヘ', 65422 => 'ホ', 65423 => 'マ', 65424 => 'ミ', 65425 => 'ム', 65426 => 'メ', 65427 => 'モ', 65428 => 'ヤ', 65429 => 'ユ', 65430 => 'ヨ', 65431 => 'ラ', 65432 => 'リ', 65433 => 'ル', 65434 => 'レ', 65435 => 'ロ', 65436 => 'ワ', 65437 => 'ン', 65438 => '゙', 65439 => '゚', 65441 => 'ᄀ', 65442 => 'ᄁ', 65443 => 'ᆪ', 65444 => 'ᄂ', 65445 => 'ᆬ', 65446 => 'ᆭ', 65447 => 'ᄃ', 65448 => 'ᄄ', 65449 => 'ᄅ', 65450 => 'ᆰ', 65451 => 'ᆱ', 65452 => 'ᆲ', 65453 => 'ᆳ', 65454 => 'ᆴ', 65455 => 'ᆵ', 65456 => 'ᄚ', 65457 => 'ᄆ', 65458 => 'ᄇ', 65459 => 'ᄈ', 65460 => 'ᄡ', 65461 => 'ᄉ', 65462 => 'ᄊ', 65463 => 'ᄋ', 65464 => 'ᄌ', 65465 => 'ᄍ', 65466 => 'ᄎ', 65467 => 'ᄏ', 65468 => 'ᄐ', 65469 => 'ᄑ', 65470 => 'ᄒ', 65474 => 'ᅡ', 65475 => 'ᅢ', 65476 => 'ᅣ', 65477 => 'ᅤ', 65478 => 'ᅥ', 65479 => 'ᅦ', 65482 => 'ᅧ', 65483 => 'ᅨ', 65484 => 'ᅩ', 65485 => 'ᅪ', 65486 => 'ᅫ', 65487 => 'ᅬ', 65490 => 'ᅭ', 65491 => 'ᅮ', 65492 => 'ᅯ', 65493 => 'ᅰ', 65494 => 'ᅱ', 65495 => 'ᅲ', 65498 => 'ᅳ', 65499 => 'ᅴ', 65500 => 'ᅵ', 65504 => '¢', 65505 => '£', 65506 => '¬', 65508 => '¦', 65509 => '¥', 65510 => '₩', 65512 => '│', 65513 => '←', 65514 => '↑', 65515 => '→', 65516 => '↓', 65517 => '■', 65518 => '○', 66560 => '𐐨', 66561 => '𐐩', 66562 => '𐐪', 66563 => '𐐫', 66564 => '𐐬', 66565 => '𐐭', 66566 => '𐐮', 66567 => '𐐯', 66568 => '𐐰', 66569 => '𐐱', 66570 => '𐐲', 66571 => '𐐳', 66572 => '𐐴', 66573 => '𐐵', 66574 => '𐐶', 66575 => '𐐷', 66576 => '𐐸', 66577 => '𐐹', 66578 => '𐐺', 66579 => '𐐻', 66580 => '𐐼', 66581 => '𐐽', 66582 => '𐐾', 66583 => '𐐿', 66584 => '𐑀', 66585 => '𐑁', 66586 => '𐑂', 66587 => '𐑃', 66588 => '𐑄', 66589 => '𐑅', 66590 => '𐑆', 66591 => '𐑇', 66592 => '𐑈', 66593 => '𐑉', 66594 => '𐑊', 66595 => '𐑋', 66596 => '𐑌', 66597 => '𐑍', 66598 => '𐑎', 66599 => '𐑏', 66736 => '𐓘', 66737 => '𐓙', 66738 => '𐓚', 66739 => '𐓛', 66740 => '𐓜', 66741 => '𐓝', 66742 => '𐓞', 66743 => '𐓟', 66744 => '𐓠', 66745 => '𐓡', 66746 => '𐓢', 66747 => '𐓣', 66748 => '𐓤', 66749 => '𐓥', 66750 => '𐓦', 66751 => '𐓧', 66752 => '𐓨', 66753 => '𐓩', 66754 => '𐓪', 66755 => '𐓫', 66756 => '𐓬', 66757 => '𐓭', 66758 => '𐓮', 66759 => '𐓯', 66760 => '𐓰', 66761 => '𐓱', 66762 => '𐓲', 66763 => '𐓳', 66764 => '𐓴', 66765 => '𐓵', 66766 => '𐓶', 66767 => '𐓷', 66768 => '𐓸', 66769 => '𐓹', 66770 => '𐓺', 66771 => '𐓻', 68736 => '𐳀', 68737 => '𐳁', 68738 => '𐳂', 68739 => '𐳃', 68740 => '𐳄', 68741 => '𐳅', 68742 => '𐳆', 68743 => '𐳇', 68744 => '𐳈', 68745 => '𐳉', 68746 => '𐳊', 68747 => '𐳋', 68748 => '𐳌', 68749 => '𐳍', 68750 => '𐳎', 68751 => '𐳏', 68752 => '𐳐', 68753 => '𐳑', 68754 => '𐳒', 68755 => '𐳓', 68756 => '𐳔', 68757 => '𐳕', 68758 => '𐳖', 68759 => '𐳗', 68760 => '𐳘', 68761 => '𐳙', 68762 => '𐳚', 68763 => '𐳛', 68764 => '𐳜', 68765 => '𐳝', 68766 => '𐳞', 68767 => '𐳟', 68768 => '𐳠', 68769 => '𐳡', 68770 => '𐳢', 68771 => '𐳣', 68772 => '𐳤', 68773 => '𐳥', 68774 => '𐳦', 68775 => '𐳧', 68776 => '𐳨', 68777 => '𐳩', 68778 => '𐳪', 68779 => '𐳫', 68780 => '𐳬', 68781 => '𐳭', 68782 => '𐳮', 68783 => '𐳯', 68784 => '𐳰', 68785 => '𐳱', 68786 => '𐳲', 71840 => '𑣀', 71841 => '𑣁', 71842 => '𑣂', 71843 => '𑣃', 71844 => '𑣄', 71845 => '𑣅', 71846 => '𑣆', 71847 => '𑣇', 71848 => '𑣈', 71849 => '𑣉', 71850 => '𑣊', 71851 => '𑣋', 71852 => '𑣌', 71853 => '𑣍', 71854 => '𑣎', 71855 => '𑣏', 71856 => '𑣐', 71857 => '𑣑', 71858 => '𑣒', 71859 => '𑣓', 71860 => '𑣔', 71861 => '𑣕', 71862 => '𑣖', 71863 => '𑣗', 71864 => '𑣘', 71865 => '𑣙', 71866 => '𑣚', 71867 => '𑣛', 71868 => '𑣜', 71869 => '𑣝', 71870 => '𑣞', 71871 => '𑣟', 93760 => '𖹠', 93761 => '𖹡', 93762 => '𖹢', 93763 => '𖹣', 93764 => '𖹤', 93765 => '𖹥', 93766 => '𖹦', 93767 => '𖹧', 93768 => '𖹨', 93769 => '𖹩', 93770 => '𖹪', 93771 => '𖹫', 93772 => '𖹬', 93773 => '𖹭', 93774 => '𖹮', 93775 => '𖹯', 93776 => '𖹰', 93777 => '𖹱', 93778 => '𖹲', 93779 => '𖹳', 93780 => '𖹴', 93781 => '𖹵', 93782 => '𖹶', 93783 => '𖹷', 93784 => '𖹸', 93785 => '𖹹', 93786 => '𖹺', 93787 => '𖹻', 93788 => '𖹼', 93789 => '𖹽', 93790 => '𖹾', 93791 => '𖹿', 119134 => '𝅗𝅥', 119135 => '𝅘𝅥', 119136 => '𝅘𝅥𝅮', 119137 => '𝅘𝅥𝅯', 119138 => '𝅘𝅥𝅰', 119139 => '𝅘𝅥𝅱', 119140 => '𝅘𝅥𝅲', 119227 => '𝆹𝅥', 119228 => '𝆺𝅥', 119229 => '𝆹𝅥𝅮', 119230 => '𝆺𝅥𝅮', 119231 => '𝆹𝅥𝅯', 119232 => '𝆺𝅥𝅯', 119808 => 'a', 119809 => 'b', 119810 => 'c', 119811 => 'd', 119812 => 'e', 119813 => 'f', 119814 => 'g', 119815 => 'h', 119816 => 'i', 119817 => 'j', 119818 => 'k', 119819 => 'l', 119820 => 'm', 119821 => 'n', 119822 => 'o', 119823 => 'p', 119824 => 'q', 119825 => 'r', 119826 => 's', 119827 => 't', 119828 => 'u', 119829 => 'v', 119830 => 'w', 119831 => 'x', 119832 => 'y', 119833 => 'z', 119834 => 'a', 119835 => 'b', 119836 => 'c', 119837 => 'd', 119838 => 'e', 119839 => 'f', 119840 => 'g', 119841 => 'h', 119842 => 'i', 119843 => 'j', 119844 => 'k', 119845 => 'l', 119846 => 'm', 119847 => 'n', 119848 => 'o', 119849 => 'p', 119850 => 'q', 119851 => 'r', 119852 => 's', 119853 => 't', 119854 => 'u', 119855 => 'v', 119856 => 'w', 119857 => 'x', 119858 => 'y', 119859 => 'z', 119860 => 'a', 119861 => 'b', 119862 => 'c', 119863 => 'd', 119864 => 'e', 119865 => 'f', 119866 => 'g', 119867 => 'h', 119868 => 'i', 119869 => 'j', 119870 => 'k', 119871 => 'l', 119872 => 'm', 119873 => 'n', 119874 => 'o', 119875 => 'p', 119876 => 'q', 119877 => 'r', 119878 => 's', 119879 => 't', 119880 => 'u', 119881 => 'v', 119882 => 'w', 119883 => 'x', 119884 => 'y', 119885 => 'z', 119886 => 'a', 119887 => 'b', 119888 => 'c', 119889 => 'd', 119890 => 'e', 119891 => 'f', 119892 => 'g', 119894 => 'i', 119895 => 'j', 119896 => 'k', 119897 => 'l', 119898 => 'm', 119899 => 'n', 119900 => 'o', 119901 => 'p', 119902 => 'q', 119903 => 'r', 119904 => 's', 119905 => 't', 119906 => 'u', 119907 => 'v', 119908 => 'w', 119909 => 'x', 119910 => 'y', 119911 => 'z', 119912 => 'a', 119913 => 'b', 119914 => 'c', 119915 => 'd', 119916 => 'e', 119917 => 'f', 119918 => 'g', 119919 => 'h', 119920 => 'i', 119921 => 'j', 119922 => 'k', 119923 => 'l', 119924 => 'm', 119925 => 'n', 119926 => 'o', 119927 => 'p', 119928 => 'q', 119929 => 'r', 119930 => 's', 119931 => 't', 119932 => 'u', 119933 => 'v', 119934 => 'w', 119935 => 'x', 119936 => 'y', 119937 => 'z', 119938 => 'a', 119939 => 'b', 119940 => 'c', 119941 => 'd', 119942 => 'e', 119943 => 'f', 119944 => 'g', 119945 => 'h', 119946 => 'i', 119947 => 'j', 119948 => 'k', 119949 => 'l', 119950 => 'm', 119951 => 'n', 119952 => 'o', 119953 => 'p', 119954 => 'q', 119955 => 'r', 119956 => 's', 119957 => 't', 119958 => 'u', 119959 => 'v', 119960 => 'w', 119961 => 'x', 119962 => 'y', 119963 => 'z', 119964 => 'a', 119966 => 'c', 119967 => 'd', 119970 => 'g', 119973 => 'j', 119974 => 'k', 119977 => 'n', 119978 => 'o', 119979 => 'p', 119980 => 'q', 119982 => 's', 119983 => 't', 119984 => 'u', 119985 => 'v', 119986 => 'w', 119987 => 'x', 119988 => 'y', 119989 => 'z', 119990 => 'a', 119991 => 'b', 119992 => 'c', 119993 => 'd', 119995 => 'f', 119997 => 'h', 119998 => 'i', 119999 => 'j', 120000 => 'k', 120001 => 'l', 120002 => 'm', 120003 => 'n', 120005 => 'p', 120006 => 'q', 120007 => 'r', 120008 => 's', 120009 => 't', 120010 => 'u', 120011 => 'v', 120012 => 'w', 120013 => 'x', 120014 => 'y', 120015 => 'z', 120016 => 'a', 120017 => 'b', 120018 => 'c', 120019 => 'd', 120020 => 'e', 120021 => 'f', 120022 => 'g', 120023 => 'h', 120024 => 'i', 120025 => 'j', 120026 => 'k', 120027 => 'l', 120028 => 'm', 120029 => 'n', 120030 => 'o', 120031 => 'p', 120032 => 'q', 120033 => 'r', 120034 => 's', 120035 => 't', 120036 => 'u', 120037 => 'v', 120038 => 'w', 120039 => 'x', 120040 => 'y', 120041 => 'z', 120042 => 'a', 120043 => 'b', 120044 => 'c', 120045 => 'd', 120046 => 'e', 120047 => 'f', 120048 => 'g', 120049 => 'h', 120050 => 'i', 120051 => 'j', 120052 => 'k', 120053 => 'l', 120054 => 'm', 120055 => 'n', 120056 => 'o', 120057 => 'p', 120058 => 'q', 120059 => 'r', 120060 => 's', 120061 => 't', 120062 => 'u', 120063 => 'v', 120064 => 'w', 120065 => 'x', 120066 => 'y', 120067 => 'z', 120068 => 'a', 120069 => 'b', 120071 => 'd', 120072 => 'e', 120073 => 'f', 120074 => 'g', 120077 => 'j', 120078 => 'k', 120079 => 'l', 120080 => 'm', 120081 => 'n', 120082 => 'o', 120083 => 'p', 120084 => 'q', 120086 => 's', 120087 => 't', 120088 => 'u', 120089 => 'v', 120090 => 'w', 120091 => 'x', 120092 => 'y', 120094 => 'a', 120095 => 'b', 120096 => 'c', 120097 => 'd', 120098 => 'e', 120099 => 'f', 120100 => 'g', 120101 => 'h', 120102 => 'i', 120103 => 'j', 120104 => 'k', 120105 => 'l', 120106 => 'm', 120107 => 'n', 120108 => 'o', 120109 => 'p', 120110 => 'q', 120111 => 'r', 120112 => 's', 120113 => 't', 120114 => 'u', 120115 => 'v', 120116 => 'w', 120117 => 'x', 120118 => 'y', 120119 => 'z', 120120 => 'a', 120121 => 'b', 120123 => 'd', 120124 => 'e', 120125 => 'f', 120126 => 'g', 120128 => 'i', 120129 => 'j', 120130 => 'k', 120131 => 'l', 120132 => 'm', 120134 => 'o', 120138 => 's', 120139 => 't', 120140 => 'u', 120141 => 'v', 120142 => 'w', 120143 => 'x', 120144 => 'y', 120146 => 'a', 120147 => 'b', 120148 => 'c', 120149 => 'd', 120150 => 'e', 120151 => 'f', 120152 => 'g', 120153 => 'h', 120154 => 'i', 120155 => 'j', 120156 => 'k', 120157 => 'l', 120158 => 'm', 120159 => 'n', 120160 => 'o', 120161 => 'p', 120162 => 'q', 120163 => 'r', 120164 => 's', 120165 => 't', 120166 => 'u', 120167 => 'v', 120168 => 'w', 120169 => 'x', 120170 => 'y', 120171 => 'z', 120172 => 'a', 120173 => 'b', 120174 => 'c', 120175 => 'd', 120176 => 'e', 120177 => 'f', 120178 => 'g', 120179 => 'h', 120180 => 'i', 120181 => 'j', 120182 => 'k', 120183 => 'l', 120184 => 'm', 120185 => 'n', 120186 => 'o', 120187 => 'p', 120188 => 'q', 120189 => 'r', 120190 => 's', 120191 => 't', 120192 => 'u', 120193 => 'v', 120194 => 'w', 120195 => 'x', 120196 => 'y', 120197 => 'z', 120198 => 'a', 120199 => 'b', 120200 => 'c', 120201 => 'd', 120202 => 'e', 120203 => 'f', 120204 => 'g', 120205 => 'h', 120206 => 'i', 120207 => 'j', 120208 => 'k', 120209 => 'l', 120210 => 'm', 120211 => 'n', 120212 => 'o', 120213 => 'p', 120214 => 'q', 120215 => 'r', 120216 => 's', 120217 => 't', 120218 => 'u', 120219 => 'v', 120220 => 'w', 120221 => 'x', 120222 => 'y', 120223 => 'z', 120224 => 'a', 120225 => 'b', 120226 => 'c', 120227 => 'd', 120228 => 'e', 120229 => 'f', 120230 => 'g', 120231 => 'h', 120232 => 'i', 120233 => 'j', 120234 => 'k', 120235 => 'l', 120236 => 'm', 120237 => 'n', 120238 => 'o', 120239 => 'p', 120240 => 'q', 120241 => 'r', 120242 => 's', 120243 => 't', 120244 => 'u', 120245 => 'v', 120246 => 'w', 120247 => 'x', 120248 => 'y', 120249 => 'z', 120250 => 'a', 120251 => 'b', 120252 => 'c', 120253 => 'd', 120254 => 'e', 120255 => 'f', 120256 => 'g', 120257 => 'h', 120258 => 'i', 120259 => 'j', 120260 => 'k', 120261 => 'l', 120262 => 'm', 120263 => 'n', 120264 => 'o', 120265 => 'p', 120266 => 'q', 120267 => 'r', 120268 => 's', 120269 => 't', 120270 => 'u', 120271 => 'v', 120272 => 'w', 120273 => 'x', 120274 => 'y', 120275 => 'z', 120276 => 'a', 120277 => 'b', 120278 => 'c', 120279 => 'd', 120280 => 'e', 120281 => 'f', 120282 => 'g', 120283 => 'h', 120284 => 'i', 120285 => 'j', 120286 => 'k', 120287 => 'l', 120288 => 'm', 120289 => 'n', 120290 => 'o', 120291 => 'p', 120292 => 'q', 120293 => 'r', 120294 => 's', 120295 => 't', 120296 => 'u', 120297 => 'v', 120298 => 'w', 120299 => 'x', 120300 => 'y', 120301 => 'z', 120302 => 'a', 120303 => 'b', 120304 => 'c', 120305 => 'd', 120306 => 'e', 120307 => 'f', 120308 => 'g', 120309 => 'h', 120310 => 'i', 120311 => 'j', 120312 => 'k', 120313 => 'l', 120314 => 'm', 120315 => 'n', 120316 => 'o', 120317 => 'p', 120318 => 'q', 120319 => 'r', 120320 => 's', 120321 => 't', 120322 => 'u', 120323 => 'v', 120324 => 'w', 120325 => 'x', 120326 => 'y', 120327 => 'z', 120328 => 'a', 120329 => 'b', 120330 => 'c', 120331 => 'd', 120332 => 'e', 120333 => 'f', 120334 => 'g', 120335 => 'h', 120336 => 'i', 120337 => 'j', 120338 => 'k', 120339 => 'l', 120340 => 'm', 120341 => 'n', 120342 => 'o', 120343 => 'p', 120344 => 'q', 120345 => 'r', 120346 => 's', 120347 => 't', 120348 => 'u', 120349 => 'v', 120350 => 'w', 120351 => 'x', 120352 => 'y', 120353 => 'z', 120354 => 'a', 120355 => 'b', 120356 => 'c', 120357 => 'd', 120358 => 'e', 120359 => 'f', 120360 => 'g', 120361 => 'h', 120362 => 'i', 120363 => 'j', 120364 => 'k', 120365 => 'l', 120366 => 'm', 120367 => 'n', 120368 => 'o', 120369 => 'p', 120370 => 'q', 120371 => 'r', 120372 => 's', 120373 => 't', 120374 => 'u', 120375 => 'v', 120376 => 'w', 120377 => 'x', 120378 => 'y', 120379 => 'z', 120380 => 'a', 120381 => 'b', 120382 => 'c', 120383 => 'd', 120384 => 'e', 120385 => 'f', 120386 => 'g', 120387 => 'h', 120388 => 'i', 120389 => 'j', 120390 => 'k', 120391 => 'l', 120392 => 'm', 120393 => 'n', 120394 => 'o', 120395 => 'p', 120396 => 'q', 120397 => 'r', 120398 => 's', 120399 => 't', 120400 => 'u', 120401 => 'v', 120402 => 'w', 120403 => 'x', 120404 => 'y', 120405 => 'z', 120406 => 'a', 120407 => 'b', 120408 => 'c', 120409 => 'd', 120410 => 'e', 120411 => 'f', 120412 => 'g', 120413 => 'h', 120414 => 'i', 120415 => 'j', 120416 => 'k', 120417 => 'l', 120418 => 'm', 120419 => 'n', 120420 => 'o', 120421 => 'p', 120422 => 'q', 120423 => 'r', 120424 => 's', 120425 => 't', 120426 => 'u', 120427 => 'v', 120428 => 'w', 120429 => 'x', 120430 => 'y', 120431 => 'z', 120432 => 'a', 120433 => 'b', 120434 => 'c', 120435 => 'd', 120436 => 'e', 120437 => 'f', 120438 => 'g', 120439 => 'h', 120440 => 'i', 120441 => 'j', 120442 => 'k', 120443 => 'l', 120444 => 'm', 120445 => 'n', 120446 => 'o', 120447 => 'p', 120448 => 'q', 120449 => 'r', 120450 => 's', 120451 => 't', 120452 => 'u', 120453 => 'v', 120454 => 'w', 120455 => 'x', 120456 => 'y', 120457 => 'z', 120458 => 'a', 120459 => 'b', 120460 => 'c', 120461 => 'd', 120462 => 'e', 120463 => 'f', 120464 => 'g', 120465 => 'h', 120466 => 'i', 120467 => 'j', 120468 => 'k', 120469 => 'l', 120470 => 'm', 120471 => 'n', 120472 => 'o', 120473 => 'p', 120474 => 'q', 120475 => 'r', 120476 => 's', 120477 => 't', 120478 => 'u', 120479 => 'v', 120480 => 'w', 120481 => 'x', 120482 => 'y', 120483 => 'z', 120484 => 'ı', 120485 => 'ȷ', 120488 => 'α', 120489 => 'β', 120490 => 'γ', 120491 => 'δ', 120492 => 'ε', 120493 => 'ζ', 120494 => 'η', 120495 => 'θ', 120496 => 'ι', 120497 => 'κ', 120498 => 'λ', 120499 => 'μ', 120500 => 'ν', 120501 => 'ξ', 120502 => 'ο', 120503 => 'π', 120504 => 'ρ', 120505 => 'θ', 120506 => 'σ', 120507 => 'τ', 120508 => 'υ', 120509 => 'φ', 120510 => 'χ', 120511 => 'ψ', 120512 => 'ω', 120513 => '∇', 120514 => 'α', 120515 => 'β', 120516 => 'γ', 120517 => 'δ', 120518 => 'ε', 120519 => 'ζ', 120520 => 'η', 120521 => 'θ', 120522 => 'ι', 120523 => 'κ', 120524 => 'λ', 120525 => 'μ', 120526 => 'ν', 120527 => 'ξ', 120528 => 'ο', 120529 => 'π', 120530 => 'ρ', 120531 => 'σ', 120532 => 'σ', 120533 => 'τ', 120534 => 'υ', 120535 => 'φ', 120536 => 'χ', 120537 => 'ψ', 120538 => 'ω', 120539 => '∂', 120540 => 'ε', 120541 => 'θ', 120542 => 'κ', 120543 => 'φ', 120544 => 'ρ', 120545 => 'π', 120546 => 'α', 120547 => 'β', 120548 => 'γ', 120549 => 'δ', 120550 => 'ε', 120551 => 'ζ', 120552 => 'η', 120553 => 'θ', 120554 => 'ι', 120555 => 'κ', 120556 => 'λ', 120557 => 'μ', 120558 => 'ν', 120559 => 'ξ', 120560 => 'ο', 120561 => 'π', 120562 => 'ρ', 120563 => 'θ', 120564 => 'σ', 120565 => 'τ', 120566 => 'υ', 120567 => 'φ', 120568 => 'χ', 120569 => 'ψ', 120570 => 'ω', 120571 => '∇', 120572 => 'α', 120573 => 'β', 120574 => 'γ', 120575 => 'δ', 120576 => 'ε', 120577 => 'ζ', 120578 => 'η', 120579 => 'θ', 120580 => 'ι', 120581 => 'κ', 120582 => 'λ', 120583 => 'μ', 120584 => 'ν', 120585 => 'ξ', 120586 => 'ο', 120587 => 'π', 120588 => 'ρ', 120589 => 'σ', 120590 => 'σ', 120591 => 'τ', 120592 => 'υ', 120593 => 'φ', 120594 => 'χ', 120595 => 'ψ', 120596 => 'ω', 120597 => '∂', 120598 => 'ε', 120599 => 'θ', 120600 => 'κ', 120601 => 'φ', 120602 => 'ρ', 120603 => 'π', 120604 => 'α', 120605 => 'β', 120606 => 'γ', 120607 => 'δ', 120608 => 'ε', 120609 => 'ζ', 120610 => 'η', 120611 => 'θ', 120612 => 'ι', 120613 => 'κ', 120614 => 'λ', 120615 => 'μ', 120616 => 'ν', 120617 => 'ξ', 120618 => 'ο', 120619 => 'π', 120620 => 'ρ', 120621 => 'θ', 120622 => 'σ', 120623 => 'τ', 120624 => 'υ', 120625 => 'φ', 120626 => 'χ', 120627 => 'ψ', 120628 => 'ω', 120629 => '∇', 120630 => 'α', 120631 => 'β', 120632 => 'γ', 120633 => 'δ', 120634 => 'ε', 120635 => 'ζ', 120636 => 'η', 120637 => 'θ', 120638 => 'ι', 120639 => 'κ', 120640 => 'λ', 120641 => 'μ', 120642 => 'ν', 120643 => 'ξ', 120644 => 'ο', 120645 => 'π', 120646 => 'ρ', 120647 => 'σ', 120648 => 'σ', 120649 => 'τ', 120650 => 'υ', 120651 => 'φ', 120652 => 'χ', 120653 => 'ψ', 120654 => 'ω', 120655 => '∂', 120656 => 'ε', 120657 => 'θ', 120658 => 'κ', 120659 => 'φ', 120660 => 'ρ', 120661 => 'π', 120662 => 'α', 120663 => 'β', 120664 => 'γ', 120665 => 'δ', 120666 => 'ε', 120667 => 'ζ', 120668 => 'η', 120669 => 'θ', 120670 => 'ι', 120671 => 'κ', 120672 => 'λ', 120673 => 'μ', 120674 => 'ν', 120675 => 'ξ', 120676 => 'ο', 120677 => 'π', 120678 => 'ρ', 120679 => 'θ', 120680 => 'σ', 120681 => 'τ', 120682 => 'υ', 120683 => 'φ', 120684 => 'χ', 120685 => 'ψ', 120686 => 'ω', 120687 => '∇', 120688 => 'α', 120689 => 'β', 120690 => 'γ', 120691 => 'δ', 120692 => 'ε', 120693 => 'ζ', 120694 => 'η', 120695 => 'θ', 120696 => 'ι', 120697 => 'κ', 120698 => 'λ', 120699 => 'μ', 120700 => 'ν', 120701 => 'ξ', 120702 => 'ο', 120703 => 'π', 120704 => 'ρ', 120705 => 'σ', 120706 => 'σ', 120707 => 'τ', 120708 => 'υ', 120709 => 'φ', 120710 => 'χ', 120711 => 'ψ', 120712 => 'ω', 120713 => '∂', 120714 => 'ε', 120715 => 'θ', 120716 => 'κ', 120717 => 'φ', 120718 => 'ρ', 120719 => 'π', 120720 => 'α', 120721 => 'β', 120722 => 'γ', 120723 => 'δ', 120724 => 'ε', 120725 => 'ζ', 120726 => 'η', 120727 => 'θ', 120728 => 'ι', 120729 => 'κ', 120730 => 'λ', 120731 => 'μ', 120732 => 'ν', 120733 => 'ξ', 120734 => 'ο', 120735 => 'π', 120736 => 'ρ', 120737 => 'θ', 120738 => 'σ', 120739 => 'τ', 120740 => 'υ', 120741 => 'φ', 120742 => 'χ', 120743 => 'ψ', 120744 => 'ω', 120745 => '∇', 120746 => 'α', 120747 => 'β', 120748 => 'γ', 120749 => 'δ', 120750 => 'ε', 120751 => 'ζ', 120752 => 'η', 120753 => 'θ', 120754 => 'ι', 120755 => 'κ', 120756 => 'λ', 120757 => 'μ', 120758 => 'ν', 120759 => 'ξ', 120760 => 'ο', 120761 => 'π', 120762 => 'ρ', 120763 => 'σ', 120764 => 'σ', 120765 => 'τ', 120766 => 'υ', 120767 => 'φ', 120768 => 'χ', 120769 => 'ψ', 120770 => 'ω', 120771 => '∂', 120772 => 'ε', 120773 => 'θ', 120774 => 'κ', 120775 => 'φ', 120776 => 'ρ', 120777 => 'π', 120778 => 'ϝ', 120779 => 'ϝ', 120782 => '0', 120783 => '1', 120784 => '2', 120785 => '3', 120786 => '4', 120787 => '5', 120788 => '6', 120789 => '7', 120790 => '8', 120791 => '9', 120792 => '0', 120793 => '1', 120794 => '2', 120795 => '3', 120796 => '4', 120797 => '5', 120798 => '6', 120799 => '7', 120800 => '8', 120801 => '9', 120802 => '0', 120803 => '1', 120804 => '2', 120805 => '3', 120806 => '4', 120807 => '5', 120808 => '6', 120809 => '7', 120810 => '8', 120811 => '9', 120812 => '0', 120813 => '1', 120814 => '2', 120815 => '3', 120816 => '4', 120817 => '5', 120818 => '6', 120819 => '7', 120820 => '8', 120821 => '9', 120822 => '0', 120823 => '1', 120824 => '2', 120825 => '3', 120826 => '4', 120827 => '5', 120828 => '6', 120829 => '7', 120830 => '8', 120831 => '9', 125184 => '𞤢', 125185 => '𞤣', 125186 => '𞤤', 125187 => '𞤥', 125188 => '𞤦', 125189 => '𞤧', 125190 => '𞤨', 125191 => '𞤩', 125192 => '𞤪', 125193 => '𞤫', 125194 => '𞤬', 125195 => '𞤭', 125196 => '𞤮', 125197 => '𞤯', 125198 => '𞤰', 125199 => '𞤱', 125200 => '𞤲', 125201 => '𞤳', 125202 => '𞤴', 125203 => '𞤵', 125204 => '𞤶', 125205 => '𞤷', 125206 => '𞤸', 125207 => '𞤹', 125208 => '𞤺', 125209 => '𞤻', 125210 => '𞤼', 125211 => '𞤽', 125212 => '𞤾', 125213 => '𞤿', 125214 => '𞥀', 125215 => '𞥁', 125216 => '𞥂', 125217 => '𞥃', 126464 => 'ا', 126465 => 'ب', 126466 => 'ج', 126467 => 'د', 126469 => 'و', 126470 => 'ز', 126471 => 'ح', 126472 => 'ط', 126473 => 'ي', 126474 => 'ك', 126475 => 'ل', 126476 => 'م', 126477 => 'ن', 126478 => 'س', 126479 => 'ع', 126480 => 'ف', 126481 => 'ص', 126482 => 'ق', 126483 => 'ر', 126484 => 'ش', 126485 => 'ت', 126486 => 'ث', 126487 => 'خ', 126488 => 'ذ', 126489 => 'ض', 126490 => 'ظ', 126491 => 'غ', 126492 => 'ٮ', 126493 => 'ں', 126494 => 'ڡ', 126495 => 'ٯ', 126497 => 'ب', 126498 => 'ج', 126500 => 'ه', 126503 => 'ح', 126505 => 'ي', 126506 => 'ك', 126507 => 'ل', 126508 => 'م', 126509 => 'ن', 126510 => 'س', 126511 => 'ع', 126512 => 'ف', 126513 => 'ص', 126514 => 'ق', 126516 => 'ش', 126517 => 'ت', 126518 => 'ث', 126519 => 'خ', 126521 => 'ض', 126523 => 'غ', 126530 => 'ج', 126535 => 'ح', 126537 => 'ي', 126539 => 'ل', 126541 => 'ن', 126542 => 'س', 126543 => 'ع', 126545 => 'ص', 126546 => 'ق', 126548 => 'ش', 126551 => 'خ', 126553 => 'ض', 126555 => 'غ', 126557 => 'ں', 126559 => 'ٯ', 126561 => 'ب', 126562 => 'ج', 126564 => 'ه', 126567 => 'ح', 126568 => 'ط', 126569 => 'ي', 126570 => 'ك', 126572 => 'م', 126573 => 'ن', 126574 => 'س', 126575 => 'ع', 126576 => 'ف', 126577 => 'ص', 126578 => 'ق', 126580 => 'ش', 126581 => 'ت', 126582 => 'ث', 126583 => 'خ', 126585 => 'ض', 126586 => 'ظ', 126587 => 'غ', 126588 => 'ٮ', 126590 => 'ڡ', 126592 => 'ا', 126593 => 'ب', 126594 => 'ج', 126595 => 'د', 126596 => 'ه', 126597 => 'و', 126598 => 'ز', 126599 => 'ح', 126600 => 'ط', 126601 => 'ي', 126603 => 'ل', 126604 => 'م', 126605 => 'ن', 126606 => 'س', 126607 => 'ع', 126608 => 'ف', 126609 => 'ص', 126610 => 'ق', 126611 => 'ر', 126612 => 'ش', 126613 => 'ت', 126614 => 'ث', 126615 => 'خ', 126616 => 'ذ', 126617 => 'ض', 126618 => 'ظ', 126619 => 'غ', 126625 => 'ب', 126626 => 'ج', 126627 => 'د', 126629 => 'و', 126630 => 'ز', 126631 => 'ح', 126632 => 'ط', 126633 => 'ي', 126635 => 'ل', 126636 => 'م', 126637 => 'ن', 126638 => 'س', 126639 => 'ع', 126640 => 'ف', 126641 => 'ص', 126642 => 'ق', 126643 => 'ر', 126644 => 'ش', 126645 => 'ت', 126646 => 'ث', 126647 => 'خ', 126648 => 'ذ', 126649 => 'ض', 126650 => 'ظ', 126651 => 'غ', 127274 => '〔s〕', 127275 => 'c', 127276 => 'r', 127277 => 'cd', 127278 => 'wz', 127280 => 'a', 127281 => 'b', 127282 => 'c', 127283 => 'd', 127284 => 'e', 127285 => 'f', 127286 => 'g', 127287 => 'h', 127288 => 'i', 127289 => 'j', 127290 => 'k', 127291 => 'l', 127292 => 'm', 127293 => 'n', 127294 => 'o', 127295 => 'p', 127296 => 'q', 127297 => 'r', 127298 => 's', 127299 => 't', 127300 => 'u', 127301 => 'v', 127302 => 'w', 127303 => 'x', 127304 => 'y', 127305 => 'z', 127306 => 'hv', 127307 => 'mv', 127308 => 'sd', 127309 => 'ss', 127310 => 'ppv', 127311 => 'wc', 127338 => 'mc', 127339 => 'md', 127340 => 'mr', 127376 => 'dj', 127488 => 'ほか', 127489 => 'ココ', 127490 => 'サ', 127504 => '手', 127505 => '字', 127506 => '双', 127507 => 'デ', 127508 => '二', 127509 => '多', 127510 => '解', 127511 => '天', 127512 => '交', 127513 => '映', 127514 => '無', 127515 => '料', 127516 => '前', 127517 => '後', 127518 => '再', 127519 => '新', 127520 => '初', 127521 => '終', 127522 => '生', 127523 => '販', 127524 => '声', 127525 => '吹', 127526 => '演', 127527 => '投', 127528 => '捕', 127529 => '一', 127530 => '三', 127531 => '遊', 127532 => '左', 127533 => '中', 127534 => '右', 127535 => '指', 127536 => '走', 127537 => '打', 127538 => '禁', 127539 => '空', 127540 => '合', 127541 => '満', 127542 => '有', 127543 => '月', 127544 => '申', 127545 => '割', 127546 => '営', 127547 => '配', 127552 => '〔本〕', 127553 => '〔三〕', 127554 => '〔二〕', 127555 => '〔安〕', 127556 => '〔点〕', 127557 => '〔打〕', 127558 => '〔盗〕', 127559 => '〔勝〕', 127560 => '〔敗〕', 127568 => '得', 127569 => '可', 130032 => '0', 130033 => '1', 130034 => '2', 130035 => '3', 130036 => '4', 130037 => '5', 130038 => '6', 130039 => '7', 130040 => '8', 130041 => '9', 194560 => '丽', 194561 => '丸', 194562 => '乁', 194563 => '𠄢', 194564 => '你', 194565 => '侮', 194566 => '侻', 194567 => '倂', 194568 => '偺', 194569 => '備', 194570 => '僧', 194571 => '像', 194572 => '㒞', 194573 => '𠘺', 194574 => '免', 194575 => '兔', 194576 => '兤', 194577 => '具', 194578 => '𠔜', 194579 => '㒹', 194580 => '內', 194581 => '再', 194582 => '𠕋', 194583 => '冗', 194584 => '冤', 194585 => '仌', 194586 => '冬', 194587 => '况', 194588 => '𩇟', 194589 => '凵', 194590 => '刃', 194591 => '㓟', 194592 => '刻', 194593 => '剆', 194594 => '割', 194595 => '剷', 194596 => '㔕', 194597 => '勇', 194598 => '勉', 194599 => '勤', 194600 => '勺', 194601 => '包', 194602 => '匆', 194603 => '北', 194604 => '卉', 194605 => '卑', 194606 => '博', 194607 => '即', 194608 => '卽', 194609 => '卿', 194610 => '卿', 194611 => '卿', 194612 => '𠨬', 194613 => '灰', 194614 => '及', 194615 => '叟', 194616 => '𠭣', 194617 => '叫', 194618 => '叱', 194619 => '吆', 194620 => '咞', 194621 => '吸', 194622 => '呈', 194623 => '周', 194624 => '咢', 194625 => '哶', 194626 => '唐', 194627 => '啓', 194628 => '啣', 194629 => '善', 194630 => '善', 194631 => '喙', 194632 => '喫', 194633 => '喳', 194634 => '嗂', 194635 => '圖', 194636 => '嘆', 194637 => '圗', 194638 => '噑', 194639 => '噴', 194640 => '切', 194641 => '壮', 194642 => '城', 194643 => '埴', 194644 => '堍', 194645 => '型', 194646 => '堲', 194647 => '報', 194648 => '墬', 194649 => '𡓤', 194650 => '売', 194651 => '壷', 194652 => '夆', 194653 => '多', 194654 => '夢', 194655 => '奢', 194656 => '𡚨', 194657 => '𡛪', 194658 => '姬', 194659 => '娛', 194660 => '娧', 194661 => '姘', 194662 => '婦', 194663 => '㛮', 194665 => '嬈', 194666 => '嬾', 194667 => '嬾', 194668 => '𡧈', 194669 => '寃', 194670 => '寘', 194671 => '寧', 194672 => '寳', 194673 => '𡬘', 194674 => '寿', 194675 => '将', 194677 => '尢', 194678 => '㞁', 194679 => '屠', 194680 => '屮', 194681 => '峀', 194682 => '岍', 194683 => '𡷤', 194684 => '嵃', 194685 => '𡷦', 194686 => '嵮', 194687 => '嵫', 194688 => '嵼', 194689 => '巡', 194690 => '巢', 194691 => '㠯', 194692 => '巽', 194693 => '帨', 194694 => '帽', 194695 => '幩', 194696 => '㡢', 194697 => '𢆃', 194698 => '㡼', 194699 => '庰', 194700 => '庳', 194701 => '庶', 194702 => '廊', 194703 => '𪎒', 194704 => '廾', 194705 => '𢌱', 194706 => '𢌱', 194707 => '舁', 194708 => '弢', 194709 => '弢', 194710 => '㣇', 194711 => '𣊸', 194712 => '𦇚', 194713 => '形', 194714 => '彫', 194715 => '㣣', 194716 => '徚', 194717 => '忍', 194718 => '志', 194719 => '忹', 194720 => '悁', 194721 => '㤺', 194722 => '㤜', 194723 => '悔', 194724 => '𢛔', 194725 => '惇', 194726 => '慈', 194727 => '慌', 194728 => '慎', 194729 => '慌', 194730 => '慺', 194731 => '憎', 194732 => '憲', 194733 => '憤', 194734 => '憯', 194735 => '懞', 194736 => '懲', 194737 => '懶', 194738 => '成', 194739 => '戛', 194740 => '扝', 194741 => '抱', 194742 => '拔', 194743 => '捐', 194744 => '𢬌', 194745 => '挽', 194746 => '拼', 194747 => '捨', 194748 => '掃', 194749 => '揤', 194750 => '𢯱', 194751 => '搢', 194752 => '揅', 194753 => '掩', 194754 => '㨮', 194755 => '摩', 194756 => '摾', 194757 => '撝', 194758 => '摷', 194759 => '㩬', 194760 => '敏', 194761 => '敬', 194762 => '𣀊', 194763 => '旣', 194764 => '書', 194765 => '晉', 194766 => '㬙', 194767 => '暑', 194768 => '㬈', 194769 => '㫤', 194770 => '冒', 194771 => '冕', 194772 => '最', 194773 => '暜', 194774 => '肭', 194775 => '䏙', 194776 => '朗', 194777 => '望', 194778 => '朡', 194779 => '杞', 194780 => '杓', 194781 => '𣏃', 194782 => '㭉', 194783 => '柺', 194784 => '枅', 194785 => '桒', 194786 => '梅', 194787 => '𣑭', 194788 => '梎', 194789 => '栟', 194790 => '椔', 194791 => '㮝', 194792 => '楂', 194793 => '榣', 194794 => '槪', 194795 => '檨', 194796 => '𣚣', 194797 => '櫛', 194798 => '㰘', 194799 => '次', 194800 => '𣢧', 194801 => '歔', 194802 => '㱎', 194803 => '歲', 194804 => '殟', 194805 => '殺', 194806 => '殻', 194807 => '𣪍', 194808 => '𡴋', 194809 => '𣫺', 194810 => '汎', 194811 => '𣲼', 194812 => '沿', 194813 => '泍', 194814 => '汧', 194815 => '洖', 194816 => '派', 194817 => '海', 194818 => '流', 194819 => '浩', 194820 => '浸', 194821 => '涅', 194822 => '𣴞', 194823 => '洴', 194824 => '港', 194825 => '湮', 194826 => '㴳', 194827 => '滋', 194828 => '滇', 194829 => '𣻑', 194830 => '淹', 194831 => '潮', 194832 => '𣽞', 194833 => '𣾎', 194834 => '濆', 194835 => '瀹', 194836 => '瀞', 194837 => '瀛', 194838 => '㶖', 194839 => '灊', 194840 => '災', 194841 => '灷', 194842 => '炭', 194843 => '𠔥', 194844 => '煅', 194845 => '𤉣', 194846 => '熜', 194848 => '爨', 194849 => '爵', 194850 => '牐', 194851 => '𤘈', 194852 => '犀', 194853 => '犕', 194854 => '𤜵', 194855 => '𤠔', 194856 => '獺', 194857 => '王', 194858 => '㺬', 194859 => '玥', 194860 => '㺸', 194861 => '㺸', 194862 => '瑇', 194863 => '瑜', 194864 => '瑱', 194865 => '璅', 194866 => '瓊', 194867 => '㼛', 194868 => '甤', 194869 => '𤰶', 194870 => '甾', 194871 => '𤲒', 194872 => '異', 194873 => '𢆟', 194874 => '瘐', 194875 => '𤾡', 194876 => '𤾸', 194877 => '𥁄', 194878 => '㿼', 194879 => '䀈', 194880 => '直', 194881 => '𥃳', 194882 => '𥃲', 194883 => '𥄙', 194884 => '𥄳', 194885 => '眞', 194886 => '真', 194887 => '真', 194888 => '睊', 194889 => '䀹', 194890 => '瞋', 194891 => '䁆', 194892 => '䂖', 194893 => '𥐝', 194894 => '硎', 194895 => '碌', 194896 => '磌', 194897 => '䃣', 194898 => '𥘦', 194899 => '祖', 194900 => '𥚚', 194901 => '𥛅', 194902 => '福', 194903 => '秫', 194904 => '䄯', 194905 => '穀', 194906 => '穊', 194907 => '穏', 194908 => '𥥼', 194909 => '𥪧', 194910 => '𥪧', 194912 => '䈂', 194913 => '𥮫', 194914 => '篆', 194915 => '築', 194916 => '䈧', 194917 => '𥲀', 194918 => '糒', 194919 => '䊠', 194920 => '糨', 194921 => '糣', 194922 => '紀', 194923 => '𥾆', 194924 => '絣', 194925 => '䌁', 194926 => '緇', 194927 => '縂', 194928 => '繅', 194929 => '䌴', 194930 => '𦈨', 194931 => '𦉇', 194932 => '䍙', 194933 => '𦋙', 194934 => '罺', 194935 => '𦌾', 194936 => '羕', 194937 => '翺', 194938 => '者', 194939 => '𦓚', 194940 => '𦔣', 194941 => '聠', 194942 => '𦖨', 194943 => '聰', 194944 => '𣍟', 194945 => '䏕', 194946 => '育', 194947 => '脃', 194948 => '䐋', 194949 => '脾', 194950 => '媵', 194951 => '𦞧', 194952 => '𦞵', 194953 => '𣎓', 194954 => '𣎜', 194955 => '舁', 194956 => '舄', 194957 => '辞', 194958 => '䑫', 194959 => '芑', 194960 => '芋', 194961 => '芝', 194962 => '劳', 194963 => '花', 194964 => '芳', 194965 => '芽', 194966 => '苦', 194967 => '𦬼', 194968 => '若', 194969 => '茝', 194970 => '荣', 194971 => '莭', 194972 => '茣', 194973 => '莽', 194974 => '菧', 194975 => '著', 194976 => '荓', 194977 => '菊', 194978 => '菌', 194979 => '菜', 194980 => '𦰶', 194981 => '𦵫', 194982 => '𦳕', 194983 => '䔫', 194984 => '蓱', 194985 => '蓳', 194986 => '蔖', 194987 => '𧏊', 194988 => '蕤', 194989 => '𦼬', 194990 => '䕝', 194991 => '䕡', 194992 => '𦾱', 194993 => '𧃒', 194994 => '䕫', 194995 => '虐', 194996 => '虜', 194997 => '虧', 194998 => '虩', 194999 => '蚩', 195000 => '蚈', 195001 => '蜎', 195002 => '蛢', 195003 => '蝹', 195004 => '蜨', 195005 => '蝫', 195006 => '螆', 195008 => '蟡', 195009 => '蠁', 195010 => '䗹', 195011 => '衠', 195012 => '衣', 195013 => '𧙧', 195014 => '裗', 195015 => '裞', 195016 => '䘵', 195017 => '裺', 195018 => '㒻', 195019 => '𧢮', 195020 => '𧥦', 195021 => '䚾', 195022 => '䛇', 195023 => '誠', 195024 => '諭', 195025 => '變', 195026 => '豕', 195027 => '𧲨', 195028 => '貫', 195029 => '賁', 195030 => '贛', 195031 => '起', 195032 => '𧼯', 195033 => '𠠄', 195034 => '跋', 195035 => '趼', 195036 => '跰', 195037 => '𠣞', 195038 => '軔', 195039 => '輸', 195040 => '𨗒', 195041 => '𨗭', 195042 => '邔', 195043 => '郱', 195044 => '鄑', 195045 => '𨜮', 195046 => '鄛', 195047 => '鈸', 195048 => '鋗', 195049 => '鋘', 195050 => '鉼', 195051 => '鏹', 195052 => '鐕', 195053 => '𨯺', 195054 => '開', 195055 => '䦕', 195056 => '閷', 195057 => '𨵷', 195058 => '䧦', 195059 => '雃', 195060 => '嶲', 195061 => '霣', 195062 => '𩅅', 195063 => '𩈚', 195064 => '䩮', 195065 => '䩶', 195066 => '韠', 195067 => '𩐊', 195068 => '䪲', 195069 => '𩒖', 195070 => '頋', 195071 => '頋', 195072 => '頩', 195073 => '𩖶', 195074 => '飢', 195075 => '䬳', 195076 => '餩', 195077 => '馧', 195078 => '駂', 195079 => '駾', 195080 => '䯎', 195081 => '𩬰', 195082 => '鬒', 195083 => '鱀', 195084 => '鳽', 195085 => '䳎', 195086 => '䳭', 195087 => '鵧', 195088 => '𪃎', 195089 => '䳸', 195090 => '𪄅', 195091 => '𪈎', 195092 => '𪊑', 195093 => '麻', 195094 => '䵖', 195095 => '黹', 195096 => '黾', 195097 => '鼅', 195098 => '鼏', 195099 => '鼖', 195100 => '鼻', 195101 => '𪘀', ); true, 847 => true, 6155 => true, 6156 => true, 6157 => true, 8203 => true, 8288 => true, 8292 => true, 65024 => true, 65025 => true, 65026 => true, 65027 => true, 65028 => true, 65029 => true, 65030 => true, 65031 => true, 65032 => true, 65033 => true, 65034 => true, 65035 => true, 65036 => true, 65037 => true, 65038 => true, 65039 => true, 65279 => true, 113824 => true, 113825 => true, 113826 => true, 113827 => true, 917760 => true, 917761 => true, 917762 => true, 917763 => true, 917764 => true, 917765 => true, 917766 => true, 917767 => true, 917768 => true, 917769 => true, 917770 => true, 917771 => true, 917772 => true, 917773 => true, 917774 => true, 917775 => true, 917776 => true, 917777 => true, 917778 => true, 917779 => true, 917780 => true, 917781 => true, 917782 => true, 917783 => true, 917784 => true, 917785 => true, 917786 => true, 917787 => true, 917788 => true, 917789 => true, 917790 => true, 917791 => true, 917792 => true, 917793 => true, 917794 => true, 917795 => true, 917796 => true, 917797 => true, 917798 => true, 917799 => true, 917800 => true, 917801 => true, 917802 => true, 917803 => true, 917804 => true, 917805 => true, 917806 => true, 917807 => true, 917808 => true, 917809 => true, 917810 => true, 917811 => true, 917812 => true, 917813 => true, 917814 => true, 917815 => true, 917816 => true, 917817 => true, 917818 => true, 917819 => true, 917820 => true, 917821 => true, 917822 => true, 917823 => true, 917824 => true, 917825 => true, 917826 => true, 917827 => true, 917828 => true, 917829 => true, 917830 => true, 917831 => true, 917832 => true, 917833 => true, 917834 => true, 917835 => true, 917836 => true, 917837 => true, 917838 => true, 917839 => true, 917840 => true, 917841 => true, 917842 => true, 917843 => true, 917844 => true, 917845 => true, 917846 => true, 917847 => true, 917848 => true, 917849 => true, 917850 => true, 917851 => true, 917852 => true, 917853 => true, 917854 => true, 917855 => true, 917856 => true, 917857 => true, 917858 => true, 917859 => true, 917860 => true, 917861 => true, 917862 => true, 917863 => true, 917864 => true, 917865 => true, 917866 => true, 917867 => true, 917868 => true, 917869 => true, 917870 => true, 917871 => true, 917872 => true, 917873 => true, 917874 => true, 917875 => true, 917876 => true, 917877 => true, 917878 => true, 917879 => true, 917880 => true, 917881 => true, 917882 => true, 917883 => true, 917884 => true, 917885 => true, 917886 => true, 917887 => true, 917888 => true, 917889 => true, 917890 => true, 917891 => true, 917892 => true, 917893 => true, 917894 => true, 917895 => true, 917896 => true, 917897 => true, 917898 => true, 917899 => true, 917900 => true, 917901 => true, 917902 => true, 917903 => true, 917904 => true, 917905 => true, 917906 => true, 917907 => true, 917908 => true, 917909 => true, 917910 => true, 917911 => true, 917912 => true, 917913 => true, 917914 => true, 917915 => true, 917916 => true, 917917 => true, 917918 => true, 917919 => true, 917920 => true, 917921 => true, 917922 => true, 917923 => true, 917924 => true, 917925 => true, 917926 => true, 917927 => true, 917928 => true, 917929 => true, 917930 => true, 917931 => true, 917932 => true, 917933 => true, 917934 => true, 917935 => true, 917936 => true, 917937 => true, 917938 => true, 917939 => true, 917940 => true, 917941 => true, 917942 => true, 917943 => true, 917944 => true, 917945 => true, 917946 => true, 917947 => true, 917948 => true, 917949 => true, 917950 => true, 917951 => true, 917952 => true, 917953 => true, 917954 => true, 917955 => true, 917956 => true, 917957 => true, 917958 => true, 917959 => true, 917960 => true, 917961 => true, 917962 => true, 917963 => true, 917964 => true, 917965 => true, 917966 => true, 917967 => true, 917968 => true, 917969 => true, 917970 => true, 917971 => true, 917972 => true, 917973 => true, 917974 => true, 917975 => true, 917976 => true, 917977 => true, 917978 => true, 917979 => true, 917980 => true, 917981 => true, 917982 => true, 917983 => true, 917984 => true, 917985 => true, 917986 => true, 917987 => true, 917988 => true, 917989 => true, 917990 => true, 917991 => true, 917992 => true, 917993 => true, 917994 => true, 917995 => true, 917996 => true, 917997 => true, 917998 => true, 917999 => true, ); 'ss', 962 => 'σ', 8204 => '', 8205 => '', ); tokens[] = $token; return $this; } public function freeze() { return $this; } public function getNext() { if ($this->peeking) { $this->peeking = false; $this->used[] = $this->peeked; return $this->peeked; } if (!isset($this->tokens[$this->cursor])) { throw new InternalErrorException('Unexpected token stream end.'); } return $this->tokens[$this->cursor++]; } public function getPeek() { if (!$this->peeking) { $this->peeked = $this->getNext(); $this->peeking = true; } return $this->peeked; } public function getUsed() { return $this->used; } public function getNextIdentifier() { $next = $this->getNext(); if (!$next->isIdentifier()) { throw SyntaxErrorException::unexpectedToken('identifier', $next); } return $next->getValue(); } public function getNextIdentifierOrStar() { $next = $this->getNext(); if ($next->isIdentifier()) { return $next->getValue(); } if ($next->isDelimiter(['*'])) { return null; } throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next); } public function skipWhitespace() { $peek = $this->getPeek(); if ($peek->isWhitespace()) { $this->getNext(); } } } handlers = [ new Handler\WhitespaceHandler(), new Handler\IdentifierHandler($patterns, $escaping), new Handler\HashHandler($patterns, $escaping), new Handler\StringHandler($patterns, $escaping), new Handler\NumberHandler($patterns), new Handler\CommentHandler(), ]; } public function tokenize(Reader $reader) { $stream = new TokenStream(); while (!$reader->isEOF()) { foreach ($this->handlers as $handler) { if ($handler->handle($reader, $stream)) { continue 2; } } $stream->push(new Token(Token::TYPE_DELIMITER, $reader->getSubstring(1), $reader->getPosition())); $reader->moveForward(1); } return $stream ->push(new Token(Token::TYPE_FILE_END, null, $reader->getPosition())) ->freeze(); } } patterns = $patterns; } public function escapeUnicode($value) { $value = $this->replaceUnicodeSequences($value); return preg_replace($this->patterns->getSimpleEscapePattern(), '$1', $value); } public function escapeUnicodeAndNewLine($value) { $value = preg_replace($this->patterns->getNewLineEscapePattern(), '', $value); return $this->escapeUnicode($value); } private function replaceUnicodeSequences($value) { return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) { $c = hexdec($match[1]); 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 ''; }, $value); } } unicodeEscapePattern = '\\\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?'; $this->simpleEscapePattern = '\\\\(.)'; $this->newLineEscapePattern = '\\\\(?:\n|\r\n|\r|\f)'; $this->escapePattern = $this->unicodeEscapePattern.'|\\\\[^\n\r\f0-9a-f]'; $this->stringEscapePattern = $this->newLineEscapePattern.'|'.$this->escapePattern; $this->nonAsciiPattern = '[^\x00-\x7F]'; $this->nmCharPattern = '[_a-z0-9-]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; $this->nmStartPattern = '[_a-z]|'.$this->escapePattern.'|'.$this->nonAsciiPattern; $this->identifierPattern = '-?(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*'; $this->hashPattern = '#((?:'.$this->nmCharPattern.')+)'; $this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)'; $this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*'; } public function getNewLineEscapePattern() { return '~^'.$this->newLineEscapePattern.'~'; } public function getSimpleEscapePattern() { return '~^'.$this->simpleEscapePattern.'~'; } public function getUnicodeEscapePattern() { return '~^'.$this->unicodeEscapePattern.'~i'; } public function getIdentifierPattern() { return '~^'.$this->identifierPattern.'~i'; } public function getHashPattern() { return '~^'.$this->hashPattern.'~i'; } public function getNumberPattern() { return '~^'.$this->numberPattern.'~'; } public function getQuotedStringPattern($quote) { return '~^'.sprintf($this->quotedStringPattern, $quote).'~i'; } } source = $source; $this->length = \strlen($source); } public function isEOF() { return $this->position >= $this->length; } public function getPosition() { return $this->position; } public function getRemainingLength() { return $this->length - $this->position; } public function getSubstring($length, $offset = 0) { return substr($this->source, $this->position + $offset, $length); } public function getOffset($string) { $position = strpos($this->source, $string, $this->position); return false === $position ? false : $position - $this->position; } public function findPattern($pattern) { $source = substr($this->source, $this->position); if (preg_match($pattern, $source, $matches)) { return $matches; } return false; } public function moveForward($length) { $this->position += $length; } public function moveToEnd() { $this->position = $this->length; } } patterns = $patterns; $this->escaping = $escaping; } public function handle(Reader $reader, TokenStream $stream) { $quote = $reader->getSubstring(1); if (!\in_array($quote, ["'", '"'])) { return false; } $reader->moveForward(1); $match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote)); if (!$match) { throw new InternalErrorException(sprintf('Should have found at least an empty match at %d.', $reader->getPosition())); } if (\strlen($match[0]) === $reader->getRemainingLength()) { throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); } if ($quote !== $reader->getSubstring(1, \strlen($match[0]))) { throw SyntaxErrorException::unclosedString($reader->getPosition() - 1); } $string = $this->escaping->escapeUnicodeAndNewLine($match[0]); $stream->push(new Token(Token::TYPE_STRING, $string, $reader->getPosition())); $reader->moveForward(\strlen($match[0]) + 1); return true; } } patterns = $patterns; } public function handle(Reader $reader, TokenStream $stream) { $match = $reader->findPattern($this->patterns->getNumberPattern()); if (!$match) { return false; } $stream->push(new Token(Token::TYPE_NUMBER, $match[0], $reader->getPosition())); $reader->moveForward(\strlen($match[0])); return true; } } patterns = $patterns; $this->escaping = $escaping; } public function handle(Reader $reader, TokenStream $stream) { $match = $reader->findPattern($this->patterns->getIdentifierPattern()); if (!$match) { return false; } $value = $this->escaping->escapeUnicode($match[0]); $stream->push(new Token(Token::TYPE_IDENTIFIER, $value, $reader->getPosition())); $reader->moveForward(\strlen($match[0])); return true; } } findPattern('~^[ \t\r\n\f]+~'); if (false === $match) { return false; } $stream->push(new Token(Token::TYPE_WHITESPACE, $match[0], $reader->getPosition())); $reader->moveForward(\strlen($match[0])); return true; } } getSubstring(2)) { return false; } $offset = $reader->getOffset('*/'); if (false === $offset) { $reader->moveToEnd(); } else { $reader->moveForward($offset + 2); } return true; } } patterns = $patterns; $this->escaping = $escaping; } public function handle(Reader $reader, TokenStream $stream) { $match = $reader->findPattern($this->patterns->getHashPattern()); if (!$match) { return false; } $value = $this->escaping->escapeUnicode($match[1]); $stream->push(new Token(Token::TYPE_HASH, $value, $reader->getPosition())); $reader->moveForward(\strlen($match[0])); return true; } } type = $type; $this->value = $value; $this->position = $position; } public function getType() { return $this->type; } public function getValue() { return $this->value; } public function getPosition() { return $this->position; } public function isFileEnd() { return self::TYPE_FILE_END === $this->type; } public function isDelimiter(array $values = []) { if (self::TYPE_DELIMITER !== $this->type) { return false; } if (empty($values)) { return true; } return \in_array($this->value, $values); } public function isWhitespace() { return self::TYPE_WHITESPACE === $this->type; } public function isIdentifier() { return self::TYPE_IDENTIFIER === $this->type; } public function isHash() { return self::TYPE_HASH === $this->type; } public function isNumber() { return self::TYPE_NUMBER === $this->type; } public function isString() { return self::TYPE_STRING === $this->type; } public function __toString() { if ($this->value) { return sprintf('<%s "%s" at %s>', $this->type, $this->value, $this->position); } return sprintf('<%s at %s>', $this->type, $this->position); } } tokenizer = $tokenizer ?: new Tokenizer(); } public function parse($source) { $reader = new Reader($source); $stream = $this->tokenizer->tokenize($reader); return $this->parseSelectorList($stream); } public static function parseSeries(array $tokens) { foreach ($tokens as $token) { if ($token->isString()) { throw SyntaxErrorException::stringAsFunctionArgument(); } } $joined = trim(implode('', array_map(function (Token $token) { return $token->getValue(); }, $tokens))); $int = function ($string) { if (!is_numeric($string)) { throw SyntaxErrorException::stringAsFunctionArgument(); } return (int) $string; }; switch (true) { case 'odd' === $joined: return [2, 1]; case 'even' === $joined: return [2, 0]; case 'n' === $joined: return [1, 0]; case false === strpos($joined, 'n'): return [0, $int($joined)]; } $split = explode('n', $joined); $first = isset($split[0]) ? $split[0] : null; return [ $first ? ('-' === $first || '+' === $first ? $int($first.'1') : $int($first)) : 1, isset($split[1]) && $split[1] ? $int($split[1]) : 0, ]; } private function parseSelectorList(TokenStream $stream) { $stream->skipWhitespace(); $selectors = []; while (true) { $selectors[] = $this->parserSelectorNode($stream); if ($stream->getPeek()->isDelimiter([','])) { $stream->getNext(); $stream->skipWhitespace(); } else { break; } } return $selectors; } private function parserSelectorNode(TokenStream $stream) { list($result, $pseudoElement) = $this->parseSimpleSelector($stream); while (true) { $stream->skipWhitespace(); $peek = $stream->getPeek(); if ($peek->isFileEnd() || $peek->isDelimiter([','])) { break; } if (null !== $pseudoElement) { throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); } if ($peek->isDelimiter(['+', '>', '~'])) { $combinator = $stream->getNext()->getValue(); $stream->skipWhitespace(); } else { $combinator = ' '; } list($nextSelector, $pseudoElement) = $this->parseSimpleSelector($stream); $result = new Node\CombinedSelectorNode($result, $combinator, $nextSelector); } return new Node\SelectorNode($result, $pseudoElement); } private function parseSimpleSelector(TokenStream $stream, $insideNegation = false) { $stream->skipWhitespace(); $selectorStart = \count($stream->getUsed()); $result = $this->parseElementNode($stream); $pseudoElement = null; while (true) { $peek = $stream->getPeek(); if ($peek->isWhitespace() || $peek->isFileEnd() || $peek->isDelimiter([',', '+', '>', '~']) || ($insideNegation && $peek->isDelimiter([')'])) ) { break; } if (null !== $pseudoElement) { throw SyntaxErrorException::pseudoElementFound($pseudoElement, 'not at the end of a selector'); } if ($peek->isHash()) { $result = new Node\HashNode($result, $stream->getNext()->getValue()); } elseif ($peek->isDelimiter(['.'])) { $stream->getNext(); $result = new Node\ClassNode($result, $stream->getNextIdentifier()); } elseif ($peek->isDelimiter(['['])) { $stream->getNext(); $result = $this->parseAttributeNode($result, $stream); } elseif ($peek->isDelimiter([':'])) { $stream->getNext(); if ($stream->getPeek()->isDelimiter([':'])) { $stream->getNext(); $pseudoElement = $stream->getNextIdentifier(); continue; } $identifier = $stream->getNextIdentifier(); if (\in_array(strtolower($identifier), ['first-line', 'first-letter', 'before', 'after'])) { $pseudoElement = $identifier; continue; } if (!$stream->getPeek()->isDelimiter(['('])) { $result = new Node\PseudoNode($result, $identifier); continue; } $stream->getNext(); $stream->skipWhitespace(); if ('not' === strtolower($identifier)) { if ($insideNegation) { throw SyntaxErrorException::nestedNot(); } list($argument, $argumentPseudoElement) = $this->parseSimpleSelector($stream, true); $next = $stream->getNext(); if (null !== $argumentPseudoElement) { throw SyntaxErrorException::pseudoElementFound($argumentPseudoElement, 'inside ::not()'); } if (!$next->isDelimiter([')'])) { throw SyntaxErrorException::unexpectedToken('")"', $next); } $result = new Node\NegationNode($result, $argument); } else { $arguments = []; $next = null; while (true) { $stream->skipWhitespace(); $next = $stream->getNext(); if ($next->isIdentifier() || $next->isString() || $next->isNumber() || $next->isDelimiter(['+', '-']) ) { $arguments[] = $next; } elseif ($next->isDelimiter([')'])) { break; } else { throw SyntaxErrorException::unexpectedToken('an argument', $next); } } if (empty($arguments)) { throw SyntaxErrorException::unexpectedToken('at least one argument', $next); } $result = new Node\FunctionNode($result, $identifier, $arguments); } } else { throw SyntaxErrorException::unexpectedToken('selector', $peek); } } if (\count($stream->getUsed()) === $selectorStart) { throw SyntaxErrorException::unexpectedToken('selector', $stream->getPeek()); } return [$result, $pseudoElement]; } private function parseElementNode(TokenStream $stream) { $peek = $stream->getPeek(); if ($peek->isIdentifier() || $peek->isDelimiter(['*'])) { if ($peek->isIdentifier()) { $namespace = $stream->getNext()->getValue(); } else { $stream->getNext(); $namespace = null; } if ($stream->getPeek()->isDelimiter(['|'])) { $stream->getNext(); $element = $stream->getNextIdentifierOrStar(); } else { $element = $namespace; $namespace = null; } } else { $element = $namespace = null; } return new Node\ElementNode($namespace, $element); } private function parseAttributeNode(Node\NodeInterface $selector, TokenStream $stream) { $stream->skipWhitespace(); $attribute = $stream->getNextIdentifierOrStar(); if (null === $attribute && !$stream->getPeek()->isDelimiter(['|'])) { throw SyntaxErrorException::unexpectedToken('"|"', $stream->getPeek()); } if ($stream->getPeek()->isDelimiter(['|'])) { $stream->getNext(); if ($stream->getPeek()->isDelimiter(['='])) { $namespace = null; $stream->getNext(); $operator = '|='; } else { $namespace = $attribute; $attribute = $stream->getNextIdentifier(); $operator = null; } } else { $namespace = $operator = null; } if (null === $operator) { $stream->skipWhitespace(); $next = $stream->getNext(); if ($next->isDelimiter([']'])) { return new Node\AttributeNode($selector, $namespace, $attribute, 'exists', null); } elseif ($next->isDelimiter(['='])) { $operator = '='; } elseif ($next->isDelimiter(['^', '$', '*', '~', '|', '!']) && $stream->getPeek()->isDelimiter(['=']) ) { $operator = $next->getValue().'='; $stream->getNext(); } else { throw SyntaxErrorException::unexpectedToken('operator', $next); } } $stream->skipWhitespace(); $value = $stream->getNext(); if ($value->isNumber()) { $value = new Token(Token::TYPE_STRING, (string) $value->getValue(), $value->getPosition()); } if (!($value->isIdentifier() || $value->isString())) { throw SyntaxErrorException::unexpectedToken('string or identifier', $value); } $stream->skipWhitespace(); $next = $stream->getNext(); if (!$next->isDelimiter([']'])) { throw SyntaxErrorException::unexpectedToken('"]"', $next); } return new Node\AttributeNode($selector, $namespace, $attribute, $operator, $value->getValue()); } } translator = new Translator(); if ($html) { $this->translator->registerExtension(new HtmlExtension($this->translator)); } $this->translator ->registerParserShortcut(new EmptyStringParser()) ->registerParserShortcut(new ElementParser()) ->registerParserShortcut(new ClassParser()) ->registerParserShortcut(new HashParser()) ; } public function toXPath($cssExpr, $prefix = 'descendant-or-self::') { return $this->translator->cssToXPath($cssExpr, $prefix); } } selector = $selector; $this->name = strtolower($name); $this->arguments = $arguments; } public function getSelector() { return $this->selector; } public function getName() { return $this->name; } public function getArguments() { return $this->arguments; } public function getSpecificity() { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } public function __toString() { $arguments = implode(', ', array_map(function (Token $token) { return "'".$token->getValue()."'"; }, $this->arguments)); return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : ''); } } tree = $tree; $this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null; } public function getTree() { return $this->tree; } public function getPseudoElement() { return $this->pseudoElement; } public function getSpecificity() { return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0)); } public function __toString() { return sprintf('%s[%s%s]', $this->getNodeName(), $this->tree, $this->pseudoElement ? '::'.$this->pseudoElement : ''); } } selector = $selector; $this->name = $name; } public function getSelector() { return $this->selector; } public function getName() { return $this->name; } public function getSpecificity() { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } public function __toString() { return sprintf('%s[%s.%s]', $this->getNodeName(), $this->selector, $this->name); } } namespace = $namespace; $this->element = $element; } public function getNamespace() { return $this->namespace; } public function getElement() { return $this->element; } public function getSpecificity() { return new Specificity(0, 0, $this->element ? 1 : 0); } public function __toString() { $element = $this->element ?: '*'; return sprintf('%s[%s]', $this->getNodeName(), $this->namespace ? $this->namespace.'|'.$element : $element); } } selector = $selector; $this->namespace = $namespace; $this->attribute = $attribute; $this->operator = $operator; $this->value = $value; } public function getSelector() { return $this->selector; } public function getNamespace() { return $this->namespace; } public function getAttribute() { return $this->attribute; } public function getOperator() { return $this->operator; } public function getValue() { return $this->value; } public function getSpecificity() { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } public function __toString() { $attribute = $this->namespace ? $this->namespace.'|'.$this->attribute : $this->attribute; return 'exists' === $this->operator ? sprintf('%s[%s[%s]]', $this->getNodeName(), $this->selector, $attribute) : sprintf("%s[%s[%s %s '%s']]", $this->getNodeName(), $this->selector, $attribute, $this->operator, $this->value); } } a = $a; $this->b = $b; $this->c = $c; } public function plus(self $specificity) { return new self($this->a + $specificity->a, $this->b + $specificity->b, $this->c + $specificity->c); } public function getValue() { return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR; } public function compareTo(self $specificity) { if ($this->a !== $specificity->a) { return $this->a > $specificity->a ? 1 : -1; } if ($this->b !== $specificity->b) { return $this->b > $specificity->b ? 1 : -1; } if ($this->c !== $specificity->c) { return $this->c > $specificity->c ? 1 : -1; } return 0; } } selector = $selector; $this->subSelector = $subSelector; } public function getSelector() { return $this->selector; } public function getSubSelector() { return $this->subSelector; } public function getSpecificity() { return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); } public function __toString() { return sprintf('%s[%s:not(%s)]', $this->getNodeName(), $this->selector, $this->subSelector); } } nodeName) { $this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', static::class); } return $this->nodeName; } } selector = $selector; $this->combinator = $combinator; $this->subSelector = $subSelector; } public function getSelector() { return $this->selector; } public function getCombinator() { return $this->combinator; } public function getSubSelector() { return $this->subSelector; } public function getSpecificity() { return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity()); } public function __toString() { $combinator = ' ' === $this->combinator ? '' : $this->combinator; return sprintf('%s[%s %s %s]', $this->getNodeName(), $this->selector, $combinator, $this->subSelector); } } selector = $selector; $this->identifier = strtolower($identifier); } public function getSelector() { return $this->selector; } public function getIdentifier() { return $this->identifier; } public function getSpecificity() { return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0)); } public function __toString() { return sprintf('%s[%s:%s]', $this->getNodeName(), $this->selector, $this->identifier); } } selector = $selector; $this->id = $id; } public function getSelector() { return $this->selector; } public function getId() { return $this->id; } public function getSpecificity() { return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0)); } public function __toString() { return sprintf('%s[%s#%s]', $this->getNodeName(), $this->selector, $this->id); } } mainParser = $parser ?: new Parser(); $this ->registerExtension(new Extension\NodeExtension()) ->registerExtension(new Extension\CombinationExtension()) ->registerExtension(new Extension\FunctionExtension()) ->registerExtension(new Extension\PseudoClassExtension()) ->registerExtension(new Extension\AttributeMatchingExtension()) ; } public static function getXpathLiteral($element) { if (false === strpos($element, "'")) { return "'".$element."'"; } if (false === strpos($element, '"')) { return '"'.$element.'"'; } $string = $element; $parts = []; while (true) { if (false !== $pos = strpos($string, "'")) { $parts[] = sprintf("'%s'", substr($string, 0, $pos)); $parts[] = "\"'\""; $string = substr($string, $pos + 1); } else { $parts[] = "'$string'"; break; } } return sprintf('concat(%s)', implode(', ', $parts)); } public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::') { $selectors = $this->parseSelectors($cssExpr); foreach ($selectors as $index => $selector) { if (null !== $selector->getPseudoElement()) { throw new ExpressionErrorException('Pseudo-elements are not supported.'); } $selectors[$index] = $this->selectorToXPath($selector, $prefix); } return implode(' | ', $selectors); } public function selectorToXPath(SelectorNode $selector, $prefix = 'descendant-or-self::') { return ($prefix ?: '').$this->nodeToXPath($selector); } public function registerExtension(Extension\ExtensionInterface $extension) { $this->extensions[$extension->getName()] = $extension; $this->nodeTranslators = array_merge($this->nodeTranslators, $extension->getNodeTranslators()); $this->combinationTranslators = array_merge($this->combinationTranslators, $extension->getCombinationTranslators()); $this->functionTranslators = array_merge($this->functionTranslators, $extension->getFunctionTranslators()); $this->pseudoClassTranslators = array_merge($this->pseudoClassTranslators, $extension->getPseudoClassTranslators()); $this->attributeMatchingTranslators = array_merge($this->attributeMatchingTranslators, $extension->getAttributeMatchingTranslators()); return $this; } public function getExtension($name) { if (!isset($this->extensions[$name])) { throw new ExpressionErrorException(sprintf('Extension "%s" not registered.', $name)); } return $this->extensions[$name]; } public function registerParserShortcut(ParserInterface $shortcut) { $this->shortcutParsers[] = $shortcut; return $this; } public function nodeToXPath(NodeInterface $node) { if (!isset($this->nodeTranslators[$node->getNodeName()])) { throw new ExpressionErrorException(sprintf('Node "%s" not supported.', $node->getNodeName())); } return \call_user_func($this->nodeTranslators[$node->getNodeName()], $node, $this); } public function addCombination($combiner, NodeInterface $xpath, NodeInterface $combinedXpath) { if (!isset($this->combinationTranslators[$combiner])) { throw new ExpressionErrorException(sprintf('Combiner "%s" not supported.', $combiner)); } return \call_user_func($this->combinationTranslators[$combiner], $this->nodeToXPath($xpath), $this->nodeToXPath($combinedXpath)); } public function addFunction(XPathExpr $xpath, FunctionNode $function) { if (!isset($this->functionTranslators[$function->getName()])) { throw new ExpressionErrorException(sprintf('Function "%s" not supported.', $function->getName())); } return \call_user_func($this->functionTranslators[$function->getName()], $xpath, $function); } public function addPseudoClass(XPathExpr $xpath, $pseudoClass) { if (!isset($this->pseudoClassTranslators[$pseudoClass])) { throw new ExpressionErrorException(sprintf('Pseudo-class "%s" not supported.', $pseudoClass)); } return \call_user_func($this->pseudoClassTranslators[$pseudoClass], $xpath); } public function addAttributeMatching(XPathExpr $xpath, $operator, $attribute, $value) { if (!isset($this->attributeMatchingTranslators[$operator])) { throw new ExpressionErrorException(sprintf('Attribute matcher operator "%s" not supported.', $operator)); } return \call_user_func($this->attributeMatchingTranslators[$operator], $xpath, $attribute, $value); } private function parseSelectors($css) { foreach ($this->shortcutParsers as $shortcut) { $tokens = $shortcut->parse($css); if (!empty($tokens)) { return $tokens; } } return $this->mainParser->parse($css); } } [$this, 'translateExists'], '=' => [$this, 'translateEquals'], '~=' => [$this, 'translateIncludes'], '|=' => [$this, 'translateDashMatch'], '^=' => [$this, 'translatePrefixMatch'], '$=' => [$this, 'translateSuffixMatch'], '*=' => [$this, 'translateSubstringMatch'], '!=' => [$this, 'translateDifferent'], ]; } public function translateExists(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition($attribute); } public function translateEquals(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition(sprintf('%s = %s', $attribute, Translator::getXpathLiteral($value))); } public function translateIncludes(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition($value ? sprintf( '%1$s and contains(concat(\' \', normalize-space(%1$s), \' \'), %2$s)', $attribute, Translator::getXpathLiteral(' '.$value.' ') ) : '0'); } public function translateDashMatch(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition(sprintf( '%1$s and (%1$s = %2$s or starts-with(%1$s, %3$s))', $attribute, Translator::getXpathLiteral($value), Translator::getXpathLiteral($value.'-') )); } public function translatePrefixMatch(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition($value ? sprintf( '%1$s and starts-with(%1$s, %2$s)', $attribute, Translator::getXpathLiteral($value) ) : '0'); } public function translateSuffixMatch(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition($value ? sprintf( '%1$s and substring(%1$s, string-length(%1$s)-%2$s) = %3$s', $attribute, \strlen($value) - 1, Translator::getXpathLiteral($value) ) : '0'); } public function translateSubstringMatch(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition($value ? sprintf( '%1$s and contains(%1$s, %2$s)', $attribute, Translator::getXpathLiteral($value) ) : '0'); } public function translateDifferent(XPathExpr $xpath, $attribute, $value) { return $xpath->addCondition(sprintf( $value ? 'not(%1$s) or %1$s != %2$s' : '%s != %s', $attribute, Translator::getXpathLiteral($value) )); } public function getName() { return 'attribute-matching'; } } [$this, 'translateNthChild'], 'nth-last-child' => [$this, 'translateNthLastChild'], 'nth-of-type' => [$this, 'translateNthOfType'], 'nth-last-of-type' => [$this, 'translateNthLastOfType'], 'contains' => [$this, 'translateContains'], 'lang' => [$this, 'translateLang'], ]; } public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $last = false, $addNameTest = true) { try { list($a, $b) = Parser::parseSeries($function->getArguments()); } catch (SyntaxErrorException $e) { throw new ExpressionErrorException(sprintf('Invalid series: "%s".', implode('", "', $function->getArguments())), 0, $e); } $xpath->addStarPrefix(); if ($addNameTest) { $xpath->addNameTest(); } if (0 === $a) { return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b)); } if ($a < 0) { if ($b < 1) { return $xpath->addCondition('false()'); } $sign = '<='; } else { $sign = '>='; } $expr = 'position()'; if ($last) { $expr = 'last() - '.$expr; --$b; } if (0 !== $b) { $expr .= ' - '.$b; } $conditions = [sprintf('%s %s 0', $expr, $sign)]; if (1 !== $a && -1 !== $a) { $conditions[] = sprintf('(%s) mod %d = 0', $expr, $a); } return $xpath->addCondition(implode(' and ', $conditions)); } public function translateNthLastChild(XPathExpr $xpath, FunctionNode $function) { return $this->translateNthChild($xpath, $function, true); } public function translateNthOfType(XPathExpr $xpath, FunctionNode $function) { return $this->translateNthChild($xpath, $function, false, false); } public function translateNthLastOfType(XPathExpr $xpath, FunctionNode $function) { if ('*' === $xpath->getElement()) { throw new ExpressionErrorException('"*:nth-of-type()" is not implemented.'); } return $this->translateNthChild($xpath, $function, true, false); } public function translateContains(XPathExpr $xpath, FunctionNode $function) { $arguments = $function->getArguments(); foreach ($arguments as $token) { if (!($token->isString() || $token->isIdentifier())) { throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments)); } } return $xpath->addCondition(sprintf( 'contains(string(.), %s)', Translator::getXpathLiteral($arguments[0]->getValue()) )); } public function translateLang(XPathExpr $xpath, FunctionNode $function) { $arguments = $function->getArguments(); foreach ($arguments as $token) { if (!($token->isString() || $token->isIdentifier())) { throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments)); } } return $xpath->addCondition(sprintf( 'lang(%s)', Translator::getXpathLiteral($arguments[0]->getValue()) )); } public function getName() { return 'function'; } } getExtension('node') ->setFlag(NodeExtension::ELEMENT_NAME_IN_LOWER_CASE, true) ->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true); } public function getPseudoClassTranslators() { return [ 'checked' => [$this, 'translateChecked'], 'link' => [$this, 'translateLink'], 'disabled' => [$this, 'translateDisabled'], 'enabled' => [$this, 'translateEnabled'], 'selected' => [$this, 'translateSelected'], 'invalid' => [$this, 'translateInvalid'], 'hover' => [$this, 'translateHover'], 'visited' => [$this, 'translateVisited'], ]; } public function getFunctionTranslators() { return [ 'lang' => [$this, 'translateLang'], ]; } public function translateChecked(XPathExpr $xpath) { return $xpath->addCondition( '(@checked ' ."and (name(.) = 'input' or name(.) = 'command')" ."and (@type = 'checkbox' or @type = 'radio'))" ); } public function translateLink(XPathExpr $xpath) { return $xpath->addCondition("@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')"); } public function translateDisabled(XPathExpr $xpath) { return $xpath->addCondition( '(' .'@disabled and' .'(' ."(name(.) = 'input' and @type != 'hidden')" ." or name(.) = 'button'" ." or name(.) = 'select'" ." or name(.) = 'textarea'" ." or name(.) = 'command'" ." or name(.) = 'fieldset'" ." or name(.) = 'optgroup'" ." or name(.) = 'option'" .')' .') or (' ."(name(.) = 'input' and @type != 'hidden')" ." or name(.) = 'button'" ." or name(.) = 'select'" ." or name(.) = 'textarea'" .')' .' and ancestor::fieldset[@disabled]' ); } public function translateEnabled(XPathExpr $xpath) { return $xpath->addCondition( '(' .'@href and (' ."name(.) = 'a'" ." or name(.) = 'link'" ." or name(.) = 'area'" .')' .') or (' .'(' ."name(.) = 'command'" ." or name(.) = 'fieldset'" ." or name(.) = 'optgroup'" .')' .' and not(@disabled)' .') or (' .'(' ."(name(.) = 'input' and @type != 'hidden')" ." or name(.) = 'button'" ." or name(.) = 'select'" ." or name(.) = 'textarea'" ." or name(.) = 'keygen'" .')' .' and not (@disabled or ancestor::fieldset[@disabled])' .') or (' ."name(.) = 'option' and not(" .'@disabled or ancestor::optgroup[@disabled]' .')' .')' ); } public function translateLang(XPathExpr $xpath, FunctionNode $function) { $arguments = $function->getArguments(); foreach ($arguments as $token) { if (!($token->isString() || $token->isIdentifier())) { throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments)); } } return $xpath->addCondition(sprintf( 'ancestor-or-self::*[@lang][1][starts-with(concat(' ."translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '-')" .', %s)]', 'lang', Translator::getXpathLiteral(strtolower($arguments[0]->getValue()).'-') )); } public function translateSelected(XPathExpr $xpath) { return $xpath->addCondition("(@selected and name(.) = 'option')"); } public function translateInvalid(XPathExpr $xpath) { return $xpath->addCondition('0'); } public function translateHover(XPathExpr $xpath) { return $xpath->addCondition('0'); } public function translateVisited(XPathExpr $xpath) { return $xpath->addCondition('0'); } public function getName() { return 'html'; } } flags = $flags; } public function setFlag($flag, $on) { if ($on && !$this->hasFlag($flag)) { $this->flags += $flag; } if (!$on && $this->hasFlag($flag)) { $this->flags -= $flag; } return $this; } public function hasFlag($flag) { return (bool) ($this->flags & $flag); } public function getNodeTranslators() { return [ 'Selector' => [$this, 'translateSelector'], 'CombinedSelector' => [$this, 'translateCombinedSelector'], 'Negation' => [$this, 'translateNegation'], 'Function' => [$this, 'translateFunction'], 'Pseudo' => [$this, 'translatePseudo'], 'Attribute' => [$this, 'translateAttribute'], 'Class' => [$this, 'translateClass'], 'Hash' => [$this, 'translateHash'], 'Element' => [$this, 'translateElement'], ]; } public function translateSelector(Node\SelectorNode $node, Translator $translator) { return $translator->nodeToXPath($node->getTree()); } public function translateCombinedSelector(Node\CombinedSelectorNode $node, Translator $translator) { return $translator->addCombination($node->getCombinator(), $node->getSelector(), $node->getSubSelector()); } public function translateNegation(Node\NegationNode $node, Translator $translator) { $xpath = $translator->nodeToXPath($node->getSelector()); $subXpath = $translator->nodeToXPath($node->getSubSelector()); $subXpath->addNameTest(); if ($subXpath->getCondition()) { return $xpath->addCondition(sprintf('not(%s)', $subXpath->getCondition())); } return $xpath->addCondition('0'); } public function translateFunction(Node\FunctionNode $node, Translator $translator) { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addFunction($xpath, $node); } public function translatePseudo(Node\PseudoNode $node, Translator $translator) { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addPseudoClass($xpath, $node->getIdentifier()); } public function translateAttribute(Node\AttributeNode $node, Translator $translator) { $name = $node->getAttribute(); $safe = $this->isSafeName($name); if ($this->hasFlag(self::ATTRIBUTE_NAME_IN_LOWER_CASE)) { $name = strtolower($name); } if ($node->getNamespace()) { $name = sprintf('%s:%s', $node->getNamespace(), $name); $safe = $safe && $this->isSafeName($node->getNamespace()); } $attribute = $safe ? '@'.$name : sprintf('attribute::*[name() = %s]', Translator::getXpathLiteral($name)); $value = $node->getValue(); $xpath = $translator->nodeToXPath($node->getSelector()); if ($this->hasFlag(self::ATTRIBUTE_VALUE_IN_LOWER_CASE)) { $value = strtolower($value); } return $translator->addAttributeMatching($xpath, $node->getOperator(), $attribute, $value); } public function translateClass(Node\ClassNode $node, Translator $translator) { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addAttributeMatching($xpath, '~=', '@class', $node->getName()); } public function translateHash(Node\HashNode $node, Translator $translator) { $xpath = $translator->nodeToXPath($node->getSelector()); return $translator->addAttributeMatching($xpath, '=', '@id', $node->getId()); } public function translateElement(Node\ElementNode $node) { $element = $node->getElement(); if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) { $element = strtolower($element); } if ($element) { $safe = $this->isSafeName($element); } else { $element = '*'; $safe = true; } if ($node->getNamespace()) { $element = sprintf('%s:%s', $node->getNamespace(), $element); $safe = $safe && $this->isSafeName($node->getNamespace()); } $xpath = new XPathExpr('', $element); if (!$safe) { $xpath->addNameTest(); } return $xpath; } public function getName() { return 'node'; } private function isSafeName($name) { return 0 < preg_match('~^[a-zA-Z_][a-zA-Z0-9_.-]*$~', $name); } } [$this, 'translateDescendant'], '>' => [$this, 'translateChild'], '+' => [$this, 'translateDirectAdjacent'], '~' => [$this, 'translateIndirectAdjacent'], ]; } public function translateDescendant(XPathExpr $xpath, XPathExpr $combinedXpath) { return $xpath->join('/descendant-or-self::*/', $combinedXpath); } public function translateChild(XPathExpr $xpath, XPathExpr $combinedXpath) { return $xpath->join('/', $combinedXpath); } public function translateDirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) { return $xpath ->join('/following-sibling::', $combinedXpath) ->addNameTest() ->addCondition('position() = 1'); } public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedXpath) { return $xpath->join('/following-sibling::', $combinedXpath); } public function getName() { return 'combination'; } } [$this, 'translateRoot'], 'first-child' => [$this, 'translateFirstChild'], 'last-child' => [$this, 'translateLastChild'], 'first-of-type' => [$this, 'translateFirstOfType'], 'last-of-type' => [$this, 'translateLastOfType'], 'only-child' => [$this, 'translateOnlyChild'], 'only-of-type' => [$this, 'translateOnlyOfType'], 'empty' => [$this, 'translateEmpty'], ]; } public function translateRoot(XPathExpr $xpath) { return $xpath->addCondition('not(parent::*)'); } public function translateFirstChild(XPathExpr $xpath) { return $xpath ->addStarPrefix() ->addNameTest() ->addCondition('position() = 1'); } public function translateLastChild(XPathExpr $xpath) { return $xpath ->addStarPrefix() ->addNameTest() ->addCondition('position() = last()'); } public function translateFirstOfType(XPathExpr $xpath) { if ('*' === $xpath->getElement()) { throw new ExpressionErrorException('"*:first-of-type" is not implemented.'); } return $xpath ->addStarPrefix() ->addCondition('position() = 1'); } public function translateLastOfType(XPathExpr $xpath) { if ('*' === $xpath->getElement()) { throw new ExpressionErrorException('"*:last-of-type" is not implemented.'); } return $xpath ->addStarPrefix() ->addCondition('position() = last()'); } public function translateOnlyChild(XPathExpr $xpath) { return $xpath ->addStarPrefix() ->addNameTest() ->addCondition('last() = 1'); } public function translateOnlyOfType(XPathExpr $xpath) { $element = $xpath->getElement(); if ('*' === $element) { throw new ExpressionErrorException('"*:only-of-type" is not implemented.'); } return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element)); } public function translateEmpty(XPathExpr $xpath) { return $xpath->addCondition('not(*) and not(string-length())'); } public function getName() { return 'pseudo-class'; } } path = $path; $this->element = $element; $this->condition = $condition; if ($starPrefix) { $this->addStarPrefix(); } } public function getElement() { return $this->element; } public function addCondition($condition) { $this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition; return $this; } public function getCondition() { return $this->condition; } public function addNameTest() { if ('*' !== $this->element) { $this->addCondition('name() = '.Translator::getXpathLiteral($this->element)); $this->element = '*'; } return $this; } public function addStarPrefix() { $this->path .= '*/'; return $this; } public function join($combiner, self $expr) { $path = $this->__toString().$combiner; if ('*/' !== $expr->path) { $path .= $expr->path; } $this->path = $path; $this->element = $expr->element; $this->condition = $expr->condition; return $this; } public function __toString() { $path = $this->path.$this->element; $condition = null === $this->condition || '' === $this->condition ? '' : '['.$this->condition.']'; return $path.$condition; } } 255) { return (string) $int; } if ($int < 0) { $int += 256; } return \chr($int); } } setServerParameters($server); $this->history = $history ?: new History(); $this->cookieJar = $cookieJar ?: new CookieJar(); } public function followRedirects($followRedirect = true) { $this->followRedirects = (bool) $followRedirect; } public function isFollowingRedirects() { return $this->followRedirects; } public function setMaxRedirects($maxRedirects) { $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects; $this->followRedirects = -1 != $this->maxRedirects; } public function getMaxRedirects() { return $this->maxRedirects; } public function insulate($insulated = true) { if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) { throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.'); } $this->insulated = (bool) $insulated; } public function setServerParameters(array $server) { $this->server = array_merge([ 'HTTP_USER_AGENT' => 'Symfony BrowserKit', ], $server); } public function setServerParameter($key, $value) { $this->server[$key] = $value; } public function getServerParameter($key, $default = '') { return isset($this->server[$key]) ? $this->server[$key] : $default; } public function getHistory() { return $this->history; } public function getCookieJar() { return $this->cookieJar; } public function getCrawler() { return $this->crawler; } public function getInternalResponse() { return $this->internalResponse; } public function getResponse() { return $this->response; } public function getInternalRequest() { return $this->internalRequest; } public function getRequest() { return $this->request; } public function click(Link $link) { if ($link instanceof Form) { return $this->submit($link); } return $this->request($link->getMethod(), $link->getUri()); } public function submit(Form $form, array $values = []) { $form->setValues($values); return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles()); } public function request($method, $uri, array $parameters = [], array $files = [], array $server = [], $content = null, $changeHistory = true) { if ($this->isMainRequest) { $this->redirectCount = 0; } else { ++$this->redirectCount; } $originalUri = $uri; $uri = $this->getAbsoluteUri($uri); $server = array_merge($this->server, $server); if (!empty($server['HTTP_HOST']) && null === parse_url($originalUri, \PHP_URL_HOST)) { $uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri); } if (isset($server['HTTPS']) && null === parse_url($originalUri, \PHP_URL_SCHEME)) { $uri = preg_replace('{^'.parse_url($uri, \PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri); } if (!isset($server['HTTP_REFERER']) && !$this->history->isEmpty()) { $server['HTTP_REFERER'] = $this->history->current()->getUri(); } if (empty($server['HTTP_HOST'])) { $server['HTTP_HOST'] = $this->extractHost($uri); } $server['HTTPS'] = 'https' == parse_url($uri, \PHP_URL_SCHEME); $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content); $this->request = $this->filterRequest($this->internalRequest); if (true === $changeHistory) { $this->history->add($this->internalRequest); } if ($this->insulated) { $this->response = $this->doRequestInProcess($this->request); } else { $this->response = $this->doRequest($this->request); } $this->internalResponse = $this->filterResponse($this->response); $this->cookieJar->updateFromResponse($this->internalResponse, $uri); $status = $this->internalResponse->getStatus(); if ($status >= 300 && $status < 400) { $this->redirect = $this->internalResponse->getHeader('Location'); } else { $this->redirect = null; } if ($this->followRedirects && $this->redirect) { $this->redirects[serialize($this->history->current())] = true; return $this->crawler = $this->followRedirect(); } return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type')); } protected function doRequestInProcess($request) { $deprecationsFile = tempnam(sys_get_temp_dir(), 'deprec'); putenv('SYMFONY_DEPRECATIONS_SERIALIZE='.$deprecationsFile); $_ENV['SYMFONY_DEPRECATIONS_SERIALIZE'] = $deprecationsFile; $process = new PhpProcess($this->getScript($request), null, null); $process->run(); if (file_exists($deprecationsFile)) { $deprecations = file_get_contents($deprecationsFile); unlink($deprecationsFile); foreach ($deprecations ? unserialize($deprecations) : [] as $deprecation) { if ($deprecation[0]) { trigger_error($deprecation[1], \E_USER_DEPRECATED); } else { @trigger_error($deprecation[1], \E_USER_DEPRECATED); } } } if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) { throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s.', $process->getOutput(), $process->getErrorOutput())); } return unserialize($process->getOutput()); } abstract protected function doRequest($request); protected function getScript($request) { throw new \LogicException('To insulate requests, you need to override the getScript() method.'); } protected function filterRequest(Request $request) { return $request; } protected function filterResponse($response) { return $response; } protected function createCrawlerFromContent($uri, $content, $type) { if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { return null; } $crawler = new Crawler(null, $uri); $crawler->addContent($content, $type); return $crawler; } public function back() { do { $request = $this->history->back(); } while (\array_key_exists(serialize($request), $this->redirects)); return $this->requestFromRequest($request, false); } public function forward() { do { $request = $this->history->forward(); } while (\array_key_exists(serialize($request), $this->redirects)); return $this->requestFromRequest($request, false); } public function reload() { return $this->requestFromRequest($this->history->current(), false); } public function followRedirect() { if (empty($this->redirect)) { throw new \LogicException('The request was not redirected.'); } if (-1 !== $this->maxRedirects) { if ($this->redirectCount > $this->maxRedirects) { $this->redirectCount = 0; throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects)); } } $request = $this->internalRequest; if (\in_array($this->internalResponse->getStatus(), [301, 302, 303])) { $method = 'GET'; $files = []; $content = null; } else { $method = $request->getMethod(); $files = $request->getFiles(); $content = $request->getContent(); } if ('GET' === strtoupper($method)) { $parameters = []; } else { $parameters = $request->getParameters(); } $server = $request->getServer(); $server = $this->updateServerFromUri($server, $this->redirect); $this->isMainRequest = false; $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content); $this->isMainRequest = true; return $response; } public function restart() { $this->cookieJar->clear(); $this->history->clear(); } protected function getAbsoluteUri($uri) { if (0 === strpos($uri, 'http://') || 0 === strpos($uri, 'https://')) { return $uri; } if (!$this->history->isEmpty()) { $currentUri = $this->history->current()->getUri(); } else { $currentUri = sprintf('http%s://%s/', isset($this->server['HTTPS']) ? 's' : '', isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost' ); } if (0 === strpos($uri, '//')) { return parse_url($currentUri, \PHP_URL_SCHEME).':'.$uri; } if (!$uri || '#' == $uri[0] || '?' == $uri[0]) { return preg_replace('/[#?].*?$/', '', $currentUri).$uri; } if ('/' !== $uri[0]) { $path = parse_url($currentUri, \PHP_URL_PATH); if ('/' !== substr($path, -1)) { $path = substr($path, 0, strrpos($path, '/') + 1); } $uri = $path.$uri; } return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri; } protected function requestFromRequest(Request $request, $changeHistory = true) { return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory); } private function updateServerFromUri($server, $uri) { $server['HTTP_HOST'] = $this->extractHost($uri); $scheme = parse_url($uri, \PHP_URL_SCHEME); $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme; unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']); return $server; } private function extractHost($uri) { $host = parse_url($uri, \PHP_URL_HOST); if ($port = parse_url($uri, \PHP_URL_PORT)) { return $host.':'.$port; } return $host; } } cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; } public function get($name, $path = '/', $domain = null) { $this->flushExpiredCookies(); foreach ($this->cookieJar as $cookieDomain => $pathCookies) { if ($cookieDomain && $domain) { $cookieDomain = '.'.ltrim($cookieDomain, '.'); if ($cookieDomain !== substr('.'.$domain, -\strlen($cookieDomain))) { continue; } } foreach ($pathCookies as $cookiePath => $namedCookies) { if (0 !== strpos($path, $cookiePath)) { continue; } if (isset($namedCookies[$name])) { return $namedCookies[$name]; } } } return null; } public function expire($name, $path = '/', $domain = null) { if (null === $path) { $path = '/'; } if (empty($domain)) { $domains = array_keys($this->cookieJar); } else { $domains = [$domain]; } foreach ($domains as $domain) { unset($this->cookieJar[$domain][$path][$name]); if (empty($this->cookieJar[$domain][$path])) { unset($this->cookieJar[$domain][$path]); if (empty($this->cookieJar[$domain])) { unset($this->cookieJar[$domain]); } } } } public function clear() { $this->cookieJar = []; } public function updateFromSetCookie(array $setCookies, $uri = null) { $cookies = []; foreach ($setCookies as $cookie) { foreach (explode(',', $cookie) as $i => $part) { if (0 === $i || preg_match('/^(?P\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) { $cookies[] = ltrim($part); } else { $cookies[\count($cookies) - 1] .= ','.$part; } } } foreach ($cookies as $cookie) { try { $this->set(Cookie::fromString($cookie, $uri)); } catch (\InvalidArgumentException $e) { } } } public function updateFromResponse(Response $response, $uri = null) { $this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri); } public function all() { $this->flushExpiredCookies(); $flattenedCookies = []; foreach ($this->cookieJar as $path) { foreach ($path as $cookies) { foreach ($cookies as $cookie) { $flattenedCookies[] = $cookie; } } } return $flattenedCookies; } public function allValues($uri, $returnsRawValue = false) { $this->flushExpiredCookies(); $parts = array_replace(['path' => '/'], parse_url($uri)); $cookies = []; foreach ($this->cookieJar as $domain => $pathCookies) { if ($domain) { $domain = '.'.ltrim($domain, '.'); if ($domain != substr('.'.$parts['host'], -\strlen($domain))) { continue; } } foreach ($pathCookies as $path => $namedCookies) { if ($path != substr($parts['path'], 0, \strlen($path))) { continue; } foreach ($namedCookies as $cookie) { if ($cookie->isSecure() && 'https' != $parts['scheme']) { continue; } $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue(); } } } return $cookies; } public function allRawValues($uri) { return $this->allValues($uri, true); } public function flushExpiredCookies() { foreach ($this->cookieJar as $domain => $pathCookies) { foreach ($pathCookies as $path => $namedCookies) { foreach ($namedCookies as $name => $cookie) { if ($cookie->isExpired()) { unset($this->cookieJar[$domain][$path][$name]); } } } } } } value = urldecode($value); $this->rawValue = $value; } else { $this->value = $value; $this->rawValue = rawurlencode($value); } $this->name = $name; $this->path = empty($path) ? '/' : $path; $this->domain = $domain; $this->secure = (bool) $secure; $this->httponly = (bool) $httponly; if (null !== $expires) { $timestampAsDateTime = \DateTime::createFromFormat('U', $expires); if (false === $timestampAsDateTime) { throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.', $expires)); } $this->expires = $timestampAsDateTime->format('U'); } } public function __toString() { $cookie = sprintf('%s=%s', $this->name, $this->rawValue); if (null !== $this->expires) { $dateTime = \DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT')); $cookie .= '; expires='.str_replace('+0000', '', $dateTime->format(self::$dateFormats[0])); } if ('' !== $this->domain) { $cookie .= '; domain='.$this->domain; } if ($this->path) { $cookie .= '; path='.$this->path; } if ($this->secure) { $cookie .= '; secure'; } if ($this->httponly) { $cookie .= '; httponly'; } return $cookie; } public static function fromString($cookie, $url = null) { $parts = explode(';', $cookie); if (false === strpos($parts[0], '=')) { throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0])); } list($name, $value) = explode('=', array_shift($parts), 2); $values = [ 'name' => trim($name), 'value' => trim($value), 'expires' => null, 'path' => '/', 'domain' => '', 'secure' => false, 'httponly' => false, 'passedRawValue' => true, ]; if (null !== $url) { if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host'])) { throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url)); } $values['domain'] = $urlParts['host']; $values['path'] = isset($urlParts['path']) ? substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) : ''; } foreach ($parts as $part) { $part = trim($part); if ('secure' === strtolower($part)) { if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) { continue; } $values['secure'] = true; continue; } if ('httponly' === strtolower($part)) { $values['httponly'] = true; continue; } if (2 === \count($elements = explode('=', $part, 2))) { if ('expires' === strtolower($elements[0])) { $elements[1] = self::parseDate($elements[1]); } $values[strtolower($elements[0])] = $elements[1]; } } return new static( $values['name'], $values['value'], $values['expires'], $values['path'], $values['domain'], $values['secure'], $values['httponly'], $values['passedRawValue'] ); } private static function parseDate($dateValue) { if (($length = \strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length - 1]) { $dateValue = substr($dateValue, 1, -1); } foreach (self::$dateFormats as $dateFormat) { if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) { return $date->format('U'); } } if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) { return $date->format('U'); } return null; } public function getName() { return $this->name; } public function getValue() { return $this->value; } public function getRawValue() { return $this->rawValue; } public function getExpiresTime() { return $this->expires; } public function getPath() { return $this->path; } public function getDomain() { return $this->domain; } public function isSecure() { return $this->secure; } public function isHttpOnly() { return $this->httponly; } public function isExpired() { return null !== $this->expires && 0 != $this->expires && $this->expires <= time(); } } uri = $uri; $this->method = $method; $this->parameters = $parameters; $this->files = $files; $this->cookies = $cookies; $this->server = $server; $this->content = $content; } public function getUri() { return $this->uri; } public function getMethod() { return $this->method; } public function getParameters() { return $this->parameters; } public function getFiles() { return $this->files; } public function getCookies() { return $this->cookies; } public function getServer() { return $this->server; } public function getContent() { return $this->content; } } stack = []; $this->position = -1; } public function add(Request $request) { $this->stack = \array_slice($this->stack, 0, $this->position + 1); $this->stack[] = clone $request; $this->position = \count($this->stack) - 1; } public function isEmpty() { return 0 == \count($this->stack); } public function back() { if ($this->position < 1) { throw new \LogicException('You are already on the first page.'); } return clone $this->stack[--$this->position]; } public function forward() { if ($this->position > \count($this->stack) - 2) { throw new \LogicException('You are already on the last page.'); } return clone $this->stack[++$this->position]; } public function current() { if (-1 == $this->position) { throw new \LogicException('The page history is empty.'); } return clone $this->stack[$this->position]; } } content = $content; $this->status = $status; $this->headers = $headers; } public function __toString() { $headers = ''; foreach ($this->headers as $name => $value) { if (\is_string($value)) { $headers .= $this->buildHeader($name, $value); } else { foreach ($value as $headerValue) { $headers .= $this->buildHeader($name, $headerValue); } } } return $headers."\n".$this->content; } protected function buildHeader($name, $value) { return sprintf("%s: %s\n", $name, $value); } public function getContent() { return $this->content; } public function getStatus() { return $this->status; } public function getHeaders() { return $this->headers; } public function getHeader($header, $first = true) { $normalizedHeader = str_replace('-', '_', strtolower($header)); foreach ($this->headers as $key => $value) { if (str_replace('-', '_', strtolower($key)) === $normalizedHeader) { if ($first) { return \is_array($value) ? (\count($value) ? $value[0] : '') : $value; } return \is_array($value) ? $value : [$value]; } } return $first ? null : []; } } = 80000) { require_once __DIR__.'/Resources/mb_convert_variables.php8'; } elseif (!function_exists('mb_convert_variables')) { 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_ord')) { function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); } } if (!function_exists('mb_chr')) { function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); } } if (!function_exists('mb_scrub')) { function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } } if (!function_exists('mb_str_split')) { function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); } } if (extension_loaded('mbstring')) { return; } if (!defined('MB_CASE_UPPER')) { define('MB_CASE_UPPER', 0); } if (!defined('MB_CASE_LOWER')) { define('MB_CASE_LOWER', 1); } if (!defined('MB_CASE_TITLE')) { define('MB_CASE_TITLE', 2); } = $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; } 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) { static $titleRegexp = null; if (null === $titleRegexp) { $titleRegexp = self::getData('titleCaseRegexp'); } $s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $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) { if (0 > $offset += self::mb_strlen($needle)) { $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_str_split($string, $split_length = 1, $encoding = null) { if (null !== $string && !\is_scalar($string) && !(\is_object($string) && \method_exists($string, '__toString'))) { trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', E_USER_WARNING); return null; } if (1 > $split_length = (int) $split_length) { trigger_error('The length of each segment must be greater than zero', E_USER_WARNING); return false; } if (null === $encoding) { $encoding = mb_internal_encoding(); } if ('UTF-8' === $encoding = self::getEncoding($encoding)) { $rx = '/('; while (65535 < $split_length) { $rx .= '.{65535}'; $split_length -= 65535; } $rx .= '.{'.$split_length.'})/us'; return preg_split($rx, $string, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } $result = array(); $length = mb_strlen($string, $encoding); for ($i = 0; $i < $length; $i += $split_length) { $result[] = mb_substr($string, $i, $split_length, $encoding); } return $result; } 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 (string) 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) { $pos = strrpos($haystack, $needle); } else { $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); } if (1 === \strlen($s)) { return \ord($s); } $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(array $s) { return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, '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; } if ('UTF-8' === $encoding) { return 'UTF-8'; } $encoding = strtoupper($encoding); if ('8BIT' === $encoding || 'BINARY' === $encoding) { return 'CP850'; } if ('UTF8' === $encoding) { return 'UTF-8'; } return $encoding; } } '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', 'IJ' => 'ij', 'Ĵ' => 'ĵ', 'Ķ' => 'ķ', 'Ĺ' => 'ĺ', 'Ļ' => 'ļ', 'Ľ' => 'ľ', 'Ŀ' => 'ŀ', 'Ł' => 'ł', 'Ń' => 'ń', 'Ņ' => 'ņ', 'Ň' => 'ň', 'Ŋ' => 'ŋ', 'Ō' => 'ō', 'Ŏ' => 'ŏ', 'Ő' => 'ő', 'Œ' => 'œ', 'Ŕ' => 'ŕ', 'Ŗ' => 'ŗ', 'Ř' => 'ř', 'Ś' => 'ś', 'Ŝ' => 'ŝ', 'Ş' => 'ş', 'Š' => 'š', 'Ţ' => 'ţ', 'Ť' => 'ť', 'Ŧ' => 'ŧ', 'Ũ' => 'ũ', 'Ū' => 'ū', 'Ŭ' => 'ŭ', 'Ů' => 'ů', 'Ű' => 'ű', 'Ų' => 'ų', 'Ŵ' => 'ŵ', 'Ŷ' => 'ŷ', 'Ÿ' => 'ÿ', 'Ź' => 'ź', 'Ż' => 'ż', 'Ž' => 'ž', 'Ɓ' => 'ɓ', 'Ƃ' => 'ƃ', 'Ƅ' => 'ƅ', 'Ɔ' => 'ɔ', 'Ƈ' => 'ƈ', 'Ɖ' => 'ɖ', 'Ɗ' => 'ɗ', 'Ƌ' => 'ƌ', 'Ǝ' => 'ǝ', 'Ə' => 'ə', 'Ɛ' => 'ɛ', 'Ƒ' => 'ƒ', 'Ɠ' => 'ɠ', 'Ɣ' => 'ɣ', 'Ɩ' => 'ɩ', 'Ɨ' => 'ɨ', 'Ƙ' => 'ƙ', 'Ɯ' => 'ɯ', 'Ɲ' => 'ɲ', 'Ɵ' => 'ɵ', 'Ơ' => 'ơ', 'Ƣ' => 'ƣ', 'Ƥ' => 'ƥ', 'Ʀ' => 'ʀ', 'Ƨ' => 'ƨ', 'Ʃ' => 'ʃ', 'Ƭ' => 'ƭ', 'Ʈ' => 'ʈ', 'Ư' => 'ư', 'Ʊ' => 'ʊ', 'Ʋ' => 'ʋ', 'Ƴ' => 'ƴ', 'Ƶ' => 'ƶ', 'Ʒ' => 'ʒ', 'Ƹ' => 'ƹ', 'Ƽ' => 'ƽ', 'DŽ' => 'dž', 'Dž' => 'dž', 'LJ' => 'lj', 'Lj' => 'lj', 'NJ' => 'nj', 'Nj' => 'nj', 'Ǎ' => 'ǎ', 'Ǐ' => 'ǐ', 'Ǒ' => 'ǒ', 'Ǔ' => 'ǔ', 'Ǖ' => 'ǖ', 'Ǘ' => 'ǘ', 'Ǚ' => 'ǚ', 'Ǜ' => 'ǜ', 'Ǟ' => 'ǟ', 'Ǡ' => 'ǡ', 'Ǣ' => 'ǣ', 'Ǥ' => 'ǥ', 'Ǧ' => 'ǧ', 'Ǩ' => 'ǩ', 'Ǫ' => 'ǫ', 'Ǭ' => 'ǭ', 'Ǯ' => 'ǯ', 'DZ' => 'dz', 'Dz' => 'dz', 'Ǵ' => 'ǵ', 'Ƕ' => 'ƕ', 'Ƿ' => 'ƿ', 'Ǹ' => 'ǹ', 'Ǻ' => 'ǻ', 'Ǽ' => 'ǽ', 'Ǿ' => 'ǿ', 'Ȁ' => 'ȁ', 'Ȃ' => 'ȃ', 'Ȅ' => 'ȅ', 'Ȇ' => 'ȇ', 'Ȉ' => 'ȉ', 'Ȋ' => 'ȋ', 'Ȍ' => 'ȍ', 'Ȏ' => 'ȏ', 'Ȑ' => 'ȑ', 'Ȓ' => 'ȓ', 'Ȕ' => 'ȕ', 'Ȗ' => 'ȗ', 'Ș' => 'ș', 'Ț' => 'ț', 'Ȝ' => 'ȝ', 'Ȟ' => 'ȟ', 'Ƞ' => 'ƞ', 'Ȣ' => 'ȣ', 'Ȥ' => 'ȥ', 'Ȧ' => 'ȧ', 'Ȩ' => 'ȩ', 'Ȫ' => 'ȫ', 'Ȭ' => 'ȭ', 'Ȯ' => 'ȯ', 'Ȱ' => 'ȱ', 'Ȳ' => 'ȳ', 'Ⱥ' => 'ⱥ', 'Ȼ' => 'ȼ', 'Ƚ' => 'ƚ', 'Ⱦ' => 'ⱦ', 'Ɂ' => 'ɂ', 'Ƀ' => 'ƀ', 'Ʉ' => 'ʉ', 'Ʌ' => 'ʌ', 'Ɇ' => 'ɇ', 'Ɉ' => 'ɉ', 'Ɋ' => 'ɋ', 'Ɍ' => 'ɍ', 'Ɏ' => 'ɏ', 'Ͱ' => 'ͱ', 'Ͳ' => 'ͳ', 'Ͷ' => 'ͷ', 'Ϳ' => 'ϳ', 'Ά' => 'ά', 'Έ' => 'έ', 'Ή' => 'ή', 'Ί' => 'ί', 'Ό' => 'ό', 'Ύ' => 'ύ', 'Ώ' => 'ώ', 'Α' => 'α', 'Β' => 'β', 'Γ' => 'γ', 'Δ' => 'δ', 'Ε' => 'ε', 'Ζ' => 'ζ', 'Η' => 'η', 'Θ' => 'θ', 'Ι' => 'ι', 'Κ' => 'κ', 'Λ' => 'λ', 'Μ' => 'μ', 'Ν' => 'ν', 'Ξ' => 'ξ', 'Ο' => 'ο', 'Π' => 'π', 'Ρ' => 'ρ', 'Σ' => 'σ', 'Τ' => 'τ', 'Υ' => 'υ', 'Φ' => 'φ', 'Χ' => 'χ', 'Ψ' => 'ψ', 'Ω' => 'ω', 'Ϊ' => 'ϊ', 'Ϋ' => 'ϋ', 'Ϗ' => 'ϗ', 'Ϙ' => 'ϙ', 'Ϛ' => 'ϛ', 'Ϝ' => 'ϝ', 'Ϟ' => 'ϟ', 'Ϡ' => 'ϡ', 'Ϣ' => 'ϣ', 'Ϥ' => 'ϥ', 'Ϧ' => 'ϧ', 'Ϩ' => 'ϩ', 'Ϫ' => 'ϫ', 'Ϭ' => 'ϭ', 'Ϯ' => 'ϯ', 'ϴ' => 'θ', 'Ϸ' => 'ϸ', 'Ϲ' => 'ϲ', 'Ϻ' => 'ϻ', 'Ͻ' => 'ͻ', 'Ͼ' => 'ͼ', 'Ͽ' => 'ͽ', 'Ѐ' => 'ѐ', 'Ё' => 'ё', 'Ђ' => 'ђ', 'Ѓ' => 'ѓ', 'Є' => 'є', 'Ѕ' => 'ѕ', 'І' => 'і', 'Ї' => 'ї', 'Ј' => 'ј', 'Љ' => 'љ', 'Њ' => 'њ', 'Ћ' => 'ћ', 'Ќ' => 'ќ', 'Ѝ' => 'ѝ', 'Ў' => 'ў', 'Џ' => 'џ', 'А' => 'а', 'Б' => 'б', 'В' => 'в', 'Г' => 'г', 'Д' => 'д', 'Е' => 'е', 'Ж' => 'ж', 'З' => 'з', 'И' => 'и', 'Й' => 'й', 'К' => 'к', 'Л' => 'л', 'М' => 'м', 'Н' => 'н', 'О' => 'о', 'П' => 'п', 'Р' => 'р', 'С' => 'с', 'Т' => 'т', 'У' => 'у', 'Ф' => 'ф', 'Х' => 'х', 'Ц' => 'ц', 'Ч' => 'ч', 'Ш' => 'ш', 'Щ' => 'щ', 'Ъ' => 'ъ', 'Ы' => 'ы', 'Ь' => 'ь', 'Э' => 'э', 'Ю' => 'ю', 'Я' => 'я', 'Ѡ' => 'ѡ', 'Ѣ' => 'ѣ', 'Ѥ' => 'ѥ', 'Ѧ' => 'ѧ', 'Ѩ' => 'ѩ', 'Ѫ' => 'ѫ', 'Ѭ' => 'ѭ', 'Ѯ' => 'ѯ', 'Ѱ' => 'ѱ', 'Ѳ' => 'ѳ', 'Ѵ' => 'ѵ', 'Ѷ' => 'ѷ', 'Ѹ' => 'ѹ', 'Ѻ' => 'ѻ', 'Ѽ' => 'ѽ', 'Ѿ' => 'ѿ', 'Ҁ' => 'ҁ', 'Ҋ' => 'ҋ', 'Ҍ' => 'ҍ', 'Ҏ' => 'ҏ', 'Ґ' => 'ґ', 'Ғ' => 'ғ', 'Ҕ' => 'ҕ', 'Җ' => 'җ', 'Ҙ' => 'ҙ', 'Қ' => 'қ', 'Ҝ' => 'ҝ', 'Ҟ' => 'ҟ', 'Ҡ' => 'ҡ', 'Ң' => 'ң', 'Ҥ' => 'ҥ', 'Ҧ' => 'ҧ', 'Ҩ' => 'ҩ', 'Ҫ' => 'ҫ', 'Ҭ' => 'ҭ', 'Ү' => 'ү', 'Ұ' => 'ұ', 'Ҳ' => 'ҳ', 'Ҵ' => 'ҵ', 'Ҷ' => 'ҷ', 'Ҹ' => 'ҹ', 'Һ' => 'һ', 'Ҽ' => 'ҽ', 'Ҿ' => 'ҿ', 'Ӏ' => 'ӏ', 'Ӂ' => 'ӂ', 'Ӄ' => 'ӄ', 'Ӆ' => 'ӆ', 'Ӈ' => 'ӈ', 'Ӊ' => 'ӊ', 'Ӌ' => 'ӌ', 'Ӎ' => 'ӎ', 'Ӑ' => 'ӑ', 'Ӓ' => 'ӓ', 'Ӕ' => 'ӕ', 'Ӗ' => 'ӗ', 'Ә' => 'ә', 'Ӛ' => 'ӛ', 'Ӝ' => 'ӝ', 'Ӟ' => 'ӟ', 'Ӡ' => 'ӡ', 'Ӣ' => 'ӣ', 'Ӥ' => 'ӥ', 'Ӧ' => 'ӧ', 'Ө' => 'ө', 'Ӫ' => 'ӫ', 'Ӭ' => 'ӭ', 'Ӯ' => 'ӯ', 'Ӱ' => 'ӱ', 'Ӳ' => 'ӳ', 'Ӵ' => 'ӵ', 'Ӷ' => 'ӷ', 'Ӹ' => 'ӹ', 'Ӻ' => 'ӻ', 'Ӽ' => 'ӽ', 'Ӿ' => 'ӿ', 'Ԁ' => 'ԁ', 'Ԃ' => 'ԃ', 'Ԅ' => 'ԅ', 'Ԇ' => 'ԇ', 'Ԉ' => 'ԉ', 'Ԋ' => 'ԋ', 'Ԍ' => 'ԍ', 'Ԏ' => 'ԏ', 'Ԑ' => 'ԑ', 'Ԓ' => 'ԓ', 'Ԕ' => 'ԕ', 'Ԗ' => 'ԗ', 'Ԙ' => 'ԙ', 'Ԛ' => 'ԛ', 'Ԝ' => 'ԝ', 'Ԟ' => 'ԟ', 'Ԡ' => 'ԡ', 'Ԣ' => 'ԣ', 'Ԥ' => 'ԥ', 'Ԧ' => 'ԧ', 'Ԩ' => 'ԩ', 'Ԫ' => 'ԫ', 'Ԭ' => 'ԭ', 'Ԯ' => 'ԯ', 'Ա' => 'ա', 'Բ' => 'բ', 'Գ' => 'գ', 'Դ' => 'դ', 'Ե' => 'ե', 'Զ' => 'զ', 'Է' => 'է', 'Ը' => 'ը', 'Թ' => 'թ', 'Ժ' => 'ժ', 'Ի' => 'ի', 'Լ' => 'լ', 'Խ' => 'խ', 'Ծ' => 'ծ', 'Կ' => 'կ', 'Հ' => 'հ', 'Ձ' => 'ձ', 'Ղ' => 'ղ', 'Ճ' => 'ճ', 'Մ' => 'մ', 'Յ' => 'յ', 'Ն' => 'ն', 'Շ' => 'շ', 'Ո' => 'ո', 'Չ' => 'չ', 'Պ' => 'պ', 'Ջ' => 'ջ', 'Ռ' => 'ռ', 'Ս' => 'ս', 'Վ' => 'վ', 'Տ' => 'տ', 'Ր' => 'ր', 'Ց' => 'ց', 'Ւ' => 'ւ', 'Փ' => 'փ', 'Ք' => 'ք', 'Օ' => 'օ', 'Ֆ' => 'ֆ', 'Ⴀ' => 'ⴀ', 'Ⴁ' => 'ⴁ', 'Ⴂ' => 'ⴂ', 'Ⴃ' => 'ⴃ', 'Ⴄ' => 'ⴄ', 'Ⴅ' => 'ⴅ', 'Ⴆ' => 'ⴆ', 'Ⴇ' => 'ⴇ', 'Ⴈ' => 'ⴈ', 'Ⴉ' => 'ⴉ', 'Ⴊ' => 'ⴊ', 'Ⴋ' => 'ⴋ', 'Ⴌ' => 'ⴌ', 'Ⴍ' => 'ⴍ', 'Ⴎ' => 'ⴎ', 'Ⴏ' => 'ⴏ', 'Ⴐ' => 'ⴐ', 'Ⴑ' => 'ⴑ', 'Ⴒ' => 'ⴒ', 'Ⴓ' => 'ⴓ', 'Ⴔ' => 'ⴔ', 'Ⴕ' => 'ⴕ', 'Ⴖ' => 'ⴖ', 'Ⴗ' => 'ⴗ', 'Ⴘ' => 'ⴘ', 'Ⴙ' => 'ⴙ', 'Ⴚ' => 'ⴚ', 'Ⴛ' => 'ⴛ', 'Ⴜ' => 'ⴜ', 'Ⴝ' => 'ⴝ', 'Ⴞ' => 'ⴞ', 'Ⴟ' => 'ⴟ', 'Ⴠ' => 'ⴠ', 'Ⴡ' => 'ⴡ', 'Ⴢ' => 'ⴢ', 'Ⴣ' => 'ⴣ', 'Ⴤ' => 'ⴤ', 'Ⴥ' => 'ⴥ', 'Ⴧ' => 'ⴧ', 'Ⴭ' => 'ⴭ', 'Ꭰ' => 'ꭰ', 'Ꭱ' => 'ꭱ', 'Ꭲ' => 'ꭲ', 'Ꭳ' => 'ꭳ', 'Ꭴ' => 'ꭴ', 'Ꭵ' => 'ꭵ', 'Ꭶ' => 'ꭶ', 'Ꭷ' => 'ꭷ', 'Ꭸ' => 'ꭸ', 'Ꭹ' => 'ꭹ', 'Ꭺ' => 'ꭺ', 'Ꭻ' => 'ꭻ', 'Ꭼ' => 'ꭼ', 'Ꭽ' => 'ꭽ', 'Ꭾ' => 'ꭾ', 'Ꭿ' => 'ꭿ', 'Ꮀ' => 'ꮀ', 'Ꮁ' => 'ꮁ', 'Ꮂ' => 'ꮂ', 'Ꮃ' => 'ꮃ', 'Ꮄ' => 'ꮄ', 'Ꮅ' => 'ꮅ', 'Ꮆ' => 'ꮆ', 'Ꮇ' => 'ꮇ', 'Ꮈ' => 'ꮈ', 'Ꮉ' => 'ꮉ', 'Ꮊ' => 'ꮊ', 'Ꮋ' => 'ꮋ', 'Ꮌ' => 'ꮌ', 'Ꮍ' => 'ꮍ', 'Ꮎ' => 'ꮎ', 'Ꮏ' => 'ꮏ', 'Ꮐ' => 'ꮐ', 'Ꮑ' => 'ꮑ', 'Ꮒ' => 'ꮒ', 'Ꮓ' => 'ꮓ', 'Ꮔ' => 'ꮔ', 'Ꮕ' => 'ꮕ', 'Ꮖ' => 'ꮖ', 'Ꮗ' => 'ꮗ', 'Ꮘ' => 'ꮘ', 'Ꮙ' => 'ꮙ', 'Ꮚ' => 'ꮚ', 'Ꮛ' => 'ꮛ', 'Ꮜ' => 'ꮜ', 'Ꮝ' => 'ꮝ', 'Ꮞ' => 'ꮞ', 'Ꮟ' => 'ꮟ', 'Ꮠ' => 'ꮠ', 'Ꮡ' => 'ꮡ', 'Ꮢ' => 'ꮢ', 'Ꮣ' => 'ꮣ', 'Ꮤ' => 'ꮤ', 'Ꮥ' => 'ꮥ', 'Ꮦ' => 'ꮦ', 'Ꮧ' => 'ꮧ', 'Ꮨ' => 'ꮨ', 'Ꮩ' => 'ꮩ', 'Ꮪ' => 'ꮪ', 'Ꮫ' => 'ꮫ', 'Ꮬ' => 'ꮬ', 'Ꮭ' => 'ꮭ', 'Ꮮ' => 'ꮮ', 'Ꮯ' => 'ꮯ', 'Ꮰ' => 'ꮰ', 'Ꮱ' => 'ꮱ', 'Ꮲ' => 'ꮲ', 'Ꮳ' => 'ꮳ', 'Ꮴ' => 'ꮴ', 'Ꮵ' => 'ꮵ', 'Ꮶ' => 'ꮶ', 'Ꮷ' => 'ꮷ', 'Ꮸ' => 'ꮸ', 'Ꮹ' => 'ꮹ', 'Ꮺ' => 'ꮺ', 'Ꮻ' => 'ꮻ', 'Ꮼ' => 'ꮼ', 'Ꮽ' => 'ꮽ', 'Ꮾ' => 'ꮾ', 'Ꮿ' => 'ꮿ', 'Ᏸ' => 'ᏸ', 'Ᏹ' => 'ᏹ', 'Ᏺ' => 'ᏺ', 'Ᏻ' => 'ᏻ', 'Ᏼ' => 'ᏼ', 'Ᏽ' => 'ᏽ', 'Ა' => 'ა', 'Ბ' => 'ბ', 'Გ' => 'გ', 'Დ' => 'დ', 'Ე' => 'ე', 'Ვ' => 'ვ', 'Ზ' => 'ზ', 'Თ' => 'თ', 'Ი' => 'ი', 'Კ' => 'კ', 'Ლ' => 'ლ', 'Მ' => 'მ', 'Ნ' => 'ნ', 'Ო' => 'ო', 'Პ' => 'პ', 'Ჟ' => 'ჟ', 'Რ' => 'რ', 'Ს' => 'ს', 'Ტ' => 'ტ', 'Უ' => 'უ', 'Ფ' => 'ფ', 'Ქ' => 'ქ', 'Ღ' => 'ღ', 'Ყ' => 'ყ', 'Შ' => 'შ', 'Ჩ' => 'ჩ', 'Ც' => 'ც', 'Ძ' => 'ძ', 'Წ' => 'წ', 'Ჭ' => 'ჭ', 'Ხ' => 'ხ', 'Ჯ' => 'ჯ', 'Ჰ' => 'ჰ', 'Ჱ' => 'ჱ', 'Ჲ' => 'ჲ', 'Ჳ' => 'ჳ', 'Ჴ' => 'ჴ', 'Ჵ' => 'ჵ', 'Ჶ' => 'ჶ', 'Ჷ' => 'ჷ', 'Ჸ' => 'ჸ', 'Ჹ' => 'ჹ', 'Ჺ' => 'ჺ', 'Ჽ' => 'ჽ', 'Ჾ' => 'ჾ', 'Ჿ' => 'ჿ', 'Ḁ' => 'ḁ', 'Ḃ' => 'ḃ', 'Ḅ' => 'ḅ', 'Ḇ' => 'ḇ', 'Ḉ' => 'ḉ', 'Ḋ' => 'ḋ', 'Ḍ' => 'ḍ', 'Ḏ' => 'ḏ', 'Ḑ' => 'ḑ', 'Ḓ' => 'ḓ', 'Ḕ' => 'ḕ', 'Ḗ' => 'ḗ', 'Ḙ' => 'ḙ', 'Ḛ' => 'ḛ', 'Ḝ' => 'ḝ', 'Ḟ' => 'ḟ', 'Ḡ' => 'ḡ', 'Ḣ' => 'ḣ', 'Ḥ' => 'ḥ', 'Ḧ' => 'ḧ', 'Ḩ' => 'ḩ', 'Ḫ' => 'ḫ', 'Ḭ' => 'ḭ', 'Ḯ' => 'ḯ', 'Ḱ' => 'ḱ', 'Ḳ' => 'ḳ', 'Ḵ' => 'ḵ', 'Ḷ' => 'ḷ', 'Ḹ' => 'ḹ', 'Ḻ' => 'ḻ', 'Ḽ' => 'ḽ', 'Ḿ' => 'ḿ', 'Ṁ' => 'ṁ', 'Ṃ' => 'ṃ', 'Ṅ' => 'ṅ', 'Ṇ' => 'ṇ', 'Ṉ' => 'ṉ', 'Ṋ' => 'ṋ', 'Ṍ' => 'ṍ', 'Ṏ' => 'ṏ', 'Ṑ' => 'ṑ', 'Ṓ' => 'ṓ', 'Ṕ' => 'ṕ', 'Ṗ' => 'ṗ', 'Ṙ' => 'ṙ', 'Ṛ' => 'ṛ', 'Ṝ' => 'ṝ', 'Ṟ' => 'ṟ', 'Ṡ' => 'ṡ', 'Ṣ' => 'ṣ', 'Ṥ' => 'ṥ', 'Ṧ' => 'ṧ', 'Ṩ' => 'ṩ', 'Ṫ' => 'ṫ', 'Ṭ' => 'ṭ', 'Ṯ' => 'ṯ', 'Ṱ' => 'ṱ', 'Ṳ' => 'ṳ', 'Ṵ' => 'ṵ', 'Ṷ' => 'ṷ', 'Ṹ' => 'ṹ', 'Ṻ' => 'ṻ', 'Ṽ' => 'ṽ', 'Ṿ' => 'ṿ', 'Ẁ' => 'ẁ', 'Ẃ' => 'ẃ', 'Ẅ' => 'ẅ', 'Ẇ' => 'ẇ', 'Ẉ' => 'ẉ', 'Ẋ' => 'ẋ', 'Ẍ' => 'ẍ', 'Ẏ' => 'ẏ', 'Ẑ' => 'ẑ', 'Ẓ' => 'ẓ', 'Ẕ' => 'ẕ', 'ẞ' => 'ß', 'Ạ' => 'ạ', 'Ả' => 'ả', 'Ấ' => 'ấ', 'Ầ' => 'ầ', 'Ẩ' => 'ẩ', 'Ẫ' => 'ẫ', 'Ậ' => 'ậ', 'Ắ' => 'ắ', 'Ằ' => 'ằ', 'Ẳ' => 'ẳ', 'Ẵ' => 'ẵ', 'Ặ' => 'ặ', 'Ẹ' => 'ẹ', 'Ẻ' => 'ẻ', 'Ẽ' => 'ẽ', 'Ế' => 'ế', 'Ề' => 'ề', 'Ể' => 'ể', 'Ễ' => 'ễ', 'Ệ' => 'ệ', 'Ỉ' => 'ỉ', 'Ị' => 'ị', 'Ọ' => 'ọ', 'Ỏ' => 'ỏ', 'Ố' => 'ố', 'Ồ' => 'ồ', 'Ổ' => 'ổ', 'Ỗ' => 'ỗ', 'Ộ' => 'ộ', 'Ớ' => 'ớ', 'Ờ' => 'ờ', 'Ở' => 'ở', 'Ỡ' => 'ỡ', 'Ợ' => 'ợ', 'Ụ' => 'ụ', 'Ủ' => 'ủ', 'Ứ' => 'ứ', 'Ừ' => 'ừ', 'Ử' => 'ử', 'Ữ' => 'ữ', 'Ự' => 'ự', 'Ỳ' => 'ỳ', 'Ỵ' => 'ỵ', 'Ỷ' => 'ỷ', 'Ỹ' => 'ỹ', 'Ỻ' => 'ỻ', 'Ỽ' => 'ỽ', 'Ỿ' => 'ỿ', 'Ἀ' => 'ἀ', 'Ἁ' => 'ἁ', 'Ἂ' => 'ἂ', 'Ἃ' => 'ἃ', 'Ἄ' => 'ἄ', 'Ἅ' => 'ἅ', 'Ἆ' => 'ἆ', 'Ἇ' => 'ἇ', 'Ἐ' => 'ἐ', 'Ἑ' => 'ἑ', 'Ἒ' => 'ἒ', 'Ἓ' => 'ἓ', 'Ἔ' => 'ἔ', 'Ἕ' => 'ἕ', 'Ἠ' => 'ἠ', 'Ἡ' => 'ἡ', 'Ἢ' => 'ἢ', 'Ἣ' => 'ἣ', 'Ἤ' => 'ἤ', 'Ἥ' => 'ἥ', 'Ἦ' => 'ἦ', 'Ἧ' => 'ἧ', 'Ἰ' => 'ἰ', 'Ἱ' => 'ἱ', 'Ἲ' => 'ἲ', 'Ἳ' => 'ἳ', 'Ἴ' => 'ἴ', 'Ἵ' => 'ἵ', 'Ἶ' => 'ἶ', 'Ἷ' => 'ἷ', 'Ὀ' => 'ὀ', 'Ὁ' => 'ὁ', 'Ὂ' => 'ὂ', 'Ὃ' => 'ὃ', 'Ὄ' => 'ὄ', 'Ὅ' => 'ὅ', 'Ὑ' => 'ὑ', 'Ὓ' => 'ὓ', 'Ὕ' => 'ὕ', 'Ὗ' => 'ὗ', 'Ὠ' => 'ὠ', 'Ὡ' => 'ὡ', 'Ὢ' => 'ὢ', 'Ὣ' => 'ὣ', 'Ὤ' => 'ὤ', 'Ὥ' => 'ὥ', 'Ὦ' => 'ὦ', 'Ὧ' => 'ὧ', 'ᾈ' => 'ᾀ', 'ᾉ' => 'ᾁ', 'ᾊ' => 'ᾂ', 'ᾋ' => 'ᾃ', 'ᾌ' => 'ᾄ', 'ᾍ' => 'ᾅ', 'ᾎ' => 'ᾆ', 'ᾏ' => 'ᾇ', 'ᾘ' => 'ᾐ', 'ᾙ' => 'ᾑ', 'ᾚ' => 'ᾒ', 'ᾛ' => 'ᾓ', 'ᾜ' => 'ᾔ', 'ᾝ' => 'ᾕ', 'ᾞ' => 'ᾖ', 'ᾟ' => 'ᾗ', 'ᾨ' => 'ᾠ', 'ᾩ' => 'ᾡ', 'ᾪ' => 'ᾢ', 'ᾫ' => 'ᾣ', 'ᾬ' => 'ᾤ', 'ᾭ' => 'ᾥ', 'ᾮ' => 'ᾦ', 'ᾯ' => 'ᾧ', 'Ᾰ' => 'ᾰ', 'Ᾱ' => 'ᾱ', 'Ὰ' => 'ὰ', 'Ά' => 'ά', 'ᾼ' => 'ᾳ', 'Ὲ' => 'ὲ', 'Έ' => 'έ', 'Ὴ' => 'ὴ', 'Ή' => 'ή', 'ῌ' => 'ῃ', 'Ῐ' => 'ῐ', 'Ῑ' => 'ῑ', 'Ὶ' => 'ὶ', 'Ί' => 'ί', 'Ῠ' => 'ῠ', 'Ῡ' => 'ῡ', 'Ὺ' => 'ὺ', 'Ύ' => 'ύ', 'Ῥ' => 'ῥ', 'Ὸ' => 'ὸ', 'Ό' => 'ό', 'Ὼ' => 'ὼ', 'Ώ' => 'ώ', 'ῼ' => 'ῳ', 'Ω' => 'ω', 'K' => 'k', 'Å' => 'å', 'Ⅎ' => 'ⅎ', 'Ⅰ' => 'ⅰ', 'Ⅱ' => 'ⅱ', 'Ⅲ' => 'ⅲ', 'Ⅳ' => 'ⅳ', 'Ⅴ' => 'ⅴ', 'Ⅵ' => 'ⅵ', 'Ⅶ' => 'ⅶ', 'Ⅷ' => 'ⅷ', 'Ⅸ' => 'ⅸ', 'Ⅹ' => 'ⅹ', 'Ⅺ' => 'ⅺ', 'Ⅻ' => 'ⅻ', 'Ⅼ' => 'ⅼ', 'Ⅽ' => 'ⅽ', 'Ⅾ' => 'ⅾ', 'Ⅿ' => 'ⅿ', 'Ↄ' => 'ↄ', 'Ⓐ' => 'ⓐ', 'Ⓑ' => 'ⓑ', 'Ⓒ' => 'ⓒ', 'Ⓓ' => 'ⓓ', 'Ⓔ' => 'ⓔ', 'Ⓕ' => 'ⓕ', 'Ⓖ' => 'ⓖ', 'Ⓗ' => 'ⓗ', 'Ⓘ' => 'ⓘ', 'Ⓙ' => 'ⓙ', 'Ⓚ' => 'ⓚ', 'Ⓛ' => 'ⓛ', 'Ⓜ' => 'ⓜ', 'Ⓝ' => 'ⓝ', 'Ⓞ' => 'ⓞ', 'Ⓟ' => 'ⓟ', 'Ⓠ' => 'ⓠ', 'Ⓡ' => 'ⓡ', 'Ⓢ' => 'ⓢ', 'Ⓣ' => 'ⓣ', 'Ⓤ' => 'ⓤ', 'Ⓥ' => 'ⓥ', 'Ⓦ' => 'ⓦ', 'Ⓧ' => 'ⓧ', 'Ⓨ' => 'ⓨ', 'Ⓩ' => 'ⓩ', 'Ⰰ' => 'ⰰ', 'Ⰱ' => 'ⰱ', 'Ⰲ' => 'ⰲ', 'Ⰳ' => 'ⰳ', 'Ⰴ' => 'ⰴ', 'Ⰵ' => 'ⰵ', 'Ⰶ' => 'ⰶ', 'Ⰷ' => 'ⰷ', 'Ⰸ' => 'ⰸ', 'Ⰹ' => 'ⰹ', 'Ⰺ' => 'ⰺ', 'Ⰻ' => 'ⰻ', 'Ⰼ' => 'ⰼ', 'Ⰽ' => 'ⰽ', 'Ⰾ' => 'ⰾ', 'Ⰿ' => 'ⰿ', 'Ⱀ' => 'ⱀ', 'Ⱁ' => 'ⱁ', 'Ⱂ' => 'ⱂ', 'Ⱃ' => 'ⱃ', 'Ⱄ' => 'ⱄ', 'Ⱅ' => 'ⱅ', 'Ⱆ' => 'ⱆ', 'Ⱇ' => 'ⱇ', 'Ⱈ' => 'ⱈ', 'Ⱉ' => 'ⱉ', 'Ⱊ' => 'ⱊ', 'Ⱋ' => 'ⱋ', 'Ⱌ' => 'ⱌ', 'Ⱍ' => 'ⱍ', 'Ⱎ' => 'ⱎ', 'Ⱏ' => 'ⱏ', 'Ⱐ' => 'ⱐ', 'Ⱑ' => 'ⱑ', 'Ⱒ' => 'ⱒ', 'Ⱓ' => 'ⱓ', 'Ⱔ' => 'ⱔ', 'Ⱕ' => 'ⱕ', 'Ⱖ' => 'ⱖ', 'Ⱗ' => 'ⱗ', 'Ⱘ' => 'ⱘ', 'Ⱙ' => 'ⱙ', 'Ⱚ' => 'ⱚ', 'Ⱛ' => 'ⱛ', 'Ⱜ' => 'ⱜ', 'Ⱝ' => 'ⱝ', 'Ⱞ' => 'ⱞ', 'Ⱡ' => 'ⱡ', 'Ɫ' => 'ɫ', 'Ᵽ' => 'ᵽ', 'Ɽ' => 'ɽ', 'Ⱨ' => 'ⱨ', 'Ⱪ' => 'ⱪ', 'Ⱬ' => 'ⱬ', 'Ɑ' => 'ɑ', 'Ɱ' => 'ɱ', 'Ɐ' => 'ɐ', 'Ɒ' => 'ɒ', 'Ⱳ' => 'ⱳ', 'Ⱶ' => 'ⱶ', 'Ȿ' => 'ȿ', 'Ɀ' => 'ɀ', 'Ⲁ' => 'ⲁ', 'Ⲃ' => 'ⲃ', 'Ⲅ' => 'ⲅ', 'Ⲇ' => 'ⲇ', 'Ⲉ' => 'ⲉ', 'Ⲋ' => 'ⲋ', 'Ⲍ' => 'ⲍ', 'Ⲏ' => 'ⲏ', 'Ⲑ' => 'ⲑ', 'Ⲓ' => 'ⲓ', 'Ⲕ' => 'ⲕ', 'Ⲗ' => 'ⲗ', 'Ⲙ' => 'ⲙ', 'Ⲛ' => 'ⲛ', 'Ⲝ' => 'ⲝ', 'Ⲟ' => 'ⲟ', 'Ⲡ' => 'ⲡ', 'Ⲣ' => 'ⲣ', 'Ⲥ' => 'ⲥ', 'Ⲧ' => 'ⲧ', 'Ⲩ' => 'ⲩ', 'Ⲫ' => 'ⲫ', 'Ⲭ' => 'ⲭ', 'Ⲯ' => 'ⲯ', 'Ⲱ' => 'ⲱ', 'Ⲳ' => 'ⲳ', 'Ⲵ' => 'ⲵ', 'Ⲷ' => 'ⲷ', 'Ⲹ' => 'ⲹ', 'Ⲻ' => 'ⲻ', 'Ⲽ' => 'ⲽ', 'Ⲿ' => 'ⲿ', 'Ⳁ' => 'ⳁ', 'Ⳃ' => 'ⳃ', 'Ⳅ' => 'ⳅ', 'Ⳇ' => 'ⳇ', 'Ⳉ' => 'ⳉ', 'Ⳋ' => 'ⳋ', 'Ⳍ' => 'ⳍ', 'Ⳏ' => 'ⳏ', 'Ⳑ' => 'ⳑ', 'Ⳓ' => 'ⳓ', 'Ⳕ' => 'ⳕ', 'Ⳗ' => 'ⳗ', 'Ⳙ' => 'ⳙ', 'Ⳛ' => 'ⳛ', 'Ⳝ' => 'ⳝ', 'Ⳟ' => 'ⳟ', 'Ⳡ' => 'ⳡ', 'Ⳣ' => 'ⳣ', 'Ⳬ' => 'ⳬ', 'Ⳮ' => 'ⳮ', 'Ⳳ' => 'ⳳ', 'Ꙁ' => 'ꙁ', 'Ꙃ' => 'ꙃ', 'Ꙅ' => 'ꙅ', 'Ꙇ' => 'ꙇ', 'Ꙉ' => 'ꙉ', 'Ꙋ' => 'ꙋ', 'Ꙍ' => 'ꙍ', 'Ꙏ' => 'ꙏ', 'Ꙑ' => 'ꙑ', 'Ꙓ' => 'ꙓ', 'Ꙕ' => 'ꙕ', 'Ꙗ' => 'ꙗ', 'Ꙙ' => 'ꙙ', 'Ꙛ' => 'ꙛ', 'Ꙝ' => 'ꙝ', 'Ꙟ' => 'ꙟ', 'Ꙡ' => 'ꙡ', 'Ꙣ' => 'ꙣ', 'Ꙥ' => 'ꙥ', 'Ꙧ' => 'ꙧ', 'Ꙩ' => 'ꙩ', 'Ꙫ' => 'ꙫ', 'Ꙭ' => 'ꙭ', 'Ꚁ' => 'ꚁ', 'Ꚃ' => 'ꚃ', 'Ꚅ' => 'ꚅ', 'Ꚇ' => 'ꚇ', 'Ꚉ' => 'ꚉ', 'Ꚋ' => 'ꚋ', 'Ꚍ' => 'ꚍ', 'Ꚏ' => 'ꚏ', 'Ꚑ' => 'ꚑ', 'Ꚓ' => 'ꚓ', 'Ꚕ' => 'ꚕ', 'Ꚗ' => 'ꚗ', 'Ꚙ' => 'ꚙ', 'Ꚛ' => 'ꚛ', 'Ꜣ' => 'ꜣ', 'Ꜥ' => 'ꜥ', 'Ꜧ' => 'ꜧ', 'Ꜩ' => 'ꜩ', 'Ꜫ' => 'ꜫ', 'Ꜭ' => 'ꜭ', 'Ꜯ' => 'ꜯ', 'Ꜳ' => 'ꜳ', 'Ꜵ' => 'ꜵ', 'Ꜷ' => 'ꜷ', 'Ꜹ' => 'ꜹ', 'Ꜻ' => 'ꜻ', 'Ꜽ' => 'ꜽ', 'Ꜿ' => 'ꜿ', 'Ꝁ' => 'ꝁ', 'Ꝃ' => 'ꝃ', 'Ꝅ' => 'ꝅ', 'Ꝇ' => 'ꝇ', 'Ꝉ' => 'ꝉ', 'Ꝋ' => 'ꝋ', 'Ꝍ' => 'ꝍ', 'Ꝏ' => 'ꝏ', 'Ꝑ' => 'ꝑ', 'Ꝓ' => 'ꝓ', 'Ꝕ' => 'ꝕ', 'Ꝗ' => 'ꝗ', 'Ꝙ' => 'ꝙ', 'Ꝛ' => 'ꝛ', 'Ꝝ' => 'ꝝ', 'Ꝟ' => 'ꝟ', 'Ꝡ' => 'ꝡ', 'Ꝣ' => 'ꝣ', 'Ꝥ' => 'ꝥ', 'Ꝧ' => 'ꝧ', 'Ꝩ' => 'ꝩ', 'Ꝫ' => 'ꝫ', 'Ꝭ' => 'ꝭ', 'Ꝯ' => 'ꝯ', 'Ꝺ' => 'ꝺ', 'Ꝼ' => 'ꝼ', 'Ᵹ' => 'ᵹ', 'Ꝿ' => 'ꝿ', 'Ꞁ' => 'ꞁ', 'Ꞃ' => 'ꞃ', 'Ꞅ' => 'ꞅ', 'Ꞇ' => 'ꞇ', 'Ꞌ' => 'ꞌ', 'Ɥ' => 'ɥ', 'Ꞑ' => 'ꞑ', 'Ꞓ' => 'ꞓ', 'Ꞗ' => 'ꞗ', 'Ꞙ' => 'ꞙ', 'Ꞛ' => 'ꞛ', 'Ꞝ' => 'ꞝ', 'Ꞟ' => 'ꞟ', 'Ꞡ' => 'ꞡ', 'Ꞣ' => 'ꞣ', 'Ꞥ' => 'ꞥ', 'Ꞧ' => 'ꞧ', 'Ꞩ' => 'ꞩ', 'Ɦ' => 'ɦ', 'Ɜ' => 'ɜ', 'Ɡ' => 'ɡ', 'Ɬ' => 'ɬ', 'Ɪ' => 'ɪ', 'Ʞ' => 'ʞ', 'Ʇ' => 'ʇ', 'Ʝ' => 'ʝ', 'Ꭓ' => 'ꭓ', 'Ꞵ' => 'ꞵ', 'Ꞷ' => 'ꞷ', 'Ꞹ' => 'ꞹ', 'Ꞻ' => 'ꞻ', 'Ꞽ' => 'ꞽ', 'Ꞿ' => 'ꞿ', 'Ꟃ' => 'ꟃ', 'Ꞔ' => 'ꞔ', 'Ʂ' => 'ʂ', 'Ᶎ' => 'ᶎ', 'Ꟈ' => 'ꟈ', 'Ꟊ' => 'ꟊ', 'Ꟶ' => 'ꟶ', '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', '𐐀' => '𐐨', '𐐁' => '𐐩', '𐐂' => '𐐪', '𐐃' => '𐐫', '𐐄' => '𐐬', '𐐅' => '𐐭', '𐐆' => '𐐮', '𐐇' => '𐐯', '𐐈' => '𐐰', '𐐉' => '𐐱', '𐐊' => '𐐲', '𐐋' => '𐐳', '𐐌' => '𐐴', '𐐍' => '𐐵', '𐐎' => '𐐶', '𐐏' => '𐐷', '𐐐' => '𐐸', '𐐑' => '𐐹', '𐐒' => '𐐺', '𐐓' => '𐐻', '𐐔' => '𐐼', '𐐕' => '𐐽', '𐐖' => '𐐾', '𐐗' => '𐐿', '𐐘' => '𐑀', '𐐙' => '𐑁', '𐐚' => '𐑂', '𐐛' => '𐑃', '𐐜' => '𐑄', '𐐝' => '𐑅', '𐐞' => '𐑆', '𐐟' => '𐑇', '𐐠' => '𐑈', '𐐡' => '𐑉', '𐐢' => '𐑊', '𐐣' => '𐑋', '𐐤' => '𐑌', '𐐥' => '𐑍', '𐐦' => '𐑎', '𐐧' => '𐑏', '𐒰' => '𐓘', '𐒱' => '𐓙', '𐒲' => '𐓚', '𐒳' => '𐓛', '𐒴' => '𐓜', '𐒵' => '𐓝', '𐒶' => '𐓞', '𐒷' => '𐓟', '𐒸' => '𐓠', '𐒹' => '𐓡', '𐒺' => '𐓢', '𐒻' => '𐓣', '𐒼' => '𐓤', '𐒽' => '𐓥', '𐒾' => '𐓦', '𐒿' => '𐓧', '𐓀' => '𐓨', '𐓁' => '𐓩', '𐓂' => '𐓪', '𐓃' => '𐓫', '𐓄' => '𐓬', '𐓅' => '𐓭', '𐓆' => '𐓮', '𐓇' => '𐓯', '𐓈' => '𐓰', '𐓉' => '𐓱', '𐓊' => '𐓲', '𐓋' => '𐓳', '𐓌' => '𐓴', '𐓍' => '𐓵', '𐓎' => '𐓶', '𐓏' => '𐓷', '𐓐' => '𐓸', '𐓑' => '𐓹', '𐓒' => '𐓺', '𐓓' => '𐓻', '𐲀' => '𐳀', '𐲁' => '𐳁', '𐲂' => '𐳂', '𐲃' => '𐳃', '𐲄' => '𐳄', '𐲅' => '𐳅', '𐲆' => '𐳆', '𐲇' => '𐳇', '𐲈' => '𐳈', '𐲉' => '𐳉', '𐲊' => '𐳊', '𐲋' => '𐳋', '𐲌' => '𐳌', '𐲍' => '𐳍', '𐲎' => '𐳎', '𐲏' => '𐳏', '𐲐' => '𐳐', '𐲑' => '𐳑', '𐲒' => '𐳒', '𐲓' => '𐳓', '𐲔' => '𐳔', '𐲕' => '𐳕', '𐲖' => '𐳖', '𐲗' => '𐳗', '𐲘' => '𐳘', '𐲙' => '𐳙', '𐲚' => '𐳚', '𐲛' => '𐳛', '𐲜' => '𐳜', '𐲝' => '𐳝', '𐲞' => '𐳞', '𐲟' => '𐳟', '𐲠' => '𐳠', '𐲡' => '𐳡', '𐲢' => '𐳢', '𐲣' => '𐳣', '𐲤' => '𐳤', '𐲥' => '𐳥', '𐲦' => '𐳦', '𐲧' => '𐳧', '𐲨' => '𐳨', '𐲩' => '𐳩', '𐲪' => '𐳪', '𐲫' => '𐳫', '𐲬' => '𐳬', '𐲭' => '𐳭', '𐲮' => '𐳮', '𐲯' => '𐳯', '𐲰' => '𐳰', '𐲱' => '𐳱', '𐲲' => '𐳲', '𑢠' => '𑣀', '𑢡' => '𑣁', '𑢢' => '𑣂', '𑢣' => '𑣃', '𑢤' => '𑣄', '𑢥' => '𑣅', '𑢦' => '𑣆', '𑢧' => '𑣇', '𑢨' => '𑣈', '𑢩' => '𑣉', '𑢪' => '𑣊', '𑢫' => '𑣋', '𑢬' => '𑣌', '𑢭' => '𑣍', '𑢮' => '𑣎', '𑢯' => '𑣏', '𑢰' => '𑣐', '𑢱' => '𑣑', '𑢲' => '𑣒', '𑢳' => '𑣓', '𑢴' => '𑣔', '𑢵' => '𑣕', '𑢶' => '𑣖', '𑢷' => '𑣗', '𑢸' => '𑣘', '𑢹' => '𑣙', '𑢺' => '𑣚', '𑢻' => '𑣛', '𑢼' => '𑣜', '𑢽' => '𑣝', '𑢾' => '𑣞', '𑢿' => '𑣟', '𖹀' => '𖹠', '𖹁' => '𖹡', '𖹂' => '𖹢', '𖹃' => '𖹣', '𖹄' => '𖹤', '𖹅' => '𖹥', '𖹆' => '𖹦', '𖹇' => '𖹧', '𖹈' => '𖹨', '𖹉' => '𖹩', '𖹊' => '𖹪', '𖹋' => '𖹫', '𖹌' => '𖹬', '𖹍' => '𖹭', '𖹎' => '𖹮', '𖹏' => '𖹯', '𖹐' => '𖹰', '𖹑' => '𖹱', '𖹒' => '𖹲', '𖹓' => '𖹳', '𖹔' => '𖹴', '𖹕' => '𖹵', '𖹖' => '𖹶', '𖹗' => '𖹷', '𖹘' => '𖹸', '𖹙' => '𖹹', '𖹚' => '𖹺', '𖹛' => '𖹻', '𖹜' => '𖹼', '𖹝' => '𖹽', '𖹞' => '𖹾', '𖹟' => '𖹿', '𞤀' => '𞤢', '𞤁' => '𞤣', '𞤂' => '𞤤', '𞤃' => '𞤥', '𞤄' => '𞤦', '𞤅' => '𞤧', '𞤆' => '𞤨', '𞤇' => '𞤩', '𞤈' => '𞤪', '𞤉' => '𞤫', '𞤊' => '𞤬', '𞤋' => '𞤭', '𞤌' => '𞤮', '𞤍' => '𞤯', '𞤎' => '𞤰', '𞤏' => '𞤱', '𞤐' => '𞤲', '𞤑' => '𞤳', '𞤒' => '𞤴', '𞤓' => '𞤵', '𞤔' => '𞤶', '𞤕' => '𞤷', '𞤖' => '𞤸', '𞤗' => '𞤹', '𞤘' => '𞤺', '𞤙' => '𞤻', '𞤚' => '𞤼', '𞤛' => '𞤽', '𞤜' => '𞤾', '𞤝' => '𞤿', '𞤞' => '𞥀', '𞤟' => '𞥁', '𞤠' => '𞥂', '𞤡' => '𞥃', ); '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', 'ij' => 'IJ', 'ĵ' => 'Ĵ', 'ķ' => 'Ķ', 'ĺ' => 'Ĺ', 'ļ' => 'Ļ', 'ľ' => 'Ľ', 'ŀ' => 'Ŀ', 'ł' => 'Ł', 'ń' => 'Ń', 'ņ' => 'Ņ', 'ň' => 'Ň', 'ŋ' => 'Ŋ', 'ō' => 'Ō', 'ŏ' => 'Ŏ', 'ő' => 'Ő', 'œ' => 'Œ', 'ŕ' => 'Ŕ', 'ŗ' => 'Ŗ', 'ř' => 'Ř', 'ś' => 'Ś', 'ŝ' => 'Ŝ', 'ş' => 'Ş', 'š' => 'Š', 'ţ' => 'Ţ', 'ť' => 'Ť', 'ŧ' => 'Ŧ', 'ũ' => 'Ũ', 'ū' => 'Ū', 'ŭ' => 'Ŭ', 'ů' => 'Ů', 'ű' => 'Ű', 'ų' => 'Ų', 'ŵ' => 'Ŵ', 'ŷ' => 'Ŷ', 'ź' => 'Ź', 'ż' => 'Ż', 'ž' => 'Ž', 'ſ' => 'S', 'ƀ' => 'Ƀ', 'ƃ' => 'Ƃ', 'ƅ' => 'Ƅ', 'ƈ' => 'Ƈ', 'ƌ' => 'Ƌ', 'ƒ' => 'Ƒ', 'ƕ' => 'Ƕ', 'ƙ' => 'Ƙ', 'ƚ' => 'Ƚ', 'ƞ' => 'Ƞ', 'ơ' => 'Ơ', 'ƣ' => 'Ƣ', 'ƥ' => 'Ƥ', 'ƨ' => 'Ƨ', 'ƭ' => 'Ƭ', 'ư' => 'Ư', 'ƴ' => 'Ƴ', 'ƶ' => 'Ƶ', 'ƹ' => 'Ƹ', 'ƽ' => 'Ƽ', 'ƿ' => 'Ƿ', 'Dž' => 'DŽ', 'dž' => 'DŽ', 'Lj' => 'LJ', 'lj' => 'LJ', 'Nj' => 'NJ', 'nj' => 'NJ', 'ǎ' => 'Ǎ', 'ǐ' => 'Ǐ', 'ǒ' => 'Ǒ', 'ǔ' => 'Ǔ', 'ǖ' => 'Ǖ', 'ǘ' => 'Ǘ', 'ǚ' => 'Ǚ', 'ǜ' => 'Ǜ', 'ǝ' => 'Ǝ', 'ǟ' => 'Ǟ', 'ǡ' => 'Ǡ', 'ǣ' => 'Ǣ', 'ǥ' => 'Ǥ', 'ǧ' => 'Ǧ', 'ǩ' => 'Ǩ', 'ǫ' => 'Ǫ', 'ǭ' => 'Ǭ', 'ǯ' => 'Ǯ', 'Dz' => 'DZ', 'dz' => 'DZ', 'ǵ' => 'Ǵ', 'ǹ' => 'Ǹ', 'ǻ' => 'Ǻ', 'ǽ' => 'Ǽ', 'ǿ' => 'Ǿ', 'ȁ' => 'Ȁ', 'ȃ' => 'Ȃ', 'ȅ' => 'Ȅ', 'ȇ' => 'Ȇ', 'ȉ' => 'Ȉ', 'ȋ' => 'Ȋ', 'ȍ' => 'Ȍ', 'ȏ' => 'Ȏ', 'ȑ' => 'Ȑ', 'ȓ' => 'Ȓ', 'ȕ' => 'Ȕ', 'ȗ' => 'Ȗ', 'ș' => 'Ș', 'ț' => 'Ț', 'ȝ' => 'Ȝ', 'ȟ' => 'Ȟ', 'ȣ' => 'Ȣ', 'ȥ' => 'Ȥ', 'ȧ' => 'Ȧ', 'ȩ' => 'Ȩ', 'ȫ' => 'Ȫ', 'ȭ' => 'Ȭ', 'ȯ' => 'Ȯ', 'ȱ' => 'Ȱ', 'ȳ' => 'Ȳ', 'ȼ' => 'Ȼ', 'ȿ' => 'Ȿ', 'ɀ' => 'Ɀ', 'ɂ' => 'Ɂ', 'ɇ' => 'Ɇ', 'ɉ' => 'Ɉ', 'ɋ' => 'Ɋ', 'ɍ' => 'Ɍ', 'ɏ' => 'Ɏ', 'ɐ' => 'Ɐ', 'ɑ' => 'Ɑ', 'ɒ' => 'Ɒ', 'ɓ' => 'Ɓ', 'ɔ' => 'Ɔ', 'ɖ' => 'Ɖ', 'ɗ' => 'Ɗ', 'ə' => 'Ə', 'ɛ' => 'Ɛ', 'ɜ' => 'Ɜ', 'ɠ' => 'Ɠ', 'ɡ' => 'Ɡ', 'ɣ' => 'Ɣ', 'ɥ' => 'Ɥ', 'ɦ' => 'Ɦ', 'ɨ' => 'Ɨ', 'ɩ' => 'Ɩ', 'ɪ' => 'Ɪ', 'ɫ' => 'Ɫ', 'ɬ' => 'Ɬ', 'ɯ' => 'Ɯ', 'ɱ' => 'Ɱ', 'ɲ' => 'Ɲ', 'ɵ' => 'Ɵ', 'ɽ' => 'Ɽ', 'ʀ' => 'Ʀ', 'ʂ' => 'Ʂ', 'ʃ' => 'Ʃ', 'ʇ' => 'Ʇ', 'ʈ' => 'Ʈ', 'ʉ' => 'Ʉ', 'ʊ' => 'Ʊ', 'ʋ' => 'Ʋ', 'ʌ' => 'Ʌ', 'ʒ' => 'Ʒ', 'ʝ' => 'Ʝ', 'ʞ' => 'Ʞ', 'ͅ' => 'Ι', 'ͱ' => 'Ͱ', 'ͳ' => 'Ͳ', 'ͷ' => 'Ͷ', 'ͻ' => 'Ͻ', 'ͼ' => 'Ͼ', 'ͽ' => 'Ͽ', 'ά' => 'Ά', 'έ' => 'Έ', 'ή' => 'Ή', 'ί' => 'Ί', 'α' => 'Α', 'β' => 'Β', 'γ' => 'Γ', 'δ' => 'Δ', 'ε' => 'Ε', 'ζ' => 'Ζ', 'η' => 'Η', 'θ' => 'Θ', 'ι' => 'Ι', 'κ' => 'Κ', 'λ' => 'Λ', 'μ' => 'Μ', 'ν' => 'Ν', 'ξ' => 'Ξ', 'ο' => 'Ο', 'π' => 'Π', 'ρ' => 'Ρ', 'ς' => 'Σ', 'σ' => 'Σ', 'τ' => 'Τ', 'υ' => 'Υ', 'φ' => 'Φ', 'χ' => 'Χ', 'ψ' => 'Ψ', 'ω' => 'Ω', 'ϊ' => 'Ϊ', 'ϋ' => 'Ϋ', 'ό' => 'Ό', 'ύ' => 'Ύ', 'ώ' => 'Ώ', 'ϐ' => 'Β', 'ϑ' => 'Θ', 'ϕ' => 'Φ', 'ϖ' => 'Π', 'ϗ' => 'Ϗ', 'ϙ' => 'Ϙ', 'ϛ' => 'Ϛ', 'ϝ' => 'Ϝ', 'ϟ' => 'Ϟ', 'ϡ' => 'Ϡ', 'ϣ' => 'Ϣ', 'ϥ' => 'Ϥ', 'ϧ' => 'Ϧ', 'ϩ' => 'Ϩ', 'ϫ' => 'Ϫ', 'ϭ' => 'Ϭ', 'ϯ' => 'Ϯ', 'ϰ' => 'Κ', 'ϱ' => 'Ρ', 'ϲ' => 'Ϲ', 'ϳ' => 'Ϳ', 'ϵ' => 'Ε', 'ϸ' => 'Ϸ', 'ϻ' => 'Ϻ', 'а' => 'А', 'б' => 'Б', 'в' => 'В', 'г' => 'Г', 'д' => 'Д', 'е' => 'Е', 'ж' => 'Ж', 'з' => 'З', 'и' => 'И', 'й' => 'Й', 'к' => 'К', 'л' => 'Л', 'м' => 'М', 'н' => 'Н', 'о' => 'О', 'п' => 'П', 'р' => 'Р', 'с' => 'С', 'т' => 'Т', 'у' => 'У', 'ф' => 'Ф', 'х' => 'Х', 'ц' => 'Ц', 'ч' => 'Ч', 'ш' => 'Ш', 'щ' => 'Щ', 'ъ' => 'Ъ', 'ы' => 'Ы', 'ь' => 'Ь', 'э' => 'Э', 'ю' => 'Ю', 'я' => 'Я', 'ѐ' => 'Ѐ', 'ё' => 'Ё', 'ђ' => 'Ђ', 'ѓ' => 'Ѓ', 'є' => 'Є', 'ѕ' => 'Ѕ', 'і' => 'І', 'ї' => 'Ї', 'ј' => 'Ј', 'љ' => 'Љ', 'њ' => 'Њ', 'ћ' => 'Ћ', 'ќ' => 'Ќ', 'ѝ' => 'Ѝ', 'ў' => 'Ў', 'џ' => 'Џ', 'ѡ' => 'Ѡ', 'ѣ' => 'Ѣ', 'ѥ' => 'Ѥ', 'ѧ' => 'Ѧ', 'ѩ' => 'Ѩ', 'ѫ' => 'Ѫ', 'ѭ' => 'Ѭ', 'ѯ' => 'Ѯ', 'ѱ' => 'Ѱ', 'ѳ' => 'Ѳ', 'ѵ' => 'Ѵ', 'ѷ' => 'Ѷ', 'ѹ' => 'Ѹ', 'ѻ' => 'Ѻ', 'ѽ' => 'Ѽ', 'ѿ' => 'Ѿ', 'ҁ' => 'Ҁ', 'ҋ' => 'Ҋ', 'ҍ' => 'Ҍ', 'ҏ' => 'Ҏ', 'ґ' => 'Ґ', 'ғ' => 'Ғ', 'ҕ' => 'Ҕ', 'җ' => 'Җ', 'ҙ' => 'Ҙ', 'қ' => 'Қ', 'ҝ' => 'Ҝ', 'ҟ' => 'Ҟ', 'ҡ' => 'Ҡ', 'ң' => 'Ң', 'ҥ' => 'Ҥ', 'ҧ' => 'Ҧ', 'ҩ' => 'Ҩ', 'ҫ' => 'Ҫ', 'ҭ' => 'Ҭ', 'ү' => 'Ү', 'ұ' => 'Ұ', 'ҳ' => 'Ҳ', 'ҵ' => 'Ҵ', 'ҷ' => 'Ҷ', 'ҹ' => 'Ҹ', 'һ' => 'Һ', 'ҽ' => 'Ҽ', 'ҿ' => 'Ҿ', 'ӂ' => 'Ӂ', 'ӄ' => 'Ӄ', 'ӆ' => 'Ӆ', 'ӈ' => 'Ӈ', 'ӊ' => 'Ӊ', 'ӌ' => 'Ӌ', 'ӎ' => 'Ӎ', 'ӏ' => 'Ӏ', 'ӑ' => 'Ӑ', 'ӓ' => 'Ӓ', 'ӕ' => 'Ӕ', 'ӗ' => 'Ӗ', 'ә' => 'Ә', 'ӛ' => 'Ӛ', 'ӝ' => 'Ӝ', 'ӟ' => 'Ӟ', 'ӡ' => 'Ӡ', 'ӣ' => 'Ӣ', 'ӥ' => 'Ӥ', 'ӧ' => 'Ӧ', 'ө' => 'Ө', 'ӫ' => 'Ӫ', 'ӭ' => 'Ӭ', 'ӯ' => 'Ӯ', 'ӱ' => 'Ӱ', 'ӳ' => 'Ӳ', 'ӵ' => 'Ӵ', 'ӷ' => 'Ӷ', 'ӹ' => 'Ӹ', 'ӻ' => 'Ӻ', 'ӽ' => 'Ӽ', 'ӿ' => 'Ӿ', 'ԁ' => 'Ԁ', 'ԃ' => 'Ԃ', 'ԅ' => 'Ԅ', 'ԇ' => 'Ԇ', 'ԉ' => 'Ԉ', 'ԋ' => 'Ԋ', 'ԍ' => 'Ԍ', 'ԏ' => 'Ԏ', 'ԑ' => 'Ԑ', 'ԓ' => 'Ԓ', 'ԕ' => 'Ԕ', 'ԗ' => 'Ԗ', 'ԙ' => 'Ԙ', 'ԛ' => 'Ԛ', 'ԝ' => 'Ԝ', 'ԟ' => 'Ԟ', 'ԡ' => 'Ԡ', 'ԣ' => 'Ԣ', 'ԥ' => 'Ԥ', 'ԧ' => 'Ԧ', 'ԩ' => 'Ԩ', 'ԫ' => 'Ԫ', 'ԭ' => 'Ԭ', 'ԯ' => 'Ԯ', 'ա' => 'Ա', 'բ' => 'Բ', 'գ' => 'Գ', 'դ' => 'Դ', 'ե' => 'Ե', 'զ' => 'Զ', 'է' => 'Է', 'ը' => 'Ը', 'թ' => 'Թ', 'ժ' => 'Ժ', 'ի' => 'Ի', 'լ' => 'Լ', 'խ' => 'Խ', 'ծ' => 'Ծ', 'կ' => 'Կ', 'հ' => 'Հ', 'ձ' => 'Ձ', 'ղ' => 'Ղ', 'ճ' => 'Ճ', 'մ' => 'Մ', 'յ' => 'Յ', 'ն' => 'Ն', 'շ' => 'Շ', 'ո' => 'Ո', 'չ' => 'Չ', 'պ' => 'Պ', 'ջ' => 'Ջ', 'ռ' => 'Ռ', 'ս' => 'Ս', 'վ' => 'Վ', 'տ' => 'Տ', 'ր' => 'Ր', 'ց' => 'Ց', 'ւ' => 'Ւ', 'փ' => 'Փ', 'ք' => 'Ք', 'օ' => 'Օ', 'ֆ' => 'Ֆ', 'ა' => 'Ა', 'ბ' => 'Ბ', 'გ' => 'Გ', 'დ' => 'Დ', 'ე' => 'Ე', 'ვ' => 'Ვ', 'ზ' => 'Ზ', 'თ' => 'Თ', 'ი' => 'Ი', 'კ' => 'Კ', 'ლ' => 'Ლ', 'მ' => 'Მ', 'ნ' => 'Ნ', 'ო' => 'Ო', 'პ' => 'Პ', 'ჟ' => 'Ჟ', 'რ' => 'Რ', 'ს' => 'Ს', 'ტ' => 'Ტ', 'უ' => 'Უ', 'ფ' => 'Ფ', 'ქ' => 'Ქ', 'ღ' => 'Ღ', 'ყ' => 'Ყ', 'შ' => 'Შ', 'ჩ' => 'Ჩ', 'ც' => 'Ც', 'ძ' => 'Ძ', 'წ' => 'Წ', 'ჭ' => 'Ჭ', 'ხ' => 'Ხ', 'ჯ' => 'Ჯ', 'ჰ' => 'Ჰ', 'ჱ' => 'Ჱ', 'ჲ' => 'Ჲ', 'ჳ' => 'Ჳ', 'ჴ' => 'Ჴ', 'ჵ' => 'Ჵ', 'ჶ' => 'Ჶ', 'ჷ' => 'Ჷ', 'ჸ' => 'Ჸ', 'ჹ' => 'Ჹ', 'ჺ' => 'Ჺ', 'ჽ' => 'Ჽ', 'ჾ' => 'Ჾ', 'ჿ' => 'Ჿ', 'ᏸ' => 'Ᏸ', 'ᏹ' => 'Ᏹ', 'ᏺ' => 'Ᏺ', 'ᏻ' => 'Ᏻ', 'ᏼ' => 'Ᏼ', 'ᏽ' => 'Ᏽ', 'ᲀ' => 'В', 'ᲁ' => 'Д', 'ᲂ' => 'О', 'ᲃ' => 'С', 'ᲄ' => 'Т', 'ᲅ' => 'Т', 'ᲆ' => 'Ъ', 'ᲇ' => 'Ѣ', 'ᲈ' => 'Ꙋ', 'ᵹ' => 'Ᵹ', 'ᵽ' => 'Ᵽ', 'ᶎ' => 'Ᶎ', 'ḁ' => 'Ḁ', 'ḃ' => 'Ḃ', 'ḅ' => 'Ḅ', 'ḇ' => 'Ḇ', 'ḉ' => 'Ḉ', 'ḋ' => 'Ḋ', 'ḍ' => 'Ḍ', 'ḏ' => 'Ḏ', 'ḑ' => 'Ḑ', 'ḓ' => 'Ḓ', 'ḕ' => 'Ḕ', 'ḗ' => 'Ḗ', 'ḙ' => 'Ḙ', 'ḛ' => 'Ḛ', 'ḝ' => 'Ḝ', 'ḟ' => 'Ḟ', 'ḡ' => 'Ḡ', 'ḣ' => 'Ḣ', 'ḥ' => 'Ḥ', 'ḧ' => 'Ḧ', 'ḩ' => 'Ḩ', 'ḫ' => 'Ḫ', 'ḭ' => 'Ḭ', 'ḯ' => 'Ḯ', 'ḱ' => 'Ḱ', 'ḳ' => 'Ḳ', 'ḵ' => 'Ḵ', 'ḷ' => 'Ḷ', 'ḹ' => 'Ḹ', 'ḻ' => 'Ḻ', 'ḽ' => 'Ḽ', 'ḿ' => 'Ḿ', 'ṁ' => 'Ṁ', 'ṃ' => 'Ṃ', 'ṅ' => 'Ṅ', 'ṇ' => 'Ṇ', 'ṉ' => 'Ṉ', 'ṋ' => 'Ṋ', 'ṍ' => 'Ṍ', 'ṏ' => 'Ṏ', 'ṑ' => 'Ṑ', 'ṓ' => 'Ṓ', 'ṕ' => 'Ṕ', 'ṗ' => 'Ṗ', 'ṙ' => 'Ṙ', 'ṛ' => 'Ṛ', 'ṝ' => 'Ṝ', 'ṟ' => 'Ṟ', 'ṡ' => 'Ṡ', 'ṣ' => 'Ṣ', 'ṥ' => 'Ṥ', 'ṧ' => 'Ṧ', 'ṩ' => 'Ṩ', 'ṫ' => 'Ṫ', 'ṭ' => 'Ṭ', 'ṯ' => 'Ṯ', 'ṱ' => 'Ṱ', 'ṳ' => 'Ṳ', 'ṵ' => 'Ṵ', 'ṷ' => 'Ṷ', 'ṹ' => 'Ṹ', 'ṻ' => 'Ṻ', 'ṽ' => 'Ṽ', 'ṿ' => 'Ṿ', 'ẁ' => 'Ẁ', 'ẃ' => 'Ẃ', 'ẅ' => 'Ẅ', 'ẇ' => 'Ẇ', 'ẉ' => 'Ẉ', 'ẋ' => 'Ẋ', 'ẍ' => 'Ẍ', 'ẏ' => 'Ẏ', 'ẑ' => 'Ẑ', 'ẓ' => 'Ẓ', 'ẕ' => 'Ẕ', 'ẛ' => 'Ṡ', 'ạ' => 'Ạ', 'ả' => 'Ả', 'ấ' => 'Ấ', 'ầ' => 'Ầ', 'ẩ' => 'Ẩ', 'ẫ' => 'Ẫ', 'ậ' => 'Ậ', 'ắ' => 'Ắ', 'ằ' => 'Ằ', 'ẳ' => 'Ẳ', 'ẵ' => 'Ẵ', 'ặ' => 'Ặ', 'ẹ' => 'Ẹ', 'ẻ' => 'Ẻ', 'ẽ' => 'Ẽ', 'ế' => 'Ế', 'ề' => 'Ề', 'ể' => 'Ể', 'ễ' => 'Ễ', 'ệ' => 'Ệ', 'ỉ' => 'Ỉ', 'ị' => 'Ị', 'ọ' => 'Ọ', 'ỏ' => 'Ỏ', 'ố' => 'Ố', 'ồ' => 'Ồ', 'ổ' => 'Ổ', 'ỗ' => 'Ỗ', 'ộ' => 'Ộ', 'ớ' => 'Ớ', 'ờ' => 'Ờ', 'ở' => 'Ở', 'ỡ' => 'Ỡ', 'ợ' => 'Ợ', 'ụ' => 'Ụ', 'ủ' => 'Ủ', 'ứ' => 'Ứ', 'ừ' => 'Ừ', 'ử' => 'Ử', 'ữ' => 'Ữ', 'ự' => 'Ự', 'ỳ' => 'Ỳ', 'ỵ' => 'Ỵ', 'ỷ' => 'Ỷ', 'ỹ' => 'Ỹ', 'ỻ' => 'Ỻ', 'ỽ' => 'Ỽ', 'ỿ' => 'Ỿ', 'ἀ' => 'Ἀ', 'ἁ' => 'Ἁ', 'ἂ' => 'Ἂ', 'ἃ' => 'Ἃ', 'ἄ' => 'Ἄ', 'ἅ' => 'Ἅ', 'ἆ' => 'Ἆ', 'ἇ' => 'Ἇ', 'ἐ' => 'Ἐ', 'ἑ' => 'Ἑ', 'ἒ' => 'Ἒ', 'ἓ' => 'Ἓ', 'ἔ' => 'Ἔ', 'ἕ' => 'Ἕ', 'ἠ' => 'Ἠ', 'ἡ' => 'Ἡ', 'ἢ' => 'Ἢ', 'ἣ' => 'Ἣ', 'ἤ' => 'Ἤ', 'ἥ' => 'Ἥ', 'ἦ' => 'Ἦ', 'ἧ' => 'Ἧ', 'ἰ' => 'Ἰ', 'ἱ' => 'Ἱ', 'ἲ' => 'Ἲ', 'ἳ' => 'Ἳ', 'ἴ' => 'Ἴ', 'ἵ' => 'Ἵ', 'ἶ' => 'Ἶ', 'ἷ' => 'Ἷ', 'ὀ' => 'Ὀ', 'ὁ' => 'Ὁ', 'ὂ' => 'Ὂ', 'ὃ' => 'Ὃ', 'ὄ' => 'Ὄ', 'ὅ' => 'Ὅ', 'ὑ' => 'Ὑ', 'ὓ' => 'Ὓ', 'ὕ' => 'Ὕ', 'ὗ' => 'Ὗ', 'ὠ' => 'Ὠ', 'ὡ' => 'Ὡ', 'ὢ' => 'Ὢ', 'ὣ' => 'Ὣ', 'ὤ' => 'Ὤ', 'ὥ' => 'Ὥ', 'ὦ' => 'Ὦ', 'ὧ' => 'Ὧ', 'ὰ' => 'Ὰ', 'ά' => 'Ά', 'ὲ' => 'Ὲ', 'έ' => 'Έ', 'ὴ' => 'Ὴ', 'ή' => 'Ή', 'ὶ' => 'Ὶ', 'ί' => 'Ί', 'ὸ' => 'Ὸ', 'ό' => 'Ό', 'ὺ' => 'Ὺ', 'ύ' => 'Ύ', 'ὼ' => 'Ὼ', 'ώ' => 'Ώ', 'ᾀ' => 'ᾈ', 'ᾁ' => 'ᾉ', 'ᾂ' => 'ᾊ', 'ᾃ' => 'ᾋ', 'ᾄ' => 'ᾌ', 'ᾅ' => 'ᾍ', 'ᾆ' => 'ᾎ', 'ᾇ' => 'ᾏ', 'ᾐ' => 'ᾘ', 'ᾑ' => 'ᾙ', 'ᾒ' => 'ᾚ', 'ᾓ' => 'ᾛ', 'ᾔ' => 'ᾜ', 'ᾕ' => 'ᾝ', 'ᾖ' => 'ᾞ', 'ᾗ' => 'ᾟ', 'ᾠ' => 'ᾨ', 'ᾡ' => 'ᾩ', 'ᾢ' => 'ᾪ', 'ᾣ' => 'ᾫ', 'ᾤ' => 'ᾬ', 'ᾥ' => 'ᾭ', 'ᾦ' => 'ᾮ', 'ᾧ' => 'ᾯ', 'ᾰ' => 'Ᾰ', 'ᾱ' => 'Ᾱ', 'ᾳ' => 'ᾼ', 'ι' => 'Ι', 'ῃ' => 'ῌ', 'ῐ' => 'Ῐ', 'ῑ' => 'Ῑ', 'ῠ' => 'Ῠ', 'ῡ' => 'Ῡ', 'ῥ' => 'Ῥ', 'ῳ' => 'ῼ', 'ⅎ' => 'Ⅎ', 'ⅰ' => 'Ⅰ', 'ⅱ' => 'Ⅱ', 'ⅲ' => 'Ⅲ', 'ⅳ' => 'Ⅳ', 'ⅴ' => 'Ⅴ', 'ⅵ' => 'Ⅵ', 'ⅶ' => 'Ⅶ', 'ⅷ' => 'Ⅷ', 'ⅸ' => 'Ⅸ', 'ⅹ' => 'Ⅹ', 'ⅺ' => 'Ⅺ', 'ⅻ' => 'Ⅻ', 'ⅼ' => 'Ⅼ', 'ⅽ' => 'Ⅽ', 'ⅾ' => 'Ⅾ', 'ⅿ' => 'Ⅿ', 'ↄ' => 'Ↄ', 'ⓐ' => 'Ⓐ', 'ⓑ' => 'Ⓑ', 'ⓒ' => 'Ⓒ', 'ⓓ' => 'Ⓓ', 'ⓔ' => 'Ⓔ', 'ⓕ' => 'Ⓕ', 'ⓖ' => 'Ⓖ', 'ⓗ' => 'Ⓗ', 'ⓘ' => 'Ⓘ', 'ⓙ' => 'Ⓙ', 'ⓚ' => 'Ⓚ', 'ⓛ' => 'Ⓛ', 'ⓜ' => 'Ⓜ', 'ⓝ' => 'Ⓝ', 'ⓞ' => 'Ⓞ', 'ⓟ' => 'Ⓟ', 'ⓠ' => 'Ⓠ', 'ⓡ' => 'Ⓡ', 'ⓢ' => 'Ⓢ', 'ⓣ' => 'Ⓣ', 'ⓤ' => 'Ⓤ', 'ⓥ' => 'Ⓥ', 'ⓦ' => 'Ⓦ', 'ⓧ' => 'Ⓧ', 'ⓨ' => 'Ⓨ', 'ⓩ' => 'Ⓩ', 'ⰰ' => 'Ⰰ', 'ⰱ' => 'Ⰱ', 'ⰲ' => 'Ⰲ', 'ⰳ' => 'Ⰳ', 'ⰴ' => 'Ⰴ', 'ⰵ' => 'Ⰵ', 'ⰶ' => 'Ⰶ', 'ⰷ' => 'Ⰷ', 'ⰸ' => 'Ⰸ', 'ⰹ' => 'Ⰹ', 'ⰺ' => 'Ⰺ', 'ⰻ' => 'Ⰻ', 'ⰼ' => 'Ⰼ', 'ⰽ' => 'Ⰽ', 'ⰾ' => 'Ⰾ', 'ⰿ' => 'Ⰿ', 'ⱀ' => 'Ⱀ', 'ⱁ' => 'Ⱁ', 'ⱂ' => 'Ⱂ', 'ⱃ' => 'Ⱃ', 'ⱄ' => 'Ⱄ', 'ⱅ' => 'Ⱅ', 'ⱆ' => 'Ⱆ', 'ⱇ' => 'Ⱇ', 'ⱈ' => 'Ⱈ', 'ⱉ' => 'Ⱉ', 'ⱊ' => 'Ⱊ', 'ⱋ' => 'Ⱋ', 'ⱌ' => 'Ⱌ', 'ⱍ' => 'Ⱍ', 'ⱎ' => 'Ⱎ', 'ⱏ' => 'Ⱏ', 'ⱐ' => 'Ⱐ', 'ⱑ' => 'Ⱑ', 'ⱒ' => 'Ⱒ', 'ⱓ' => 'Ⱓ', 'ⱔ' => 'Ⱔ', 'ⱕ' => 'Ⱕ', 'ⱖ' => 'Ⱖ', 'ⱗ' => 'Ⱗ', 'ⱘ' => 'Ⱘ', 'ⱙ' => 'Ⱙ', 'ⱚ' => 'Ⱚ', 'ⱛ' => 'Ⱛ', 'ⱜ' => 'Ⱜ', 'ⱝ' => 'Ⱝ', 'ⱞ' => 'Ⱞ', 'ⱡ' => 'Ⱡ', 'ⱥ' => 'Ⱥ', 'ⱦ' => 'Ⱦ', 'ⱨ' => 'Ⱨ', 'ⱪ' => 'Ⱪ', 'ⱬ' => 'Ⱬ', 'ⱳ' => 'Ⱳ', 'ⱶ' => 'Ⱶ', 'ⲁ' => 'Ⲁ', 'ⲃ' => 'Ⲃ', 'ⲅ' => 'Ⲅ', 'ⲇ' => 'Ⲇ', 'ⲉ' => 'Ⲉ', 'ⲋ' => 'Ⲋ', 'ⲍ' => 'Ⲍ', 'ⲏ' => 'Ⲏ', 'ⲑ' => 'Ⲑ', 'ⲓ' => 'Ⲓ', 'ⲕ' => 'Ⲕ', 'ⲗ' => 'Ⲗ', 'ⲙ' => 'Ⲙ', 'ⲛ' => 'Ⲛ', 'ⲝ' => 'Ⲝ', 'ⲟ' => 'Ⲟ', 'ⲡ' => 'Ⲡ', 'ⲣ' => 'Ⲣ', 'ⲥ' => 'Ⲥ', 'ⲧ' => 'Ⲧ', 'ⲩ' => 'Ⲩ', 'ⲫ' => 'Ⲫ', 'ⲭ' => 'Ⲭ', 'ⲯ' => 'Ⲯ', 'ⲱ' => 'Ⲱ', 'ⲳ' => 'Ⲳ', 'ⲵ' => 'Ⲵ', 'ⲷ' => 'Ⲷ', 'ⲹ' => 'Ⲹ', 'ⲻ' => 'Ⲻ', 'ⲽ' => 'Ⲽ', 'ⲿ' => 'Ⲿ', 'ⳁ' => 'Ⳁ', 'ⳃ' => 'Ⳃ', 'ⳅ' => 'Ⳅ', 'ⳇ' => 'Ⳇ', 'ⳉ' => 'Ⳉ', 'ⳋ' => 'Ⳋ', 'ⳍ' => 'Ⳍ', 'ⳏ' => 'Ⳏ', 'ⳑ' => 'Ⳑ', 'ⳓ' => 'Ⳓ', 'ⳕ' => 'Ⳕ', 'ⳗ' => 'Ⳗ', 'ⳙ' => 'Ⳙ', 'ⳛ' => 'Ⳛ', 'ⳝ' => 'Ⳝ', 'ⳟ' => 'Ⳟ', 'ⳡ' => 'Ⳡ', 'ⳣ' => 'Ⳣ', 'ⳬ' => 'Ⳬ', 'ⳮ' => 'Ⳮ', 'ⳳ' => 'Ⳳ', 'ⴀ' => 'Ⴀ', 'ⴁ' => 'Ⴁ', 'ⴂ' => 'Ⴂ', 'ⴃ' => 'Ⴃ', 'ⴄ' => 'Ⴄ', 'ⴅ' => 'Ⴅ', 'ⴆ' => 'Ⴆ', 'ⴇ' => 'Ⴇ', 'ⴈ' => 'Ⴈ', 'ⴉ' => 'Ⴉ', 'ⴊ' => 'Ⴊ', 'ⴋ' => 'Ⴋ', 'ⴌ' => 'Ⴌ', 'ⴍ' => 'Ⴍ', 'ⴎ' => 'Ⴎ', 'ⴏ' => 'Ⴏ', 'ⴐ' => 'Ⴐ', 'ⴑ' => 'Ⴑ', 'ⴒ' => 'Ⴒ', 'ⴓ' => 'Ⴓ', 'ⴔ' => 'Ⴔ', 'ⴕ' => 'Ⴕ', 'ⴖ' => 'Ⴖ', 'ⴗ' => 'Ⴗ', 'ⴘ' => 'Ⴘ', 'ⴙ' => 'Ⴙ', 'ⴚ' => 'Ⴚ', 'ⴛ' => 'Ⴛ', 'ⴜ' => 'Ⴜ', 'ⴝ' => 'Ⴝ', 'ⴞ' => 'Ⴞ', 'ⴟ' => 'Ⴟ', 'ⴠ' => 'Ⴠ', 'ⴡ' => 'Ⴡ', 'ⴢ' => 'Ⴢ', 'ⴣ' => 'Ⴣ', 'ⴤ' => 'Ⴤ', 'ⴥ' => 'Ⴥ', 'ⴧ' => 'Ⴧ', 'ⴭ' => 'Ⴭ', 'ꙁ' => 'Ꙁ', 'ꙃ' => 'Ꙃ', 'ꙅ' => 'Ꙅ', 'ꙇ' => 'Ꙇ', 'ꙉ' => 'Ꙉ', 'ꙋ' => 'Ꙋ', 'ꙍ' => 'Ꙍ', 'ꙏ' => 'Ꙏ', 'ꙑ' => 'Ꙑ', 'ꙓ' => 'Ꙓ', 'ꙕ' => 'Ꙕ', 'ꙗ' => 'Ꙗ', 'ꙙ' => 'Ꙙ', 'ꙛ' => 'Ꙛ', 'ꙝ' => 'Ꙝ', 'ꙟ' => 'Ꙟ', 'ꙡ' => 'Ꙡ', 'ꙣ' => 'Ꙣ', 'ꙥ' => 'Ꙥ', 'ꙧ' => 'Ꙧ', 'ꙩ' => 'Ꙩ', 'ꙫ' => 'Ꙫ', 'ꙭ' => 'Ꙭ', 'ꚁ' => 'Ꚁ', 'ꚃ' => 'Ꚃ', 'ꚅ' => 'Ꚅ', 'ꚇ' => 'Ꚇ', 'ꚉ' => 'Ꚉ', 'ꚋ' => 'Ꚋ', 'ꚍ' => 'Ꚍ', 'ꚏ' => 'Ꚏ', 'ꚑ' => 'Ꚑ', 'ꚓ' => 'Ꚓ', 'ꚕ' => 'Ꚕ', 'ꚗ' => 'Ꚗ', 'ꚙ' => 'Ꚙ', 'ꚛ' => 'Ꚛ', 'ꜣ' => 'Ꜣ', 'ꜥ' => 'Ꜥ', 'ꜧ' => 'Ꜧ', 'ꜩ' => 'Ꜩ', 'ꜫ' => 'Ꜫ', 'ꜭ' => 'Ꜭ', 'ꜯ' => 'Ꜯ', 'ꜳ' => 'Ꜳ', 'ꜵ' => 'Ꜵ', 'ꜷ' => 'Ꜷ', 'ꜹ' => 'Ꜹ', 'ꜻ' => 'Ꜻ', 'ꜽ' => 'Ꜽ', 'ꜿ' => 'Ꜿ', 'ꝁ' => 'Ꝁ', 'ꝃ' => 'Ꝃ', 'ꝅ' => 'Ꝅ', 'ꝇ' => 'Ꝇ', 'ꝉ' => 'Ꝉ', 'ꝋ' => 'Ꝋ', 'ꝍ' => 'Ꝍ', 'ꝏ' => 'Ꝏ', 'ꝑ' => 'Ꝑ', 'ꝓ' => 'Ꝓ', 'ꝕ' => 'Ꝕ', 'ꝗ' => 'Ꝗ', 'ꝙ' => 'Ꝙ', 'ꝛ' => 'Ꝛ', 'ꝝ' => 'Ꝝ', 'ꝟ' => 'Ꝟ', 'ꝡ' => 'Ꝡ', 'ꝣ' => 'Ꝣ', 'ꝥ' => 'Ꝥ', 'ꝧ' => 'Ꝧ', 'ꝩ' => 'Ꝩ', 'ꝫ' => 'Ꝫ', 'ꝭ' => 'Ꝭ', 'ꝯ' => 'Ꝯ', 'ꝺ' => 'Ꝺ', 'ꝼ' => 'Ꝼ', 'ꝿ' => 'Ꝿ', 'ꞁ' => 'Ꞁ', 'ꞃ' => 'Ꞃ', 'ꞅ' => 'Ꞅ', 'ꞇ' => 'Ꞇ', 'ꞌ' => 'Ꞌ', 'ꞑ' => 'Ꞑ', 'ꞓ' => 'Ꞓ', 'ꞔ' => 'Ꞔ', 'ꞗ' => 'Ꞗ', 'ꞙ' => 'Ꞙ', 'ꞛ' => 'Ꞛ', 'ꞝ' => 'Ꞝ', 'ꞟ' => 'Ꞟ', 'ꞡ' => 'Ꞡ', 'ꞣ' => 'Ꞣ', 'ꞥ' => 'Ꞥ', 'ꞧ' => 'Ꞧ', 'ꞩ' => 'Ꞩ', 'ꞵ' => 'Ꞵ', 'ꞷ' => 'Ꞷ', 'ꞹ' => 'Ꞹ', 'ꞻ' => 'Ꞻ', 'ꞽ' => 'Ꞽ', 'ꞿ' => 'Ꞿ', 'ꟃ' => 'Ꟃ', 'ꟈ' => 'Ꟈ', 'ꟊ' => 'Ꟊ', 'ꟶ' => 'Ꟶ', 'ꭓ' => 'Ꭓ', 'ꭰ' => 'Ꭰ', 'ꭱ' => 'Ꭱ', 'ꭲ' => 'Ꭲ', 'ꭳ' => 'Ꭳ', 'ꭴ' => 'Ꭴ', 'ꭵ' => 'Ꭵ', 'ꭶ' => 'Ꭶ', 'ꭷ' => 'Ꭷ', 'ꭸ' => 'Ꭸ', 'ꭹ' => 'Ꭹ', 'ꭺ' => 'Ꭺ', 'ꭻ' => 'Ꭻ', 'ꭼ' => 'Ꭼ', 'ꭽ' => 'Ꭽ', 'ꭾ' => 'Ꭾ', 'ꭿ' => 'Ꭿ', 'ꮀ' => 'Ꮀ', 'ꮁ' => 'Ꮁ', 'ꮂ' => 'Ꮂ', 'ꮃ' => 'Ꮃ', 'ꮄ' => 'Ꮄ', 'ꮅ' => 'Ꮅ', 'ꮆ' => 'Ꮆ', 'ꮇ' => 'Ꮇ', 'ꮈ' => 'Ꮈ', 'ꮉ' => 'Ꮉ', 'ꮊ' => 'Ꮊ', 'ꮋ' => 'Ꮋ', 'ꮌ' => 'Ꮌ', 'ꮍ' => 'Ꮍ', 'ꮎ' => 'Ꮎ', 'ꮏ' => 'Ꮏ', 'ꮐ' => 'Ꮐ', 'ꮑ' => 'Ꮑ', 'ꮒ' => 'Ꮒ', 'ꮓ' => 'Ꮓ', 'ꮔ' => 'Ꮔ', 'ꮕ' => 'Ꮕ', 'ꮖ' => 'Ꮖ', 'ꮗ' => 'Ꮗ', 'ꮘ' => 'Ꮘ', 'ꮙ' => 'Ꮙ', 'ꮚ' => 'Ꮚ', 'ꮛ' => 'Ꮛ', 'ꮜ' => 'Ꮜ', 'ꮝ' => 'Ꮝ', 'ꮞ' => 'Ꮞ', 'ꮟ' => 'Ꮟ', 'ꮠ' => 'Ꮠ', 'ꮡ' => 'Ꮡ', 'ꮢ' => 'Ꮢ', 'ꮣ' => 'Ꮣ', 'ꮤ' => 'Ꮤ', 'ꮥ' => 'Ꮥ', 'ꮦ' => 'Ꮦ', 'ꮧ' => 'Ꮧ', 'ꮨ' => 'Ꮨ', 'ꮩ' => 'Ꮩ', 'ꮪ' => 'Ꮪ', 'ꮫ' => 'Ꮫ', 'ꮬ' => 'Ꮬ', 'ꮭ' => 'Ꮭ', 'ꮮ' => 'Ꮮ', 'ꮯ' => 'Ꮯ', 'ꮰ' => 'Ꮰ', 'ꮱ' => 'Ꮱ', 'ꮲ' => 'Ꮲ', 'ꮳ' => 'Ꮳ', 'ꮴ' => 'Ꮴ', 'ꮵ' => 'Ꮵ', 'ꮶ' => 'Ꮶ', 'ꮷ' => 'Ꮷ', 'ꮸ' => 'Ꮸ', 'ꮹ' => 'Ꮹ', 'ꮺ' => 'Ꮺ', 'ꮻ' => 'Ꮻ', 'ꮼ' => 'Ꮼ', 'ꮽ' => 'Ꮽ', 'ꮾ' => 'Ꮾ', 'ꮿ' => 'Ꮿ', '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', '𐐨' => '𐐀', '𐐩' => '𐐁', '𐐪' => '𐐂', '𐐫' => '𐐃', '𐐬' => '𐐄', '𐐭' => '𐐅', '𐐮' => '𐐆', '𐐯' => '𐐇', '𐐰' => '𐐈', '𐐱' => '𐐉', '𐐲' => '𐐊', '𐐳' => '𐐋', '𐐴' => '𐐌', '𐐵' => '𐐍', '𐐶' => '𐐎', '𐐷' => '𐐏', '𐐸' => '𐐐', '𐐹' => '𐐑', '𐐺' => '𐐒', '𐐻' => '𐐓', '𐐼' => '𐐔', '𐐽' => '𐐕', '𐐾' => '𐐖', '𐐿' => '𐐗', '𐑀' => '𐐘', '𐑁' => '𐐙', '𐑂' => '𐐚', '𐑃' => '𐐛', '𐑄' => '𐐜', '𐑅' => '𐐝', '𐑆' => '𐐞', '𐑇' => '𐐟', '𐑈' => '𐐠', '𐑉' => '𐐡', '𐑊' => '𐐢', '𐑋' => '𐐣', '𐑌' => '𐐤', '𐑍' => '𐐥', '𐑎' => '𐐦', '𐑏' => '𐐧', '𐓘' => '𐒰', '𐓙' => '𐒱', '𐓚' => '𐒲', '𐓛' => '𐒳', '𐓜' => '𐒴', '𐓝' => '𐒵', '𐓞' => '𐒶', '𐓟' => '𐒷', '𐓠' => '𐒸', '𐓡' => '𐒹', '𐓢' => '𐒺', '𐓣' => '𐒻', '𐓤' => '𐒼', '𐓥' => '𐒽', '𐓦' => '𐒾', '𐓧' => '𐒿', '𐓨' => '𐓀', '𐓩' => '𐓁', '𐓪' => '𐓂', '𐓫' => '𐓃', '𐓬' => '𐓄', '𐓭' => '𐓅', '𐓮' => '𐓆', '𐓯' => '𐓇', '𐓰' => '𐓈', '𐓱' => '𐓉', '𐓲' => '𐓊', '𐓳' => '𐓋', '𐓴' => '𐓌', '𐓵' => '𐓍', '𐓶' => '𐓎', '𐓷' => '𐓏', '𐓸' => '𐓐', '𐓹' => '𐓑', '𐓺' => '𐓒', '𐓻' => '𐓓', '𐳀' => '𐲀', '𐳁' => '𐲁', '𐳂' => '𐲂', '𐳃' => '𐲃', '𐳄' => '𐲄', '𐳅' => '𐲅', '𐳆' => '𐲆', '𐳇' => '𐲇', '𐳈' => '𐲈', '𐳉' => '𐲉', '𐳊' => '𐲊', '𐳋' => '𐲋', '𐳌' => '𐲌', '𐳍' => '𐲍', '𐳎' => '𐲎', '𐳏' => '𐲏', '𐳐' => '𐲐', '𐳑' => '𐲑', '𐳒' => '𐲒', '𐳓' => '𐲓', '𐳔' => '𐲔', '𐳕' => '𐲕', '𐳖' => '𐲖', '𐳗' => '𐲗', '𐳘' => '𐲘', '𐳙' => '𐲙', '𐳚' => '𐲚', '𐳛' => '𐲛', '𐳜' => '𐲜', '𐳝' => '𐲝', '𐳞' => '𐲞', '𐳟' => '𐲟', '𐳠' => '𐲠', '𐳡' => '𐲡', '𐳢' => '𐲢', '𐳣' => '𐲣', '𐳤' => '𐲤', '𐳥' => '𐲥', '𐳦' => '𐲦', '𐳧' => '𐲧', '𐳨' => '𐲨', '𐳩' => '𐲩', '𐳪' => '𐲪', '𐳫' => '𐲫', '𐳬' => '𐲬', '𐳭' => '𐲭', '𐳮' => '𐲮', '𐳯' => '𐲯', '𐳰' => '𐲰', '𐳱' => '𐲱', '𐳲' => '𐲲', '𑣀' => '𑢠', '𑣁' => '𑢡', '𑣂' => '𑢢', '𑣃' => '𑢣', '𑣄' => '𑢤', '𑣅' => '𑢥', '𑣆' => '𑢦', '𑣇' => '𑢧', '𑣈' => '𑢨', '𑣉' => '𑢩', '𑣊' => '𑢪', '𑣋' => '𑢫', '𑣌' => '𑢬', '𑣍' => '𑢭', '𑣎' => '𑢮', '𑣏' => '𑢯', '𑣐' => '𑢰', '𑣑' => '𑢱', '𑣒' => '𑢲', '𑣓' => '𑢳', '𑣔' => '𑢴', '𑣕' => '𑢵', '𑣖' => '𑢶', '𑣗' => '𑢷', '𑣘' => '𑢸', '𑣙' => '𑢹', '𑣚' => '𑢺', '𑣛' => '𑢻', '𑣜' => '𑢼', '𑣝' => '𑢽', '𑣞' => '𑢾', '𑣟' => '𑢿', '𖹠' => '𖹀', '𖹡' => '𖹁', '𖹢' => '𖹂', '𖹣' => '𖹃', '𖹤' => '𖹄', '𖹥' => '𖹅', '𖹦' => '𖹆', '𖹧' => '𖹇', '𖹨' => '𖹈', '𖹩' => '𖹉', '𖹪' => '𖹊', '𖹫' => '𖹋', '𖹬' => '𖹌', '𖹭' => '𖹍', '𖹮' => '𖹎', '𖹯' => '𖹏', '𖹰' => '𖹐', '𖹱' => '𖹑', '𖹲' => '𖹒', '𖹳' => '𖹓', '𖹴' => '𖹔', '𖹵' => '𖹕', '𖹶' => '𖹖', '𖹷' => '𖹗', '𖹸' => '𖹘', '𖹹' => '𖹙', '𖹺' => '𖹚', '𖹻' => '𖹛', '𖹼' => '𖹜', '𖹽' => '𖹝', '𖹾' => '𖹞', '𖹿' => '𖹟', '𞤢' => '𞤀', '𞤣' => '𞤁', '𞤤' => '𞤂', '𞤥' => '𞤃', '𞤦' => '𞤄', '𞤧' => '𞤅', '𞤨' => '𞤆', '𞤩' => '𞤇', '𞤪' => '𞤈', '𞤫' => '𞤉', '𞤬' => '𞤊', '𞤭' => '𞤋', '𞤮' => '𞤌', '𞤯' => '𞤍', '𞤰' => '𞤎', '𞤱' => '𞤏', '𞤲' => '𞤐', '𞤳' => '𞤑', '𞤴' => '𞤒', '𞤵' => '𞤓', '𞤶' => '𞤔', '𞤷' => '𞤕', '𞤸' => '𞤖', '𞤹' => '𞤗', '𞤺' => '𞤘', '𞤻' => '𞤙', '𞤼' => '𞤚', '𞤽' => '𞤛', '𞤾' => '𞤜', '𞤿' => '𞤝', '𞥀' => '𞤞', '𞥁' => '𞤟', '𞥂' => '𞤠', '𞥃' => '𞤡', ); suffixes = $suffixes; } public function addSuffix($suffix) { $this->suffixes[] = $suffix; } public function find($name, $default = null, array $extraDirs = []) { if (ini_get('open_basedir')) { $searchPath = array_merge(explode(\PATH_SEPARATOR, ini_get('open_basedir')), $extraDirs); $dirs = []; foreach ($searchPath as $path) { if (@is_dir($path)) { $dirs[] = $path; } else { if (basename($path) == $name && @is_executable($path)) { return $path; } } } } else { $dirs = array_merge( explode(\PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')), $extraDirs ); } $suffixes = ['']; if ('\\' === \DIRECTORY_SEPARATOR) { $pathExt = getenv('PATHEXT'); $suffixes = array_merge($pathExt ? explode(\PATH_SEPARATOR, $pathExt) : $this->suffixes, $suffixes); } foreach ($suffixes as $suffix) { foreach ($dirs as $dir) { if (@is_file($file = $dir.\DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === \DIRECTORY_SEPARATOR || @is_executable($file))) { return $file; } } } return $default; } } arguments = $arguments; } public static function create(array $arguments = []) { return new static($arguments); } public function add($argument) { $this->arguments[] = $argument; return $this; } public function setPrefix($prefix) { $this->prefix = \is_array($prefix) ? $prefix : [$prefix]; return $this; } public function setArguments(array $arguments) { $this->arguments = $arguments; return $this; } public function setWorkingDirectory($cwd) { $this->cwd = $cwd; return $this; } public function inheritEnvironmentVariables($inheritEnv = true) { $this->inheritEnv = $inheritEnv; return $this; } public function setEnv($name, $value) { $this->env[$name] = $value; return $this; } public function addEnvironmentVariables(array $variables) { $this->env = array_replace($this->env, $variables); return $this; } public function setInput($input) { $this->input = ProcessUtils::validateInput(__METHOD__, $input); return $this; } public function setTimeout($timeout) { if (null === $timeout) { $this->timeout = null; return $this; } $timeout = (float) $timeout; if ($timeout < 0) { throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); } $this->timeout = $timeout; return $this; } public function setOption($name, $value) { $this->options[$name] = $value; return $this; } public function disableOutput() { $this->outputDisabled = true; return $this; } public function enableOutput() { $this->outputDisabled = false; return $this; } public function getProcess() { if (0 === \count($this->prefix) && 0 === \count($this->arguments)) { throw new LogicException('You must add() command arguments before calling getProcess().'); } $arguments = array_merge($this->prefix, $this->arguments); $process = new Process($arguments, $this->cwd, $this->env, $this->input, $this->timeout, $this->options); $process->setCommandLine($process->getCommandLine()); if ($this->inheritEnv) { $process->inheritEnvironmentVariables(); } if ($this->outputDisabled) { $process->disableOutput(); } return $process; } } true]; private $exitcode; private $fallbackStatus = []; private $processInformation; private $outputDisabled = false; private $stdout; private $stderr; private $enhanceWindowsCompatibility = true; private $enhanceSigchildCompatibility; private $process; private $status = self::STATUS_READY; private $incrementalOutputOffset = 0; private $incrementalErrorOutputOffset = 0; private $tty = false; private $pty; private $inheritEnv = false; private $useFileHandles = false; private $processPipes; private $latestSignal; private static $sigchild; public static $exitCodes = [ 0 => 'OK', 1 => 'General error', 2 => 'Misuse of shell builtins', 126 => 'Invoked command cannot execute', 127 => 'Command not found', 128 => 'Invalid exit argument', 129 => 'Hangup', 130 => 'Interrupt', 131 => 'Quit and dump core', 132 => 'Illegal instruction', 133 => 'Trace/breakpoint trap', 134 => 'Process aborted', 135 => 'Bus error: "access to undefined portion of memory object"', 136 => 'Floating point exception: "erroneous arithmetic operation"', 137 => 'Kill (terminate immediately)', 138 => 'User-defined 1', 139 => 'Segmentation violation', 140 => 'User-defined 2', 141 => 'Write to pipe with no one reading', 142 => 'Signal raised by alarm', 143 => 'Termination (request to terminate)', 145 => 'Child process terminated, stopped (or continued*)', 146 => 'Continue if stopped', 147 => 'Stop executing temporarily', 148 => 'Terminal stop signal', 149 => 'Background process attempting to read from tty ("in")', 150 => 'Background process attempting to write to tty ("out")', 151 => 'Urgent data available on socket', 152 => 'CPU time limit exceeded', 153 => 'File size limit exceeded', 154 => 'Signal raised by timer counting virtual time: "virtual timer expired"', 155 => 'Profiling timer expired', 157 => 'Pollable event', 159 => 'Bad syscall', ]; public function __construct($commandline, $cwd = null, array $env = null, $input = null, $timeout = 60, array $options = null) { if (!\function_exists('proc_open')) { throw new RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.'); } $this->commandline = $commandline; $this->cwd = $cwd; if (null === $this->cwd && (\defined('ZEND_THREAD_SAFE') || '\\' === \DIRECTORY_SEPARATOR)) { $this->cwd = getcwd(); } if (null !== $env) { $this->setEnv($env); } $this->setInput($input); $this->setTimeout($timeout); $this->useFileHandles = '\\' === \DIRECTORY_SEPARATOR; $this->pty = false; $this->enhanceSigchildCompatibility = '\\' !== \DIRECTORY_SEPARATOR && $this->isSigchildEnabled(); if (null !== $options) { @trigger_error(sprintf('The $options parameter of the %s constructor is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), \E_USER_DEPRECATED); $this->options = array_replace($this->options, $options); } } public function __destruct() { $this->stop(0); } public function __clone() { $this->resetProcessData(); } public function run($callback = null) { $env = 1 < \func_num_args() ? func_get_arg(1) : null; $this->start($callback, $env); return $this->wait(); } public function mustRun(callable $callback = null) { if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); } $env = 1 < \func_num_args() ? func_get_arg(1) : null; if (0 !== $this->run($callback, $env)) { throw new ProcessFailedException($this); } return $this; } public function start(callable $callback = null) { if ($this->isRunning()) { throw new RuntimeException('Process is already running.'); } if (2 <= \func_num_args()) { $env = func_get_arg(1); } else { if (__CLASS__ !== static::class) { $r = new \ReflectionMethod($this, __FUNCTION__); if (__CLASS__ !== $r->getDeclaringClass()->getName() && (2 > $r->getNumberOfParameters() || 'env' !== $r->getParameters()[1]->name)) { @trigger_error(sprintf('The %s::start() method expects a second "$env" argument since Symfony 3.3. It will be made mandatory in 4.0.', static::class), \E_USER_DEPRECATED); } } $env = null; } $this->resetProcessData(); $this->starttime = $this->lastOutputTime = microtime(true); $this->callback = $this->buildCallback($callback); $this->hasCallback = null !== $callback; $descriptors = $this->getDescriptors(); $inheritEnv = $this->inheritEnv; if (\is_array($commandline = $this->commandline)) { $commandline = implode(' ', array_map([$this, 'escapeArgument'], $commandline)); if ('\\' !== \DIRECTORY_SEPARATOR) { $commandline = 'exec '.$commandline; } } if (null === $env) { $env = $this->env; } else { if ($this->env) { $env += $this->env; } $inheritEnv = true; } if (null !== $env && $inheritEnv) { $env += $this->getDefaultEnv(); } elseif (null !== $env) { @trigger_error('Not inheriting environment variables is deprecated since Symfony 3.3 and will always happen in 4.0. Set "Process::inheritEnvironmentVariables()" to true instead.', \E_USER_DEPRECATED); } else { $env = $this->getDefaultEnv(); } if ('\\' === \DIRECTORY_SEPARATOR && $this->enhanceWindowsCompatibility) { $this->options['bypass_shell'] = true; $commandline = $this->prepareWindowsCommandLine($commandline, $env); } elseif (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { $descriptors[3] = ['pipe', 'w']; $commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;'; $commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code'; $ptsWorkaround = fopen(__FILE__, 'r'); } if (\defined('HHVM_VERSION')) { $envPairs = $env; } else { $envPairs = []; foreach ($env as $k => $v) { if (false !== $v) { $envPairs[] = $k.'='.$v; } } } if (!is_dir($this->cwd)) { @trigger_error('The provided cwd does not exist. Command is currently ran against getcwd(). This behavior is deprecated since Symfony 3.4 and will be removed in 4.0.', \E_USER_DEPRECATED); } $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options); if (!\is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); } $this->status = self::STATUS_STARTED; if (isset($descriptors[3])) { $this->fallbackStatus['pid'] = (int) fgets($this->processPipes->pipes[3]); } if ($this->tty) { return; } $this->updateStatus(false); $this->checkTimeout(); } public function restart(callable $callback = null) { if ($this->isRunning()) { throw new RuntimeException('Process is already running.'); } $env = 1 < \func_num_args() ? func_get_arg(1) : null; $process = clone $this; $process->start($callback, $env); return $process; } public function wait(callable $callback = null) { $this->requireProcessIsStarted(__FUNCTION__); $this->updateStatus(false); if (null !== $callback) { if (!$this->processPipes->haveReadSupport()) { $this->stop(0); throw new \LogicException('Pass the callback to the Process::start method or enableOutput to use a callback with Process::wait.'); } $this->callback = $this->buildCallback($callback); } do { $this->checkTimeout(); $running = '\\' === \DIRECTORY_SEPARATOR ? $this->isRunning() : $this->processPipes->areOpen(); $this->readPipes($running, '\\' !== \DIRECTORY_SEPARATOR || !$running); } while ($running); while ($this->isRunning()) { $this->checkTimeout(); usleep(1000); } if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) { throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig'])); } return $this->exitcode; } public function getPid() { return $this->isRunning() ? $this->processInformation['pid'] : null; } public function signal($signal) { $this->doSignal($signal, true); return $this; } public function disableOutput() { if ($this->isRunning()) { throw new RuntimeException('Disabling output while the process is running is not possible.'); } if (null !== $this->idleTimeout) { throw new LogicException('Output can not be disabled while an idle timeout is set.'); } $this->outputDisabled = true; return $this; } public function enableOutput() { if ($this->isRunning()) { throw new RuntimeException('Enabling output while the process is running is not possible.'); } $this->outputDisabled = false; return $this; } public function isOutputDisabled() { return $this->outputDisabled; } public function getOutput() { $this->readPipesForOutput(__FUNCTION__); if (false === $ret = stream_get_contents($this->stdout, -1, 0)) { return ''; } return $ret; } public function getIncrementalOutput() { $this->readPipesForOutput(__FUNCTION__); $latest = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset); $this->incrementalOutputOffset = ftell($this->stdout); if (false === $latest) { return ''; } return $latest; } public function getIterator($flags = 0) { $this->readPipesForOutput(__FUNCTION__, false); $clearOutput = !(self::ITER_KEEP_OUTPUT & $flags); $blocking = !(self::ITER_NON_BLOCKING & $flags); $yieldOut = !(self::ITER_SKIP_OUT & $flags); $yieldErr = !(self::ITER_SKIP_ERR & $flags); while (null !== $this->callback || ($yieldOut && !feof($this->stdout)) || ($yieldErr && !feof($this->stderr))) { if ($yieldOut) { $out = stream_get_contents($this->stdout, -1, $this->incrementalOutputOffset); if (isset($out[0])) { if ($clearOutput) { $this->clearOutput(); } else { $this->incrementalOutputOffset = ftell($this->stdout); } yield self::OUT => $out; } } if ($yieldErr) { $err = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset); if (isset($err[0])) { if ($clearOutput) { $this->clearErrorOutput(); } else { $this->incrementalErrorOutputOffset = ftell($this->stderr); } yield self::ERR => $err; } } if (!$blocking && !isset($out[0]) && !isset($err[0])) { yield self::OUT => ''; } $this->checkTimeout(); $this->readPipesForOutput(__FUNCTION__, $blocking); } } public function clearOutput() { ftruncate($this->stdout, 0); fseek($this->stdout, 0); $this->incrementalOutputOffset = 0; return $this; } public function getErrorOutput() { $this->readPipesForOutput(__FUNCTION__); if (false === $ret = stream_get_contents($this->stderr, -1, 0)) { return ''; } return $ret; } public function getIncrementalErrorOutput() { $this->readPipesForOutput(__FUNCTION__); $latest = stream_get_contents($this->stderr, -1, $this->incrementalErrorOutputOffset); $this->incrementalErrorOutputOffset = ftell($this->stderr); if (false === $latest) { return ''; } return $latest; } public function clearErrorOutput() { ftruncate($this->stderr, 0); fseek($this->stderr, 0); $this->incrementalErrorOutputOffset = 0; return $this; } public function getExitCode() { if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); } $this->updateStatus(false); return $this->exitcode; } public function getExitCodeText() { if (null === $exitcode = $this->getExitCode()) { return null; } return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error'; } public function isSuccessful() { return 0 === $this->getExitCode(); } public function hasBeenSignaled() { $this->requireProcessIsTerminated(__FUNCTION__); if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); } return $this->processInformation['signaled']; } public function getTermSignal() { $this->requireProcessIsTerminated(__FUNCTION__); if ($this->isSigchildEnabled() && (!$this->enhanceSigchildCompatibility || -1 === $this->processInformation['termsig'])) { throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); } return $this->processInformation['termsig']; } public function hasBeenStopped() { $this->requireProcessIsTerminated(__FUNCTION__); return $this->processInformation['stopped']; } public function getStopSignal() { $this->requireProcessIsTerminated(__FUNCTION__); return $this->processInformation['stopsig']; } public function isRunning() { if (self::STATUS_STARTED !== $this->status) { return false; } $this->updateStatus(false); return $this->processInformation['running']; } public function isStarted() { return self::STATUS_READY != $this->status; } public function isTerminated() { $this->updateStatus(false); return self::STATUS_TERMINATED == $this->status; } public function getStatus() { $this->updateStatus(false); return $this->status; } public function stop($timeout = 10, $signal = null) { $timeoutMicro = microtime(true) + $timeout; if ($this->isRunning()) { $this->doSignal(15, false); do { usleep(1000); } while ($this->isRunning() && microtime(true) < $timeoutMicro); if ($this->isRunning()) { $this->doSignal($signal ?: 9, false); } } if ($this->isRunning()) { if (isset($this->fallbackStatus['pid'])) { unset($this->fallbackStatus['pid']); return $this->stop(0, $signal); } $this->close(); } return $this->exitcode; } public function addOutput($line) { $this->lastOutputTime = microtime(true); fseek($this->stdout, 0, \SEEK_END); fwrite($this->stdout, $line); fseek($this->stdout, $this->incrementalOutputOffset); } public function addErrorOutput($line) { $this->lastOutputTime = microtime(true); fseek($this->stderr, 0, \SEEK_END); fwrite($this->stderr, $line); fseek($this->stderr, $this->incrementalErrorOutputOffset); } public function getCommandLine() { return \is_array($this->commandline) ? implode(' ', array_map([$this, 'escapeArgument'], $this->commandline)) : $this->commandline; } public function setCommandLine($commandline) { $this->commandline = $commandline; return $this; } public function getTimeout() { return $this->timeout; } public function getIdleTimeout() { return $this->idleTimeout; } public function setTimeout($timeout) { $this->timeout = $this->validateTimeout($timeout); return $this; } public function setIdleTimeout($timeout) { if (null !== $timeout && $this->outputDisabled) { throw new LogicException('Idle timeout can not be set while the output is disabled.'); } $this->idleTimeout = $this->validateTimeout($timeout); return $this; } public function setTty($tty) { if ('\\' === \DIRECTORY_SEPARATOR && $tty) { throw new RuntimeException('TTY mode is not supported on Windows platform.'); } if ($tty) { static $isTtySupported; if (null === $isTtySupported) { $isTtySupported = (bool) @proc_open('echo 1 >/dev/null', [['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w']], $pipes); } if (!$isTtySupported) { throw new RuntimeException('TTY mode requires /dev/tty to be read/writable.'); } } $this->tty = (bool) $tty; return $this; } public function isTty() { return $this->tty; } public function setPty($bool) { $this->pty = (bool) $bool; return $this; } public function isPty() { return $this->pty; } public function getWorkingDirectory() { if (null === $this->cwd) { return getcwd() ?: null; } return $this->cwd; } public function setWorkingDirectory($cwd) { $this->cwd = $cwd; return $this; } public function getEnv() { return $this->env; } public function setEnv(array $env) { $env = array_filter($env, function ($value) { return !\is_array($value); }); $this->env = $env; return $this; } public function getInput() { return $this->input; } public function setInput($input) { if ($this->isRunning()) { throw new LogicException('Input can not be set while the process is running.'); } $this->input = ProcessUtils::validateInput(__METHOD__, $input); return $this; } public function getOptions() { @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), \E_USER_DEPRECATED); return $this->options; } public function setOptions(array $options) { @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), \E_USER_DEPRECATED); $this->options = $options; return $this; } public function getEnhanceWindowsCompatibility() { @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Enhanced Windows compatibility will always be enabled.', __METHOD__), \E_USER_DEPRECATED); return $this->enhanceWindowsCompatibility; } public function setEnhanceWindowsCompatibility($enhance) { @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Enhanced Windows compatibility will always be enabled.', __METHOD__), \E_USER_DEPRECATED); $this->enhanceWindowsCompatibility = (bool) $enhance; return $this; } public function getEnhanceSigchildCompatibility() { @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Sigchild compatibility will always be enabled.', __METHOD__), \E_USER_DEPRECATED); return $this->enhanceSigchildCompatibility; } public function setEnhanceSigchildCompatibility($enhance) { @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Sigchild compatibility will always be enabled.', __METHOD__), \E_USER_DEPRECATED); $this->enhanceSigchildCompatibility = (bool) $enhance; return $this; } public function inheritEnvironmentVariables($inheritEnv = true) { if (!$inheritEnv) { @trigger_error('Not inheriting environment variables is deprecated since Symfony 3.3 and will always happen in 4.0. Set "Process::inheritEnvironmentVariables()" to true instead.', \E_USER_DEPRECATED); } $this->inheritEnv = (bool) $inheritEnv; return $this; } public function areEnvironmentVariablesInherited() { @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Environment variables will always be inherited.', __METHOD__), \E_USER_DEPRECATED); return $this->inheritEnv; } public function checkTimeout() { if (self::STATUS_STARTED !== $this->status) { return; } if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) { $this->stop(0); throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL); } if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) { $this->stop(0); throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE); } } public static function isPtySupported() { static $result; if (null !== $result) { return $result; } if ('\\' === \DIRECTORY_SEPARATOR) { return $result = false; } return $result = (bool) @proc_open('echo 1 >/dev/null', [['pty'], ['pty'], ['pty']], $pipes); } private function getDescriptors() { if ($this->input instanceof \Iterator) { $this->input->rewind(); } if ('\\' === \DIRECTORY_SEPARATOR) { $this->processPipes = new WindowsPipes($this->input, !$this->outputDisabled || $this->hasCallback); } else { $this->processPipes = new UnixPipes($this->isTty(), $this->isPty(), $this->input, !$this->outputDisabled || $this->hasCallback); } return $this->processPipes->getDescriptors(); } protected function buildCallback(callable $callback = null) { if ($this->outputDisabled) { return function ($type, $data) use ($callback) { if (null !== $callback) { \call_user_func($callback, $type, $data); } }; } $out = self::OUT; return function ($type, $data) use ($callback, $out) { if ($out == $type) { $this->addOutput($data); } else { $this->addErrorOutput($data); } if (null !== $callback) { \call_user_func($callback, $type, $data); } }; } protected function updateStatus($blocking) { if (self::STATUS_STARTED !== $this->status) { return; } $this->processInformation = proc_get_status($this->process); $running = $this->processInformation['running']; $this->readPipes($running && $blocking, '\\' !== \DIRECTORY_SEPARATOR || !$running); if ($this->fallbackStatus && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { $this->processInformation = $this->fallbackStatus + $this->processInformation; } if (!$running) { $this->close(); } } protected function isSigchildEnabled() { if (null !== self::$sigchild) { return self::$sigchild; } if (!\function_exists('phpinfo') || \defined('HHVM_VERSION')) { return self::$sigchild = false; } ob_start(); phpinfo(\INFO_GENERAL); return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); } private function readPipesForOutput($caller, $blocking = false) { if ($this->outputDisabled) { throw new LogicException('Output has been disabled.'); } $this->requireProcessIsStarted($caller); $this->updateStatus($blocking); } private function validateTimeout($timeout) { $timeout = (float) $timeout; if (0.0 === $timeout) { $timeout = null; } elseif ($timeout < 0) { throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); } return $timeout; } private function readPipes($blocking, $close) { $result = $this->processPipes->readAndWrite($blocking, $close); $callback = $this->callback; foreach ($result as $type => $data) { if (3 !== $type) { $callback(self::STDOUT === $type ? self::OUT : self::ERR, $data); } elseif (!isset($this->fallbackStatus['signaled'])) { $this->fallbackStatus['exitcode'] = (int) $data; } } } private function close() { $this->processPipes->close(); if (\is_resource($this->process)) { proc_close($this->process); } $this->exitcode = $this->processInformation['exitcode']; $this->status = self::STATUS_TERMINATED; if (-1 === $this->exitcode) { if ($this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) { $this->exitcode = 128 + $this->processInformation['termsig']; } elseif ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { $this->processInformation['signaled'] = true; $this->processInformation['termsig'] = -1; } } $this->callback = null; return $this->exitcode; } private function resetProcessData() { $this->starttime = null; $this->callback = null; $this->exitcode = null; $this->fallbackStatus = []; $this->processInformation = null; $this->stdout = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b'); $this->stderr = fopen('php://temp/maxmemory:'.(1024 * 1024), 'w+b'); $this->process = null; $this->latestSignal = null; $this->status = self::STATUS_READY; $this->incrementalOutputOffset = 0; $this->incrementalErrorOutputOffset = 0; } private function doSignal($signal, $throwException) { if (null === $pid = $this->getPid()) { if ($throwException) { throw new LogicException('Can not send signal on a non running process.'); } return false; } if ('\\' === \DIRECTORY_SEPARATOR) { exec(sprintf('taskkill /F /T /PID %d 2>&1', $pid), $output, $exitCode); if ($exitCode && $this->isRunning()) { if ($throwException) { throw new RuntimeException(sprintf('Unable to kill the process (%s).', implode(' ', $output))); } return false; } } else { if (!$this->enhanceSigchildCompatibility || !$this->isSigchildEnabled()) { $ok = @proc_terminate($this->process, $signal); } elseif (\function_exists('posix_kill')) { $ok = @posix_kill($pid, $signal); } elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $pid), [2 => ['pipe', 'w']], $pipes)) { $ok = false === fgets($pipes[2]); } if (!$ok) { if ($throwException) { throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal)); } return false; } } $this->latestSignal = (int) $signal; $this->fallbackStatus['signaled'] = true; $this->fallbackStatus['exitcode'] = -1; $this->fallbackStatus['termsig'] = $this->latestSignal; return true; } private function prepareWindowsCommandLine($cmd, array &$env) { $uid = uniqid('', true); $varCount = 0; $varCache = []; $cmd = preg_replace_callback( '/"(?:( [^"%!^]*+ (?: (?: !LF! | "(?:\^[%!^])?+" ) [^"%!^]*+ )++ ) | [^"]*+ )"/x', function ($m) use (&$env, &$varCache, &$varCount, $uid) { if (!isset($m[1])) { return $m[0]; } if (isset($varCache[$m[0]])) { return $varCache[$m[0]]; } if (false !== strpos($value = $m[1], "\0")) { $value = str_replace("\0", '?', $value); } if (false === strpbrk($value, "\"%!\n")) { return '"'.$value.'"'; } $value = str_replace(['!LF!', '"^!"', '"^%"', '"^^"', '""'], ["\n", '!', '%', '^', '"'], $value); $value = '"'.preg_replace('/(\\\\*)"/', '$1$1\\"', $value).'"'; $var = $uid.++$varCount; $env[$var] = $value; return $varCache[$m[0]] = '!'.$var.'!'; }, $cmd ); $cmd = 'cmd /V:ON /E:ON /D /C ('.str_replace("\n", ' ', $cmd).')'; foreach ($this->processPipes->getFiles() as $offset => $filename) { $cmd .= ' '.$offset.'>"'.$filename.'"'; } return $cmd; } private function requireProcessIsStarted($functionName) { if (!$this->isStarted()) { throw new LogicException(sprintf('Process must be started before calling "%s()".', $functionName)); } } private function requireProcessIsTerminated($functionName) { if (!$this->isTerminated()) { throw new LogicException(sprintf('Process must be terminated before calling "%s()".', $functionName)); } } private function escapeArgument($argument) { if ('\\' !== \DIRECTORY_SEPARATOR) { return "'".str_replace("'", "'\\''", $argument)."'"; } if ('' === $argument = (string) $argument) { return '""'; } if (false !== strpos($argument, "\0")) { $argument = str_replace("\0", '?', $argument); } if (!preg_match('/[\/()%!^"<>&|\s]/', $argument)) { return $argument; } $argument = preg_replace('/(\\\\+)$/', '$1$1', $argument); return '"'.str_replace(['"', '^', '%', '!', "\n"], ['""', '"^^"', '"^%"', '"^!"', '!LF!'], $argument).'"'; } private function getDefaultEnv() { $env = []; foreach ($_SERVER as $k => $v) { if (\is_string($v) && false !== $v = getenv($k)) { $env[$k] = $v; } } foreach ($_ENV as $k => $v) { if (\is_string($v)) { $env[$k] = $v; } } return $env; } } onEmpty = $onEmpty; } public function write($input) { if (null === $input) { return; } if ($this->isClosed()) { throw new RuntimeException(sprintf('"%s" is closed.', static::class)); } $this->input[] = ProcessUtils::validateInput(__METHOD__, $input); } public function close() { $this->open = false; } public function isClosed() { return !$this->open; } public function getIterator() { $this->open = true; while ($this->open || $this->input) { if (!$this->input) { yield ''; continue; } $current = array_shift($this->input); if ($current instanceof \Iterator) { foreach ($current as $cur) { yield $cur; } } else { yield $current; } if (!$this->input && $this->open && null !== $onEmpty = $this->onEmpty) { $this->write($onEmpty($this)); } } } } getIterator($input::ITER_SKIP_ERR); } if ($input instanceof \Iterator) { return $input; } if ($input instanceof \Traversable) { return new \IteratorIterator($input); } throw new InvalidArgumentException(sprintf('"%s" only accepts strings, Traversable objects or stream resources.', $caller)); } return $input; } private static function isSurroundedBy($arg, $char) { return 2 < \strlen($arg) && $char === $arg[0] && $char === $arg[\strlen($arg) - 1]; } } isSuccessful()) { throw new InvalidArgumentException('Expected a failed process, but the given process was successful.'); } $error = sprintf('The command "%s" failed.'."\n\nExit Code: %s(%s)\n\nWorking directory: %s", $process->getCommandLine(), $process->getExitCode(), $process->getExitCodeText(), $process->getWorkingDirectory() ); if (!$process->isOutputDisabled()) { $error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", $process->getOutput(), $process->getErrorOutput() ); } parent::__construct($error); $this->process = $process; } public function getProcess() { return $this->process; } } process = $process; $this->timeoutType = $timeoutType; parent::__construct(sprintf( 'The process "%s" exceeded the timeout of %s seconds.', $process->getCommandLine(), $this->getExceededTimeout() )); } public function getProcess() { return $this->process; } public function isGeneralTimeout() { return self::TYPE_GENERAL === $this->timeoutType; } public function isIdleTimeout() { return self::TYPE_IDLE === $this->timeoutType; } public function getExceededTimeout() { switch ($this->timeoutType) { case self::TYPE_GENERAL: return $this->process->getTimeout(); case self::TYPE_IDLE: return $this->process->getIdleTimeout(); default: throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); } } } executableFinder = new ExecutableFinder(); } public function find($includeArgs = true) { $args = $this->findArguments(); $args = $includeArgs && $args ? ' '.implode(' ', $args) : ''; if (\defined('HHVM_VERSION')) { return (getenv('PHP_BINARY') ?: \PHP_BINARY).$args; } if (\PHP_BINARY && \in_array(\PHP_SAPI, ['cli', 'cli-server', 'phpdbg'], true)) { return \PHP_BINARY.$args; } if ($php = getenv('PHP_PATH')) { if (!@is_executable($php)) { return false; } return $php; } if ($php = getenv('PHP_PEAR_PHP_BIN')) { if (@is_executable($php)) { return $php; } } if (@is_executable($php = \PHP_BINDIR.('\\' === \DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) { return $php; } $dirs = [\PHP_BINDIR]; if ('\\' === \DIRECTORY_SEPARATOR) { $dirs[] = 'C:\xampp\php\\'; } return $this->executableFinder->find('php', false, $dirs); } public function findArguments() { $arguments = []; if (\defined('HHVM_VERSION')) { $arguments[] = '--php'; } elseif ('phpdbg' === \PHP_SAPI) { $arguments[] = '-qrr'; } return $arguments; } } 0, Process::STDERR => 0, ]; private $haveReadSupport; public function __construct($input, $haveReadSupport) { $this->haveReadSupport = (bool) $haveReadSupport; if ($this->haveReadSupport) { $pipes = [ Process::STDOUT => Process::OUT, Process::STDERR => Process::ERR, ]; $tmpDir = sys_get_temp_dir(); $lastError = 'unknown reason'; set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); for ($i = 0;; ++$i) { foreach ($pipes as $pipe => $name) { $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); if (!$h = fopen($file.'.lock', 'w')) { if (file_exists($file.'.lock')) { continue 2; } restore_error_handler(); throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError); } if (!flock($h, \LOCK_EX | \LOCK_NB)) { continue 2; } if (isset($this->lockHandles[$pipe])) { flock($this->lockHandles[$pipe], \LOCK_UN); fclose($this->lockHandles[$pipe]); } $this->lockHandles[$pipe] = $h; if (!fclose(fopen($file, 'w')) || !$h = fopen($file, 'r')) { flock($this->lockHandles[$pipe], \LOCK_UN); fclose($this->lockHandles[$pipe]); unset($this->lockHandles[$pipe]); continue 2; } $this->fileHandles[$pipe] = $h; $this->files[$pipe] = $file; } break; } restore_error_handler(); } parent::__construct($input); } public function __destruct() { $this->close(); } public function getDescriptors() { if (!$this->haveReadSupport) { $nullstream = fopen('NUL', 'c'); return [ ['pipe', 'r'], $nullstream, $nullstream, ]; } return [ ['pipe', 'r'], ['file', 'NUL', 'w'], ['file', 'NUL', 'w'], ]; } public function getFiles() { return $this->files; } public function readAndWrite($blocking, $close = false) { $this->unblock(); $w = $this->write(); $read = $r = $e = []; if ($blocking) { if ($w) { @stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6); } elseif ($this->fileHandles) { usleep(Process::TIMEOUT_PRECISION * 1E6); } } foreach ($this->fileHandles as $type => $fileHandle) { $data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]); if (isset($data[0])) { $this->readBytes[$type] += \strlen($data); $read[$type] = $data; } if ($close) { ftruncate($fileHandle, 0); fclose($fileHandle); flock($this->lockHandles[$type], \LOCK_UN); fclose($this->lockHandles[$type]); unset($this->fileHandles[$type], $this->lockHandles[$type]); } } return $read; } public function haveReadSupport() { return $this->haveReadSupport; } public function areOpen() { return $this->pipes && $this->fileHandles; } public function close() { parent::close(); foreach ($this->fileHandles as $type => $handle) { ftruncate($handle, 0); fclose($handle); flock($this->lockHandles[$type], \LOCK_UN); fclose($this->lockHandles[$type]); } $this->fileHandles = $this->lockHandles = []; } } ttyMode = (bool) $ttyMode; $this->ptyMode = (bool) $ptyMode; $this->haveReadSupport = (bool) $haveReadSupport; parent::__construct($input); } public function __destruct() { $this->close(); } public function getDescriptors() { if (!$this->haveReadSupport) { $nullstream = fopen('/dev/null', 'c'); return [ ['pipe', 'r'], $nullstream, $nullstream, ]; } if ($this->ttyMode) { return [ ['file', '/dev/tty', 'r'], ['file', '/dev/tty', 'w'], ['file', '/dev/tty', 'w'], ]; } if ($this->ptyMode && Process::isPtySupported()) { return [ ['pty'], ['pty'], ['pty'], ]; } return [ ['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w'], ]; } public function getFiles() { return []; } public function readAndWrite($blocking, $close = false) { $this->unblock(); $w = $this->write(); $read = $e = []; $r = $this->pipes; unset($r[0]); set_error_handler([$this, 'handleError']); if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { restore_error_handler(); if (!$this->hasSystemCallBeenInterrupted()) { $this->pipes = []; } return $read; } restore_error_handler(); foreach ($r as $pipe) { $read[$type = array_search($pipe, $this->pipes, true)] = ''; do { $data = @fread($pipe, self::CHUNK_SIZE); $read[$type] .= $data; } while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1]))); if (!isset($read[$type][0])) { unset($read[$type]); } if ($close && feof($pipe)) { fclose($pipe); unset($this->pipes[$type]); } } return $read; } public function haveReadSupport() { return $this->haveReadSupport; } public function areOpen() { return (bool) $this->pipes; } } input = $input; } elseif (\is_string($input)) { $this->inputBuffer = $input; } else { $this->inputBuffer = (string) $input; } } public function close() { foreach ($this->pipes as $pipe) { fclose($pipe); } $this->pipes = []; } protected function hasSystemCallBeenInterrupted() { $lastError = $this->lastError; $this->lastError = null; return null !== $lastError && false !== stripos($lastError, 'interrupted system call'); } protected function unblock() { if (!$this->blocked) { return; } foreach ($this->pipes as $pipe) { stream_set_blocking($pipe, 0); } if (\is_resource($this->input)) { stream_set_blocking($this->input, 0); } $this->blocked = false; } protected function write() { if (!isset($this->pipes[0])) { return null; } $input = $this->input; if ($input instanceof \Iterator) { if (!$input->valid()) { $input = null; } elseif (\is_resource($input = $input->current())) { stream_set_blocking($input, 0); } elseif (!isset($this->inputBuffer[0])) { if (!\is_string($input)) { if (!is_scalar($input)) { throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', \get_class($this->input), \gettype($input))); } $input = (string) $input; } $this->inputBuffer = $input; $this->input->next(); $input = null; } else { $input = null; } } $r = $e = []; $w = [$this->pipes[0]]; if (false === @stream_select($r, $w, $e, 0, 0)) { return null; } foreach ($w as $stdin) { if (isset($this->inputBuffer[0])) { $written = fwrite($stdin, $this->inputBuffer); $this->inputBuffer = substr($this->inputBuffer, $written); if (isset($this->inputBuffer[0])) { return [$this->pipes[0]]; } } if ($input) { for (;;) { $data = fread($input, self::CHUNK_SIZE); if (!isset($data[0])) { break; } $written = fwrite($stdin, $data); $data = substr($data, $written); if (isset($data[0])) { $this->inputBuffer = $data; return [$this->pipes[0]]; } } if (feof($input)) { if ($this->input instanceof \Iterator) { $this->input->next(); } else { $this->input = null; } } } } if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) { $this->input = null; fclose($this->pipes[0]); unset($this->pipes[0]); } elseif (!$w) { return [$this->pipes[0]]; } return null; } public function handleError($type, $msg) { $this->lastError = $msg; } } find(false)) { $php = null; } else { $php = array_merge([$php], $executableFinder->findArguments()); } if ('phpdbg' === \PHP_SAPI) { $file = tempnam(sys_get_temp_dir(), 'dbg'); file_put_contents($file, $script); register_shutdown_function('unlink', $file); $php[] = $file; $script = null; } if (null !== $options) { @trigger_error(sprintf('The $options parameter of the %s constructor is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), \E_USER_DEPRECATED); } parent::__construct($php, $cwd, $env, $script, $timeout, $options); } public function setPhpBinary($php) { $this->setCommandLine($php); } public function start(callable $callback = null) { if (null === $this->getCommandLine()) { throw new RuntimeException('Unable to find the PHP executable.'); } $env = 1 < \func_num_args() ? func_get_arg(1) : null; parent::start($callback, $env); } } relativePath = $relativePath; $this->relativePathname = $relativePathname; } public function getRelativePath() { return $this->relativePath; } public function getRelativePathname() { return $this->relativePathname; } public function getContents() { set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); $content = file_get_contents($this->getPathname()); restore_error_handler(); if (false === $content) { throw new \RuntimeException($error); } return $content; } } ]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); } try { $date = new \DateTime($matches[2]); $target = $date->format('U'); } catch (\Exception $e) { throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); } $operator = isset($matches[1]) ? $matches[1] : '=='; if ('since' === $operator || 'after' === $operator) { $operator = '>'; } if ('until' === $operator || 'before' === $operator) { $operator = '<'; } $this->setOperator($operator); $this->setTarget($target); } } target; } public function setTarget($target) { $this->target = $target; } public function getOperator() { return $this->operator; } public function setOperator($operator) { if (!$operator) { $operator = '=='; } if (!\in_array($operator, ['>', '<', '>=', '<=', '==', '!='])) { throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); } $this->operator = $operator; } public function test($test) { switch ($this->operator) { case '>': return $test > $this->target; case '>=': return $test >= $this->target; case '<': return $test < $this->target; case '<=': return $test <= $this->target; case '!=': return $test != $this->target; } return $test == $this->target; } } ]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) { throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); } $target = $matches[2]; if (!is_numeric($target)) { throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target)); } if (isset($matches[3])) { switch (strtolower($matches[3])) { case 'k': $target *= 1000; break; case 'ki': $target *= 1024; break; case 'm': $target *= 1000000; break; case 'mi': $target *= 1024 * 1024; break; case 'g': $target *= 1000000000; break; case 'gi': $target *= 1024 * 1024 * 1024; break; } } $this->setTarget($target); $this->setOperator(isset($matches[1]) ? $matches[1] : '=='); } } ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES; } public static function create() { return new static(); } public function directories() { $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES; return $this; } public function files() { $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES; return $this; } public function depth($level) { $this->depths[] = new Comparator\NumberComparator($level); return $this; } public function date($date) { $this->dates[] = new Comparator\DateComparator($date); return $this; } public function name($pattern) { $this->names[] = $pattern; return $this; } public function notName($pattern) { $this->notNames[] = $pattern; return $this; } public function contains($pattern) { $this->contains[] = $pattern; return $this; } public function notContains($pattern) { $this->notContains[] = $pattern; return $this; } public function path($pattern) { $this->paths[] = $pattern; return $this; } public function notPath($pattern) { $this->notPaths[] = $pattern; return $this; } public function size($size) { $this->sizes[] = new Comparator\NumberComparator($size); return $this; } public function exclude($dirs) { $this->exclude = array_merge($this->exclude, (array) $dirs); return $this; } public function ignoreDotFiles($ignoreDotFiles) { if ($ignoreDotFiles) { $this->ignore |= static::IGNORE_DOT_FILES; } else { $this->ignore &= ~static::IGNORE_DOT_FILES; } return $this; } public function ignoreVCS($ignoreVCS) { if ($ignoreVCS) { $this->ignore |= static::IGNORE_VCS_FILES; } else { $this->ignore &= ~static::IGNORE_VCS_FILES; } return $this; } public static function addVCSPattern($pattern) { foreach ((array) $pattern as $p) { self::$vcsPatterns[] = $p; } self::$vcsPatterns = array_unique(self::$vcsPatterns); } public function sort(\Closure $closure) { $this->sort = $closure; return $this; } public function sortByName() { $this->sort = Iterator\SortableIterator::SORT_BY_NAME; return $this; } public function sortByType() { $this->sort = Iterator\SortableIterator::SORT_BY_TYPE; return $this; } public function sortByAccessedTime() { $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME; return $this; } public function sortByChangedTime() { $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME; return $this; } public function sortByModifiedTime() { $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME; return $this; } public function filter(\Closure $closure) { $this->filters[] = $closure; return $this; } public function followLinks() { $this->followLinks = true; return $this; } public function ignoreUnreadableDirs($ignore = true) { $this->ignoreUnreadableDirs = (bool) $ignore; return $this; } public function in($dirs) { $resolvedDirs = []; foreach ((array) $dirs as $dir) { if (is_dir($dir)) { $resolvedDirs[] = $this->normalizeDir($dir); } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? \GLOB_BRACE : 0) | \GLOB_ONLYDIR | \GLOB_NOSORT)) { sort($glob); $resolvedDirs = array_merge($resolvedDirs, array_map([$this, 'normalizeDir'], $glob)); } else { throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir)); } } $this->dirs = array_merge($this->dirs, $resolvedDirs); return $this; } public function getIterator() { if (0 === \count($this->dirs) && 0 === \count($this->iterators)) { throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.'); } if (1 === \count($this->dirs) && 0 === \count($this->iterators)) { return $this->searchInDirectory($this->dirs[0]); } $iterator = new \AppendIterator(); foreach ($this->dirs as $dir) { $iterator->append($this->searchInDirectory($dir)); } foreach ($this->iterators as $it) { $iterator->append($it); } return $iterator; } public function append($iterator) { if ($iterator instanceof \IteratorAggregate) { $this->iterators[] = $iterator->getIterator(); } elseif ($iterator instanceof \Iterator) { $this->iterators[] = $iterator; } elseif ($iterator instanceof \Traversable || \is_array($iterator)) { $it = new \ArrayIterator(); foreach ($iterator as $file) { $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file)); } $this->iterators[] = $it; } else { throw new \InvalidArgumentException('Finder::append() method wrong argument type.'); } return $this; } public function hasResults() { foreach ($this->getIterator() as $_) { return true; } return false; } public function count() { return iterator_count($this->getIterator()); } private function searchInDirectory($dir) { $exclude = $this->exclude; $notPaths = $this->notPaths; if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) { $exclude = array_merge($exclude, self::$vcsPatterns); } if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) { $notPaths[] = '#(^|/)\..+(/|$)#'; } $minDepth = 0; $maxDepth = \PHP_INT_MAX; foreach ($this->depths as $comparator) { switch ($comparator->getOperator()) { case '>': $minDepth = $comparator->getTarget() + 1; break; case '>=': $minDepth = $comparator->getTarget(); break; case '<': $maxDepth = $comparator->getTarget() - 1; break; case '<=': $maxDepth = $comparator->getTarget(); break; default: $minDepth = $maxDepth = $comparator->getTarget(); } } $flags = \RecursiveDirectoryIterator::SKIP_DOTS; if ($this->followLinks) { $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; } $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs); if ($exclude) { $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $exclude); } $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); if ($minDepth > 0 || $maxDepth < \PHP_INT_MAX) { $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth); } if ($this->mode) { $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode); } if ($this->names || $this->notNames) { $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames); } if ($this->contains || $this->notContains) { $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); } if ($this->sizes) { $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes); } if ($this->dates) { $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates); } if ($this->filters) { $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); } if ($this->paths || $notPaths) { $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $notPaths); } if ($this->sort) { $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); $iterator = $iteratorAggregate->getIterator(); } return $iterator; } private function normalizeDir($dir) { if ('/' === $dir) { return $dir; } $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR); if (preg_match('#^(ssh2\.)?s?ftp://#', $dir)) { $dir .= '/'; } return $dir; } } comparators = $comparators; parent::__construct($iterator); } public function accept() { $fileinfo = $this->current(); if (!$fileinfo->isFile()) { return true; } $filesize = $fileinfo->getSize(); foreach ($this->comparators as $compare) { if (!$compare->test($filesize)) { return false; } } return true; } } ignoreUnreadableDirs = $ignoreUnreadableDirs; $this->rootPath = $path; if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) { $this->directorySeparator = \DIRECTORY_SEPARATOR; } } public function current() { if (null === $subPathname = $this->subPath) { $subPathname = $this->subPath = (string) $this->getSubPath(); } if ('' !== $subPathname) { $subPathname .= $this->directorySeparator; } $subPathname .= $this->getFilename(); if ('/' !== $basePath = $this->rootPath) { $basePath .= $this->directorySeparator; } return new SplFileInfo($basePath.$subPathname, $this->subPath, $subPathname); } public function getChildren() { try { $children = parent::getChildren(); if ($children instanceof self) { $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs; $children->rewindable = &$this->rewindable; $children->rootPath = $this->rootPath; } return $children; } catch (\UnexpectedValueException $e) { if ($this->ignoreUnreadableDirs) { return new \RecursiveArrayIterator([]); } else { throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e); } } } public function rewind() { if (false === $this->isRewindable()) { return; } if (\PHP_VERSION_ID < 50523 || \PHP_VERSION_ID >= 50600 && \PHP_VERSION_ID < 50607) { parent::next(); } parent::rewind(); } public function isRewindable() { if (null !== $this->rewindable) { return $this->rewindable; } if ('' === $this->getPath()) { return $this->rewindable = false; } if (false !== $stream = @opendir($this->getPath())) { $infos = stream_get_meta_data($stream); closedir($stream); if ($infos['seekable']) { return $this->rewindable = true; } } return $this->rewindable = false; } } mode = $mode; parent::__construct($iterator); } public function accept() { $fileinfo = $this->current(); if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) { return false; } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) { return false; } return true; } } matchRegexps && !$this->noMatchRegexps) { return true; } $fileinfo = $this->current(); if ($fileinfo->isDir() || !$fileinfo->isReadable()) { return false; } $content = $fileinfo->getContents(); if (!$content) { return false; } return $this->isAccepted($content); } protected function toRegex($str) { return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; } } iterator = $iterator; $this->isRecursive = $iterator instanceof \RecursiveIterator; $patterns = []; foreach ($directories as $directory) { $directory = rtrim($directory, '/'); if (!$this->isRecursive || false !== strpos($directory, '/')) { $patterns[] = preg_quote($directory, '#'); } else { $this->excludedDirs[$directory] = true; } } if ($patterns) { $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#'; } parent::__construct($iterator); } public function accept() { if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) { return false; } if ($this->excludedPattern) { $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); $path = str_replace('\\', '/', $path); return !preg_match($this->excludedPattern, $path); } return true; } public function hasChildren() { return $this->isRecursive && $this->iterator->hasChildren(); } public function getChildren() { $children = new self($this->iterator->getChildren(), []); $children->excludedDirs = $this->excludedDirs; $children->excludedPattern = $this->excludedPattern; return $children; } } comparators = $comparators; parent::__construct($iterator); } public function accept() { $fileinfo = $this->current(); if (!file_exists($fileinfo->getPathname())) { return false; } $filedate = $fileinfo->getMTime(); foreach ($this->comparators as $compare) { if (!$compare->test($filedate)) { return false; } } return true; } } isAccepted($this->current()->getFilename()); } protected function toRegex($str) { return $this->isRegex($str) ? $str : Glob::toRegex($str); } } minDepth = $minDepth; $iterator->setMaxDepth(\PHP_INT_MAX === $maxDepth ? -1 : $maxDepth); parent::__construct($iterator); } public function accept() { return $this->getInnerIterator()->getDepth() >= $this->minDepth; } } filters = $filters; parent::__construct($iterator); } public function accept() { $fileinfo = $this->current(); foreach ($this->filters as $filter) { if (false === \call_user_func($filter, $fileinfo)) { return false; } } return true; } } matchRegexps[] = $this->toRegex($pattern); } foreach ($noMatchPatterns as $pattern) { $this->noMatchRegexps[] = $this->toRegex($pattern); } parent::__construct($iterator); } protected function isAccepted($string) { foreach ($this->noMatchRegexps as $regex) { if (preg_match($regex, $string)) { return false; } } if ($this->matchRegexps) { foreach ($this->matchRegexps as $regex) { if (preg_match($regex, $string)) { return true; } } return false; } return true; } protected function isRegex($str) { if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) { $start = substr($m[1], 0, 1); $end = substr($m[1], -1); if ($start === $end) { return !preg_match('/[*?[:alnum:] \\\\]/', $start); } foreach ([['{', '}'], ['(', ')'], ['[', ']'], ['<', '>']] as $delimiters) { if ($start === $delimiters[0] && $end === $delimiters[1]) { return true; } } } return false; } abstract protected function toRegex($str); } 50607 || (\PHP_VERSION_ID > 50523 && \PHP_VERSION_ID < 50600)) { parent::rewind(); return; } $iterator = $this; while ($iterator instanceof \OuterIterator) { $innerIterator = $iterator->getInnerIterator(); if ($innerIterator instanceof RecursiveDirectoryIterator) { if ($innerIterator->isRewindable()) { $innerIterator->next(); $innerIterator->rewind(); } } elseif ($innerIterator instanceof \FilesystemIterator) { $innerIterator->next(); $innerIterator->rewind(); } $iterator = $innerIterator; } parent::rewind(); } } current()->getRelativePathname(); if ('\\' === \DIRECTORY_SEPARATOR) { $filename = str_replace('\\', '/', $filename); } return $this->isAccepted($filename); } protected function toRegex($str) { return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; } } iterator = $iterator; if (self::SORT_BY_NAME === $sort) { $this->sort = static function ($a, $b) { return strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); }; } elseif (self::SORT_BY_TYPE === $sort) { $this->sort = static function ($a, $b) { if ($a->isDir() && $b->isFile()) { return -1; } elseif ($a->isFile() && $b->isDir()) { return 1; } return strcmp($a->getRealPath() ?: $a->getPathname(), $b->getRealPath() ?: $b->getPathname()); }; } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { $this->sort = static function ($a, $b) { return $a->getATime() - $b->getATime(); }; } elseif (self::SORT_BY_CHANGED_TIME === $sort) { $this->sort = static function ($a, $b) { return $a->getCTime() - $b->getCTime(); }; } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { $this->sort = static function ($a, $b) { return $a->getMTime() - $b->getMTime(); }; } elseif (\is_callable($sort)) { $this->sort = $sort; } else { throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.'); } } public function getIterator() { $array = iterator_to_array($this->iterator, true); uasort($array, $this->sort); return new \ArrayIterator($array); } } baseHref = $baseHref; $this->initialize(); } public function getFormNode() { return $this->node; } public function setValues(array $values) { foreach ($values as $name => $value) { $this->fields->set($name, $value); } return $this; } public function getValues() { $values = []; foreach ($this->fields->all() as $name => $field) { if ($field->isDisabled()) { continue; } if (!$field instanceof Field\FileFormField && $field->hasValue()) { $values[$name] = $field->getValue(); } } return $values; } public function getFiles() { if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) { return []; } $files = []; foreach ($this->fields->all() as $name => $field) { if ($field->isDisabled()) { continue; } if ($field instanceof Field\FileFormField) { $files[$name] = $field->getValue(); } } return $files; } public function getPhpValues() { $values = []; foreach ($this->getValues() as $name => $value) { $qs = http_build_query([$name => $value], '', '&'); if (!empty($qs)) { parse_str($qs, $expandedValue); $varName = substr($name, 0, \strlen(key($expandedValue))); $values = array_replace_recursive($values, [$varName => current($expandedValue)]); } } return $values; } public function getPhpFiles() { $values = []; foreach ($this->getFiles() as $name => $value) { $qs = http_build_query([$name => $value], '', '&'); if (!empty($qs)) { parse_str($qs, $expandedValue); $varName = substr($name, 0, \strlen(key($expandedValue))); array_walk_recursive( $expandedValue, function (&$value, $key) { if (ctype_digit($value) && ('size' === $key || 'error' === $key)) { $value = (int) $value; } } ); reset($expandedValue); $values = array_replace_recursive($values, [$varName => current($expandedValue)]); } } return $values; } public function getUri() { $uri = parent::getUri(); if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) { $query = parse_url($uri, \PHP_URL_QUERY); $currentParameters = []; if ($query) { parse_str($query, $currentParameters); } $queryString = http_build_query(array_merge($currentParameters, $this->getValues()), '', '&'); $pos = strpos($uri, '?'); $base = false === $pos ? $uri : substr($uri, 0, $pos); $uri = rtrim($base.'?'.$queryString, '?'); } return $uri; } protected function getRawUri() { if ($this->button !== $this->node && $this->button->getAttribute('formaction')) { return $this->button->getAttribute('formaction'); } return $this->node->getAttribute('action'); } public function getMethod() { if (null !== $this->method) { return $this->method; } if ($this->button !== $this->node && $this->button->getAttribute('formmethod')) { return strtoupper($this->button->getAttribute('formmethod')); } return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET'; } public function has($name) { return $this->fields->has($name); } public function remove($name) { $this->fields->remove($name); } public function get($name) { return $this->fields->get($name); } public function set(FormField $field) { $this->fields->add($field); } public function all() { return $this->fields->all(); } public function offsetExists($name) { return $this->has($name); } public function offsetGet($name) { return $this->fields->get($name); } public function offsetSet($name, $value) { $this->fields->set($name, $value); } public function offsetUnset($name) { $this->fields->remove($name); } public function disableValidation() { foreach ($this->fields->all() as $field) { if ($field instanceof Field\ChoiceFormField) { $field->disableValidation(); } } return $this; } protected function setNode(\DOMElement $node) { $this->button = $node; if ('button' === $node->nodeName || ('input' === $node->nodeName && \in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image']))) { if ($node->hasAttribute('form')) { $formId = $node->getAttribute('form'); $form = $node->ownerDocument->getElementById($formId); if (null === $form) { throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); } $this->node = $form; return; } do { if (null === $node = $node->parentNode) { throw new \LogicException('The selected node does not have a form ancestor.'); } } while ('form' !== $node->nodeName); } elseif ('form' !== $node->nodeName) { throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); } $this->node = $node; } private function initialize() { $this->fields = new FormFieldRegistry(); $xpath = new \DOMXPath($this->node->ownerDocument); if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) { $name = $this->button->getAttribute('name'); $this->button->setAttribute('value', '0'); $this->button->setAttribute('name', $name.'.x'); $this->set(new Field\InputFormField($this->button)); $this->button->setAttribute('name', $name.'.y'); $this->set(new Field\InputFormField($this->button)); $this->button->setAttribute('name', $name); } else { $this->set(new Field\InputFormField($this->button)); } } if ($this->node->hasAttribute('id')) { $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); $fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[not(ancestor::template)]', $formId)); foreach ($fieldNodes as $node) { $this->addField($node); } } else { $fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[not(ancestor::template)]', $this->node); foreach ($fieldNodes as $node) { $this->addField($node); } } if ($this->baseHref && '' !== $this->node->getAttribute('action')) { $this->currentUri = $this->baseHref; } } private function addField(\DOMElement $node) { if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { return; } $nodeName = $node->nodeName; if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) { $this->set(new Field\ChoiceFormField($node)); } elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) { if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) { $this->get($node->getAttribute('name'))->addChoice($node); } else { $this->set(new Field\ChoiceFormField($node)); } } elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) { $this->set(new Field\FileFormField($node)); } elseif ('input' == $nodeName && !\in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image'])) { $this->set(new Field\InputFormField($node)); } elseif ('textarea' == $nodeName) { $this->set(new Field\TextareaFormField($node)); } } } node->nodeName) { throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); } $this->value = ''; foreach ($this->node->childNodes as $node) { $this->value .= $node->wholeText; } } } type, ['checkbox', 'radio']) && null === $this->value) { return false; } return true; } public function isDisabled() { if (parent::isDisabled() && 'select' === $this->type) { return true; } foreach ($this->options as $option) { if ($option['value'] == $this->value && $option['disabled']) { return true; } } return false; } public function select($value) { $this->setValue($value); } public function tick() { if ('checkbox' !== $this->type) { throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); } $this->setValue(true); } public function untick() { if ('checkbox' !== $this->type) { throw new \LogicException(sprintf('You cannot untick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); } $this->setValue(false); } public function setValue($value) { if ('checkbox' === $this->type && false === $value) { $this->value = null; } elseif ('checkbox' === $this->type && true === $value) { $this->value = $this->options[0]['value']; } else { if (\is_array($value)) { if (!$this->multiple) { throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name)); } foreach ($value as $v) { if (!$this->containsOption($v, $this->options)) { throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: "%s").', $this->name, $v, implode('", "', $this->availableOptionValues()))); } } } elseif (!$this->containsOption($value, $this->options)) { throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: "%s").', $this->name, $value, implode('", "', $this->availableOptionValues()))); } if ($this->multiple) { $value = (array) $value; } if (\is_array($value)) { $this->value = $value; } else { parent::setValue($value); } } } public function addChoice(\DOMElement $node) { if (!$this->multiple && 'radio' !== $this->type) { throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name)); } $option = $this->buildOptionValue($node); $this->options[] = $option; if ($node->hasAttribute('checked')) { $this->value = $option['value']; } } public function getType() { return $this->type; } public function isMultiple() { return $this->multiple; } protected function initialize() { if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) { throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); } if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) { throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is "%s").', $this->node->getAttribute('type'))); } $this->value = null; $this->options = []; $this->multiple = false; if ('input' == $this->node->nodeName) { $this->type = strtolower($this->node->getAttribute('type')); $optionValue = $this->buildOptionValue($this->node); $this->options[] = $optionValue; if ($this->node->hasAttribute('checked')) { $this->value = $optionValue['value']; } } else { $this->type = 'select'; if ($this->node->hasAttribute('multiple')) { $this->multiple = true; $this->value = []; $this->name = str_replace('[]', '', $this->name); } $found = false; foreach ($this->xpath->query('descendant::option', $this->node) as $option) { $optionValue = $this->buildOptionValue($option); $this->options[] = $optionValue; if ($option->hasAttribute('selected')) { $found = true; if ($this->multiple) { $this->value[] = $optionValue['value']; } else { $this->value = $optionValue['value']; } } } if (!$found && !$this->multiple && !empty($this->options)) { $this->value = $this->options[0]['value']; } } } private function buildOptionValue(\DOMElement $node) { $option = []; $defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on'; $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue; $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; $option['disabled'] = $node->hasAttribute('disabled'); return $option; } public function containsOption($optionValue, $options) { if ($this->validationDisabled) { return true; } foreach ($options as $option) { if ($option['value'] == $optionValue) { return true; } } return false; } public function availableOptionValues() { $values = []; foreach ($this->options as $option) { $values[] = $option['value']; } return $values; } public function disableValidation() { $this->validationDisabled = true; return $this; } } node = $node; $this->name = $node->getAttribute('name'); $this->xpath = new \DOMXPath($node->ownerDocument); $this->initialize(); } public function getLabel() { $xpath = new \DOMXPath($this->node->ownerDocument); if ($this->node->hasAttribute('id')) { $labels = $xpath->query(sprintf('descendant::label[@for="%s"]', $this->node->getAttribute('id'))); if ($labels->length > 0) { return $labels->item(0); } } $labels = $xpath->query('ancestor::label[1]', $this->node); return $labels->length > 0 ? $labels->item(0) : null; } public function getName() { return $this->name; } public function getValue() { return $this->value; } public function setValue($value) { $this->value = (string) $value; } public function hasValue() { return true; } public function isDisabled() { return $this->node->hasAttribute('disabled'); } abstract protected function initialize(); } node->nodeName && 'button' !== $this->node->nodeName) { throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); } $type = strtolower($this->node->getAttribute('type')); if ('checkbox' === $type) { throw new \LogicException('Checkboxes should be instances of ChoiceFormField.'); } if ('file' === $type) { throw new \LogicException('File inputs should be instances of FileFormField.'); } $this->value = $this->node->getAttribute('value'); } } value = ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0]; } public function upload($value) { $this->setValue($value); } public function setValue($value) { if (null !== $value && is_readable($value)) { $error = \UPLOAD_ERR_OK; $size = filesize($value); $info = pathinfo($value); $name = $info['basename']; $tmp = sys_get_temp_dir().'/'.strtr(substr(base64_encode(hash('sha256', uniqid(mt_rand(), true), true)), 0, 7), '/', '_'); if (\array_key_exists('extension', $info)) { $tmp .= '.'.$info['extension']; } if (is_file($tmp)) { unlink($tmp); } copy($value, $tmp); $value = $tmp; } else { $error = \UPLOAD_ERR_NO_FILE; $size = 0; $name = ''; $value = ''; } $this->value = ['name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size]; } public function setFilePath($path) { parent::setValue($path); } protected function initialize() { if ('input' !== $this->node->nodeName) { throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); } if ('file' !== strtolower($this->node->getAttribute('type'))) { throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is "%s").', $this->node->getAttribute('type'))); } $this->setValue(null); } } node->getAttribute('href'); } protected function setNode(\DOMElement $node) { if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) { throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); } $this->node = $node; } } uri = $uri; $this->baseHref = $baseHref ?: $uri; $this->add($node); } public function getUri() { return $this->uri; } public function getBaseHref() { return $this->baseHref; } public function clear() { $this->nodes = []; $this->document = null; } public function add($node) { if ($node instanceof \DOMNodeList) { $this->addNodeList($node); } elseif ($node instanceof \DOMNode) { $this->addNode($node); } elseif (\is_array($node)) { $this->addNodes($node); } elseif (\is_string($node)) { $this->addContent($node); } elseif (null !== $node) { throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', \is_object($node) ? \get_class($node) : \gettype($node))); } } public function addContent($content, $type = null) { if (empty($type)) { $type = 0 === strpos($content, ']+charset *= *["\']?([a-zA-Z\-0-9_:.]+)/i', $content, $matches)) { $charset = $matches[1]; } if (null === $charset) { $charset = preg_match('//u', $content) ? 'UTF-8' : 'ISO-8859-1'; } if ('x' === $xmlMatches[1]) { $this->addXmlContent($content, $charset); } else { $this->addHtmlContent($content, $charset); } } public function addHtmlContent($content, $charset = 'UTF-8') { $internalErrors = libxml_use_internal_errors(true); if (\LIBXML_VERSION < 20900) { $disableEntities = libxml_disable_entity_loader(true); } $dom = new \DOMDocument('1.0', $charset); $dom->validateOnParse = true; set_error_handler(function () { throw new \Exception(); }); try { $content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset); } catch (\Exception $e) { } catch (\ValueError $e) { } finally { restore_error_handler(); } if ('' !== trim($content)) { @$dom->loadHTML($content); } libxml_use_internal_errors($internalErrors); if (\LIBXML_VERSION < 20900) { libxml_disable_entity_loader($disableEntities); } $this->addDocument($dom); $base = $this->filterRelativeXPath('descendant-or-self::base')->extract(['href']); $baseHref = current($base); if (\count($base) && !empty($baseHref)) { if ($this->baseHref) { $linkNode = $dom->createElement('a'); $linkNode->setAttribute('href', $baseHref); $link = new Link($linkNode, $this->baseHref); $this->baseHref = $link->getUri(); } else { $this->baseHref = $baseHref; } } } public function addXmlContent($content, $charset = 'UTF-8', $options = \LIBXML_NONET) { if (!preg_match('/xmlns:/', $content)) { $content = str_replace('xmlns', 'ns', $content); } $internalErrors = libxml_use_internal_errors(true); if (\LIBXML_VERSION < 20900) { $disableEntities = libxml_disable_entity_loader(true); } $dom = new \DOMDocument('1.0', $charset); $dom->validateOnParse = true; if ('' !== trim($content)) { @$dom->loadXML($content, $options); } libxml_use_internal_errors($internalErrors); if (\LIBXML_VERSION < 20900) { libxml_disable_entity_loader($disableEntities); } $this->addDocument($dom); $this->isHtml = false; } public function addDocument(\DOMDocument $dom) { if ($dom->documentElement) { $this->addNode($dom->documentElement); } } public function addNodeList(\DOMNodeList $nodes) { foreach ($nodes as $node) { if ($node instanceof \DOMNode) { $this->addNode($node); } } } public function addNodes(array $nodes) { foreach ($nodes as $node) { $this->add($node); } } public function addNode(\DOMNode $node) { if ($node instanceof \DOMDocument) { $node = $node->documentElement; } if (null !== $this->document && $this->document !== $node->ownerDocument) { throw new \InvalidArgumentException('Attaching DOM nodes from multiple documents in the same crawler is forbidden.'); } if (null === $this->document) { $this->document = $node->ownerDocument; } if (\in_array($node, $this->nodes, true)) { return; } $this->nodes[] = $node; } public function eq($position) { if (isset($this->nodes[$position])) { return $this->createSubCrawler($this->nodes[$position]); } return $this->createSubCrawler(null); } public function each(\Closure $closure) { $data = []; foreach ($this->nodes as $i => $node) { $data[] = $closure($this->createSubCrawler($node), $i); } return $data; } public function slice($offset = 0, $length = null) { return $this->createSubCrawler(\array_slice($this->nodes, $offset, $length)); } public function reduce(\Closure $closure) { $nodes = []; foreach ($this->nodes as $i => $node) { if (false !== $closure($this->createSubCrawler($node), $i)) { $nodes[] = $node; } } return $this->createSubCrawler($nodes); } public function first() { return $this->eq(0); } public function last() { return $this->eq(\count($this->nodes) - 1); } public function siblings() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } return $this->createSubCrawler($this->sibling($this->getNode(0)->parentNode->firstChild)); } public function nextAll() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } return $this->createSubCrawler($this->sibling($this->getNode(0))); } public function previousAll() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } return $this->createSubCrawler($this->sibling($this->getNode(0), 'previousSibling')); } public function parents() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } $node = $this->getNode(0); $nodes = []; while ($node = $node->parentNode) { if (\XML_ELEMENT_NODE === $node->nodeType) { $nodes[] = $node; } } return $this->createSubCrawler($nodes); } public function children() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } $node = $this->getNode(0)->firstChild; return $this->createSubCrawler($node ? $this->sibling($node) : []); } public function attr($attribute) { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } $node = $this->getNode(0); return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null; } public function nodeName() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } return $this->getNode(0)->nodeName; } public function text() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } return $this->getNode(0)->nodeValue; } public function html() { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } $html = ''; foreach ($this->getNode(0)->childNodes as $child) { $html .= $child->ownerDocument->saveHTML($child); } return $html; } public function evaluate($xpath) { if (null === $this->document) { throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.'); } $data = []; $domxpath = $this->createDOMXPath($this->document, $this->findNamespacePrefixes($xpath)); foreach ($this->nodes as $node) { $data[] = $domxpath->evaluate($xpath, $node); } if (isset($data[0]) && $data[0] instanceof \DOMNodeList) { return $this->createSubCrawler($data); } return $data; } public function extract($attributes) { $attributes = (array) $attributes; $count = \count($attributes); $data = []; foreach ($this->nodes as $node) { $elements = []; foreach ($attributes as $attribute) { if ('_text' === $attribute) { $elements[] = $node->nodeValue; } else { $elements[] = $node->getAttribute($attribute); } } $data[] = 1 === $count ? $elements[0] : $elements; } return $data; } public function filterXPath($xpath) { $xpath = $this->relativize($xpath); if ('' === $xpath) { return $this->createSubCrawler(null); } return $this->filterRelativeXPath($xpath); } public function filter($selector) { if (!class_exists(CssSelectorConverter::class)) { throw new \RuntimeException('To filter with a CSS selector, install the CssSelector component ("composer require symfony/css-selector"). Or use filterXpath instead.'); } $converter = new CssSelectorConverter($this->isHtml); return $this->filterRelativeXPath($converter->toXPath($selector)); } public function selectLink($value) { $xpath = sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) ', static::xpathLiteral(' '.$value.' ')). sprintf('or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]]', static::xpathLiteral(' '.$value.' ')); return $this->filterRelativeXPath($xpath); } public function selectImage($value) { $xpath = sprintf('descendant-or-self::img[contains(normalize-space(string(@alt)), %s)]', static::xpathLiteral($value)); return $this->filterRelativeXPath($xpath); } public function selectButton($value) { $translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")'; $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%1$s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, static::xpathLiteral(' '.$value.' ')). sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)). sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)); return $this->filterRelativeXPath($xpath); } public function link($method = 'get') { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } $node = $this->getNode(0); if (!$node instanceof \DOMElement) { throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', \get_class($node))); } return new Link($node, $this->baseHref, $method); } public function links() { $links = []; foreach ($this->nodes as $node) { if (!$node instanceof \DOMElement) { throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', \get_class($node))); } $links[] = new Link($node, $this->baseHref, 'get'); } return $links; } public function image() { if (!\count($this)) { throw new \InvalidArgumentException('The current node list is empty.'); } $node = $this->getNode(0); if (!$node instanceof \DOMElement) { throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', \get_class($node))); } return new Image($node, $this->baseHref); } public function images() { $images = []; foreach ($this as $node) { if (!$node instanceof \DOMElement) { throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', \get_class($node))); } $images[] = new Image($node, $this->baseHref); } return $images; } public function form(array $values = null, $method = null) { if (!$this->nodes) { throw new \InvalidArgumentException('The current node list is empty.'); } $node = $this->getNode(0); if (!$node instanceof \DOMElement) { throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', \get_class($node))); } $form = new Form($node, $this->uri, $method, $this->baseHref); if (null !== $values) { $form->setValues($values); } return $form; } public function setDefaultNamespacePrefix($prefix) { $this->defaultNamespacePrefix = $prefix; } public function registerNamespace($prefix, $namespace) { $this->namespaces[$prefix] = $namespace; } public static function xpathLiteral($s) { if (false === strpos($s, "'")) { return sprintf("'%s'", $s); } if (false === strpos($s, '"')) { return sprintf('"%s"', $s); } $string = $s; $parts = []; while (true) { if (false !== $pos = strpos($string, "'")) { $parts[] = sprintf("'%s'", substr($string, 0, $pos)); $parts[] = "\"'\""; $string = substr($string, $pos + 1); } else { $parts[] = "'$string'"; break; } } return sprintf('concat(%s)', implode(', ', $parts)); } private function filterRelativeXPath($xpath) { $prefixes = $this->findNamespacePrefixes($xpath); $crawler = $this->createSubCrawler(null); foreach ($this->nodes as $node) { $domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes); $crawler->add($domxpath->query($xpath, $node)); } return $crawler; } private function relativize($xpath) { $expressions = []; $nonMatchingExpression = 'a[name() = "b"]'; $xpathLen = \strlen($xpath); $openedBrackets = 0; $startPosition = strspn($xpath, " \t\n\r\0\x0B"); for ($i = $startPosition; $i <= $xpathLen; ++$i) { $i += strcspn($xpath, '"\'[]|', $i); if ($i < $xpathLen) { switch ($xpath[$i]) { case '"': case "'": if (false === $i = strpos($xpath, $xpath[$i], $i + 1)) { return $xpath; } continue 2; case '[': ++$openedBrackets; continue 2; case ']': --$openedBrackets; continue 2; } } if ($openedBrackets) { continue; } if ($startPosition < $xpathLen && '(' === $xpath[$startPosition]) { $j = 1 + strspn($xpath, "( \t\n\r\0\x0B", $startPosition + 1); $parenthesis = substr($xpath, $startPosition, $j); $startPosition += $j; } else { $parenthesis = ''; } $expression = rtrim(substr($xpath, $startPosition, $i - $startPosition)); if (0 === strpos($expression, 'self::*/')) { $expression = './'.substr($expression, 8); } if ('' === $expression) { $expression = $nonMatchingExpression; } elseif (0 === strpos($expression, '//')) { $expression = 'descendant-or-self::'.substr($expression, 2); } elseif (0 === strpos($expression, './/')) { $expression = 'descendant-or-self::'.substr($expression, 3); } elseif (0 === strpos($expression, './')) { $expression = 'self::'.substr($expression, 2); } elseif (0 === strpos($expression, 'child::')) { $expression = 'self::'.substr($expression, 7); } elseif ('/' === $expression[0] || '.' === $expression[0] || 0 === strpos($expression, 'self::')) { $expression = $nonMatchingExpression; } elseif (0 === strpos($expression, 'descendant::')) { $expression = 'descendant-or-self::'.substr($expression, 12); } elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) { $expression = $nonMatchingExpression; } elseif (0 !== strpos($expression, 'descendant-or-self::')) { $expression = 'self::'.$expression; } $expressions[] = $parenthesis.$expression; if ($i === $xpathLen) { return implode(' | ', $expressions); } $i += strspn($xpath, " \t\n\r\0\x0B", $i + 1); $startPosition = $i + 1; } return $xpath; } public function getNode($position) { return isset($this->nodes[$position]) ? $this->nodes[$position] : null; } public function count() { return \count($this->nodes); } public function getIterator() { return new \ArrayIterator($this->nodes); } protected function sibling($node, $siblingDir = 'nextSibling') { $nodes = []; do { if ($node !== $this->getNode(0) && 1 === $node->nodeType) { $nodes[] = $node; } } while ($node = $node->$siblingDir); return $nodes; } private function createDOMXPath(\DOMDocument $document, array $prefixes = []) { $domxpath = new \DOMXPath($document); foreach ($prefixes as $prefix) { $namespace = $this->discoverNamespace($domxpath, $prefix); if (null !== $namespace) { $domxpath->registerNamespace($prefix, $namespace); } } return $domxpath; } private function discoverNamespace(\DOMXPath $domxpath, $prefix) { if (isset($this->namespaces[$prefix])) { return $this->namespaces[$prefix]; } $namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix)); return ($node = $namespaces->item(0)) ? $node->nodeValue : null; } private function findNamespacePrefixes($xpath) { if (preg_match_all('/(?P[a-z_][a-z_0-9\-\.]*+):[^"\/:]/i', $xpath, $matches)) { return array_unique($matches['prefix']); } return []; } private function createSubCrawler($nodes) { $crawler = new static($nodes, $this->uri, $this->baseHref); $crawler->isHtml = $this->isHtml; $crawler->document = $this->document; $crawler->namespaces = $this->namespaces; return $crawler; } } node->getAttribute('src'); } protected function setNode(\DOMElement $node) { if ('img' !== $node->nodeName) { throw new \LogicException(sprintf('Unable to visualize a "%s" tag.', $node->nodeName)); } $this->node = $node; } } getSegments($field->getName()); $target = &$this->fields; while ($segments) { if (!\is_array($target)) { $target = []; } $path = array_shift($segments); if ('' === $path) { $target = &$target[]; } else { $target = &$target[$path]; } } $target = $field; } public function remove($name) { $segments = $this->getSegments($name); $target = &$this->fields; while (\count($segments) > 1) { $path = array_shift($segments); if (!\is_array($target) || !\array_key_exists($path, $target)) { return; } $target = &$target[$path]; } unset($target[array_shift($segments)]); } public function &get($name) { $segments = $this->getSegments($name); $target = &$this->fields; while ($segments) { $path = array_shift($segments); if (!\is_array($target) || !\array_key_exists($path, $target)) { throw new \InvalidArgumentException(sprintf('Unreachable field "%s".', $path)); } $target = &$target[$path]; } return $target; } public function has($name) { try { $this->get($name); return true; } catch (\InvalidArgumentException $e) { return false; } } public function set($name, $value) { $target = &$this->get($name); if ((!\is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) { $target->setValue($value); } elseif (\is_array($value)) { $registry = new static(); $registry->base = $name; $registry->fields = $value; foreach ($registry->all() as $k => $v) { $this->set($k, $v); } } else { throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name)); } } public function all() { return $this->walk($this->fields, $this->base); } private function walk(array $array, $base = '', array &$output = []) { foreach ($array as $k => $v) { $path = empty($base) ? $k : sprintf('%s[%s]', $base, $k); if (\is_array($v)) { $this->walk($v, $path, $output); } else { $output[$path] = $v; } } return $output; } private function getSegments($name) { if (preg_match('/^(?P[^[]+)(?P(\[.*)|$)/', $name, $m)) { $segments = [$m['base']]; while (!empty($m['extra'])) { $extra = $m['extra']; if (preg_match('/^\[(?P.*?)\](?P.*)$/', $extra, $m)) { $segments[] = $m['segment']; } else { $segments[] = $extra; } } return $segments; } return [$name]; } } setNode($node); $this->method = $method ? strtoupper($method) : null; $this->currentUri = $currentUri; } public function getNode() { return $this->node; } public function getMethod() { return $this->method; } public function getUri() { $uri = trim($this->getRawUri()); if (null !== parse_url($uri, \PHP_URL_SCHEME)) { return $uri; } if (!$uri) { return $this->currentUri; } if ('#' === $uri[0]) { return $this->cleanupAnchor($this->currentUri).$uri; } $baseUri = $this->cleanupUri($this->currentUri); if ('?' === $uri[0]) { return $baseUri.$uri; } if (0 === strpos($uri, '//')) { return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri; } $baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri); if ('/' === $uri[0]) { return $baseUri.$uri; } $path = parse_url(substr($this->currentUri, \strlen($baseUri)), \PHP_URL_PATH); $path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri); return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path; } abstract protected function getRawUri(); protected function canonicalizePath($path) { if ('' === $path || '/' === $path) { return $path; } if ('.' === substr($path, -1)) { $path .= '/'; } $output = []; foreach (explode('/', $path) as $segment) { if ('..' === $segment) { array_pop($output); } elseif ('.' !== $segment) { $output[] = $segment; } } return implode('/', $output); } abstract protected function setNode(\DOMElement $node); private function cleanupUri($uri) { return $this->cleanupQuery($this->cleanupAnchor($uri)); } private function cleanupQuery($uri) { if (false !== $pos = strpos($uri, '?')) { return substr($uri, 0, $pos); } return $uri; } private function cleanupAnchor($uri) { if (false !== $pos = strpos($uri, '#')) { return substr($uri, 0, $pos); } return $uri; } } = 70000) { return; } if (!defined('PHP_INT_MIN')) { define('PHP_INT_MIN', ~PHP_INT_MAX); } if (!function_exists('intdiv')) { function intdiv($num1, $num2) { return p\Php70::intdiv($num1, $num2); } } if (!function_exists('preg_replace_callback_array')) { function preg_replace_callback_array(array $pattern, $subject, $limit = -1, &$count = 0, $flags = null) { return p\Php70::preg_replace_callback_array($pattern, $subject, $limit, $count); } } if (!function_exists('error_clear_last')) { function error_clear_last() { return p\Php70::error_clear_last(); } } $callback) { $result = preg_replace_callback($pattern, $callback, $result, $limit, $c); $count += $c; } return $result; } public static function error_clear_last() { static $handler; if (!$handler) { $handler = function () { return false; }; } set_error_handler($handler); @trigger_error(''); restore_error_handler(); } private static function intArg($value, $caller, $pos) { if (\is_int($value)) { return $value; } if (!\is_numeric($value) || PHP_INT_MAX <= ($value += 0) || ~PHP_INT_MAX >= $value) { throw new \TypeError(sprintf('%s() expects parameter %d to be integer, %s given', $caller, $pos, \gettype($value))); } return (int) $value; } } throwAt(0, true); } DebugClassLoader::enable(); } } debug = $debug; $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; $this->fileLinkFormat = $fileLinkFormat; } public static function register($debug = true, $charset = null, $fileLinkFormat = null) { $handler = new static($debug, $charset, $fileLinkFormat); $prev = set_exception_handler([$handler, 'handle']); if (\is_array($prev) && $prev[0] instanceof ErrorHandler) { restore_exception_handler(); $prev[0]->setExceptionHandler([$handler, 'handle']); } return $handler; } public function setHandler(callable $handler = null) { $old = $this->handler; $this->handler = $handler; return $old; } public function setFileLinkFormat($fileLinkFormat) { $old = $this->fileLinkFormat; $this->fileLinkFormat = $fileLinkFormat; return $old; } 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()) { } if (isset($this->caughtBuffer[0])) { ob_start(function ($buffer) { if ($this->caughtLength) { $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) { throw $exception; } } } 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)); } public function getHtml($exception) { if (!$exception instanceof FlattenException) { $exception = FlattenException::create($exception); } return $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); } 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.'; } if (!$this->debug) { return <<

$title

EOF; } $content = ''; 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'
EOF , $ind, $total, $class, $message); foreach ($e['trace'] as $trace) { $content .= '\n"; } $content .= "\n

(%d/%d) %s

%s

'; if ($trace['function']) { $content .= sprintf('at %s%s%s(%s)', $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 .= "
\n
\n"; } } catch (\Exception $e) { 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 <<

$title

$symfonyGhostImageContents
$content
EOF; } public function getStylesheet(FlattenException $exception) { if (!$this->debug) { return <<<'EOF' body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; } .container { margin: 30px; max-width: 600px; } h1 { color: #dc3545; font-size: 24px; } EOF; } 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 << $content EOF; } private function formatClass($class) { $parts = explode('\\', $class); return sprintf('%s', $class, array_pop($parts)); } private function formatPath($path, $line) { $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path); $fmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); if (!$fmt) { return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); } if (\is_string($fmt)) { $i = strpos($f = $fmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f); $fmt = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE); for ($i = 1; isset($fmt[$i]); ++$i) { if (0 === strpos($path, $k = $fmt[$i++])) { $path = substr_replace($path, $fmt[$i], 0, \strlen($k)); break; } } $link = strtr($fmt[0], ['%f' => $path, '%l' => $line]); } else { try { $link = $fmt->format($path, $line); } catch (\Exception $e) { return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); } } return sprintf('in %s%s', $this->escapeHtml($link), $file, 0 < $line ? ' line '.$line : ''); } private function formatArgs(array $args) { $result = []; foreach ($args as $key => $item) { if ('object' === $item[0]) { $formattedValue = sprintf('object(%s)', $this->formatClass($item[1])); } elseif ('array' === $item[0]) { $formattedValue = sprintf('array(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); } elseif ('null' === $item[0]) { $formattedValue = 'null'; } elseif ('boolean' === $item[0]) { $formattedValue = ''.strtolower(var_export($item[1], true)).''; } elseif ('resource' === $item[0]) { $formattedValue = 'resource'; } 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); } private function escapeHtml($str) { return htmlspecialchars($str, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset); } private function getSymfonyGhostAsSvg() { return ''; } } 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1]; private static $darwinCache = ['/' => ['/', []]]; 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) { self::$caseCheck = 0; } elseif (substr($test, -\strlen($file)) === $file) { self::$caseCheck = 1; } elseif (false !== stripos(\PHP_OS, 'darwin')) { self::$caseCheck = 2; } else { self::$caseCheck = 0; } } } public function getClassLoader() { return $this->classLoader; } public static function enable() { 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 = [new static($function), 'loadClass']; } spl_autoload_register($function); } } 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); } } public function findFile($class) { return $this->isFinder ? $this->classLoader[0]->findFile($class) ?: null : null; } 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) { } elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) { include $file; return; } elseif (false === include $file) { 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)); } $deprecations = $this->checkAnnotations($refl, $name); if (isset(self::$php7Reserved[strtolower($refl->getShortName())])) { $deprecations[] = sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()); } foreach ($deprecations as $message) { @trigger_error($message, \E_USER_DEPRECATED); } } if (!$file) { return; } 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 && $message = $this->checkCase($refl, $file, $class)) { throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2])); } } public function checkAnnotations(\ReflectionClass $refl, $class) { $deprecations = []; if (2 > $len = 1 + (strpos($class, '\\') ?: strpos($class, '_'))) { $len = 0; $ns = ''; } else { $ns = str_replace('_', '\\', substr($class, 0, $len)); } if (false !== $doc = $refl->getDocComment()) { foreach (['final', 'deprecated', 'internal'] as $annotation) { if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) { self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; } } } $parent = get_parent_class($class); $parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent); if ($parent) { $parentAndOwnInterfaces[$parent] = $parent; if (!isset(self::$checkedClasses[$parent])) { $this->checkClass($parent); } if (isset(self::$final[$parent])) { $deprecations[] = 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], $class); } } foreach ($parentAndOwnInterfaces + class_uses($class, false) as $use) { if (!isset(self::$checkedClasses[$use])) { $this->checkClass($use); } if (isset(self::$deprecated[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len) && !isset(self::$deprecated[$class])) { $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]); } if (isset(self::$internal[$use]) && strncmp($ns, str_replace('_', '\\', $use), $len)) { $deprecations[] = 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], $class); } } if (trait_exists($class)) { return $deprecations; } self::$finalMethods[$class] = []; self::$internalMethods[$class] = []; foreach ($parentAndOwnInterfaces as $use) { foreach (['finalMethods', 'internalMethods'] as $property) { if (isset(self::${$property}[$use])) { self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use]; } } } foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { if ($method->class !== $class) { continue; } if ($parent && isset(self::$finalMethods[$parent][$method->name])) { list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; $deprecations[] = 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, $class); } if (isset(self::$internalMethods[$class][$method->name])) { list($declaringClass, $message) = self::$internalMethods[$class][$method->name]; if (strncmp($ns, $declaringClass, $len)) { $deprecations[] = 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, $class); } } if (false === $doc = $method->getDocComment()) { continue; } foreach (['final', 'internal'] as $annotation) { if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) { $message = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; self::${$annotation.'Methods'}[$class][$method->name] = [$class, $message]; } } } return $deprecations; } public function checkCase(\ReflectionClass $refl, $file, $class) { $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 (!$tail) { return null; } $tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail); $tailLen = \strlen($tail); $real = $refl->getFileName(); if (2 === self::$caseCheck) { $real = $this->darwinRealpath($real); } if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) ) { return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)]; } return null; } private function darwinRealpath($real) { $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] = [$dir, []]; 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]) && ') : eval()\'d code' === substr($file, -17)) { $file = substr($file, 0, strrpos($file, '(', -17)); } if (isset($dirFiles[$file])) { return $real.$dirFiles[$file]; } $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; } return $real.$dirFiles[$kFile]; } 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; } } '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 = [ \E_DEPRECATED => [null, LogLevel::INFO], \E_USER_DEPRECATED => [null, LogLevel::INFO], \E_NOTICE => [null, LogLevel::WARNING], \E_USER_NOTICE => [null, LogLevel::WARNING], \E_STRICT => [null, LogLevel::WARNING], \E_WARNING => [null, LogLevel::WARNING], \E_USER_WARNING => [null, LogLevel::WARNING], \E_COMPILE_WARNING => [null, LogLevel::WARNING], \E_CORE_WARNING => [null, LogLevel::WARNING], \E_USER_ERROR => [null, LogLevel::CRITICAL], \E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], \E_COMPILE_ERROR => [null, LogLevel::CRITICAL], \E_PARSE => [null, LogLevel::CRITICAL], \E_ERROR => [null, LogLevel::CRITICAL], \E_CORE_ERROR => [null, LogLevel::CRITICAL], ]; private $thrownErrors = 0x1FFF; private $scopedErrors = 0x1FFF; private $tracedErrors = 0x77FB; private $screamedErrors = 0x55; private $loggedErrors = 0; private $traceReflector; private $isRecursive = 0; private $isRoot = false; private $exceptionHandler; private $bootstrappingLogger; private static $reservedMemory; private static $stackedErrors = []; private static $stackedErrorLevels = []; private static $toStringException = null; private static $silencedErrorCache = []; private static $silencedErrorCount = 0; private static $exitCode = 0; 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([$handler, 'handleError'])) { restore_error_handler(); set_error_handler([$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(); $handlerIsRegistered = \is_array($prev) && $handler === $prev[0]; } else { $handlerIsRegistered = true; } if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) { restore_exception_handler(); if (!$handlerIsRegistered) { $handler = $prev[0]; } elseif ($handler !== $prev[0] && $replace) { set_exception_handler([$handler, 'handleException']); $p = $prev[0]->setExceptionHandler(null); $handler->setExceptionHandler($p); $prev[0]->setExceptionHandler($p); } } 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); } public function setDefaultLogger(LoggerInterface $logger, $levels = \E_ALL, $replace = false) { $loggers = []; if (\is_array($levels)) { foreach ($levels as $type => $logLevel) { if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { $loggers[$type] = [$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); } public function setLoggers(array $loggers) { $prevLogged = $this->loggedErrors; $prev = $this->loggers; $flush = []; foreach ($loggers as $type => $log) { if (!isset($prev[$type])) { throw new \InvalidArgumentException('Unknown error type: '.$type); } if (!\is_array($log)) { $log = [$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; } public function setExceptionHandler(callable $handler = null) { $prev = $this->exceptionHandler; $this->exceptionHandler = $handler; return $prev; } 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; } public function scopeAt($levels, $replace = false) { $prev = $this->scopedErrors; $this->scopedErrors = (int) $levels; if (!$replace) { $this->scopedErrors |= $prev; } return $prev; } public function traceAt($levels, $replace = false) { $prev = $this->tracedErrors; $this->tracedErrors = (int) $levels; if (!$replace) { $this->tracedErrors |= $prev; } return $prev; } public function screamAt($levels, $replace = false) { $prev = $this->screamedErrors; $this->screamedErrors = (int) $levels; if (!$replace) { $this->screamedErrors |= $prev; } return $prev; } 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([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors); } else { set_error_handler([$this, 'handleError']); } } } } public function handleError($type, $message, $file, $line) { if (\PHP_VERSION_ID >= 70300 && \E_WARNING === $type && '"' === $message[0] && false !== strpos($message, '" targeting switch is equivalent to "break')) { $type = \E_DEPRECATED; } $level = error_reporting(); $silenced = 0 === ($level & $type); $level |= \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 !$silenced && $type && $log; } $scope = $this->scopedErrors & $type; if (4 < $numArgs = \func_num_args()) { $context = func_get_arg(4) ?: []; $backtrace = 5 < $numArgs ? func_get_arg(5) : null; } else { $context = []; $backtrace = null; } if (isset($context['GLOBALS']) && $scope) { $e = $context; unset($e['GLOBALS'], $context); $context = $e; } if (null !== $backtrace && $type & \E_ERROR) { $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) : []; $errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace); } elseif (isset(self::$silencedErrorCache[$id][$message])) { $lightTrace = null; $errorAsException = self::$silencedErrorCache[$id][$message]; ++$errorAsException->count; } else { $lightTrace = []; $errorAsException = null; } if (100 < ++self::$silencedErrorCount) { self::$silencedErrorCache = $lightTrace = []; self::$silencedErrorCount = 1; } if ($errorAsException) { self::$silencedErrorCache[$id][$message] = $errorAsException; } if (null === $lightTrace) { return true; } } else { if ($scope) { $errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context); } else { $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); } 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, []); } } if ($throw) { if (\PHP_VERSION_ID < 70400 && \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']) ) { foreach ($context as $e) { if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { if (1 === $i) { $errorAsException = $e; break; } self::$toStringException = $e; return true; } } if (1 < $i) { $this->handleException($errorAsException); return false; } } } } throw $errorAsException; } if ($this->isRecursive) { $log = 0; } elseif (self::$stackedErrorLevels) { self::$stackedErrors[] = [ $this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $logMessage, $errorAsException ? ['exception' => $errorAsException] : [], ]; } else { if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) { $currentErrorHandler = set_error_handler('var_dump'); restore_error_handler(); } try { $this->isRecursive = true; $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []); } finally { $this->isRecursive = false; if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) { set_error_handler($currentErrorHandler); } } } return !$silenced && $type && $log; } 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 = [ '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, ['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; } } } $exceptionHandler = $this->exceptionHandler; $this->exceptionHandler = null; try { if (null !== $exceptionHandler) { $exceptionHandler($exception); return; } $handlerException = $handlerException ?: $exception; } catch (\Exception $handlerException) { } catch (\Throwable $handlerException) { } if ($exception === $handlerException) { self::$reservedMemory = null; throw $exception; } $this->handleException($handlerException); } public static function handleFatalError(array $error = null) { if (null === self::$reservedMemory) { return; } $handler = self::$reservedMemory = null; $handlers = []; $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 = []; if ($exit = null === $error) { $error = error_get_last(); } try { while (self::$stackedErrorLevels) { static::unstackErrors(); } } catch (\Exception $exception) { } catch (\Throwable $exception) { } if ($error && $error['type'] &= \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) { $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) { } if ($exit && self::$exitCode) { $exitCode = self::$exitCode; register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); } } 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); } 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)) { error_reporting($errorReportingLevel); } } if (empty(self::$stackedErrorLevels)) { $errors = self::$stackedErrors; self::$stackedErrors = []; foreach ($errors as $error) { $error[0]->log($error[1], $error[2], $error[3]); } } } protected function getFatalErrorHandlers() { return [ 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; } } 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); } private function getClassCandidates($class) { if (!\is_array($functions = spl_autoload_functions())) { return []; } $classes = []; foreach ($functions as $function) { if (!\is_array($function)) { continue; } 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); } private function findClassInPath($path, $class, $prefix) { if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { return []; } $classes = []; $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; } private function convertFileToClass($path, $file, $prefix) { $candidates = [ $namespacedClass = str_replace([$path.\DIRECTORY_SEPARATOR, '.php', '/'], ['', '', '\\'], $file), $prefix.$namespacedClass, $prefix.'\\'.$namespacedClass, str_replace('\\', '_', $namespacedClass), str_replace('\\', '_', $prefix.$namespacedClass), str_replace('\\', '_', $prefix.'\\'.$namespacedClass), ]; if ($prefix) { $candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); }); } foreach ($candidates as $candidate) { if ($this->classExists($candidate)) { return $candidate; } } try { require_once $file; } catch (\Throwable $e) { return null; } foreach ($candidates as $candidate) { if ($this->classExists($candidate)) { return $candidate; } } return null; } private function classExists($class) { return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); } } $messageLen) { return null; } if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { return null; } $prefix = 'Call to undefined function '; $prefixLen = \strlen($prefix); if (0 !== strpos($error['message'], $prefix)) { return null; } $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 = []; 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); } } logs[] = [$level, $message, $context]; } public function cleanLogs() { $logs = $this->logs; $this->logs = []; return $logs; } } context = $context; } 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; } } getCode(), $previous->getSeverity(), $previous->getFile(), $previous->getLine(), null, true, null, $previous->getPrevious() ); $this->setTrace($previous->getTrace()); } } 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 [ 'severity' => $this->severity, 'file' => $this->file, 'line' => $this->line, 'trace' => $this->trace, 'count' => $this->count, ]; } } 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'])) { if (isset($frame['class'])) { $frame['type'] = '::'; } } elseif ('dynamic' === $frame['type']) { $frame['type'] = '->'; } elseif ('static' === $frame['type']) { $frame['type'] = '::'; } 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 = []; } $this->setTrace($trace); } } protected function setTrace($trace) { $traceReflector = new \ReflectionProperty('Exception', 'trace'); $traceReflector->setAccessible(true); $traceReflector->setValue($this, $trace); } } 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(), $e->getPrevious() ); $this->setTrace($e->getTrace()); } } 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 = []; foreach (array_merge([$this], $this->getAllPrevious()) as $exception) { $exceptions[] = [ '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(self $previous) { $this->previous = $previous; } public function getAllPrevious() { $exceptions = []; $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 = []; $this->trace[] = [ 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => $file, 'line' => $line, 'args' => [], ]; foreach ($trace as $entry) { $class = ''; $namespace = ''; if (isset($entry['class'])) { $parts = explode('\\', $entry['class']); $class = array_pop($parts); $namespace = implode('\\', $parts); } $this->trace[] = [ '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']) : [], ]; } } private function flattenArgs($args, $level = 0, &$count = 0) { $result = []; foreach ($args as $key => $value) { if (++$count > 1e4) { return ['array', '*SKIPPED over 10000 entries*']; } if ($value instanceof \__PHP_Incomplete_Class) { $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)]; } elseif (\is_object($value)) { $result[$key] = ['object', \get_class($value)]; } elseif (\is_array($value)) { if ($level > 10) { $result[$key] = ['array', '*DEEP NESTED ARRAY*']; } else { $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)]; } } elseif (null === $value) { $result[$key] = ['null', null]; } elseif (\is_bool($value)) { $result[$key] = ['boolean', $value]; } elseif (\is_int($value)) { $result[$key] = ['integer', $value]; } elseif (\is_float($value)) { $result[$key] = ['float', $value]; } elseif (\is_resource($value)) { $result[$key] = ['resource', get_resource_type($value)]; } else { $result[$key] = ['string', (string) $value]; } } return $result; } private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) { $array = new \ArrayObject($value); return $array['__PHP_Incomplete_Class_Name']; } } getCode(), $previous->getSeverity(), $previous->getFile(), $previous->getLine(), null, true, null, $previous->getPrevious() ); $this->setTrace($previous->getTrace()); } } getCode(), $previous->getSeverity(), $previous->getFile(), $previous->getLine(), null, true, null, $previous->getPrevious() ); $this->setTrace($previous->getTrace()); } } setError($error); } public function getError() { return $this->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; } public function setExitCode($exitCode) { $this->exitCode = (int) $exitCode; $r = new \ReflectionProperty($this->error, 'code'); $r->setAccessible(true); $r->setValue($this->error, $this->exitCode); } public function getExitCode() { return null !== $this->exitCode ? $this->exitCode : (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1); } } commandShouldRun = false; } public function enableCommand() { return $this->commandShouldRun = true; } public function commandShouldRun() { return $this->commandShouldRun; } } setException($exception); $this->exitCode = (int) $exitCode; } public function getException() { return $this->exception; } public function setException(\Exception $exception) { $this->exception = $exception; } public function getExitCode() { return $this->exitCode; } } setExitCode($exitCode); } public function setExitCode($exitCode) { $this->exitCode = (int) $exitCode; } public function getExitCode() { return $this->exitCode; } } command = $command; $this->input = $input; $this->output = $output; } public function getCommand() { return $this->command; } public function getInput() { return $this->input; } public function getOutput() { return $this->output; } } 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()); } } public function setDecorated($decorated) { parent::setDecorated($decorated); $this->stderr->setDecorated($decorated); } public function setFormatter(OutputFormatterInterface $formatter) { parent::setFormatter($formatter); $this->stderr->setFormatter($formatter); } public function setVerbosity($level) { parent::setVerbosity($level); $this->stderr->setVerbosity($level); } public function getErrorOutput() { return $this->stderr; } public function setErrorOutput(OutputInterface $error) { $this->stderr = $error; } protected function hasStdoutSupport() { return false === $this->isRunningOS400(); } protected function hasStderrSupport() { return false === $this->isRunningOS400(); } private function isRunningOS400() { $checks = [ \function_exists('php_uname') ? php_uname('s') : '', getenv('OSTYPE'), \PHP_OS, ]; return false !== stripos(implode(';', $checks), 'OS400'); } private function openOutputStream() { if (!$this->hasStdoutSupport()) { return fopen('php://output', 'w'); } return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w'); } private function openErrorStream() { return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w'); } } buffer; $this->buffer = ''; return $content; } protected function doWrite($message, $newline) { $this->buffer .= $message; if ($newline) { $this->buffer .= \PHP_EOL; } } } verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; $this->formatter = $formatter ?: new OutputFormatter(); $this->formatter->setDecorated($decorated); } public function setFormatter(OutputFormatterInterface $formatter) { $this->formatter = $formatter; } public function getFormatter() { return $this->formatter; } public function setDecorated($decorated) { $this->formatter->setDecorated($decorated); } public function isDecorated() { return $this->formatter->isDecorated(); } public function setVerbosity($level) { $this->verbosity = (int) $level; } public function getVerbosity() { return $this->verbosity; } public function isQuiet() { return self::VERBOSITY_QUIET === $this->verbosity; } public function isVerbose() { return self::VERBOSITY_VERBOSE <= $this->verbosity; } public function isVeryVerbose() { return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; } public function isDebug() { return self::VERBOSITY_DEBUG <= $this->verbosity; } public function writeln($messages, $options = self::OUTPUT_NORMAL) { $this->write($messages, true, $options); } 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); } } abstract protected function doWrite($message, $newline); } stream = $stream; if (null === $decorated) { $decorated = $this->hasColorSupport(); } parent::__construct($verbosity, $decorated, $formatter); } public function getStream() { return $this->stream; } protected function doWrite($message, $newline) { if ($newline) { $message .= \PHP_EOL; } @fwrite($this->stream, $message); fflush($this->stream); } protected function hasColorSupport() { if ('Hyper' === getenv('TERM_PROGRAM')) { return true; } if (\DIRECTORY_SEPARATOR === '\\') { return (\function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support($this->stream)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } if (\function_exists('stream_isatty')) { return @stream_isatty($this->stream); } if (\function_exists('posix_isatty')) { return @posix_isatty($this->stream); } $stat = @fstat($this->stream); return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } } &1', $output, $exitcode); return self::$stty = 0 === $exitcode; } private static function initDimensions() { if ('\\' === \DIRECTORY_SEPARATOR) { if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) { self::$width = (int) $matches[1]; self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2]; } elseif (!self::hasVt100Support() && self::hasSttyAvailable()) { self::initDimensionsUsingStty(); } elseif (null !== $dimensions = self::getConsoleMode()) { self::$width = (int) $dimensions[0]; self::$height = (int) $dimensions[1]; } } else { self::initDimensionsUsingStty(); } } private static function hasVt100Support() { return \function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'w')); } private static function initDimensionsUsingStty() { if ($sttyString = self::getSttyColumns()) { if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { self::$width = (int) $matches[2]; self::$height = (int) $matches[1]; } elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { self::$width = (int) $matches[2]; self::$height = (int) $matches[1]; } } } private static function getConsoleMode() { $info = self::readFromProcess('mode CON'); if (null === $info || !preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { return null; } return [(int) $matches[2], (int) $matches[1]]; } private static function getSttyColumns() { return self::readFromProcess('stty -a | grep columns'); } private static function readFromProcess($command) { if (!\function_exists('proc_open')) { return null; } $descriptorspec = [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ]; $process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); if (!\is_resource($process)) { return null; } $info = stream_get_contents($pipes[1]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); return $info; } } input = $input; } } output = $output; if (!self::$styles) { self::$styles = self::initStyles(); } $this->setStyle('default'); } public static function setStyleDefinition($name, TableStyle $style) { if (!self::$styles) { self::$styles = self::initStyles(); } self::$styles[$name] = $style; } 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)); } public function setStyle($name) { $this->style = $this->resolveStyle($name); return $this; } public function getStyle() { return $this->style; } public function setColumnStyle($columnIndex, $name) { $columnIndex = (int) $columnIndex; $this->columnStyles[$columnIndex] = $this->resolveStyle($name); return $this; } public function getColumnStyle($columnIndex) { if (isset($this->columnStyles[$columnIndex])) { return $this->columnStyles[$columnIndex]; } return $this->getStyle(); } public function setColumnWidth($columnIndex, $width) { $this->columnWidths[(int) $columnIndex] = (int) $width; return $this; } public function setColumnWidths(array $widths) { $this->columnWidths = []; 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 = [$headers]; } $this->headers = $headers; return $this; } public function setRows(array $rows) { $this->rows = []; 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; } 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(); } 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)); } private function renderColumnSeparator() { return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); } 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); } private function renderCell(array $row, $column, $cellFormat) { $cell = isset($row[$column]) ? $row[$column] : ''; $width = $this->effectiveColumnWidths[$column]; if ($cell instanceof TableCell && $cell->getColspan() > 1) { foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn]; } } 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())); } private function calculateNumberOfColumns() { if (null !== $this->numberOfColumns) { return; } $columns = [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 = []; for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) { $rows = $this->fillNextRows($rows, $rowKey); foreach ($rows[$rowKey] as $column => $cell) { if (!strstr($cell, "\n")) { continue; } $lines = explode("\n", str_replace("\n", "\n", $cell)); foreach ($lines as $lineKey => $line) { if ($cell instanceof TableCell) { $line = new TableCell($line, ['colspan' => $cell->getColspan()]); } if (0 === $lineKey) { $rows[$rowKey][$column] = $line; } else { $unmergedRows[$rowKey][$lineKey][$column] = $line; } } } } $tableRows = []; foreach ($rows as $rowKey => $row) { $tableRows[] = $this->fillCells($row); if (isset($unmergedRows[$rowKey])) { $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); } } return $tableRows; } private function fillNextRows(array $rows, $line) { $unmergedRows = []; 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 = [$cell]; if (strstr($cell, "\n")) { $lines = explode("\n", str_replace("\n", "\n", $cell)); $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan()]); unset($lines[0]); } $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows); foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan()]); if ($nbLines === $unmergedRowKey - $line) { break; } } } } foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { foreach ($unmergedRow as $cellKey => $cell) { array_splice($rows[$unmergedRowKey], $cellKey, 0, [$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, [$row]); } } return $rows; } private function fillCells($row) { $newRow = []; foreach ($row as $column => $cell) { $newRow[] = $cell; if ($cell instanceof TableCell && $cell->getColspan() > 1) { foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { $newRow[] = ''; } } } return $newRow ?: $row; } private function copyRow(array $rows, $line) { $row = $rows[$line]; foreach ($row as $cellKey => $cellValue) { $row[$cellKey] = ''; if ($cellValue instanceof TableCell) { $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]); } } return $row; } private function getNumberOfColumns(array $row) { $columns = \count($row); foreach ($row as $column) { $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; } return $columns; } private function getRowColumns(array $row) { $columns = range(0, $this->numberOfColumns - 1); foreach ($row as $cellKey => $cell) { if ($cell instanceof TableCell && $cell->getColspan() > 1) { $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); } } return $columns; } private function calculateColumnsWidth(array $rows) { for ($column = 0; $column < $this->numberOfColumns; ++$column) { $lengths = []; 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) + Helper::strlen($this->style->getCellRowContentFormat()) - 2; } } private function getColumnSeparatorWidth() { return Helper::strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); } 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); } private function cleanup() { $this->effectiveColumnWidths = []; $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 [ '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)); } } register('txt', new TextDescriptor()) ->register('xml', new XmlDescriptor()) ->register('json', new JsonDescriptor()) ->register('md', new MarkdownDescriptor()) ; } public function describe(OutputInterface $output, $object, array $options = []) { $options = array_merge([ '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); } public function register($format, DescriptorInterface $descriptor) { $this->descriptors[$format] = $descriptor; return $this; } public function getName() { return 'descriptor'; } } '; 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 = []; private $overwrite = true; private $terminal; private $firstRun = true; private static $formatters; private static $formats; 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()) { $this->overwrite = false; $this->setRedrawFrequency($max / 10); } $this->startTime = time(); } public static function setPlaceholderFormatterDefinition($name, callable $callable) { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } self::$formatters[$name] = $callable; } public static function getPlaceholderFormatterDefinition($name) { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; } public static function setFormatDefinition($name, $format) { if (!self::$formats) { self::$formats = self::initFormats(); } self::$formats[$name] = $format; } public static function getFormatDefinition($name) { if (!self::$formats) { self::$formats = self::initFormats(); } return isset(self::$formats[$name]) ? self::$formats[$name] : null; } public function setMessage($message, $name = 'message') { $this->messages[$name] = $message; } public function getMessage($name = 'message') { return $this->messages[$name]; } public function getStartTime() { return $this->startTime; } public function getMaxSteps() { return $this->max; } public function getProgress() { return $this->step; } private function getStepWidth() { return $this->stepWidth; } public function getProgressPercent() { return $this->percent; } public function setBarWidth($size) { $this->barWidth = max(1, (int) $size); } public function getBarWidth() { return $this->barWidth; } public function setBarCharacter($char) { $this->barChar = $char; } public function getBarCharacter() { if (null === $this->barChar) { return $this->max ? '=' : $this->emptyBarChar; } return $this->barChar; } public function setEmptyBarCharacter($char) { $this->emptyBarChar = $char; } public function getEmptyBarCharacter() { return $this->emptyBarChar; } public function setProgressCharacter($char) { $this->progressChar = $char; } public function getProgressCharacter() { return $this->progressChar; } public function setFormat($format) { $this->format = null; $this->internalFormat = $format; } public function setRedrawFrequency($freq) { $this->redrawFreq = max((int) $freq, 1); } public function start($max = null) { $this->startTime = time(); $this->step = 0; $this->percent = 0.0; if (null !== $max) { $this->setMaxSteps($max); } $this->display(); } public function advance($step = 1) { $this->setProgress($this->step + $step); } public function setOverwrite($overwrite) { $this->overwrite = (bool) $overwrite; } 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(); } } public function finish() { if (!$this->max) { $this->max = $this->step; } if ($this->step === $this->max && !$this->overwrite) { return; } $this->setProgress($this->max); } 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()); } public function clear() { if (!$this->overwrite) { return; } if (null === $this->format) { $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); } $this->overwrite(''); } private function setRealFormat($format) { 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"); } private function setMaxSteps($max) { $this->max = max(0, (int) $max); $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; } private function overwrite($message) { if ($this->overwrite) { if (!$this->firstRun) { if ($this->formatLineCount > 0) { $message = str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount).$message; } $message = "\x0D\x1B[2K$message"; } } elseif ($this->step > 0) { $message = \PHP_EOL.$message; } $this->firstRun = false; $this->output->write($message); } private function determineBestFormat() { switch ($this->output->getVerbosity()) { 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 [ 'bar' => function (self $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 (self $bar) { return Helper::formatTime(time() - $bar->getStartTime()); }, 'remaining' => function (self $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 (self $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 (self $bar) { return Helper::formatMemory(memory_get_usage(true)); }, 'current' => function (self $bar) { return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT); }, 'max' => function (self $bar) { return $bar->getMaxSteps(); }, 'percent' => function (self $bar) { return floor($bar->getProgressPercent() * 100); }, ]; } private static function initFormats() { return [ '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%', ]; } 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); $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); } } $helper) { $this->set($helper, \is_int($alias) ? null : $alias); } } public function set(HelperInterface $helper, $alias = null) { $this->helpers[$helper->getName()] = $helper; if (null !== $alias) { $this->helpers[$alias] = $helper; } $helper->setHelperSet($this); } public function has($name) { return isset($this->helpers[$name]); } 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; } public function getCommand() { return $this->command; } public function getIterator() { return new \ArrayIterator($this->helpers); } } helperSet = $helperSet; } public function getHelperSet() { return $this->helperSet; } public static function strlen($string) { if (false === $encoding = mb_detect_encoding($string, null, true)) { return \strlen($string); } return mb_strwidth($string, $encoding); } 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 = [ [0, '< 1 sec'], [1, '1 sec'], [2, 'secs', 1], [60, '1 min'], [120, 'mins', 60], [3600, '1 hr'], [7200, 'hrs', 3600], [86400, '1 day'], [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); $string = $formatter->format($string); $string = preg_replace("/\033\[[^m]*m/", '', $string); $formatter->setDecorated($isDecorated); return $string; } } [%s] %s', $style, $section, $style, $message); } public function formatBlock($messages, $style, $large = false) { if (!\is_array($messages)) { $messages = [$messages]; } $len = 0; $lines = []; foreach ($messages as $message) { $message = OutputFormatter::escape($message); $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); $len = max(self::strlen($message) + ($large ? 4 : 2), $len); } $messages = $large ? [str_repeat(' ', $len)] : []; for ($i = 0; isset($lines[$i]); ++$i) { $messages[] = $lines[$i].str_repeat(' ', $len - self::strlen($lines[$i])); } if ($large) { $messages[] = str_repeat(' ', $len); } for ($i = 0; isset($messages[$i]); ++$i) { $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); } return implode("\n", $messages); } public function truncate($message, $length, $suffix = '...') { $computedLength = $length - self::strlen($suffix); if ($computedLength > self::strlen($message)) { return $message; } return self::substr($message, 0, $length).$suffix; } public function getName() { return 'formatter'; } } started[$id] = ['border' => ++$this->count % \count($this->colors)]; return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); } 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 %s ', $this->getBorder($id), $errorPrefix); $this->started[$id]['err'] = true; } $message .= str_replace("\n", sprintf("\n%s %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 %s ', $this->getBorder($id), $prefix); $this->started[$id]['out'] = true; } $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); } return $message; } 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 %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); } $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); unset($this->started[$id]['out'], $this->started[$id]['err']); return $message; } private function getBorder($id) { return sprintf(' ', $this->colors[$this->started[$id]['border']]); } public function getName() { return 'debug_formatter'; } } output = $output; if (null === $format) { $format = $this->determineBestFormat(); } if (null === $indicatorValues) { $indicatorValues = ['-', '\\', '|', '/']; } $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(); } public function setMessage($message) { $this->message = $message; $this->display(); } 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(); } 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(); } 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; } public static function getFormatDefinition($name) { if (!self::$formats) { self::$formats = self::initFormats(); } return isset(self::$formats[$name]) ? self::$formats[$name] : null; } public static function setPlaceholderFormatterDefinition($name, $callable) { if (!self::$formatters) { self::$formatters = self::initPlaceholderFormatters(); } self::$formatters[$name] = $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()) { 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'; } } 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 [ 'indicator' => function (self $indicator) { return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)]; }, 'message' => function (self $indicator) { return $indicator->message; }, 'elapsed' => function (self $indicator) { return Helper::formatTime(time() - $indicator->startTime); }, 'memory' => function () { return Helper::formatMemory(memory_get_usage(true)); }, ]; } private static function initFormats() { return [ '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%)', ]; } } 1, 'colspan' => 1, ]; public function __construct($value = '', array $options = []) { if (is_numeric($value) && !\is_string($value)) { $value = (string) $value; } $this->value = $value; 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); } public function __toString() { return $this->value; } public function getColspan() { return (int) $this->options['colspan']; } public function getRowspan() { return (int) $this->options['rowspan']; } } %s'; private $cellRowFormat = '%s'; private $cellRowContentFormat = ' %s '; private $borderFormat = '%s'; private $padType = \STR_PAD_RIGHT; public function setPaddingChar($paddingChar) { if (!$paddingChar) { throw new LogicException('The padding char must not be empty.'); } $this->paddingChar = $paddingChar; return $this; } public function getPaddingChar() { return $this->paddingChar; } public function setHorizontalBorderChar($horizontalBorderChar) { $this->horizontalBorderChar = $horizontalBorderChar; return $this; } public function getHorizontalBorderChar() { return $this->horizontalBorderChar; } public function setVerticalBorderChar($verticalBorderChar) { $this->verticalBorderChar = $verticalBorderChar; return $this; } public function getVerticalBorderChar() { return $this->verticalBorderChar; } public function setCrossingChar($crossingChar) { $this->crossingChar = $crossingChar; return $this; } public function getCrossingChar() { return $this->crossingChar; } public function setCellHeaderFormat($cellHeaderFormat) { $this->cellHeaderFormat = $cellHeaderFormat; return $this; } public function getCellHeaderFormat() { return $this->cellHeaderFormat; } public function setCellRowFormat($cellRowFormat) { $this->cellRowFormat = $cellRowFormat; return $this; } public function getCellRowFormat() { return $this->cellRowFormat; } public function setCellRowContentFormat($cellRowContentFormat) { $this->cellRowContentFormat = $cellRowContentFormat; return $this; } public function getCellRowContentFormat() { return $this->cellRowContentFormat; } public function setBorderFormat($borderFormat) { $this->borderFormat = $borderFormat; return $this; } public function getBorderFormat() { return $this->borderFormat; } public function setPadType($padType) { if (!\in_array($padType, [\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; } public function getPadType() { return $this->padType; } } getErrorOutput(); } if (!$input->isInteractive()) { $default = $question->getDefault(); if (null === $default) { return $default; } if ($validator = $question->getValidator()) { return \call_user_func($question->getValidator(), $default); } elseif ($question instanceof ChoiceQuestion) { $choices = $question->getChoices(); if (!$question->isMultiselect()) { return isset($choices[$default]) ? $choices[$default] : $default; } $default = explode(',', $default); foreach ($default as $k => $v) { $v = trim($v); $default[$k] = isset($choices[$v]) ? $choices[$v] : $v; } } return $default; } 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); } 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; } 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; } public function getName() { return 'question'; } public static function disableStty() { self::$stty = false; } private function doAsk(OutputInterface $output, Question $question) { $this->writePrompt($output, $question); $inputStream = $this->inputStream ?: \STDIN; $autocomplete = $question->getAutocompleterValues(); if (\function_exists('sapi_windows_cp_set')) { @sapi_windows_cp_set(1252); } if (null === $autocomplete || !self::$stty || !Terminal::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; } protected function writePrompt(OutputInterface $output, Question $question) { $message = $question->getQuestion(); if ($question instanceof ChoiceQuestion) { $output->writeln(array_merge([ $question->getQuestion(), ], $this->formatChoiceQuestionChoices($question, 'info'))); $message = $question->getPrompt(); } $output->write($message); } protected function formatChoiceQuestionChoices(ChoiceQuestion $question, $tag) { $messages = []; $maxWidth = max(array_map('self::strlen', array_keys($choices = $question->getChoices()))); foreach ($choices as $key => $value) { $padding = str_repeat(' ', $maxWidth - self::strlen($key)); $messages[] = sprintf(" [<$tag>%s$padding] %s", $key, $value); } return $messages; } 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->getMessage().''; } $output->writeln($message); } private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete) { $fullChoice = ''; $ret = ''; $i = 0; $ofs = -1; $matches = $autocomplete; $numMatches = \count($matches); $sttyMode = shell_exec('stty -g'); shell_exec('stty -icanon -echo'); $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); while (!feof($inputStream)) { $c = fread($inputStream, 1); if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { shell_exec(sprintf('stty %s', $sttyMode)); throw new RuntimeException('Aborted.'); } elseif ("\177" === $c) { if (0 === $numMatches && 0 !== $i) { --$i; $fullChoice = self::substr($fullChoice, 0, $i); $output->write("\033[1D"); } if (0 === $i) { $ofs = -1; $matches = $autocomplete; $numMatches = \count($matches); } else { $numMatches = 0; } $ret = self::substr($ret, 0, $i); } elseif ("\033" === $c) { $c .= fread($inputStream, 2); 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]; $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); $output->write($remainingCharacters); $fullChoice .= $remainingCharacters; $i = self::strlen($fullChoice); } if ("\n" === $c) { $output->write($c); break; } $numMatches = 0; } continue; } else { if ("\x80" <= $c) { $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]); } $output->write($c); $ret .= $c; $fullChoice .= $c; ++$i; $tempRet = $ret; if ($question instanceof ChoiceQuestion && $question->isMultiselect()) { $tempRet = $this->mostRecentlyEnteredValue($fullChoice); } $numMatches = 0; $ofs = 0; foreach ($autocomplete as $value) { if (0 === strpos($value, $tempRet)) { $matches[$numMatches++] = $value; } } } $output->write("\033[K"); if ($numMatches > 0 && -1 !== $ofs) { $output->write("\0337"); $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))); $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).''); $output->write("\0338"); } } shell_exec(sprintf('stty %s', $sttyMode)); return $fullChoice; } private function mostRecentlyEnteredValue($entered) { if (false === strpos($entered, ',')) { return $entered; } $choices = explode(',', $entered); if (\strlen($lastChoice = trim($choices[\count($choices) - 1])) > 0) { return $lastChoice; } return $entered; } private function getHiddenResponse(OutputInterface $output, $inputStream) { if ('\\' === \DIRECTORY_SEPARATOR) { $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; 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 (self::$stty && Terminal::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.'); } 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; } private function getShell() { if (null !== self::$shell) { return self::$shell; } self::$shell = false; if (file_exists('/usr/bin/env')) { $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) { if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { self::$shell = $sh; break; } } } return self::$shell; } } 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('%s', $this->escapeString($error))); } return $process; } 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; } 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); } public function getName() { return 'process'; } } getValidator(); $question->setValidator(function ($value) use ($validator) { if (null !== $validator) { $value = $validator($value); } else { 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); } protected function writePrompt(OutputInterface $output, Question $question) { $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion()); $default = $question->getDefault(); switch (true) { case null === $default: $text = sprintf(' %s:', $text); break; case $question instanceof ConfirmationQuestion: $text = sprintf(' %s (yes/no) [%s]:', $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(' %s [%s]:', $text, OutputFormatter::escape(implode(', ', $default))); break; case $question instanceof ChoiceQuestion: $choices = $question->getChoices(); $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape(isset($choices[$default]) ? $choices[$default] : $default)); break; default: $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($default)); } $output->writeln($text); $prompt = ' > '; if ($question instanceof ChoiceQuestion) { $output->writeln($this->formatChoiceQuestionChoices($question, 'comment')); $prompt = $question->getPrompt(); } $output->write($prompt); } protected function writeError(OutputInterface $output, \Exception $error) { if ($output instanceof SymfonyStyle) { $output->newLine(); $output->error($error->getMessage()); return; } parent::writeError($output, $error); } } '; private $errorMessage = 'Value "%s" is invalid'; 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); } public function getChoices() { return $this->choices; } public function setMultiselect($multiselect) { $this->multiselect = $multiselect; $this->setValidator($this->getDefaultValidator()); return $this; } public function isMultiselect() { return $this->multiselect; } public function getPrompt() { return $this->prompt; } public function setPrompt($prompt) { $this->prompt = $prompt; return $this; } public function setErrorMessage($errorMessage) { $this->errorMessage = $errorMessage; $this->setValidator($this->getDefaultValidator()); return $this; } private function getDefaultValidator() { $choices = $this->choices; $errorMessage = $this->errorMessage; $multiselect = $this->multiselect; $isAssoc = $this->isAssoc($choices); return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { if ($multiselect) { if (!preg_match('/^[^,]+(?:,[^,]+)*$/', $selected, $matches)) { throw new InvalidArgumentException(sprintf($errorMessage, $selected)); } $selectedChoices = array_map('trim', explode(',', $selected)); } else { $selectedChoices = [trim($selected)]; } $multiselectChoices = []; foreach ($selectedChoices as $value) { $results = []; 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); }; } } trueAnswerRegex = $trueAnswerRegex; $this->setNormalizer($this->getDefaultNormalizer()); } 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; }; } } question = $question; $this->default = $default; } public function getQuestion() { return $this->question; } public function getDefault() { return $this->default; } public function isHidden() { return $this->hidden; } public function setHidden($hidden) { if ($this->autocompleterValues) { throw new LogicException('A hidden question cannot use the autocompleter.'); } $this->hidden = (bool) $hidden; return $this; } public function isHiddenFallback() { return $this->hiddenFallback; } public function setHiddenFallback($fallback) { $this->hiddenFallback = (bool) $fallback; return $this; } public function getAutocompleterValues() { return $this->autocompleterValues; } 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; } public function setValidator(callable $validator = null) { $this->validator = $validator; return $this; } public function getValidator() { return $this->validator; } public function setMaxAttempts($attempts) { if (null !== $attempts) { $attempts = (int) $attempts; if ($attempts < 1) { throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); } } $this->attempts = $attempts; return $this; } public function getMaxAttempts() { return $this->attempts; } public function setNormalizer(callable $normalizer) { $this->normalizer = $normalizer; return $this; } public function getNormalizer() { return $this->normalizer; } protected function isAssoc($array) { return (bool) \count(array_filter(array_keys($array), 'is_string')); } } logger = $logger; } public function onConsoleError(ConsoleErrorEvent $event) { if (null === $this->logger) { return; } $error = $event->getError(); if (!$inputString = $this->getInputString($event)) { $this->logger->error('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]); return; } $this->logger->error('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $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)) { $this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]); return; } $this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]); } public static function getSubscribedEvents() { return [ ConsoleEvents::ERROR => ['onConsoleError', -128], ConsoleEvents::TERMINATE => ['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(["'$commandName'", "\"$commandName\""], $commandName, (string) $input); } return (string) $input; } return $commandName; } } 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; } 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 (!$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; } public function doRun(InputInterface $input, OutputInterface $output) { if (true === $input->hasParameterOption(['--version', '-V'], true)) { $output->writeln($this->getLongVersion()); return 0; } try { $input->bind($this->getDefinition()); } catch (ExceptionInterface $e) { } $name = $this->getCommandName($input); if (true === $input->hasParameterOption(['--help', '-h'], true)) { if (!$name) { $name = 'help'; $input = new ArrayInput(['command_name' => $this->defaultCommand]); } else { $this->wantHelps = true; } } if (!$name) { $name = $this->defaultCommand; $definition = $this->getDefinition(); $definition->setArguments(array_merge( $definition->getArguments(), [ 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), ] )); } try { $e = $this->runningCommand = null; $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; } public function getHelperSet() { if (!$this->helperSet) { $this->helperSet = $this->getDefaultHelperSet(); } return $this->helperSet; } public function setDefinition(InputDefinition $definition) { $this->definition = $definition; } public function getDefinition() { if (!$this->definition) { $this->definition = $this->getDefaultInputDefinition(); } if ($this->singleCommand) { $inputDefinition = $this->definition; $inputDefinition->setArguments(); return $inputDefinition; } return $this->definition; } public function getHelp() { return $this->getLongVersion(); } public function areExceptionsCaught() { return $this->catchExceptions; } public function setCatchExceptions($boolean) { $this->catchExceptions = (bool) $boolean; } public function isAutoExitEnabled() { return $this->autoExit; } public function setAutoExit($boolean) { $this->autoExit = (bool) $boolean; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getVersion() { return $this->version; } public function setVersion($version) { $this->version = $version; } public function getLongVersion() { if ('UNKNOWN' !== $this->getName()) { if ('UNKNOWN' !== $this->getVersion()) { return sprintf('%s %s', $this->getName(), $this->getVersion()); } return $this->getName(); } return 'Console Tool'; } public function register($name) { return $this->add(new Command($name)); } public function addCommands(array $commands) { foreach ($commands as $command) { $this->add($command); } } public function add(Command $command) { $this->init(); $command->setApplication($this); if (!$command->isEnabled()) { $command->setApplication(null); return null; } $command->getDefinition(); 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; } public function get($name) { $this->init(); if (!$this->has($name)) { throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); } if (!isset($this->commands[$name])) { throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name)); } $command = $this->commands[$name]; if ($this->wantHelps) { $this->wantHelps = false; $helpCommand = $this->get('help'); $helpCommand->setCommand($command); return $helpCommand; } return $command; } public function has($name) { $this->init(); return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name))); } public function getNamespaces() { $namespaces = []; foreach ($this->all() as $command) { if ($command->isHidden()) { continue; } $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))); } 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); } public function find($name) { $this->init(); $aliases = []; foreach ($this->commands as $command) { foreach ($command->getAliases() as $alias) { if (!$this->has($alias)) { $this->commands[$alias] = $command; } } } if ($this->has($name)) { return $this->get($name); } $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 (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { if (false !== $pos = strrpos($name, ':')) { $this->findNamespace(substr($name, 0, $pos)); } $message = sprintf('Command "%s" is not defined.', $name); if ($alternatives = $this->findAlternatives($name, $allCommands)) { $alternatives = array_filter($alternatives, function ($name) { return !$this->get($name)->isHidden(); }); 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, array_values($alternatives)); } 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) { if (!$commandList[$nameOrAlias] instanceof Command) { $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias); } $commandName = $commandList[$nameOrAlias]->getName(); $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]->isHidden()) { return false; } $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(array_filter($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)); } 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 = []; 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; } public static function getAbbreviations($names) { $abbrevs = []; foreach ($names as $name) { for ($len = \strlen($name); $len > 0; --$len) { $abbrev = substr($name, 0, $len); $abbrevs[$abbrev][] = $name; } } return $abbrevs; } public function renderException(\Exception $e, OutputInterface $output) { $output->writeln('', OutputInterface::VERBOSITY_QUIET); $this->doRenderException($e, $output); if (null !== $this->runningCommand) { $output->writeln(sprintf('%s', 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; if (\defined('HHVM_VERSION') && $width > 1 << 31) { $width = 1 << 31; } $lines = []; foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) { foreach ($this->splitStringByWidth($line, $width - 4) as $line) { $lineLength = Helper::strlen($line) + 4; $lines[] = [$line, $lineLength]; $len = max($lineLength, $len); } } $messages = []; if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $messages[] = sprintf('%s', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); } $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - Helper::strlen($title)))); } foreach ($lines as $line) { $messages[] = sprintf(' %s %s', 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('Exception trace:', OutputInterface::VERBOSITY_QUIET); $trace = $e->getTrace(); array_unshift($trace, [ 'function' => '', 'file' => $e->getFile() ?: 'n/a', 'line' => $e->getLine() ?: 'n/a', 'args' => [], ]); 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 = isset($trace[$i]['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 at %s:%s', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET); } $output->writeln('', OutputInterface::VERBOSITY_QUIET); } } while ($e = $e->getPrevious()); } protected function getTerminalWidth() { @trigger_error(sprintf('The "%s()" method 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(); } protected function getTerminalHeight() { @trigger_error(sprintf('The "%s()" method 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(); } public function getTerminalDimensions() { @trigger_error(sprintf('The "%s()" method 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(), $this->terminal->getHeight()]; } public function setTerminalDimensions($width, $height) { @trigger_error(sprintf('The "%s()" method 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; } protected function configureIO(InputInterface $input, OutputInterface $output) { if (true === $input->hasParameterOption(['--ansi'], true)) { $output->setDecorated(true); } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) { $output->setDecorated(false); } if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) { $input->setInteractive(false); } elseif (\function_exists('posix_isatty')) { $inputStream = null; if ($input instanceof StreamableInputInterface) { $inputStream = $input->getStream(); } 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(['--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; } 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); } try { $command->mergeApplicationDefinition(); $input->bind($command->getDefinition()); } catch (ExceptionInterface $e) { } $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(); } protected function getCommandName(InputInterface $input) { return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument(); } protected function getDefaultInputDefinition() { return new InputDefinition([ 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'), ]); } protected function getDefaultCommands() { return [new HelpCommand(), new ListCommand()]; } protected function getDefaultHelperSet() { return new HelperSet([ new FormatterHelper(), new DebugFormatterHelper(), new ProcessHelper(), new QuestionHelper(), ]); } private function getAbbreviationSuggestions($abbrevs) { return ' '.implode("\n ", $abbrevs); } public function extractNamespace($name, $limit = null) { $parts = explode(':', $name, -1); return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit)); } private function findAlternatives($name, $collection) { $threshold = 1e3; $alternatives = []; $collectionParts = []; 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); } public function setDefaultCommand($commandName, $isSingleCommand = false) { $this->defaultCommand = $commandName; if ($isSingleCommand) { $this->find($commandName); $this->singleCommand = true; } return $this; } public function isSingleCommand() { return $this->singleCommand; } private function splitStringByWidth($string, $width) { if (false === $encoding = mb_detect_encoding($string, null, true)) { return str_split($string, $width); } $utf8String = mb_convert_encoding($string, 'utf8', $encoding); $lines = []; $line = ''; foreach (preg_split('//u', $utf8String) as $char) { if (mb_strwidth($line.$char, 'utf8') <= $width) { $line .= $char; continue; } $lines[] = str_pad($line, $width); $line = $char; } $lines[] = \count($lines) ? str_pad($line, $width) : $line; mb_convert_variables($encoding, 'utf8', $lines); return $lines; } private function extractAllNamespaces($name) { $parts = explode(':', $name, -1); $namespaces = []; 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); } } } input = $input; $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH; $this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); parent::__construct($output); } public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = true) { $messages = \is_array($messages) ? array_values($messages) : [$messages]; $this->autoPrependBlock(); $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape)); $this->newLine(); } public function title($message) { $this->autoPrependBlock(); $this->writeln([ sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), sprintf('%s', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), ]); $this->newLine(); } public function section($message) { $this->autoPrependBlock(); $this->writeln([ sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), sprintf('%s', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), ]); $this->newLine(); } public function listing(array $elements) { $this->autoPrependText(); $elements = array_map(function ($element) { return sprintf(' * %s', $element); }, $elements); $this->writeln($elements); $this->newLine(); } public function text($message) { $this->autoPrependText(); $messages = \is_array($message) ? array_values($message) : [$message]; foreach ($messages as $message) { $this->writeln(sprintf(' %s', $message)); } } public function comment($message) { $this->block($message, null, null, ' // ', false, false); } public function success($message) { $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); } public function error($message) { $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); } public function warning($message) { $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); } public function note($message) { $this->block($message, 'NOTE', 'fg=yellow', ' ! '); } public function caution($message) { $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); } public function table(array $headers, array $rows) { $style = clone Table::getStyleDefinition('symfony-style-guide'); $style->setCellHeaderFormat('%s'); $table = new Table($this); $table->setHeaders($headers); $table->setRows($rows); $table->setStyle($style); $table->render(); $this->newLine(); } public function ask($question, $default = null, $validator = null) { $question = new Question($question, $default); $question->setValidator($validator); return $this->askQuestion($question); } public function askHidden($question, $validator = null) { $question = new Question($question); $question->setHidden(true); $question->setValidator($validator); return $this->askQuestion($question); } public function confirm($question, $default = true) { return $this->askQuestion(new ConfirmationQuestion($question, $default)); } public function choice($question, array $choices, $default = null) { if (null !== $default) { $values = array_flip($choices); $default = isset($values[$default]) ? $values[$default] : $default; } return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); } public function progressStart($max = 0) { $this->progressBar = $this->createProgressBar($max); $this->progressBar->start(); } public function progressAdvance($step = 1) { $this->getProgressBar()->advance($step); } public function progressFinish() { $this->getProgressBar()->finish(); $this->newLine(2); $this->progressBar = null; } public function createProgressBar($max = 0) { $progressBar = parent::createProgressBar($max); if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) { $progressBar->setEmptyBarCharacter('░'); $progressBar->setProgressCharacter(''); $progressBar->setBarCharacter('▓'); } return $progressBar; } 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; } public function writeln($messages, $type = self::OUTPUT_NORMAL) { parent::writeln($messages, $type); $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); } public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) { parent::write($messages, $newline, $type); $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); } public function newLine($count = 1) { parent::newLine($count); $this->bufferedOutput->write(str_repeat("\n", $count)); } public function getErrorStyle() { return new self($this->input, $this->getErrorOutput()); } 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])) { $this->newLine(); return; } $this->newLine(2 - substr_count($chars, "\n")); } private function autoPrependText() { $fetched = $this->bufferedOutput->fetch(); if ("\n" !== substr($fetched, -1)) { $this->newLine(); } } private function reduceBuffer($messages) { return array_map(function ($value) { return substr($value, -4); }, array_merge([$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 = []; if (null !== $type) { $type = sprintf('[%s] ', $type); $indentLength = \strlen($type); $lineIndentation = str_repeat(' ', $indentLength); } 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; } } output = $output; } public function newLine($count = 1) { $this->output->write(str_repeat(\PHP_EOL, $count)); } public function createProgressBar($max = 0) { return new ProgressBar($this->output, $max); } public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) { $this->output->write($messages, $newline, $type); } public function writeln($messages, $type = self::OUTPUT_NORMAL) { $this->output->writeln($messages, $type); } public function setVerbosity($level) { $this->output->setVerbosity($level); } public function getVerbosity() { return $this->output->getVerbosity(); } public function setDecorated($decorated) { $this->output->setDecorated($decorated); } public function isDecorated() { return $this->output->isDecorated(); } public function setFormatter(OutputFormatterInterface $formatter) { $this->output->setFormatter($formatter); } public function getFormatter() { return $this->output->getFormatter(); } public function isQuiet() { return $this->output->isQuiet(); } public function isVerbose() { return $this->output->isVerbose(); } public function isVeryVerbose() { return $this->output->isVeryVerbose(); } public function isDebug() { return $this->output->isDebug(); } protected function getErrorOutput() { if (!$this->output instanceof ConsoleOutputInterface) { return $this->output; } return $this->output->getErrorOutput(); } } command = $command; } public function execute(array $input, array $options = []) { if (!isset($input['command']) && (null !== $application = $this->command->getApplication()) && $application->getDefinition()->hasArgument('command') ) { $input = array_merge(['command' => $this->command->getName()], $input); } $this->input = new ArrayInput($input); $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); } public function getDisplay($normalize = false) { if (null === $this->output) { throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?'); } rewind($this->output->getStream()); $display = stream_get_contents($this->output->getStream()); if ($normalize) { $display = str_replace(\PHP_EOL, "\n", $display); } return $display; } public function getInput() { return $this->input; } public function getOutput() { return $this->output; } public function getStatusCode() { return $this->statusCode; } public function setInputs(array $inputs) { $this->inputs = $inputs; return $this; } private static function createStream(array $inputs) { $stream = fopen('php://memory', 'r+', false); foreach ($inputs as $input) { fwrite($stream, $input.\PHP_EOL); } rewind($stream); return $stream; } } application = $application; } public function run(array $input, $options = []) { $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); } 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; } 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; } public function getInput() { return $this->input; } public function getOutput() { return $this->output; } public function getStatusCode() { return $this->statusCode; } } 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 = [ 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 $formatLevelMap = []) { $this->output = $output; $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; } public function log($level, $message, array $context = []) { if (!isset($this->verbosityLevelMap[$level])) { throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); } $output = $this->output; if (self::ERROR === $this->formatLevelMap[$level]) { if ($this->output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } $this->errored = true; } if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]); } } public function hasErrored() { return $this->errored; } private function interpolate($message, array $context) { if (false === strpos($message, '{')) { return $message; } $replacements = []; 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); } } setName('list') ->setDefinition($this->createDefinition()) ->setDescription('Lists commands') ->setHelp(<<<'EOF' The %command.name% command lists all commands: php %command.full_name% You can also display the commands for a specific namespace: php %command.full_name% test You can also output the information in other formats by using the --format option: php %command.full_name% --format=xml It's also possible to get raw list of commands (useful for embedding command runner): php %command.full_name% --raw EOF ) ; } public function getNativeDefinition() { return $this->createDefinition(); } protected function execute(InputInterface $input, OutputInterface $output) { $helper = new DescriptorHelper(); $helper->describe($output, $this->getApplication(), [ 'format' => $input->getOption('format'), 'raw_text' => $input->getOption('raw'), 'namespace' => $input->getArgument('namespace'), ]); } private function createDefinition() { return new InputDefinition([ 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'), ]); } } 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; } private function release() { if ($this->lock) { $this->lock->release(); $this->lock = null; } } } ignoreValidationErrors(); $this ->setName('help') ->setDefinition([ 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 %command.name% command displays help for a given command: php %command.full_name% list You can also output the help in other formats by using the --format option: php %command.full_name% --format=xml list To display the list of available commands, please use the list command. EOF ) ; } public function setCommand(Command $command) { $this->command = $command; } 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, [ 'format' => $input->getOption('format'), 'raw_text' => $input->getOption('raw'), ]); $this->command = null; } } class ? static::$defaultName : null; } public function __construct($name = null) { $this->definition = new InputDefinition(); if (null !== $name || null !== $name = static::getDefaultName()) { $this->setName($name); } $this->configure(); } 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; } public function getHelperSet() { return $this->helperSet; } public function getApplication() { return $this->application; } public function isEnabled() { return true; } protected function configure() { } protected function execute(InputInterface $input, OutputInterface $output) { throw new LogicException('You must override the execute() method in the concrete command class.'); } protected function interact(InputInterface $input, OutputInterface $output) { } protected function initialize(InputInterface $input, OutputInterface $output) { } public function run(InputInterface $input, OutputInterface $output) { $this->getSynopsis(true); $this->getSynopsis(false); $this->mergeApplicationDefinition(); 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 (!@cli_set_process_title($this->processTitle)) { if ('Darwin' === \PHP_OS) { $output->writeln('Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', OutputInterface::VERBOSITY_VERY_VERBOSE); } else { cli_set_process_title($this->processTitle); } } } elseif (\function_exists('setproctitle')) { setproctitle($this->processTitle); } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { $output->writeln('Install the proctitle PECL to be able to change the process title.'); } } if ($input->isInteractive()) { $this->interact($input, $output); } 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; } public function setCode(callable $code) { if ($code instanceof \Closure) { $r = new \ReflectionFunction($code); if (null === $r->getClosureThis()) { if (\PHP_VERSION_ID < 70000) { $code = @\Closure::bind($code, $this); } else { $code = \Closure::bind($code, $this); } } } $this->code = $code; return $this; } public function mergeApplicationDefinition($mergeArgs = true) { if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { return; } $this->definition->addOptions($this->application->getDefinition()->getOptions()); $this->applicationDefinitionMerged = true; if ($mergeArgs) { $currentArguments = $this->definition->getArguments(); $this->definition->setArguments($this->application->getDefinition()->getArguments()); $this->definition->addArguments($currentArguments); $this->applicationDefinitionMergedWithArgs = true; } } public function setDefinition($definition) { if ($definition instanceof InputDefinition) { $this->definition = $definition; } else { $this->definition->setDefinition($definition); } $this->applicationDefinitionMerged = false; return $this; } public function getDefinition() { if (null === $this->definition) { throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); } return $this->definition; } public function getNativeDefinition() { return $this->getDefinition(); } public function addArgument($name, $mode = null, $description = '', $default = null) { $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); 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; } public function setName($name) { $this->validateName($name); $this->name = $name; return $this; } public function setProcessTitle($title) { $this->processTitle = $title; return $this; } public function getName() { return $this->name; } public function setHidden($hidden) { $this->hidden = (bool) $hidden; return $this; } public function isHidden() { return $this->hidden; } public function setDescription($description) { $this->description = $description; return $this; } public function getDescription() { return $this->description; } public function setHelp($help) { $this->help = $help; return $this; } public function getHelp() { return $this->help; } public function getProcessedHelp() { $name = $this->name; $isSingleCommand = $this->application && $this->application->isSingleCommand(); $placeholders = [ '%command.name%', '%command.full_name%', ]; $replacements = [ $name, $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name, ]; return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); } 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; } public function getAliases() { return $this->aliases; } 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]; } public function addUsage($usage) { if (0 !== strpos($usage, $this->name)) { $usage = sprintf('%s %s', $this->name, $usage); } $this->usages[] = $usage; return $this; } public function getUsages() { return $this->usages; } 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); } private function validateName($name) { if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); } } } factories = $factories; } public function has($name) { return isset($this->factories[$name]); } 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(); } public function getNames() { return array_keys($this->factories); } } container = $container; $this->commandMap = $commandMap; } 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]); } public function has($name) { return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]); } public function getNames() { return array_keys($this->commandMap); } } commandLoaderServiceId = $commandLoaderServiceId; $this->commandTag = $commandTag; } public function process(ContainerBuilder $container) { $commandServices = $container->findTaggedServiceIds($this->commandTag, true); $lazyCommandMap = []; $lazyCommandRefs = []; $serviceIds = []; $lazyServiceIds = []; 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 = []; foreach ($tags as $tag) { if (isset($tag['command'])) { $aliases[] = $tag['command']; $lazyCommandMap[$tag['command']] = $id; } } $definition->addMethodCall('setName', [$commandName]); if ($aliases) { $definition->addMethodCall('setAliases', [$aliases]); } } $container ->register($this->commandLoaderServiceId, ContainerCommandLoader::class) ->setPublic(true) ->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]); $container->setParameter('console.command.ids', $serviceIds); $container->setParameter('console.lazy_command.ids', $lazyServiceIds); } } alternatives = $alternatives; } public function getAlternatives() { return $this->alternatives; } } 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(); } public function setDecorated($decorated) { $this->decorated = (bool) $decorated; } public function isDecorated() { return $this->decorated; } public function setStyle($name, OutputFormatterStyleInterface $style) { $this->styles[strtolower($name)] = $style; } public function hasStyle($name) { return isset($this->styles[strtolower($name)]); } public function getStyle($name) { if (!$this->hasStyle($name)) { throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name)); } return $this->styles[strtolower($name)]; } 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; } $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); $offset = $pos + \strlen($text); 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($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, ["\0" => '\\', '\\<' => '<']); } return str_replace('\\<', '<', $output); } public function getStyleStack() { return $this->styleStack; } 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); $match[0] = strtolower($match[0]); if ('fg' == $match[0]) { $style->setForeground(strtolower($match[1])); } elseif ('bg' == $match[0]) { $style->setBackground(strtolower($match[1])); } elseif ('options' === $match[0]) { preg_match_all('([^,;]+)', strtolower($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; } private function applyCurrentStyle($text) { return $this->isDecorated() && \strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; } } ['set' => 30, 'unset' => 39], 'red' => ['set' => 31, 'unset' => 39], 'green' => ['set' => 32, 'unset' => 39], 'yellow' => ['set' => 33, 'unset' => 39], 'blue' => ['set' => 34, 'unset' => 39], 'magenta' => ['set' => 35, 'unset' => 39], 'cyan' => ['set' => 36, 'unset' => 39], 'white' => ['set' => 37, 'unset' => 39], 'default' => ['set' => 39, 'unset' => 39], ]; private static $availableBackgroundColors = [ 'black' => ['set' => 40, 'unset' => 49], 'red' => ['set' => 41, 'unset' => 49], 'green' => ['set' => 42, 'unset' => 49], 'yellow' => ['set' => 43, 'unset' => 49], 'blue' => ['set' => 44, 'unset' => 49], 'magenta' => ['set' => 45, 'unset' => 49], 'cyan' => ['set' => 46, 'unset' => 49], 'white' => ['set' => 47, 'unset' => 49], 'default' => ['set' => 49, 'unset' => 49], ]; private static $availableOptions = [ 'bold' => ['set' => 1, 'unset' => 22], 'underscore' => ['set' => 4, 'unset' => 24], 'blink' => ['set' => 5, 'unset' => 25], 'reverse' => ['set' => 7, 'unset' => 27], 'conceal' => ['set' => 8, 'unset' => 28], ]; private $foreground; private $background; private $options = []; public function __construct($foreground = null, $background = null, array $options = []) { if (null !== $foreground) { $this->setForeground($foreground); } if (null !== $background) { $this->setBackground($background); } if (\count($options)) { $this->setOptions($options); } } 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]; } 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]; } 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]; } } 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]); } } public function setOptions(array $options) { $this->options = []; foreach ($options as $option) { $this->setOption($option); } } public function apply($text) { $setCodes = []; $unsetCodes = []; 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)); } } emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); $this->reset(); } public function reset() { $this->styles = []; } public function push(OutputFormatterStyleInterface $style) { $this->styles[] = $style; } 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.'); } public function getCurrent() { if (empty($this->styles)) { return $this->emptyStyle; } return $this->styles[\count($this->styles) - 1]; } public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) { $this->emptyStyle = $emptyStyle; return $this; } public function getEmptyStyle() { return $this->emptyStyle; } } getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) { $default = sprintf(' [default: %s]', $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(' %s %s%s%s', $argument->getName(), str_repeat(' ', $spacingWidth), preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()), $default ), $options); } protected function describeInputOption(InputOption $option, array $options = []) { if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) { $default = sprintf(' [default: %s]', $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([$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(' %s %s%s%s%s', $synopsis, str_repeat(' ', $spacingWidth), preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()), $default, $option->isArray() ? ' (multiple values allowed)' : '' ), $options); } protected function describeInputDefinition(InputDefinition $definition, array $options = []) { $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); foreach ($definition->getArguments() as $argument) { $totalWidth = max($totalWidth, Helper::strlen($argument->getName())); } if ($definition->getArguments()) { $this->writeText('Arguments:', $options); $this->writeText("\n"); foreach ($definition->getArguments() as $argument) { $this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth])); $this->writeText("\n"); } } if ($definition->getArguments() && $definition->getOptions()) { $this->writeText("\n"); } if ($definition->getOptions()) { $laterOptions = []; $this->writeText('Options:', $options); foreach ($definition->getOptions() as $option) { if (\strlen($option->getShortcut()) > 1) { $laterOptions[] = $option; continue; } $this->writeText("\n"); $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); } foreach ($laterOptions as $option) { $this->writeText("\n"); $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); } } } protected function describeCommand(Command $command, array $options = []) { $command->getSynopsis(true); $command->getSynopsis(false); $command->mergeApplicationDefinition(false); $this->writeText('Usage:', $options); foreach (array_merge([$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('Help:', $options); $this->writeText("\n"); $this->writeText(' '.str_replace("\n", "\n ", $help), $options); $this->writeText("\n"); } } protected function describeApplication(Application $application, array $options = []) { $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("Usage:\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) { $describedNamespaceInfo = reset($namespaces); foreach ($describedNamespaceInfo['commands'] as $name) { $commands[$name] = $description->getCommand($name); } } $width = $this->getColumnWidth(\call_user_func_array('array_merge', array_map(function ($namespace) use ($commands) { return array_intersect($namespace['commands'], array_keys($commands)); }, array_values($namespaces)))); if ($describedNamespace) { $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); } else { $this->writeText('Available commands:', $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(' '.$namespace['id'].'', $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(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options); } } $this->writeText("\n"); } } private function writeText($content, array $options = []) { $this->write( isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, isset($options['raw_output']) ? !$options['raw_output'] : true ); } private function getCommandAliasesText(Command $command) { $text = ''; $aliases = $command->getAliases(); if ($aliases) { $text = '['.implode('|', $aliases).'] '; } return $text; } 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)); } private function getColumnWidth(array $commands) { $widths = []; 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; } private function calculateTotalWidthForOptions(array $options) { $totalWidth = 0; foreach ($options as $option) { $nameLength = 1 + max(Helper::strlen($option->getShortcut()), 1) + 4 + Helper::strlen($option->getName()); if ($option->acceptValue()) { $valueLength = 1 + Helper::strlen($option->getName()); $valueLength += $option->isValueOptional() ? 2 : 0; $nameLength += $valueLength; } $totalWidth = max($totalWidth, $nameLength); } return $totalWidth; } } 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; } 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([$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; } 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; } protected function describeInputArgument(InputArgument $argument, array $options = []) { $this->writeDocument($this->getInputArgumentDocument($argument)); } protected function describeInputOption(InputOption $option, array $options = []) { $this->writeDocument($this->getInputOptionDocument($option)); } protected function describeInputDefinition(InputDefinition $definition, array $options = []) { $this->writeDocument($this->getInputDefinitionDocument($definition)); } protected function describeCommand(Command $command, array $options = []) { $this->writeDocument($this->getCommandDocument($command)); } protected function describeApplication(Application $application, array $options = []) { $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); } private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) { foreach ($importedParent->childNodes as $childNode) { $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); } } private function writeDocument(\DOMDocument $dom) { $dom->formatOutput = true; $this->write($dom->saveXML()); } 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()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : [])); foreach ($defaults as $default) { $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); $defaultXML->appendChild($dom->createTextNode($default)); } return $dom; } 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', '-'.str_replace('|', '|-', $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()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : [])); $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; } } 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))); } } protected function write($content, $decorated = false) { $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); } abstract protected function describeInputArgument(InputArgument $argument, array $options = []); abstract protected function describeInputOption(InputOption $option, array $options = []); abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []); abstract protected function describeCommand(Command $command, array $options = []); abstract protected function describeApplication(Application $application, array $options = []); } isDecorated(); $output->setDecorated(false); parent::describe($output, $object, $options); $output->setDecorated($decorated); } protected function write($content, $decorated = true) { parent::write($content, $decorated); } protected function describeInputArgument(InputArgument $argument, array $options = []) { $this->write( '#### `'.($argument->getName() ?: '')."`\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)).'`' ); } protected function describeInputOption(InputOption $option, array $options = []) { $name = '--'.$option->getName(); if ($option->getShortcut()) { $name .= '|-'.str_replace('|', '|-', $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)).'`' ); } protected function describeInputDefinition(InputDefinition $definition, array $options = []) { 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)); } } } protected function describeCommand(Command $command, array $options = []) { $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([$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()); } } protected function describeApplication(Application $application, array $options = []) { $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'; } } application = $application; $this->namespace = $namespace; $this->showHidden = $showHidden; } public function getNamespaces() { if (null === $this->namespaces) { $this->inspectApplication(); } return $this->namespaces; } public function getCommands() { if (null === $this->commands) { $this->inspectApplication(); } return $this->commands; } 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 = []; $this->namespaces = []; $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); foreach ($this->sortCommands($all) as $namespace => $commands) { $names = []; 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] = ['id' => $namespace, 'commands' => $names]; } } private function sortCommands(array $commands) { $namespacedCommands = []; $globalCommands = []; $sortedCommands = []; foreach ($commands as $name => $command) { $key = $this->application->extractNamespace($name, 1); if (\in_array($key, ['', self::GLOBAL_NAMESPACE], true)) { $globalCommands[$name] = $command; } else { $namespacedCommands[$key][$name] = $command; } } if ($globalCommands) { ksort($globalCommands); $sortedCommands[self::GLOBAL_NAMESPACE] = $globalCommands; } if ($namespacedCommands) { ksort($namespacedCommands); foreach ($namespacedCommands as $key => $commandsSet) { ksort($commandsSet); $sortedCommands[$key] = $commandsSet; } } return $sortedCommands; } } writeData($this->getInputArgumentData($argument), $options); } protected function describeInputOption(InputOption $option, array $options = []) { $this->writeData($this->getInputOptionData($option), $options); } protected function describeInputDefinition(InputDefinition $definition, array $options = []) { $this->writeData($this->getInputDefinitionData($definition), $options); } protected function describeCommand(Command $command, array $options = []) { $this->writeData($this->getCommandData($command), $options); } protected function describeApplication(Application $application, array $options = []) { $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; $description = new ApplicationDescription($application, $describedNamespace, true); $commands = []; foreach ($description->getCommands() as $command) { $commands[] = $this->getCommandData($command); } $data = []; 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); } private function writeData(array $data, array $options) { $flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0; $this->write(json_encode($data, $flags)); } private function getInputArgumentData(InputArgument $argument) { return [ '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(), ]; } private function getInputOptionData(InputOption $option) { return [ 'name' => '--'.$option->getName(), 'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $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(), ]; } private function getInputDefinitionData(InputDefinition $definition) { $inputArguments = []; foreach ($definition->getArguments() as $name => $argument) { $inputArguments[$name] = $this->getInputArgumentData($argument); } $inputOptions = []; foreach ($definition->getOptions() as $name => $option) { $inputOptions[$name] = $this->getInputOptionData($option); } return ['arguments' => $inputArguments, 'options' => $inputOptions]; } private function getCommandData(Command $command) { $command->getSynopsis(); $command->mergeApplicationDefinition(false); return [ 'name' => $command->getName(), 'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()), 'description' => $command->getDescription(), 'help' => $command->getProcessedHelp(), 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), 'hidden' => $command->isHidden(), ]; } } setTokens($this->tokenize($input)); } private function tokenize($input) { $tokens = []; $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(['"\'', '\'"', '\'\'', '""'], '', 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 { throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10))); } $cursor += \strlen($match[0]); } return $tokens; } } 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); } public function getName() { return $this->name; } public function isRequired() { return self::REQUIRED === (self::REQUIRED & $this->mode); } public function isArray() { return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); } 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 = []; } elseif (!\is_array($default)) { throw new LogicException('A default value for an array argument must be an array.'); } } $this->default = $default; } public function getDefault() { return $this->default; } public function getDescription() { return $this->description; } } parameters = $parameters; parent::__construct($definition); } public function getFirstArgument() { foreach ($this->parameters as $param => $value) { if ($param && \is_string($param) && '-' === $param[0]) { continue; } return $value; } return null; } 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; } 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 $default; } if (\is_int($k)) { if (\in_array($v, $values)) { return true; } } elseif (\in_array($k, $values)) { return $v; } } return $default; } public function __toString() { $params = []; foreach ($this->parameters as $param => $val) { if ($param && \is_string($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) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val); } } return implode(' ', $params); } protected function parse() { foreach ($this->parameters as $key => $value) { if ('--' === $key) { return; } if (0 === strpos($key, '--')) { $this->addLongOption(substr($key, 2), $value); } elseif (0 === strpos($key, '-')) { $this->addShortOption(substr($key, 1), $value); } else { $this->addArgument($key, $value); } } } 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); } 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; } 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; } } tokens = $argv; parent::__construct($definition); } protected function setTokens(array $tokens) { $this->tokens = $tokens; } 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); } } } private function parseShortOption($token) { $name = substr($token, 1); if (\strlen($name) > 1) { if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { $this->addShortOption($name[0], substr($name, 1)); } else { $this->parseShortOptionSet($name); } } else { $this->addShortOption($name, null); } } private function parseShortOptionSet($name) { $len = \strlen($name); for ($i = 0; $i < $len; ++$i) { if (!$this->definition->hasShortcut($name[$i])) { $encoding = mb_detect_encoding($name, null, true); throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding))); } $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); } } } private function parseLongOption($token) { $name = substr($token, 2); if (false !== $pos = strpos($name, '=')) { if (0 === \strlen($value = substr($name, $pos + 1))) { 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); } } private function parseArgument($token) { $c = \count($this->arguments); if ($this->definition->hasArgument($c)) { $arg = $this->definition->getArgument($c); $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token; } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { $arg = $this->definition->getArgument($c - 1); $this->arguments[$arg->getName()][] = $token; } 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)); } } 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); } 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, ['', null], true) && $option->acceptValue() && \count($this->parsed)) { $next = array_shift($this->parsed); if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', 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; } } public function getFirstArgument() { $isOption = false; foreach ($this->tokens as $i => $token) { if ($token && '-' === $token[0]) { if (false !== strpos($token, '=') || !isset($this->tokens[$i + 1])) { continue; } $name = '-' === $token[1] ? substr($token, 2) : substr($token, -1); if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) { } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) { $isOption = true; } continue; } if ($isOption) { $isOption = false; continue; } return $token; } return null; } public function hasParameterOption($values, $onlyParams = false) { $values = (array) $values; foreach ($this->tokens as $token) { if ($onlyParams && '--' === $token) { return false; } foreach ($values as $value) { $leading = 0 === strpos($value, '--') ? $value.'=' : $value; if ($token === $value || '' !== $leading && 0 === strpos($token, $leading)) { return true; } } } return false; } 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 $default; } foreach ($values as $value) { if ($token === $value) { return array_shift($tokens); } $leading = 0 === strpos($value, '--') ? $value.'=' : $value; if ('' !== $leading && 0 === strpos($token, $leading)) { return substr($token, \strlen($leading)); } } } return $default; } 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); } } definition = new InputDefinition(); } else { $this->bind($definition); $this->validate(); } } public function bind(InputDefinition $definition) { $this->arguments = []; $this->options = []; $this->definition = $definition; $this->parse(); } abstract protected function parse(); 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))); } } public function isInteractive() { return $this->interactive; } public function setInteractive($interactive) { $this->interactive = (bool) $interactive; } public function getArguments() { return array_merge($this->definition->getArgumentDefaults(), $this->arguments); } 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(); } 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; } public function hasArgument($name) { return $this->definition->hasArgument($name); } public function getOptions() { return array_merge($this->definition->getOptionDefaults(), $this->options); } 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(); } 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; } public function hasOption($name) { return $this->definition->hasOption($name); } public function escapeToken($token) { return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); } public function setStream($stream) { $this->stream = $stream; } public function getStream() { return $this->stream; } } setDefinition($definition); } public function setDefinition(array $definition) { $arguments = []; $options = []; foreach ($definition as $item) { if ($item instanceof InputOption) { $options[] = $item; } else { $arguments[] = $item; } } $this->setArguments($arguments); $this->setOptions($options); } public function setArguments($arguments = []) { $this->arguments = []; $this->requiredCount = 0; $this->hasOptional = false; $this->hasAnArrayArgument = false; $this->addArguments($arguments); } public function addArguments($arguments = []) { if (null !== $arguments) { foreach ($arguments as $argument) { $this->addArgument($argument); } } } 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; } 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]; } public function hasArgument($name) { $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments; return isset($arguments[$name]); } public function getArguments() { return $this->arguments; } public function getArgumentCount() { return $this->hasAnArrayArgument ? \PHP_INT_MAX : \count($this->arguments); } public function getArgumentRequiredCount() { return $this->requiredCount; } public function getArgumentDefaults() { $values = []; foreach ($this->arguments as $argument) { $values[$argument->getName()] = $argument->getDefault(); } return $values; } public function setOptions($options = []) { $this->options = []; $this->shortcuts = []; $this->addOptions($options); } public function addOptions($options = []) { foreach ($options as $option) { $this->addOption($option); } } 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(); } } } public function getOption($name) { if (!$this->hasOption($name)) { throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); } return $this->options[$name]; } public function hasOption($name) { return isset($this->options[$name]); } public function getOptions() { return $this->options; } public function hasShortcut($name) { return isset($this->shortcuts[$name]); } public function getOptionForShortcut($shortcut) { return $this->getOption($this->shortcutToName($shortcut)); } public function getOptionDefaults() { $values = []; foreach ($this->options as $option) { $values[$option->getName()] = $option->getDefault(); } return $values; } public function shortcutToName($shortcut) { if (!isset($this->shortcuts[$shortcut])) { throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); } return $this->shortcuts[$shortcut]; } public function getSynopsis($short = false) { $elements = []; 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.')'; } if ($argument->isArray()) { $element .= '...'; } $elements[] = $element; } return implode(' ', $elements); } } 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); } public function getShortcut() { return $this->shortcut; } public function getName() { return $this->name; } public function acceptValue() { return $this->isValueRequired() || $this->isValueOptional(); } public function isValueRequired() { return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); } public function isValueOptional() { return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); } public function isArray() { return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); } 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 = []; } elseif (!\is_array($default)) { throw new LogicException('A default value for an array option must be an array.'); } } $this->default = $this->acceptValue() ? $default : false; } public function getDefault() { return $this->default; } public function getDescription() { return $this->description; } public function equals(self $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() ; } } = 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 = []; } } self::initialize($flags); $value = trim($value); if ('' === $value) { return ''; } if (2 & (int) ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('ASCII'); } try { $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 (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 (null !== $tag) { return new TaggedValue($tag, $result); } return $result; } finally { if (isset($mbEncoding)) { mb_internal_encoding($mbEncoding); } } } 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) && false === strpos($value, "\f") && false === strpos($value, "\n") && false === strpos($value, "\r") && false === strpos($value, "\t") && false === strpos($value, "\v"): $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) { $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; } } 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; } private static function dumpArray($value, $flags) { if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE & $flags) && !self::isHash($value)) { $output = []; foreach ($value as $val) { $output[] = self::dump($val, $flags); } return sprintf('[%s]', implode(', ', $output)); } $output = []; foreach ($value as $key => $val) { $output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); } return sprintf('{ %s }', implode(', ', $output)); } public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = [], $legacyOmittedKeySupport = false) { if (\in_array($scalar[$i], ['"', "'"])) { $output = self::parseQuotedScalar($scalar, $i); if (null !== $delimiters) { $tmp = ltrim(substr($scalar, $i), ' '); if ('' === $tmp) { throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } if (!\in_array($tmp[0], $delimiters)) { throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } } } else { if (!$delimiters) { $output = substr($scalar, $i); $i += \strlen($output); 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); } 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; } 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; } private static function parseSequence($sequence, $flags, &$i = 0, $references = []) { $output = []; $len = \strlen($sequence); ++$i; 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 '[': $value = self::parseSequence($sequence, $flags, $i, $references); break; case '{': $value = self::parseMapping($sequence, $flags, $i, $references); break; default: $isQuoted = \in_array($sequence[$i], ['"', "'"]); $value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references); if (\is_string($value) && !$isQuoted && false !== strpos($value, ': ')) { try { $pos = 0; $value = self::parseMapping('{'.$value.'}', $flags, $pos, $references); } catch (\InvalidArgumentException $e) { } } --$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); } private static function parseMapping($mapping, $flags, &$i = 0, $references = []) { $output = []; $len = \strlen($mapping); ++$i; $allowOverwrite = false; while ($i < $len) { switch ($mapping[$i]) { case ' ': case ',': ++$i; continue 2; case '}': if (self::$objectForMap) { return (object) $output; } return $output; } $isKeyQuoted = \in_array($mapping[$i], ['"', "'"], true); $key = self::parseScalar($mapping, $flags, [':', ' '], $i, false, [], true); if ('!php/const' === $key) { $key .= self::parseScalar($mapping, $flags, [':', ' '], $i, false, [], true); if ('!php/const:' === $key && ':' !== $mapping[$i]) { $key = ''; --$i; } else { $key = self::evaluateScalar($key, $flags); } } 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], [' ', ',', '[', ']', '{', '}'], 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 '[': $value = self::parseSequence($mapping, $flags, $i, $references); 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 '{': $value = self::parseMapping($mapping, $flags, $i, $references); 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, [',', '}'], $i, null === $tag, $references); 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); } private static function evaluateScalar($scalar, $flags, $references = []) { $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); } 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 null; 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 null; 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 null; case 0 === strpos($scalar, '!php/object'): if (self::$objectSupport) { if (!isset($scalar[12])) { return false; } 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 null; 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. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } return null; case 0 === strpos($scalar, '!php/const'): if (self::$constantSupport) { if (!isset($scalar[11])) { return ''; } $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. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); } return null; 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); } case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]): if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) { $scalar = str_replace('_', '', (string) $scalar); } switch (true) { case ctype_digit($scalar): if (preg_match('/^0[0-7]+$/', $scalar)) { return octdec($scalar); } $cast = (int) $scalar; return ($scalar === (string) $cast) ? $cast : $scalar; case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): if (preg_match('/^-0[0-7]+$/', $scalar)) { return -octdec(substr($scalar, 1)); } $cast = (int) $scalar; return ($scalar === (string) $cast) ? $cast : $scalar; 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([',', '_'], '', $scalar); case Parser::preg_match(self::getTimestampRegex(), $scalar): if (Yaml::PARSE_DATETIME & $flags) { 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; } private static function parseTag($value, &$i, $flags) { if ('!' !== $value[$i]) { return null; } $tagLength = strcspn($value, " \t\n", $i + 1); $tag = substr($value, $i + 1, $tagLength); $nextOffset = $i + $tagLength + 1; $nextOffset += strspn($value, ' ', $nextOffset); if ((!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && 'tagged' !== $tag) { return null; } 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); } 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); } private static function getTimestampRegex() { return <<[0-9][0-9][0-9][0-9]) -(?P[0-9][0-9]?) -(?P[0-9][0-9]?) (?:(?:[Tt]|[ \t]+) (?P[0-9][0-9]?) :(?P[0-9][0-9]) :(?P[0-9][0-9]) (?:\.(?P[0-9]*))? (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) (?::(?P[0-9][0-9]))?))?)? $~x EOF; } 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.'.'; } } tag = $tag; $this->value = $value; } public function getTag() { return $this->tag; } public function getValue() { return $this->value; } } indentation = $indentation; } 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; } 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) && !$input instanceof TaggedValue && $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")) { $blockIndentationIndicator = (' ' === substr($value, 0, 1)) ? (string) $this->indentation : ''; $output .= sprintf("%s%s%s |%s\n", $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator); foreach (explode("\n", $value) as $row) { $output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row); } continue; } if ($value instanceof TaggedValue) { $output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag()); if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) { $blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : ''; $output .= sprintf(" |%s\n", $blockIndentationIndicator); foreach (explode("\n", $value->getValue()) as $row) { $output .= sprintf("%s%s%s\n", $prefix, str_repeat(' ', $this->indentation), $row); } continue; } if ($inline - 1 <= 0 || null === $value->getValue() || is_scalar($value->getValue())) { $output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; } else { $output .= "\n"; $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags); } 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; } } = ! % @ ` ]/x', $value); } public static function escapeWithSingleQuotes($value) { return sprintf("'%s'", str_replace('\'', '\'\'', $value)); } } directoryIteratorProvider = $directoryIteratorProvider; $this->isReadableProvider = $isReadableProvider; } 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(<<%command.name% command lints a YAML file and outputs to STDOUT the first encountered syntax error. You can validates YAML contents passed from STDIN: cat filename | php %command.full_name% You can also validate the syntax of a file: php %command.full_name% filename Or of a whole directory: php %command.full_name% dirname php %command.full_name% dirname --format=json 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, [$this->validate($stdin, $flags)]); } if (!$this->isReadable($filename)) { throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); } $filesInfo = []; 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 ['file' => $file, 'line' => $e->getParsedLine(), 'valid' => false, 'message' => $e->getMessage()]; } finally { restore_error_handler(); } return ['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('OK'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); } elseif (!$info['valid']) { ++$erroredFiles; $io->text(' ERROR '.($info['file'] ? sprintf(' in %s', $info['file']) : '')); $io->text(sprintf(' >> %s', $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(), ['yml', 'yaml'])) { continue; } yield $file; } } private function getStdin() { if (0 !== ftell(\STDIN)) { return null; } $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); } } unescapeCharacter($match[0]); }; return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); } 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': return "\xC2\x85"; case '_': return "\xC2\xA0"; case 'L': return "\xE2\x80\xA8"; case 'P': 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)); } } 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); } } parsedFile = $parsedFile; $this->parsedLine = $parsedLine; $this->snippet = $snippet; $this->rawMessage = $message; $this->updateRepr(); parent::__construct($this->message, 0, $previous); } public function getSnippet() { return $this->snippet; } public function setSnippet($snippet) { $this->snippet = $snippet; $this->updateRepr(); } public function getParsedFile() { return $this->parsedFile; } public function setParsedFile($parsedFile) { $this->parsedFile = $parsedFile; $this->updateRepr(); } public function getParsedLine() { return $this->parsedLine; } 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 .= '.'; } } } ![\w!.\/:-]+)'; const BLOCK_SCALAR_HEADER_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; private $filename; private $offset = 0; private $totalNumberOfLines; private $lines = []; private $currentLineNb = -1; private $currentLine = ''; private $refs = []; private $skippedLineNumbers = []; private $locallySkippedLineNumbers = []; private $refsBeingParsed = []; 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); } } } 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; } } 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 = []; $mbEncoding = null; $e = null; $data = null; if (2 & (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 = []; $this->currentLine = ''; $this->refs = []; $this->skippedLineNumbers = []; $this->locallySkippedLineNumbers = []; $this->totalNumberOfLines = null; 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 = []; if (null === $this->totalNumberOfLines) { $this->totalNumberOfLines = \count($this->lines); } if (!$this->moveToNextLine()) { return null; } $data = []; $context = null; $allowOverwrite = false; while ($this->isCurrentLineEmpty()) { if (!$this->moveToNextLine()) { return null; } } if (null !== ($tag = $this->getLineTag($this->currentLine, $flags, false)) && !$this->moveToNextLine()) { return new TaggedValue($tag, ''); } do { if ($this->isCurrentLineEmpty()) { continue; } 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\s+)(?P.+))?$#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[^ ]+) *(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $this->refsBeingParsed[] = $isRef; $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); } 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']) && ( '!' === $values['value'][0] || self::preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $this->trimTag($values['value']), $matches) ) ) { $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); array_pop($this->refsBeingParsed); } } elseif ( self::preg_match('#^(?P(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P.+))?$#u', rtrim($this->currentLine), $values) && (false === strpos($values['key'], ' #') || \in_array($values['key'][0], ['"', "'"])) ) { 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); 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); } if (\is_float($key)) { $key = (string) $key; } if ('<<' === $key && (!isset($values['value']) || !self::preg_match('#^&(?P[^ ]+)#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)) { if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) { throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); } 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; } 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])) { 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; } } else { $data += $parsed; } } } elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P[^ ]++) *+(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $this->refsBeingParsed[] = $isRef; $values['value'] = $matches['value']; } $subTag = null; if ($mergeNode) { } elseif (!isset($values['value']) || '' === $values['value'] || 0 === strpos($values['value'], '#') || (null !== $subTag = $this->getLineTag($values['value'], $flags)) || '<<' === $key) { if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { 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])) { 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); 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]; array_pop($this->refsBeingParsed); } } else { if ('---' === $this->currentLine) { throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename); } if ($deprecatedUsage = (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); } 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; } if (0 === $this->currentLineNb) { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = false; $value = ''; foreach ($this->lines as $line) { if ('' !== ltrim($line) && '#' === ltrim($line)[0]) { continue; } if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) { throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } if ('' === trim($line)) { $value .= "\n"; } elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) { $value .= ' '; } if ('' !== trim($line) && '\\' === substr($line, -1)) { $value .= ltrim(substr($line, 0, -1)); } elseif ('' !== trim($line)) { $value .= trim($line); } if ('' === trim($line)) { $previousLineWasNewline = true; $previousLineWasTerminatedWithBackslash = false; } elseif ('\\' === substr($line, -1)) { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = true; } else { $previousLineWasNewline = false; $previousLineWasTerminatedWithBackslash = false; } } try { return Inline::parse(trim($value)); } catch (ParseException $e) { } } 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; $parser->refsBeingParsed = $this->refsBeingParsed; return $parser->doParse($yaml, $flags); } public function getRealCurrentLineNb() { $realCurrentLineNumber = $this->currentLineNb + $this->offset; foreach ($this->skippedLineNumbers as $skippedLineNumber) { if ($skippedLineNumber > $realCurrentLineNumber) { break; } ++$realCurrentLineNumber; } return $realCurrentLineNumber; } private function getCurrentLineIndentation() { return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' ')); } private function getNextEmbedBlock($indentation = null, $inSequence = false) { $oldLineIndentation = $this->getCurrentLineIndentation(); if (!$this->moveToNextLine()) { return ''; } if (null === $indentation) { $newIndent = null; $movements = 0; do { $EOF = false; 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 = []; 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]) { $this->moveToPreviousLine(); return ''; } $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); $isItComment = $this->isCurrentLineComment(); while ($this->moveToNextLine()) { if ($isItComment && !$isItUnindentedCollection) { $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); $isItComment = $this->isCurrentLineComment(); } $indent = $this->getCurrentLineIndentation(); 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); } private function moveToNextLine() { if ($this->currentLineNb >= \count($this->lines) - 1) { return false; } $this->currentLine = $this->lines[++$this->currentLineNb]; return true; } private function moveToPreviousLine() { if ($this->currentLineNb < 1) { return false; } $this->currentLine = $this->lines[--$this->currentLineNb]; return true; } 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)) { if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) { throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } 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), abs((int) $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; if (null !== $quotation && self::preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) { return Inline::parse($value, $flags, $this->refs); } $lines = []; while ($this->moveToNextLine()) { if (null === $quotation && 0 === $this->getCurrentLineIndentation()) { $this->moveToPreviousLine(); break; } $lines[] = trim($this->currentLine); $escapedLine = str_replace(['\\\\', '\\"'], '', $this->currentLine); if ('' !== $escapedLine && substr($escapedLine, -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; } } private function parseBlockScalar($style, $chomping = '', $indentation = 0) { $notEOF = $this->moveToNextLine(); if (!$notEOF) { return ''; } $isCurrentLineBlank = $this->isCurrentLineBlank(); $blockLines = []; while ($notEOF && $isCurrentLineBlank) { if ($notEOF = $this->moveToNextLine()) { $blockLines[] = ''; $isCurrentLineBlank = $this->isCurrentLineBlank(); } } 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]; } if ($notEOF = $this->moveToNextLine()) { $isCurrentLineBlank = $this->isCurrentLineBlank(); } } } elseif ($notEOF) { $blockLines[] = ''; } if ($notEOF) { $blockLines[] = ''; $this->moveToPreviousLine(); } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { $blockLines[] = ''; } 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); } if ('' === $chomping) { $text = preg_replace('/\n+$/', "\n", $text); } elseif ('-' === $chomping) { $text = preg_replace('/\n+$/', '', $text); } return $text; } 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; } private function isCurrentLineEmpty() { return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); } private function isCurrentLineBlank() { return '' == trim($this->currentLine, ' '); } private function isCurrentLineComment() { $ltrimmedLine = ltrim($this->currentLine, ' '); return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; } private function isCurrentLineLastLineInDocument() { return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); } private function cleanup($value) { $value = str_replace(["\r\n", "\r"], "\n", $value); $count = 0; $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); $this->offset += $count; $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); if (1 === $count) { $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; } $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); if (1 === $count) { $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; $value = preg_replace('#\.\.\.\s*$#', '', $value); } return $value; } 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; } private function isStringUnIndentedCollectionItem() { return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } 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; } 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 null; } if ($nextLineCheck && !$this->isNextLineIndented()) { return null; } $tag = substr($matches['tag'], 1); 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.'.'; } } parseFile($filename, $flags); } 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); } 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); } } = 70200) { return; } if (!defined('PHP_FLOAT_DIG')) { define('PHP_FLOAT_DIG', 15); } if (!defined('PHP_FLOAT_EPSILON')) { define('PHP_FLOAT_EPSILON', 2.2204460492503E-16); } if (!defined('PHP_FLOAT_MIN')) { define('PHP_FLOAT_MIN', 2.2250738585072E-308); } if (!defined('PHP_FLOAT_MAX')) { define('PHP_FLOAT_MAX', 1.7976931348623157E+308); } if (!defined('PHP_OS_FAMILY')) { define('PHP_OS_FAMILY', p\Php72::php_os_family()); } if ('\\' === DIRECTORY_SEPARATOR && !function_exists('sapi_windows_vt100_support')) { function sapi_windows_vt100_support($stream, $enable = null) { return p\Php72::sapi_windows_vt100_support($stream, $enable); } } if (!function_exists('stream_isatty')) { function stream_isatty($stream) { return p\Php72::stream_isatty($stream); } } if (!function_exists('utf8_encode')) { function utf8_encode($string) { return p\Php72::utf8_encode($string); } } if (!function_exists('utf8_decode')) { function utf8_decode($string) { return p\Php72::utf8_decode($string); } } if (!function_exists('spl_object_id')) { function spl_object_id($object) { return p\Php72::spl_object_id($object); } } if (!function_exists('mb_ord')) { function mb_ord($string, $encoding = null) { return p\Php72::mb_ord($string, $encoding); } } if (!function_exists('mb_chr')) { function mb_chr($codepoint, $encoding = null) { return p\Php72::mb_chr($codepoint, $encoding); } } if (!function_exists('mb_scrub')) { function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } } > 1, $j = 0; $i < $len; ++$i, ++$j) { switch (true) { case $s[$i] < "\x80": $s[$j] = $s[$i]; break; case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break; default: $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); break; } } return substr($s, 0, $j); } public static function utf8_decode($s) { $s = (string) $s; $len = \strlen($s); for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) { switch ($s[$i] & "\xF0") { case "\xC0": case "\xD0": $c = (\ord($s[$i] & "\x1F") << 6) | \ord($s[++$i] & "\x3F"); $s[$j] = $c < 256 ? \chr($c) : '?'; break; case "\xF0": ++$i; case "\xE0": $s[$j] = '?'; $i += 2; break; default: $s[$j] = $s[$i]; } } return substr($s, 0, $j); } public static function php_os_family() { if ('\\' === \DIRECTORY_SEPARATOR) { return 'Windows'; } $map = array( 'Darwin' => 'Darwin', 'DragonFly' => 'BSD', 'FreeBSD' => 'BSD', 'NetBSD' => 'BSD', 'OpenBSD' => 'BSD', 'Linux' => 'Linux', 'SunOS' => 'Solaris', ); return isset($map[PHP_OS]) ? $map[PHP_OS] : 'Unknown'; } public static function spl_object_id($object) { if (null === self::$hashMask) { self::initHashMask(); } if (null === $hash = spl_object_hash($object)) { return; } return self::$hashMask ^ hexdec(substr($hash, 16 - (\PHP_INT_SIZE * 2 - 1), (\PHP_INT_SIZE * 2 - 1))); } public static function sapi_windows_vt100_support($stream, $enable = null) { if (!\is_resource($stream)) { trigger_error('sapi_windows_vt100_support() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING); return false; } $meta = stream_get_meta_data($stream); if ('STDIO' !== $meta['stream_type']) { trigger_error('sapi_windows_vt100_support() was not able to analyze the specified stream', E_USER_WARNING); return false; } if (false === $enable || !self::stream_isatty($stream)) { return false; } $meta = array_map('strtolower', $meta); $stdin = 'php://stdin' === $meta['uri'] || 'php://fd/0' === $meta['uri']; return !$stdin && (false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM') || 'Hyper' === getenv('TERM_PROGRAM')); } public static function stream_isatty($stream) { if (!\is_resource($stream)) { trigger_error('stream_isatty() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING); return false; } if ('\\' === \DIRECTORY_SEPARATOR) { $stat = @fstat($stream); return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } return \function_exists('posix_isatty') && @posix_isatty($stream); } private static function initHashMask() { $obj = (object) array(); self::$hashMask = -1; $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush'); foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) { if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) { $frame['line'] = 0; break; } } if (!empty($frame['line'])) { ob_start(); debug_zval_dump($obj); self::$hashMask = (int) substr(ob_get_clean(), 17); } self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - (\PHP_INT_SIZE * 2 - 1), (\PHP_INT_SIZE * 2 - 1))); } 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) { $s = mb_convert_encoding($s, $encoding, 'UTF-8'); } return $s; } public static function mb_ord($s, $encoding = null) { if (null === $encoding) { $s = mb_convert_encoding($s, 'UTF-8'); } elseif ('UTF-8' !== $encoding) { $s = mb_convert_encoding($s, 'UTF-8', $encoding); } if (1 === \strlen($s)) { return \ord($s); } $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; } } dispatcher = $dispatcher; $this->stopwatch = $stopwatch; $this->logger = $logger; $this->wrappedListeners = []; } public function addListener($eventName, $listener, $priority = 0) { $this->dispatcher->addListener($eventName, $listener, $priority); } public function addSubscriber(EventSubscriberInterface $subscriber) { $this->dispatcher->addSubscriber($subscriber); } public function removeListener($eventName, $listener) { if (isset($this->wrappedListeners[$eventName])) { foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { if ($wrappedListener->getWrappedListener() === $listener) { $listener = $wrappedListener; unset($this->wrappedListeners[$eventName][$index]); break; } } } return $this->dispatcher->removeListener($eventName, $listener); } public function removeSubscriber(EventSubscriberInterface $subscriber) { return $this->dispatcher->removeSubscriber($subscriber); } public function getListeners($eventName = null) { return $this->dispatcher->getListeners($eventName); } public function getListenerPriority($eventName, $listener) { if (isset($this->wrappedListeners[$eventName])) { foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { if ($wrappedListener->getWrappedListener() === $listener) { return $this->dispatcher->getListenerPriority($eventName, $wrappedListener); } } } return $this->dispatcher->getListenerPriority($eventName, $listener); } public function hasListeners($eventName = null) { return $this->dispatcher->hasListeners($eventName); } public function dispatch($eventName, Event $event = null) { if (null === $this->callStack) { $this->callStack = new \SplObjectStorage(); } if (null === $event) { $event = new Event(); } if (null !== $this->logger && $event->isPropagationStopped()) { $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName)); } $this->preProcess($eventName); try { $this->preDispatch($eventName, $event); try { $e = $this->stopwatch->start($eventName, 'section'); try { $this->dispatcher->dispatch($eventName, $event); } finally { if ($e->isStarted()) { $e->stop(); } } } finally { $this->postDispatch($eventName, $event); } } finally { $this->postProcess($eventName); } return $event; } public function getCalledListeners() { if (null === $this->callStack) { return []; } $called = []; foreach ($this->callStack as $listener) { list($eventName) = $this->callStack->getInfo(); $called[] = $listener->getInfo($eventName); } return $called; } public function getNotCalledListeners() { try { $allListeners = $this->getListeners(); } catch (\Exception $e) { if (null !== $this->logger) { $this->logger->info('An exception was thrown while getting the uncalled listeners.', ['exception' => $e]); } return []; } $calledListeners = []; if (null !== $this->callStack) { foreach ($this->callStack as $calledListener) { $calledListeners[] = $calledListener->getWrappedListener(); } } $notCalled = []; foreach ($allListeners as $eventName => $listeners) { foreach ($listeners as $listener) { if (!\in_array($listener, $calledListeners, true)) { if (!$listener instanceof WrappedListener) { $listener = new WrappedListener($listener, null, $this->stopwatch, $this); } $notCalled[] = $listener->getInfo($eventName); } } } uasort($notCalled, [$this, 'sortNotCalledListeners']); return $notCalled; } public function reset() { $this->callStack = null; } public function __call($method, $arguments) { return \call_user_func_array([$this->dispatcher, $method], $arguments); } protected function preDispatch($eventName, Event $event) { } protected function postDispatch($eventName, Event $event) { } private function preProcess($eventName) { foreach ($this->dispatcher->getListeners($eventName) as $listener) { $priority = $this->getListenerPriority($eventName, $listener); $wrappedListener = new WrappedListener($listener instanceof WrappedListener ? $listener->getWrappedListener() : $listener, null, $this->stopwatch, $this); $this->wrappedListeners[$eventName][] = $wrappedListener; $this->dispatcher->removeListener($eventName, $listener); $this->dispatcher->addListener($eventName, $wrappedListener, $priority); $this->callStack->attach($wrappedListener, [$eventName]); } } private function postProcess($eventName) { unset($this->wrappedListeners[$eventName]); $skipped = false; foreach ($this->dispatcher->getListeners($eventName) as $listener) { if (!$listener instanceof WrappedListener) { continue; } $priority = $this->getListenerPriority($eventName, $listener); $this->dispatcher->removeListener($eventName, $listener); $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority); if (null !== $this->logger) { $context = ['event' => $eventName, 'listener' => $listener->getPretty()]; } if ($listener->wasCalled()) { if (null !== $this->logger) { $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context); } } else { $this->callStack->detach($listener); } if (null !== $this->logger && $skipped) { $this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context); } if ($listener->stoppedPropagation()) { if (null !== $this->logger) { $this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context); } $skipped = true; } } } private function sortNotCalledListeners(array $a, array $b) { if (0 !== $cmp = strcmp($a['event'], $b['event'])) { return $cmp; } if (\is_int($a['priority']) && !\is_int($b['priority'])) { return 1; } if (!\is_int($a['priority']) && \is_int($b['priority'])) { return -1; } if ($a['priority'] === $b['priority']) { return 0; } if ($a['priority'] > $b['priority']) { return -1; } return 1; } } listener = $listener; $this->stopwatch = $stopwatch; $this->dispatcher = $dispatcher; $this->called = false; $this->stoppedPropagation = false; if (\is_array($listener)) { $this->name = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0]; $this->pretty = $this->name.'::'.$listener[1]; } elseif ($listener instanceof \Closure) { $r = new \ReflectionFunction($listener); if (false !== strpos($r->name, '{closure}')) { $this->pretty = $this->name = 'closure'; } elseif ($class = $r->getClosureScopeClass()) { $this->name = $class->name; $this->pretty = $this->name.'::'.$r->name; } else { $this->pretty = $this->name = $r->name; } } elseif (\is_string($listener)) { $this->pretty = $this->name = $listener; } else { $this->name = \get_class($listener); $this->pretty = $this->name.'::__invoke'; } if (null !== $name) { $this->name = $name; } if (null === self::$hasClassStub) { self::$hasClassStub = class_exists(ClassStub::class); } } public function getWrappedListener() { return $this->listener; } public function wasCalled() { return $this->called; } public function stoppedPropagation() { return $this->stoppedPropagation; } public function getPretty() { return $this->pretty; } public function getInfo($eventName) { if (null === $this->stub) { $this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()'; } return [ 'event' => $eventName, 'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null), 'pretty' => $this->pretty, 'stub' => $this->stub, ]; } public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) { $dispatcher = $this->dispatcher ?: $dispatcher; $this->called = true; $this->priority = $dispatcher->getListenerPriority($eventName, $this->listener); $e = $this->stopwatch->start($this->name, 'event_listener'); \call_user_func($this->listener, $event, $eventName, $dispatcher); if ($e->isStarted()) { $e->stop(); } if ($event->isPropagationStopped()) { $this->stoppedPropagation = true; } } } dispatcher = $dispatcher; } public function dispatch($eventName, Event $event = null) { return $this->dispatcher->dispatch($eventName, $event); } public function addListener($eventName, $listener, $priority = 0) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); } public function addSubscriber(EventSubscriberInterface $subscriber) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); } public function removeListener($eventName, $listener) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); } public function removeSubscriber(EventSubscriberInterface $subscriber) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); } public function getListeners($eventName = null) { return $this->dispatcher->getListeners($eventName); } public function getListenerPriority($eventName, $listener) { return $this->dispatcher->getListenerPriority($eventName, $listener); } public function hasListeners($eventName = null) { return $this->dispatcher->hasListeners($eventName); } } getListeners($eventName)) { $this->doDispatch($listeners, $eventName, $event); } return $event; } public function getListeners($eventName = null) { if (null !== $eventName) { if (empty($this->listeners[$eventName])) { return []; } if (!isset($this->sorted[$eventName])) { $this->sortListeners($eventName); } return $this->sorted[$eventName]; } foreach ($this->listeners as $eventName => $eventListeners) { if (!isset($this->sorted[$eventName])) { $this->sortListeners($eventName); } } return array_filter($this->sorted); } public function getListenerPriority($eventName, $listener) { if (empty($this->listeners[$eventName])) { return null; } if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) { $listener[0] = $listener[0](); } foreach ($this->listeners[$eventName] as $priority => $listeners) { foreach ($listeners as $k => $v) { if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) { $v[0] = $v[0](); $this->listeners[$eventName][$priority][$k] = $v; } if ($v === $listener) { return $priority; } } } return null; } public function hasListeners($eventName = null) { if (null !== $eventName) { return !empty($this->listeners[$eventName]); } foreach ($this->listeners as $eventListeners) { if ($eventListeners) { return true; } } return false; } public function addListener($eventName, $listener, $priority = 0) { $this->listeners[$eventName][$priority][] = $listener; unset($this->sorted[$eventName]); } public function removeListener($eventName, $listener) { if (empty($this->listeners[$eventName])) { return; } if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) { $listener[0] = $listener[0](); } foreach ($this->listeners[$eventName] as $priority => $listeners) { foreach ($listeners as $k => $v) { if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) { $v[0] = $v[0](); } if ($v === $listener) { unset($listeners[$k], $this->sorted[$eventName]); } else { $listeners[$k] = $v; } } if ($listeners) { $this->listeners[$eventName][$priority] = $listeners; } else { unset($this->listeners[$eventName][$priority]); } } } public function addSubscriber(EventSubscriberInterface $subscriber) { foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { if (\is_string($params)) { $this->addListener($eventName, [$subscriber, $params]); } elseif (\is_string($params[0])) { $this->addListener($eventName, [$subscriber, $params[0]], isset($params[1]) ? $params[1] : 0); } else { foreach ($params as $listener) { $this->addListener($eventName, [$subscriber, $listener[0]], isset($listener[1]) ? $listener[1] : 0); } } } } public function removeSubscriber(EventSubscriberInterface $subscriber) { foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { if (\is_array($params) && \is_array($params[0])) { foreach ($params as $listener) { $this->removeListener($eventName, [$subscriber, $listener[0]]); } } else { $this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]); } } } protected function doDispatch($listeners, $eventName, Event $event) { foreach ($listeners as $listener) { if ($event->isPropagationStopped()) { break; } \call_user_func($listener, $event, $eventName, $this); } } private function sortListeners($eventName) { krsort($this->listeners[$eventName]); $this->sorted[$eventName] = []; foreach ($this->listeners[$eventName] as $priority => $listeners) { foreach ($listeners as $k => $listener) { if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) { $listener[0] = $listener[0](); $this->listeners[$eventName][$priority][$k] = $listener; } $this->sorted[$eventName][] = $listener; } } } } dispatcherService = $dispatcherService; $this->listenerTag = $listenerTag; $this->subscriberTag = $subscriberTag; } public function setHotPathEvents(array $hotPathEvents, $tagName = 'container.hot_path') { $this->hotPathEvents = array_flip($hotPathEvents); $this->hotPathTagName = $tagName; return $this; } public function process(ContainerBuilder $container) { if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) { return; } $definition = $container->findDefinition($this->dispatcherService); foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) { foreach ($events as $event) { $priority = isset($event['priority']) ? $event['priority'] : 0; if (!isset($event['event'])) { throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace_callback([ '/(?<=\b)[a-z]/i', '/[^a-z0-9]/i', ], function ($matches) { return strtoupper($matches[0]); }, $event['event']); $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); } $definition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]); if (isset($this->hotPathEvents[$event['event']])) { $container->getDefinition($id)->addTag($this->hotPathTagName); } } } $extractingDispatcher = new ExtractingEventDispatcher(); foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $attributes) { $def = $container->getDefinition($id); $class = $def->getClass(); if (!$r = $container->getReflectionClass($class)) { throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); } if (!$r->isSubclassOf(EventSubscriberInterface::class)) { throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class)); } $class = $r->name; ExtractingEventDispatcher::$subscriber = $class; $extractingDispatcher->addSubscriber($extractingDispatcher); foreach ($extractingDispatcher->listeners as $args) { $args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]]; $definition->addMethodCall('addListener', $args); if (isset($this->hotPathEvents[$args[0]])) { $container->getDefinition($id)->addTag($this->hotPathTagName); } } $extractingDispatcher->listeners = []; } } } class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface { public $listeners = []; public static $subscriber; public function addListener($eventName, $listener, $priority = 0) { $this->listeners[] = [$eventName, $listener[1], $priority]; } public static function getSubscribedEvents() { $callback = [self::$subscriber, 'getSubscribedEvents']; return $callback(); } } subject = $subject; $this->arguments = $arguments; } public function getSubject() { return $this->subject; } public function getArgument($key) { if ($this->hasArgument($key)) { return $this->arguments[$key]; } throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key)); } public function setArgument($key, $value) { $this->arguments[$key] = $value; return $this; } public function getArguments() { return $this->arguments; } public function setArguments(array $args = []) { $this->arguments = $args; return $this; } public function hasArgument($key) { return \array_key_exists($key, $this->arguments); } public function offsetGet($key) { return $this->getArgument($key); } public function offsetSet($key, $value) { $this->setArgument($key, $value); } public function offsetUnset($key) { if ($this->hasArgument($key)) { unset($this->arguments[$key]); } } public function offsetExists($key) { return $this->hasArgument($key); } public function getIterator() { return new \ArrayIterator($this->arguments); } } container = $container; $class = static::class; if ($this instanceof \PHPUnit_Framework_MockObject_MockObject || $this instanceof MockObject || $this instanceof \Prophecy\Doubler\DoubleInterface) { $class = get_parent_class($class); } if (__CLASS__ !== $class) { @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), \E_USER_DEPRECATED); } } public function addListenerService($eventName, $callback, $priority = 0) { @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), \E_USER_DEPRECATED); if (!\is_array($callback) || 2 !== \count($callback)) { throw new \InvalidArgumentException('Expected an ["service", "method"] argument.'); } $this->listenerIds[$eventName][] = [$callback[0], $callback[1], $priority]; } public function removeListener($eventName, $listener) { $this->lazyLoad($eventName); if (isset($this->listenerIds[$eventName])) { foreach ($this->listenerIds[$eventName] as $i => list($serviceId, $method)) { $key = $serviceId.'.'.$method; if (isset($this->listeners[$eventName][$key]) && $listener === [$this->listeners[$eventName][$key], $method]) { unset($this->listeners[$eventName][$key]); if (empty($this->listeners[$eventName])) { unset($this->listeners[$eventName]); } unset($this->listenerIds[$eventName][$i]); if (empty($this->listenerIds[$eventName])) { unset($this->listenerIds[$eventName]); } } } } parent::removeListener($eventName, $listener); } public function hasListeners($eventName = null) { if (null === $eventName) { return $this->listenerIds || $this->listeners || parent::hasListeners(); } if (isset($this->listenerIds[$eventName])) { return true; } return parent::hasListeners($eventName); } public function getListeners($eventName = null) { if (null === $eventName) { foreach ($this->listenerIds as $serviceEventName => $args) { $this->lazyLoad($serviceEventName); } } else { $this->lazyLoad($eventName); } return parent::getListeners($eventName); } public function getListenerPriority($eventName, $listener) { $this->lazyLoad($eventName); return parent::getListenerPriority($eventName, $listener); } public function addSubscriberService($serviceId, $class) { @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), \E_USER_DEPRECATED); foreach ($class::getSubscribedEvents() as $eventName => $params) { if (\is_string($params)) { $this->listenerIds[$eventName][] = [$serviceId, $params, 0]; } elseif (\is_string($params[0])) { $this->listenerIds[$eventName][] = [$serviceId, $params[0], isset($params[1]) ? $params[1] : 0]; } else { foreach ($params as $listener) { $this->listenerIds[$eventName][] = [$serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0]; } } } } public function getContainer() { @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 as its class will be removed in 4.0. Inject the container or the services you need in your listeners/subscribers instead.', \E_USER_DEPRECATED); return $this->container; } protected function lazyLoad($eventName) { if (isset($this->listenerIds[$eventName])) { foreach ($this->listenerIds[$eventName] as list($serviceId, $method, $priority)) { $listener = $this->container->get($serviceId); $key = $serviceId.'.'.$method; if (!isset($this->listeners[$eventName][$key])) { $this->addListener($eventName, [$listener, $method], $priority); } elseif ($this->listeners[$eventName][$key] !== $listener) { parent::removeListener($eventName, [$this->listeners[$eventName][$key], $method]); $this->addListener($eventName, [$listener, $method], $priority); } $this->listeners[$eventName][$key] = $listener; } } } } propagationStopped; } public function stopPropagation() { $this->propagationStopped = true; } } buildAndCacheFromFactory($className); } private function buildAndCacheFromFactory($className) { $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); $instance = $factory(); if ($this->isSafeToClone(new ReflectionClass($instance))) { self::$cachedCloneables[$className] = clone $instance; } return $instance; } private function buildFactory($className) { $reflectionClass = $this->getReflectionClass($className); if ($this->isInstantiableViaReflection($reflectionClass)) { return function () use ($reflectionClass) { return $reflectionClass->newInstanceWithoutConstructor(); }; } $serializedString = sprintf( '%s:%d:"%s":0:{}', $this->getSerializationFormat($reflectionClass), strlen($className), $className ); $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); return function () use ($serializedString) { return unserialize($serializedString); }; } private function getReflectionClass($className) { if (! class_exists($className)) { throw InvalidArgumentException::fromNonExistingClass($className); } $reflection = new ReflectionClass($className); if ($reflection->isAbstract()) { throw InvalidArgumentException::fromAbstractClass($reflection); } return $reflection; } private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) { set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) { $error = UnexpectedValueException::fromUncleanUnSerialization( $reflectionClass, $message, $code, $file, $line ); }); $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); restore_error_handler(); if ($error) { throw $error; } } private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) { try { unserialize($serializedString); } catch (Exception $exception) { restore_error_handler(); throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); } } private function isInstantiableViaReflection(ReflectionClass $reflectionClass) { if (\PHP_VERSION_ID >= 50600) { return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); } return \PHP_VERSION_ID >= 50400 && ! $this->hasInternalAncestors($reflectionClass); } private function hasInternalAncestors(ReflectionClass $reflectionClass) { do { if ($reflectionClass->isInternal()) { return true; } } while ($reflectionClass = $reflectionClass->getParentClass()); return false; } private function getSerializationFormat(ReflectionClass $reflectionClass) { if ($this->isPhpVersionWithBrokenSerializationFormat() && $reflectionClass->implementsInterface('Serializable') ) { return self::SERIALIZATION_FORMAT_USE_UNSERIALIZER; } return self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER; } private function isPhpVersionWithBrokenSerializationFormat() { return PHP_VERSION_ID === 50429 || PHP_VERSION_ID === 50513; } private function isSafeToClone(ReflectionClass $reflection) { if (method_exists($reflection, 'isCloneable') && ! $reflection->isCloneable()) { return false; } return ! $reflection->hasMethod('__clone'); } } getName() ), 0, $exception ); } public static function fromUncleanUnSerialization( ReflectionClass $reflectionClass, $errorString, $errorCode, $errorFile, $errorLine ) { return new self( sprintf( 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' . 'in file "%s" at line "%d"', $reflectionClass->getName(), $errorFile, $errorLine ), 0, new Exception($errorString, $errorCode) ); } } = 50400 && trait_exists($className)) { return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); } return new self(sprintf('The provided class "%s" does not exist', $className)); } public static function fromAbstractClass(ReflectionClass $reflectionClass) { return new self(sprintf( 'The provided class "%s" is abstract, and can not be instantiated', $reflectionClass->getName() )); } } uriRetriever = $uriRetriever ?: new UriRetriever(); $this->uriResolver = $uriResolver ?: new UriResolver(); } public function getUriRetriever() { return $this->uriRetriever; } public function getUriResolver() { return $this->uriResolver; } public function addSchema($id, $schema = null) { if (is_null($schema) && $id !== self::INTERNAL_PROVIDED_SCHEMA_URI) { $schema = $this->uriRetriever->retrieve($id); } if (is_array($schema)) { $schema = BaseConstraint::arrayToObjectRecursive($schema); } if (is_object($schema) && property_exists($schema, 'id')) { if ($schema->id == 'http://json-schema.org/draft-04/schema#') { $schema->properties->id->format = 'uri-reference'; } elseif ($schema->id == 'http://json-schema.org/draft-03/schema#') { $schema->properties->id->format = 'uri-reference'; $schema->properties->{'$ref'}->format = 'uri-reference'; } } $this->expandRefs($schema, $id); $this->schemas[$id] = $schema; } private function expandRefs(&$schema, $base = null) { if (!is_object($schema)) { if (is_array($schema)) { foreach ($schema as &$member) { $this->expandRefs($member, $base); } } return; } if (property_exists($schema, 'id') && is_string($schema->id) && $base != $schema->id) { $base = $this->uriResolver->resolve($schema->id, $base); } if (property_exists($schema, '$ref') && is_string($schema->{'$ref'})) { $refPointer = new JsonPointer($this->uriResolver->resolve($schema->{'$ref'}, $base)); $schema->{'$ref'} = (string) $refPointer; } foreach ($schema as &$member) { $this->expandRefs($member, $base); } } public function getSchema($id) { if (!array_key_exists($id, $this->schemas)) { $this->addSchema($id); } return $this->schemas[$id]; } public function resolveRef($ref) { $jsonPointer = new JsonPointer($ref); $fileName = $jsonPointer->getFilename(); if (!strlen($fileName)) { throw new UnresolvableJsonPointerException(sprintf( "Could not resolve fragment '%s': no file is defined", $jsonPointer->getPropertyPathAsString() )); } $refSchema = $this->getSchema($fileName); foreach ($jsonPointer->getPropertyPaths() as $path) { if (is_object($refSchema) && property_exists($refSchema, $path)) { $refSchema = $this->resolveRefSchema($refSchema->{$path}); } elseif (is_array($refSchema) && array_key_exists($path, $refSchema)) { $refSchema = $this->resolveRefSchema($refSchema[$path]); } else { throw new UnresolvableJsonPointerException(sprintf( 'File: %s is found, but could not resolve fragment: %s', $jsonPointer->getFilename(), $jsonPointer->getPropertyPathAsString() )); } } return $refSchema; } public function resolveRefSchema($refSchema) { if (is_object($refSchema) && property_exists($refSchema, '$ref') && is_string($refSchema->{'$ref'})) { $newSchema = $this->resolveRef($refSchema->{'$ref'}); $refSchema = (object) (get_object_vars($refSchema) + get_object_vars($newSchema)); unset($refSchema->{'$ref'}); } return $refSchema; } } filename = $splitRef[0]; if (array_key_exists(1, $splitRef)) { $this->propertyPaths = $this->decodePropertyPaths($splitRef[1]); } } private function decodePropertyPaths($propertyPathString) { $paths = array(); foreach (explode('/', trim($propertyPathString, '/')) as $path) { $path = $this->decodePath($path); if (is_string($path) && '' !== $path) { $paths[] = $path; } } return $paths; } private function encodePropertyPaths() { return array_map( array($this, 'encodePath'), $this->getPropertyPaths() ); } private function decodePath($path) { return strtr($path, array('~1' => '/', '~0' => '~', '%25' => '%')); } private function encodePath($path) { return strtr($path, array('/' => '~1', '~' => '~0', '%' => '%25')); } public function getFilename() { return $this->filename; } public function getPropertyPaths() { return $this->propertyPaths; } public function withPropertyPaths(array $propertyPaths) { $new = clone $this; $new->propertyPaths = $propertyPaths; return $new; } public function getPropertyPathAsString() { return rtrim('#/' . implode('/', $this->encodePropertyPaths()), '/'); } public function __toString() { return $this->getFilename() . $this->getPropertyPathAsString(); } public function setFromDefault() { $this->fromDefault = true; } public function fromDefault() { return $this->fromDefault; } } 'package://dist/schema/json-schema-draft-$1.json' ); protected $allowedInvalidContentTypeEndpoints = array( 'http://json-schema.org/', 'https://json-schema.org/' ); protected $uriRetriever = null; private $schemaCache = array(); public function addInvalidContentTypeEndpoint($endpoint) { $this->allowedInvalidContentTypeEndpoints[] = $endpoint; } public function confirmMediaType($uriRetriever, $uri) { $contentType = $uriRetriever->getContentType(); if (is_null($contentType)) { return; } if (in_array($contentType, array(Validator::SCHEMA_MEDIA_TYPE, 'application/json'))) { return; } foreach ($this->allowedInvalidContentTypeEndpoints as $endpoint) { if (strpos($uri, $endpoint) === 0) { return true; } } throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', Validator::SCHEMA_MEDIA_TYPE)); } public function getUriRetriever() { if (is_null($this->uriRetriever)) { $this->setUriRetriever(new FileGetContents()); } return $this->uriRetriever; } public function resolvePointer($jsonSchema, $uri) { $resolver = new UriResolver(); $parsed = $resolver->parse($uri); if (empty($parsed['fragment'])) { return $jsonSchema; } $path = explode('/', $parsed['fragment']); while ($path) { $pathElement = array_shift($path); if (!empty($pathElement)) { $pathElement = str_replace('~1', '/', $pathElement); $pathElement = str_replace('~0', '~', $pathElement); if (!empty($jsonSchema->$pathElement)) { $jsonSchema = $jsonSchema->$pathElement; } else { throw new ResourceNotFoundException( 'Fragment "' . $parsed['fragment'] . '" not found' . ' in ' . $uri ); } if (!is_object($jsonSchema)) { throw new ResourceNotFoundException( 'Fragment part "' . $pathElement . '" is no object ' . ' in ' . $uri ); } } } return $jsonSchema; } public function retrieve($uri, $baseUri = null, $translate = true) { $resolver = new UriResolver(); $resolvedUri = $fetchUri = $resolver->resolve($uri, $baseUri); $arParts = $resolver->parse($resolvedUri); if (isset($arParts['fragment'])) { unset($arParts['fragment']); $fetchUri = $resolver->generate($arParts); } if ($translate) { $fetchUri = $this->translate($fetchUri); } $jsonSchema = $this->loadSchema($fetchUri); $jsonSchema = $this->resolvePointer($jsonSchema, $resolvedUri); if ($jsonSchema instanceof \stdClass) { $jsonSchema->id = $resolvedUri; } return $jsonSchema; } protected function loadSchema($fetchUri) { if (isset($this->schemaCache[$fetchUri])) { return $this->schemaCache[$fetchUri]; } $uriRetriever = $this->getUriRetriever(); $contents = $this->uriRetriever->retrieve($fetchUri); $this->confirmMediaType($uriRetriever, $fetchUri); $jsonSchema = json_decode($contents); if (JSON_ERROR_NONE < $error = json_last_error()) { throw new JsonDecodingException($error); } $this->schemaCache[$fetchUri] = $jsonSchema; return $jsonSchema; } public function setUriRetriever(UriRetrieverInterface $uriRetriever) { $this->uriRetriever = $uriRetriever; return $this; } public function parse($uri) { preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match); $components = array(); if (5 < count($match)) { $components = array( 'scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5] ); } if (7 < count($match)) { $components['query'] = $match[7]; } if (9 < count($match)) { $components['fragment'] = $match[9]; } return $components; } public function generate(array $components) { $uri = $components['scheme'] . '://' . $components['authority'] . $components['path']; if (array_key_exists('query', $components)) { $uri .= $components['query']; } if (array_key_exists('fragment', $components)) { $uri .= $components['fragment']; } return $uri; } public function resolve($uri, $baseUri = null) { $components = $this->parse($uri); $path = $components['path']; if ((array_key_exists('scheme', $components)) && ('http' === $components['scheme'])) { return $uri; } $baseComponents = $this->parse($baseUri); $basePath = $baseComponents['path']; $baseComponents['path'] = UriResolver::combineRelativePathWithBasePath($path, $basePath); return $this->generate($baseComponents); } public function isValid($uri) { $components = $this->parse($uri); return !empty($components); } public function setTranslation($from, $to) { $this->translationMap[$from] = $to; } public function translate($uri) { foreach ($this->translationMap as $from => $to) { $uri = preg_replace($from, $to, $uri); } $uri = preg_replace('|^package://|', sprintf('file://%s/', realpath(__DIR__ . '/../../..')), $uri); return $uri; } } $match[2], 'authority' => $match[4], 'path' => $match[5] ); } if (7 < count($match)) { $components['query'] = $match[7]; } if (9 < count($match)) { $components['fragment'] = $match[9]; } return $components; } public function generate(array $components) { $uri = $components['scheme'] . '://' . $components['authority'] . $components['path']; if (array_key_exists('query', $components) && strlen($components['query'])) { $uri .= '?' . $components['query']; } if (array_key_exists('fragment', $components)) { $uri .= '#' . $components['fragment']; } return $uri; } public function resolve($uri, $baseUri = null) { if ( !is_null($baseUri) && !filter_var($baseUri, \FILTER_VALIDATE_URL) && !preg_match('|^[^/]+://|u', $baseUri) ) { if (is_file($baseUri)) { $baseUri = 'file://' . realpath($baseUri); } elseif (is_dir($baseUri)) { $baseUri = 'file://' . realpath($baseUri) . '/'; } else { $baseUri = 'file://' . getcwd() . '/' . $baseUri; } } if ($uri == '') { return $baseUri; } $components = $this->parse($uri); $path = $components['path']; if (!empty($components['scheme'])) { return $uri; } $baseComponents = $this->parse($baseUri); $basePath = $baseComponents['path']; $baseComponents['path'] = self::combineRelativePathWithBasePath($path, $basePath); if (isset($components['fragment'])) { $baseComponents['fragment'] = $components['fragment']; } return $this->generate($baseComponents); } public static function combineRelativePathWithBasePath($relativePath, $basePath) { $relativePath = self::normalizePath($relativePath); if ($relativePath == '') { return $basePath; } if ($relativePath[0] == '/') { return $relativePath; } $basePathSegments = explode('/', $basePath); preg_match('|^/?(\.\./(?:\./)*)*|', $relativePath, $match); $numLevelUp = strlen($match[0]) /3 + 1; if ($numLevelUp >= count($basePathSegments)) { throw new UriResolverException(sprintf("Unable to resolve URI '%s' from base '%s'", $relativePath, $basePath)); } $basePathSegments = array_slice($basePathSegments, 0, -$numLevelUp); $path = preg_replace('|^/?(\.\./(\./)*)*|', '', $relativePath); return implode('/', $basePathSegments) . '/' . $path; } private static function normalizePath($path) { $path = preg_replace('|((?parse($uri); return !empty($components); } } contentType; } } messageBody = $response; if (!empty($http_response_header)) { $this->fetchContentType($http_response_header); } else { $this->contentType = null; } return $this->messageBody; } private function fetchContentType(array $headers) { foreach ($headers as $header) { if ($this->contentType = self::getContentTypeMatchInHeader($header)) { return true; } } return false; } protected static function getContentTypeMatchInHeader($header) { if (0 < preg_match("/Content-Type:(\V*)/ims", $header, $match)) { return trim($match[1]); } return null; } } fetchMessageBody($response); $this->fetchContentType($response); curl_close($ch); return $this->messageBody; } private function fetchMessageBody($response) { preg_match("/(?:\r\n){2}(.*)$/ms", $response, $match); $this->messageBody = $match[1]; } protected function fetchContentType($response) { if (0 < preg_match("/Content-Type:(\V*)/ims", $response, $match)) { $this->contentType = trim($match[1]); return true; } return false; } } schemas = $schemas; $this->contentType = $contentType; } public function retrieve($uri) { if (!array_key_exists($uri, $this->schemas)) { throw new \JsonSchema\Exception\ResourceNotFoundException(sprintf( 'The JSON schema "%s" was not found.', $uri )); } return $this->schemas[$uri]; } } object = $object; } public function current() { $this->initialize(); return $this->data[$this->position]; } public function next() { $this->initialize(); $this->position++; } public function key() { $this->initialize(); return $this->position; } public function valid() { $this->initialize(); return isset($this->data[$this->position]); } public function rewind() { $this->initialize(); $this->position = 0; } public function count() { $this->initialize(); return count($this->data); } private function initialize() { if (!$this->initialized) { $this->data = $this->buildDataFromObject($this->object); $this->initialized = true; } } private function buildDataFromObject($object) { $result = array(); $stack = new \SplStack(); $stack->push($object); while (!$stack->isEmpty()) { $current = $stack->pop(); if (is_object($current)) { array_push($result, $current); } foreach ($this->getDataFromItem($current) as $propertyName => $propertyValue) { if (is_object($propertyValue) || is_array($propertyValue)) { $stack->push($propertyValue); } } } return $result; } private function getDataFromItem($item) { if (!is_object($item) && !is_array($item)) { return array(); } return is_object($item) ? get_object_vars($item) : $item; } } {$property}; } return $value[$property]; } public static function propertySet(&$value, $property, $data) { if (is_object($value)) { $value->{$property} = $data; } else { $value[$property] = $data; } } public static function propertyExists($value, $property) { if (is_object($value)) { return property_exists($value, $property); } return array_key_exists($property, $value); } public static function propertyCount($value) { if (is_object($value)) { return count(get_object_vars($value)); } return count($value); } private static function isAssociativeArray($arr) { return array_keys($arr) !== range(0, count($arr) - 1); } } {$property}; } public static function propertySet(&$value, $property, $data) { $value->{$property} = $data; } public static function propertyExists($value, $property) { return property_exists($value, $property); } public static function propertyCount($value) { if (!is_object($value)) { return 0; } return count(get_object_vars($value)); } } incrementPath($path ?: new JsonPointer(''), $i); if ($fromDefault) { $path->setFromDefault(); } $this->validateCommonProperties($value, $schema, $path, $i); $this->validateOfProperties($value, $schema, $path, ''); $this->validateTypes($value, $schema, $path, $i); } public function validateTypes(&$value, $schema, JsonPointer $path, $i = null) { if ($this->getTypeCheck()->isArray($value)) { $this->checkArray($value, $schema, $path, $i); } if (LooseTypeCheck::isObject($value)) { $this->checkObject( $value, $schema, $path, isset($schema->properties) ? $schema->properties : null, isset($schema->additionalProperties) ? $schema->additionalProperties : null, isset($schema->patternProperties) ? $schema->patternProperties : null, $this->appliedDefaults ); } if (is_string($value)) { $this->checkString($value, $schema, $path, $i); } if (is_numeric($value)) { $this->checkNumber($value, $schema, $path, $i); } if (isset($schema->enum)) { $this->checkEnum($value, $schema, $path, $i); } } protected function validateCommonProperties(&$value, $schema, JsonPointer $path, $i = '') { if (isset($schema->extends)) { if (is_string($schema->extends)) { $schema->extends = $this->validateUri($schema, $schema->extends); } if (is_array($schema->extends)) { foreach ($schema->extends as $extends) { $this->checkUndefined($value, $extends, $path, $i); } } else { $this->checkUndefined($value, $schema->extends, $path, $i); } } if (!$path->fromDefault()) { $this->applyDefaultValues($value, $schema, $path); } if ($this->getTypeCheck()->isObject($value)) { if (!($value instanceof self) && isset($schema->required) && is_array($schema->required)) { foreach ($schema->required as $required) { if (!$this->getTypeCheck()->propertyExists($value, $required)) { $this->addError( $this->incrementPath($path ?: new JsonPointer(''), $required), 'The property ' . $required . ' is required', 'required' ); } } } elseif (isset($schema->required) && !is_array($schema->required)) { if ($schema->required && $value instanceof self) { $propertyPaths = $path->getPropertyPaths(); $propertyName = end($propertyPaths); $this->addError( $path, 'The property ' . $propertyName . ' is required', 'required' ); } } else { if ($value instanceof self) { return; } } } if (!($value instanceof self)) { $this->checkType($value, $schema, $path, $i); } if (isset($schema->disallow)) { $initErrors = $this->getErrors(); $typeSchema = new \stdClass(); $typeSchema->type = $schema->disallow; $this->checkType($value, $typeSchema, $path); if (count($this->getErrors()) == count($initErrors)) { $this->addError($path, 'Disallowed value was matched', 'disallow'); } else { $this->errors = $initErrors; } } if (isset($schema->not)) { $initErrors = $this->getErrors(); $this->checkUndefined($value, $schema->not, $path, $i); if (count($this->getErrors()) == count($initErrors)) { $this->addError($path, 'Matched a schema which it should not', 'not'); } else { $this->errors = $initErrors; } } if (isset($schema->dependencies) && $this->getTypeCheck()->isObject($value)) { $this->validateDependencies($value, $schema->dependencies, $path); } } private function shouldApplyDefaultValue($requiredOnly, $schema, $name = null, $parentSchema = null) { if (!$requiredOnly) { return true; } if ( $name !== null && isset($parentSchema->required) && is_array($parentSchema->required) && in_array($name, $parentSchema->required) ) { return true; } if (isset($schema->required) && !is_array($schema->required) && $schema->required) { return true; } return false; } protected function applyDefaultValues(&$value, $schema, $path) { if (!$this->factory->getConfig(self::CHECK_MODE_APPLY_DEFAULTS)) { return; } $requiredOnly = $this->factory->getConfig(self::CHECK_MODE_ONLY_REQUIRED_DEFAULTS); if (isset($schema->properties) && LooseTypeCheck::isObject($value)) { foreach ($schema->properties as $currentProperty => $propertyDefinition) { $propertyDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($propertyDefinition); if ( !LooseTypeCheck::propertyExists($value, $currentProperty) && property_exists($propertyDefinition, 'default') && $this->shouldApplyDefaultValue($requiredOnly, $propertyDefinition, $currentProperty, $schema) ) { if (is_object($propertyDefinition->default)) { LooseTypeCheck::propertySet($value, $currentProperty, clone $propertyDefinition->default); } else { LooseTypeCheck::propertySet($value, $currentProperty, $propertyDefinition->default); } $this->appliedDefaults[] = $currentProperty; } } } elseif (isset($schema->items) && LooseTypeCheck::isArray($value)) { $items = array(); if (LooseTypeCheck::isArray($schema->items)) { $items = $schema->items; } elseif (isset($schema->minItems) && count($value) < $schema->minItems) { $items = array_fill(count($value), $schema->minItems - count($value), $schema->items); } foreach ($items as $currentItem => $itemDefinition) { $itemDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($itemDefinition); if ( !array_key_exists($currentItem, $value) && property_exists($itemDefinition, 'default') && $this->shouldApplyDefaultValue($requiredOnly, $itemDefinition)) { if (is_object($itemDefinition->default)) { $value[$currentItem] = clone $itemDefinition->default; } else { $value[$currentItem] = $itemDefinition->default; } } $path->setFromDefault(); } } elseif ( $value instanceof self && property_exists($schema, 'default') && $this->shouldApplyDefaultValue($requiredOnly, $schema)) { $value = is_object($schema->default) ? clone $schema->default : $schema->default; $path->setFromDefault(); } } protected function validateOfProperties(&$value, $schema, JsonPointer $path, $i = '') { if ($value instanceof self) { return; } if (isset($schema->allOf)) { $isValid = true; foreach ($schema->allOf as $allOf) { $initErrors = $this->getErrors(); $this->checkUndefined($value, $allOf, $path, $i); $isValid = $isValid && (count($this->getErrors()) == count($initErrors)); } if (!$isValid) { $this->addError($path, 'Failed to match all schemas', 'allOf'); } } if (isset($schema->anyOf)) { $isValid = false; $startErrors = $this->getErrors(); $caughtException = null; foreach ($schema->anyOf as $anyOf) { $initErrors = $this->getErrors(); try { $this->checkUndefined($value, $anyOf, $path, $i); if ($isValid = (count($this->getErrors()) == count($initErrors))) { break; } } catch (ValidationException $e) { $isValid = false; } } if (!$isValid) { $this->addError($path, 'Failed to match at least one schema', 'anyOf'); } else { $this->errors = $startErrors; } } if (isset($schema->oneOf)) { $allErrors = array(); $matchedSchemas = 0; $startErrors = $this->getErrors(); foreach ($schema->oneOf as $oneOf) { try { $this->errors = array(); $this->checkUndefined($value, $oneOf, $path, $i); if (count($this->getErrors()) == 0) { $matchedSchemas++; } $allErrors = array_merge($allErrors, array_values($this->getErrors())); } catch (ValidationException $e) { } } if ($matchedSchemas !== 1) { $this->addErrors(array_merge($allErrors, $startErrors)); $this->addError($path, 'Failed to match exactly one schema', 'oneOf'); } else { $this->errors = $startErrors; } } } protected function validateDependencies($value, $dependencies, JsonPointer $path, $i = '') { foreach ($dependencies as $key => $dependency) { if ($this->getTypeCheck()->propertyExists($value, $key)) { if (is_string($dependency)) { if (!$this->getTypeCheck()->propertyExists($value, $dependency)) { $this->addError($path, "$key depends on $dependency and $dependency is missing", 'dependencies'); } } elseif (is_array($dependency)) { foreach ($dependency as $d) { if (!$this->getTypeCheck()->propertyExists($value, $d)) { $this->addError($path, "$key depends on $d and $d is missing", 'dependencies'); } } } elseif (is_object($dependency)) { $this->checkUndefined($value, $dependency, $path, $i); } } } } protected function validateUri($schema, $schemaUri = null) { $resolver = new UriResolver(); $retriever = $this->factory->getUriRetriever(); $jsonSchema = null; if ($resolver->isValid($schemaUri)) { $schemaId = property_exists($schema, 'id') ? $schema->id : null; $jsonSchema = $retriever->retrieve($schemaId, $schemaUri); } return $jsonSchema; } } factory = $factory ?: new Factory(); } public function addError(JsonPointer $path = null, $message, $constraint = '', array $more = null) { $error = array( 'property' => $this->convertJsonPointerIntoPropertyPath($path ?: new JsonPointer('')), 'pointer' => ltrim(strval($path ?: new JsonPointer('')), '#'), 'message' => $message, 'constraint' => $constraint, 'context' => $this->factory->getErrorContext(), ); if ($this->factory->getConfig(Constraint::CHECK_MODE_EXCEPTIONS)) { throw new ValidationException(sprintf('Error validating %s: %s', $error['pointer'], $error['message'])); } if (is_array($more) && count($more) > 0) { $error += $more; } $this->errors[] = $error; $this->errorMask |= $error['context']; } public function addErrors(array $errors) { if ($errors) { $this->errors = array_merge($this->errors, $errors); $errorMask = &$this->errorMask; array_walk($errors, function ($error) use (&$errorMask) { if (isset($error['context'])) { $errorMask |= $error['context']; } }); } } public function getErrors($errorContext = Validator::ERROR_ALL) { if ($errorContext === Validator::ERROR_ALL) { return $this->errors; } return array_filter($this->errors, function ($error) use ($errorContext) { if ($errorContext & $error['context']) { return true; } }); } public function numErrors($errorContext = Validator::ERROR_ALL) { if ($errorContext === Validator::ERROR_ALL) { return count($this->errors); } return count($this->getErrors($errorContext)); } public function isValid() { return !$this->getErrors(); } public function reset() { $this->errors = array(); $this->errorMask = Validator::ERROR_NONE; } public function getErrorMask() { return $this->errorMask; } public static function arrayToObjectRecursive($array) { $json = json_encode($array); if (json_last_error() !== \JSON_ERROR_NONE) { $message = 'Unable to encode schema array as JSON'; if (function_exists('json_last_error_msg')) { $message .= ': ' . json_last_error_msg(); } throw new InvalidArgumentException($message); } return (object) json_decode($json); } } required) || !$schema->required)) { return; } $type = gettype($element); foreach ($schema->enum as $enum) { $enumType = gettype($enum); if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $enumType == 'object') { if ((object) $element == $enum) { return; } } if ($type === gettype($enum)) { if ($type == 'object') { if ($element == $enum) { return; } } elseif ($element === $enum) { return; } } } $this->addError($path, 'Does not have a value in the enumeration ' . json_encode($schema->enum), 'enum', array('enum' => $schema->enum)); } } appliedDefaults = $appliedDefaults; $matches = array(); if ($patternProperties) { $matches = $this->validatePatternProperties($element, $path, $patternProperties); } if ($properties) { $this->validateProperties($element, $properties, $path); } $this->validateElement($element, $matches, $schema, $path, $properties, $additionalProp); } public function validatePatternProperties($element, JsonPointer $path = null, $patternProperties) { $try = array('/', '#', '+', '~', '%'); $matches = array(); foreach ($patternProperties as $pregex => $schema) { $delimiter = '/'; foreach ($try as $delimiter) { if (strpos($pregex, $delimiter) === false) { break; } } if (@preg_match($delimiter . $pregex . $delimiter . 'u', '') === false) { $this->addError($path, 'The pattern "' . $pregex . '" is invalid', 'pregex', array('pregex' => $pregex)); continue; } foreach ($element as $i => $value) { if (preg_match($delimiter . $pregex . $delimiter . 'u', $i)) { $matches[] = $i; $this->checkUndefined($value, $schema ?: new \stdClass(), $path, $i, in_array($i, $this->appliedDefaults)); } } } return $matches; } public function validateElement($element, $matches, $schema = null, JsonPointer $path = null, $properties = null, $additionalProp = null) { $this->validateMinMaxConstraint($element, $schema, $path); foreach ($element as $i => $value) { $definition = $this->getProperty($properties, $i); if (!in_array($i, $matches) && $additionalProp === false && $this->inlineSchemaProperty !== $i && !$definition) { $this->addError($path, 'The property ' . $i . ' is not defined and the definition does not allow additional properties', 'additionalProp'); } if (!in_array($i, $matches) && $additionalProp && !$definition) { if ($additionalProp === true) { $this->checkUndefined($value, null, $path, $i, in_array($i, $this->appliedDefaults)); } else { $this->checkUndefined($value, $additionalProp, $path, $i, in_array($i, $this->appliedDefaults)); } } $require = $this->getProperty($definition, 'requires'); if ($require && !$this->getProperty($element, $require)) { $this->addError($path, 'The presence of the property ' . $i . ' requires that ' . $require . ' also be present', 'requires'); } $property = $this->getProperty($element, $i, $this->factory->createInstanceFor('undefined')); if (is_object($property)) { $this->validateMinMaxConstraint(!($property instanceof UndefinedConstraint) ? $property : $element, $definition, $path); } } } public function validateProperties(&$element, $properties = null, JsonPointer $path = null) { $undefinedConstraint = $this->factory->createInstanceFor('undefined'); foreach ($properties as $i => $value) { $property = &$this->getProperty($element, $i, $undefinedConstraint); $definition = $this->getProperty($properties, $i); if (is_object($definition)) { $this->checkUndefined($property, $definition, $path, $i, in_array($i, $this->appliedDefaults)); } } } protected function &getProperty(&$element, $property, $fallback = null) { if (is_array($element) && (isset($element[$property]) || array_key_exists($property, $element)) ) { return $element[$property]; } elseif (is_object($element) && property_exists($element, $property)) { return $element->$property; } return $fallback; } protected function validateMinMaxConstraint($element, $objectDefinition, JsonPointer $path = null) { if (isset($objectDefinition->minProperties) && !is_object($objectDefinition->minProperties)) { if ($this->getTypeCheck()->propertyCount($element) < $objectDefinition->minProperties) { $this->addError($path, 'Must contain a minimum of ' . $objectDefinition->minProperties . ' properties', 'minProperties', array('minProperties' => $objectDefinition->minProperties)); } } if (isset($objectDefinition->maxProperties) && !is_object($objectDefinition->maxProperties)) { if ($this->getTypeCheck()->propertyCount($element) > $objectDefinition->maxProperties) { $this->addError($path, 'Must contain no more than ' . $objectDefinition->maxProperties . ' properties', 'maxProperties', array('maxProperties' => $objectDefinition->maxProperties)); } } } } 'JsonSchema\Constraints\CollectionConstraint', 'collection' => 'JsonSchema\Constraints\CollectionConstraint', 'object' => 'JsonSchema\Constraints\ObjectConstraint', 'type' => 'JsonSchema\Constraints\TypeConstraint', 'undefined' => 'JsonSchema\Constraints\UndefinedConstraint', 'string' => 'JsonSchema\Constraints\StringConstraint', 'number' => 'JsonSchema\Constraints\NumberConstraint', 'enum' => 'JsonSchema\Constraints\EnumConstraint', 'format' => 'JsonSchema\Constraints\FormatConstraint', 'schema' => 'JsonSchema\Constraints\SchemaConstraint', 'validator' => 'JsonSchema\Validator' ); private $instanceCache = array(); public function __construct( SchemaStorageInterface $schemaStorage = null, UriRetrieverInterface $uriRetriever = null, $checkMode = Constraint::CHECK_MODE_NORMAL ) { $this->setConfig($checkMode); $this->uriRetriever = $uriRetriever ?: new UriRetriever(); $this->schemaStorage = $schemaStorage ?: new SchemaStorage($this->uriRetriever); } public function setConfig($checkMode = Constraint::CHECK_MODE_NORMAL) { $this->checkMode = $checkMode; } public function addConfig($options) { $this->checkMode |= $options; } public function removeConfig($options) { $this->checkMode &= ~$options; } public function getConfig($options = null) { if ($options === null) { return $this->checkMode; } return $this->checkMode & $options; } public function getUriRetriever() { return $this->uriRetriever; } public function getSchemaStorage() { return $this->schemaStorage; } public function getTypeCheck() { if (!isset($this->typeCheck[$this->checkMode])) { $this->typeCheck[$this->checkMode] = ($this->checkMode & Constraint::CHECK_MODE_TYPE_CAST) ? new TypeCheck\LooseTypeCheck() : new TypeCheck\StrictTypeCheck(); } return $this->typeCheck[$this->checkMode]; } public function setConstraintClass($name, $class) { if (!class_exists($class)) { throw new InvalidArgumentException('Unknown constraint ' . $name); } if (!in_array('JsonSchema\Constraints\ConstraintInterface', class_implements($class))) { throw new InvalidArgumentException('Invalid class ' . $name); } $this->constraintMap[$name] = $class; return $this; } public function createInstanceFor($constraintName) { if (!isset($this->constraintMap[$constraintName])) { throw new InvalidArgumentException('Unknown constraint ' . $constraintName); } if (!isset($this->instanceCache[$constraintName])) { $this->instanceCache[$constraintName] = new $this->constraintMap[$constraintName]($this); } return clone $this->instanceCache[$constraintName]; } public function getErrorContext() { return $this->errorContext; } public function setErrorContext($errorContext) { $this->errorContext = $errorContext; } } exclusiveMinimum)) { if (isset($schema->minimum)) { if ($schema->exclusiveMinimum && $element <= $schema->minimum) { $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'exclusiveMinimum', array('minimum' => $schema->minimum)); } elseif ($element < $schema->minimum) { $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum)); } } else { $this->addError($path, 'Use of exclusiveMinimum requires presence of minimum', 'missingMinimum'); } } elseif (isset($schema->minimum) && $element < $schema->minimum) { $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum)); } if (isset($schema->exclusiveMaximum)) { if (isset($schema->maximum)) { if ($schema->exclusiveMaximum && $element >= $schema->maximum) { $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'exclusiveMaximum', array('maximum' => $schema->maximum)); } elseif ($element > $schema->maximum) { $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum)); } } else { $this->addError($path, 'Use of exclusiveMaximum requires presence of maximum', 'missingMaximum'); } } elseif (isset($schema->maximum) && $element > $schema->maximum) { $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum)); } if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) { $this->addError($path, 'Is not divisible by ' . $schema->divisibleBy, 'divisibleBy', array('divisibleBy' => $schema->divisibleBy)); } if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) { $this->addError($path, 'Must be a multiple of ' . $schema->multipleOf, 'multipleOf', array('multipleOf' => $schema->multipleOf)); } $this->checkFormat($element, $schema, $path, $i); } private function fmod($number1, $number2) { $modulus = ($number1 - round($number1 / $number2) * $number2); $precision = 0.0000000001; if (-$precision < $modulus && $modulus < $precision) { return 0.0; } return $modulus; } } getTypeCheck()->propertyExists($element, $this->inlineSchemaProperty)) { $validationSchema = $this->getTypeCheck()->propertyGet($element, $this->inlineSchemaProperty); } else { throw new InvalidArgumentException('no schema found to verify against'); } if (is_array($validationSchema)) { $validationSchema = BaseConstraint::arrayToObjectRecursive($validationSchema); } if ($this->factory->getConfig(self::CHECK_MODE_VALIDATE_SCHEMA)) { if (!$this->getTypeCheck()->isObject($validationSchema)) { throw new RuntimeException('Cannot validate the schema of a non-object'); } if ($this->getTypeCheck()->propertyExists($validationSchema, '$schema')) { $schemaSpec = $this->getTypeCheck()->propertyGet($validationSchema, '$schema'); } else { $schemaSpec = self::DEFAULT_SCHEMA_SPEC; } $schemaStorage = $this->factory->getSchemaStorage(); if (!$this->getTypeCheck()->isObject($schemaSpec)) { $schemaSpec = $schemaStorage->getSchema($schemaSpec); } $initialErrorCount = $this->numErrors(); $initialConfig = $this->factory->getConfig(); $initialContext = $this->factory->getErrorContext(); $this->factory->removeConfig(self::CHECK_MODE_VALIDATE_SCHEMA | self::CHECK_MODE_APPLY_DEFAULTS); $this->factory->addConfig(self::CHECK_MODE_TYPE_CAST); $this->factory->setErrorContext(Validator::ERROR_SCHEMA_VALIDATION); try { $this->check($validationSchema, $schemaSpec); } catch (\Exception $e) { if ($this->factory->getConfig(self::CHECK_MODE_EXCEPTIONS)) { throw new InvalidSchemaException('Schema did not pass validation', 0, $e); } } if ($this->numErrors() > $initialErrorCount) { $this->addError($path, 'Schema is not valid', 'schema'); } $this->factory->setConfig($initialConfig); $this->factory->setErrorContext($initialContext); } $this->checkUndefined($element, $validationSchema, $path, $i); } } format) || $this->factory->getConfig(self::CHECK_MODE_DISABLE_FORMAT)) { return; } switch ($schema->format) { case 'date': if (!$date = $this->validateDateTime($element, 'Y-m-d')) { $this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)), 'format', array('format' => $schema->format)); } break; case 'time': if (!$this->validateDateTime($element, 'H:i:s')) { $this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)), 'format', array('format' => $schema->format)); } break; case 'date-time': if (null === Rfc3339::createFromString($element)) { $this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format)); } break; case 'utc-millisec': if (!$this->validateDateTime($element, 'U')) { $this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)), 'format', array('format' => $schema->format)); } break; case 'regex': if (!$this->validateRegex($element)) { $this->addError($path, 'Invalid regex format ' . $element, 'format', array('format' => $schema->format)); } break; case 'color': if (!$this->validateColor($element)) { $this->addError($path, 'Invalid color', 'format', array('format' => $schema->format)); } break; case 'style': if (!$this->validateStyle($element)) { $this->addError($path, 'Invalid style', 'format', array('format' => $schema->format)); } break; case 'phone': if (!$this->validatePhone($element)) { $this->addError($path, 'Invalid phone number', 'format', array('format' => $schema->format)); } break; case 'uri': if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) { $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format)); } break; case 'uriref': case 'uri-reference': if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) { if (substr($element, 0, 2) === '//') { $validURL = filter_var('scheme:' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); } elseif (substr($element, 0, 1) === '/') { $validURL = filter_var('scheme://host' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); } elseif (strlen($element)) { $pathParts = explode('/', $element, 2); if (strpos($pathParts[0], ':') !== false) { $validURL = null; } else { $validURL = filter_var('scheme://host/' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); } } else { $validURL = null; } if ($validURL === null) { $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format)); } } break; case 'email': $filterFlags = FILTER_NULL_ON_FAILURE; if (defined('FILTER_FLAG_EMAIL_UNICODE')) { $filterFlags |= constant('FILTER_FLAG_EMAIL_UNICODE'); } if (null === filter_var($element, FILTER_VALIDATE_EMAIL, $filterFlags)) { $this->addError($path, 'Invalid email', 'format', array('format' => $schema->format)); } break; case 'ip-address': case 'ipv4': if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) { $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format)); } break; case 'ipv6': if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) { $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format)); } break; case 'host-name': case 'hostname': if (!$this->validateHostname($element)) { $this->addError($path, 'Invalid hostname', 'format', array('format' => $schema->format)); } break; default: break; } } protected function validateDateTime($datetime, $format) { $dt = \DateTime::createFromFormat($format, $datetime); if (!$dt) { return false; } if ($datetime === $dt->format($format)) { return true; } if ((strpos('u', $format) !== -1) && (preg_match('/\.\d+Z$/', $datetime))) { return true; } return false; } protected function validateRegex($regex) { return false !== @preg_match('/' . $regex . '/u', ''); } protected function validateColor($color) { if (in_array(strtolower($color), array('aqua', 'black', 'blue', 'fuchsia', 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple', 'red', 'silver', 'teal', 'white', 'yellow'))) { return true; } return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color); } protected function validateStyle($style) { $properties = explode(';', rtrim($style, ';')); $invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT); return empty($invalidEntries); } protected function validatePhone($phone) { return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone); } protected function validateHostname($host) { $hostnameRegex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/i'; return preg_match($hostnameRegex, $host); } } minItems) && count($value) < $schema->minItems) { $this->addError($path, 'There must be a minimum of ' . $schema->minItems . ' items in the array', 'minItems', array('minItems' => $schema->minItems)); } if (isset($schema->maxItems) && count($value) > $schema->maxItems) { $this->addError($path, 'There must be a maximum of ' . $schema->maxItems . ' items in the array', 'maxItems', array('maxItems' => $schema->maxItems)); } if (isset($schema->uniqueItems) && $schema->uniqueItems) { $unique = $value; if (is_array($value) && count($value)) { $unique = array_map(function ($e) { return var_export($e, true); }, $value); } if (count(array_unique($unique)) != count($value)) { $this->addError($path, 'There are no duplicates allowed in the array', 'uniqueItems'); } } if (isset($schema->items)) { $this->validateItems($value, $schema, $path, $i); } } protected function validateItems(&$value, $schema = null, JsonPointer $path = null, $i = null) { if (is_object($schema->items)) { foreach ($value as $k => &$v) { $initErrors = $this->getErrors(); $this->checkUndefined($v, $schema->items, $path, $k); if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) { $secondErrors = $this->getErrors(); $this->checkUndefined($v, $schema->additionalItems, $path, $k); } if (isset($secondErrors) && count($secondErrors) < count($this->getErrors())) { $this->errors = $secondErrors; } elseif (isset($secondErrors) && count($secondErrors) === count($this->getErrors())) { $this->errors = $initErrors; } } unset($v); } else { foreach ($value as $k => &$v) { if (array_key_exists($k, $schema->items)) { $this->checkUndefined($v, $schema->items[$k], $path, $k); } else { if (property_exists($schema, 'additionalItems')) { if ($schema->additionalItems !== false) { $this->checkUndefined($v, $schema->additionalItems, $path, $k); } else { $this->addError( $path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items', 'additionalItems', array('additionalItems' => $schema->additionalItems)); } } else { $this->checkUndefined($v, new \stdClass(), $path, $k); } } } unset($v); if (count($value) > 0) { for ($k = count($value); $k < count($schema->items); $k++) { $undefinedInstance = $this->factory->createInstanceFor('undefined'); $this->checkUndefined($undefinedInstance, $schema->items[$k], $path, $k); } } } } } 'an integer', 'number' => 'a number', 'boolean' => 'a boolean', 'object' => 'an object', 'array' => 'an array', 'string' => 'a string', 'null' => 'a null', 'any' => null, 0 => null, ); public function check(&$value = null, $schema = null, JsonPointer $path = null, $i = null) { $type = isset($schema->type) ? $schema->type : null; $isValid = false; $wording = array(); if (is_array($type)) { $this->validateTypesArray($value, $type, $wording, $isValid, $path); } elseif (is_object($type)) { $this->checkUndefined($value, $type, $path); return; } else { $isValid = $this->validateType($value, $type); } if ($isValid === false) { if (!is_array($type)) { $this->validateTypeNameWording($type); $wording[] = self::$wording[$type]; } $this->addError($path, ucwords(gettype($value)) . ' value found, but ' . $this->implodeWith($wording, ', ', 'or') . ' is required', 'type'); } } protected function validateTypesArray(&$value, array $type, &$validTypesWording, &$isValid, $path) { foreach ($type as $tp) { if (is_object($tp)) { if (!$isValid) { $validator = $this->factory->createInstanceFor('type'); $subSchema = new \stdClass(); $subSchema->type = $tp; $validator->check($value, $subSchema, $path, null); $error = $validator->getErrors(); $isValid = !(bool) $error; $validTypesWording[] = self::$wording['object']; } } else { $this->validateTypeNameWording($tp); $validTypesWording[] = self::$wording[$tp]; if (!$isValid) { $isValid = $this->validateType($value, $tp); } } } } protected function implodeWith(array $elements, $delimiter = ', ', $listEnd = false) { if ($listEnd === false || !isset($elements[1])) { return implode($delimiter, $elements); } $lastElement = array_slice($elements, -1); $firsElements = join($delimiter, array_slice($elements, 0, -1)); $implodedElements = array_merge(array($firsElements), $lastElement); return join(" $listEnd ", $implodedElements); } protected function validateTypeNameWording($type) { if (!array_key_exists($type, self::$wording)) { throw new StandardUnexpectedValueException( sprintf( 'No wording for %s available, expected wordings are: [%s]', var_export($type, true), implode(', ', array_filter(self::$wording))) ); } } protected function validateType(&$value, $type) { if (!$type) { return true; } if ('any' === $type) { return true; } if ('object' === $type) { return $this->getTypeCheck()->isObject($value); } if ('array' === $type) { return $this->getTypeCheck()->isArray($value); } $coerce = $this->factory->getConfig(Constraint::CHECK_MODE_COERCE_TYPES); if ('integer' === $type) { if ($coerce) { $value = $this->toInteger($value); } return is_int($value); } if ('number' === $type) { if ($coerce) { $value = $this->toNumber($value); } return is_numeric($value) && !is_string($value); } if ('boolean' === $type) { if ($coerce) { $value = $this->toBoolean($value); } return is_bool($value); } if ('string' === $type) { return is_string($value); } if ('email' === $type) { return is_string($value); } if ('null' === $type) { return is_null($value); } throw new InvalidArgumentException((is_object($value) ? 'object' : $value) . ' is an invalid type for ' . $type); } protected function toBoolean($value) { if ($value === 'true') { return true; } if ($value === 'false') { return false; } return $value; } protected function toNumber($value) { if (is_numeric($value)) { return $value + 0; } return $value; } protected function toInteger($value) { if (is_numeric($value) && (int) $value == $value) { return (int) $value; } return $value; } } withPropertyPaths( array_merge( $path->getPropertyPaths(), array($i) ) ); return $path; } protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('collection'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $properties = null, $additionalProperties = null, $patternProperties = null, $appliedDefaults = array()) { $validator = $this->factory->createInstanceFor('object'); $validator->check($value, $schema, $path, $properties, $additionalProperties, $patternProperties, $appliedDefaults); $this->addErrors($validator->getErrors()); } protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('type'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false) { $validator = $this->factory->createInstanceFor('undefined'); $validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i, $fromDefault); $this->addErrors($validator->getErrors()); } protected function checkString($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('string'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } protected function checkNumber($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('number'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } protected function checkEnum($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('enum'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } protected function checkFormat($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('format'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } protected function getTypeCheck() { return $this->factory->getTypeCheck(); } protected function convertJsonPointerIntoPropertyPath(JsonPointer $pointer) { $result = array_map( function ($path) { return sprintf(is_numeric($path) ? '[%d]' : '.%s', $path); }, $pointer->getPropertyPaths() ); return trim(implode('', $result), '.'); } } maxLength) && $this->strlen($element) > $schema->maxLength) { $this->addError($path, 'Must be at most ' . $schema->maxLength . ' characters long', 'maxLength', array( 'maxLength' => $schema->maxLength, )); } if (isset($schema->minLength) && $this->strlen($element) < $schema->minLength) { $this->addError($path, 'Must be at least ' . $schema->minLength . ' characters long', 'minLength', array( 'minLength' => $schema->minLength, )); } if (isset($schema->pattern) && !preg_match('#' . str_replace('#', '\\#', $schema->pattern) . '#u', $element)) { $this->addError($path, 'Does not match the regex pattern ' . $schema->pattern, 'pattern', array( 'pattern' => $schema->pattern, )); } $this->checkFormat($element, $schema, $path, $i); } private function strlen($string) { if (extension_loaded('mbstring')) { return mb_strlen($string, mb_detect_encoding($string)); } return strlen($string); } } factory->getConfig(); if ($checkMode !== null) { $this->factory->setConfig($checkMode); } if (is_object($schema) && property_exists($schema, 'id')) { $schemaURI = $schema->id; } else { $schemaURI = SchemaStorage::INTERNAL_PROVIDED_SCHEMA_URI; } $this->factory->getSchemaStorage()->addSchema($schemaURI, $schema); $validator = $this->factory->createInstanceFor('schema'); $validator->check( $value, $this->factory->getSchemaStorage()->getSchema($schemaURI) ); $this->factory->setConfig($initialCheckMode); $this->addErrors(array_unique($validator->getErrors(), SORT_REGULAR)); return $validator->getErrorMask(); } public function check($value, $schema) { return $this->validate($value, $schema); } public function coerce(&$value, $schema) { return $this->validate($value, $schema, Constraint::CHECK_MODE_COERCE_TYPES); } } times = intval($times); $this->util = $util ?: new StringUtil; } public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { if ($this->times == count($calls)) { return; } $methodCalls = $object->findProphecyMethodCalls( $method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken)) ); if (count($calls)) { $message = sprintf( "Expected exactly %d calls that match:\n". " %s->%s(%s)\n". "but %d were made:\n%s", $this->times, get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), count($calls), $this->util->stringifyCalls($calls) ); } elseif (count($methodCalls)) { $message = sprintf( "Expected exactly %d calls that match:\n". " %s->%s(%s)\n". "but none were made.\n". "Recorded `%s(...)` calls:\n%s", $this->times, get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls) ); } else { $message = sprintf( "Expected exactly %d calls that match:\n". " %s->%s(%s)\n". "but none were made.", $this->times, get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard() ); } throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); } } util = $util ?: new StringUtil; } public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { if (count($calls)) { return; } $methodCalls = $object->findProphecyMethodCalls( $method->getMethodName(), new ArgumentsWildcard(array(new AnyValuesToken)) ); if (count($methodCalls)) { throw new NoCallsException(sprintf( "No calls have been made that match:\n". " %s->%s(%s)\n". "but expected at least one.\n". "Recorded `%s(...)` calls:\n%s", get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), $method->getMethodName(), $this->util->stringifyCalls($methodCalls) ), $method); } throw new NoCallsException(sprintf( "No calls have been made that match:\n". " %s->%s(%s)\n". "but expected at least one.", get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard() ), $method); } } callback = $callback; } public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { $callback = $this->callback; if ($callback instanceof Closure && method_exists('Closure', 'bind')) { $callback = Closure::bind($callback, $object); } call_user_func($callback, $calls, $object, $method); } } util = $util ?: new StringUtil; } public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) { if (!count($calls)) { return; } $verb = count($calls) === 1 ? 'was' : 'were'; throw new UnexpectedCallsException(sprintf( "No calls expected that match:\n". " %s->%s(%s)\n". "but %d %s made:\n%s", get_class($object->reveal()), $method->getMethodName(), $method->getArgumentsWildcard(), count($calls), $verb, $this->util->stringifyCalls($calls) ), $method, $calls); } } register(new ClosureComparator()); $this->register(new ProphecyComparator()); } public static function getInstance() { if (self::$instance === null) { self::$instance = new Factory; } return self::$instance; } } reveal(), $delta, $canonicalize, $ignoreCase, $processed); } } verbose = $verbose; } public function stringify($value, $exportObject = true) { if (is_array($value)) { if (range(0, count($value) - 1) === array_keys($value)) { return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']'; } $stringify = array($this, __FUNCTION__); return '['.implode(', ', array_map(function ($item, $key) use ($stringify) { return (is_integer($key) ? $key : '"'.$key.'"'). ' => '.call_user_func($stringify, $item); }, $value, array_keys($value))).']'; } if (is_resource($value)) { return get_resource_type($value).':'.$value; } if (is_object($value)) { return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value)); } if (true === $value || false === $value) { return $value ? 'true' : 'false'; } if (is_string($value)) { $str = sprintf('"%s"', str_replace("\n", '\\n', $value)); if (!$this->verbose && 50 <= strlen($str)) { return substr($str, 0, 50).'"...'; } return $str; } if (null === $value) { return 'null'; } return (string) $value; } public function stringifyCalls(array $calls) { $self = $this; return implode(PHP_EOL, array_map(function (Call $call) use ($self) { return sprintf(' - %s(%s) @ %s', $call->getMethodName(), implode(', ', array_map(array($self, 'stringify'), $call->getArguments())), str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace()) ); }, $calls)); } } $val) { if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { $key = $matches[1]; } if ($key === "\0gcdata") { continue; } $array[$key] = $val; } if ($value instanceof \SplObjectStorage) { if (property_exists('\SplObjectStorage', '__storage')) { unset($array['__storage']); } elseif (property_exists('\SplObjectStorage', 'storage')) { unset($array['storage']); } if (property_exists('\SplObjectStorage', '__key')) { unset($array['__key']); } foreach ($value as $key => $val) { $array[spl_object_hash($val)] = array( 'obj' => $val, 'inf' => $value->getInfo(), ); } } return $array; } protected static function recursiveExport(&$value, $indentation, $processed = null) { if ($value === null) { return 'null'; } if ($value === true) { return 'true'; } if ($value === false) { return 'false'; } if (is_float($value) && floatval(intval($value)) === $value) { return "$value.0"; } if (is_resource($value)) { return sprintf( 'resource(%d) of type (%s)', $value, get_resource_type($value) ); } if (is_string($value)) { if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { return 'Binary String: 0x' . bin2hex($value); } return "'" . str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . "'"; } $whitespace = str_repeat(' ', 4 * $indentation); if (!$processed) { $processed = new Context; } if (is_array($value)) { if (($key = $processed->contains($value)) !== false) { return 'Array &' . $key; } $array = $value; $key = $processed->add($value); $values = ''; if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($value[$k], $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('Array &%s (%s)', $key, $values); } if (is_object($value)) { $class = get_class($value); if ($hash = $processed->contains($value)) { return sprintf('%s:%s Object', $class, $hash); } $hash = $processed->add($value); $values = ''; $array = self::toArray($value); if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, self::recursiveExport($k, $indentation), self::recursiveExport($v, $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('%s:%s Object (%s)', $class, $hash, $values); } return var_export($value, true); } } index = $index; } public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { return count($args) > $this->index ? $args[$this->index] : null; } } callback = $callback; } public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { $callback = $this->callback; if ($callback instanceof Closure && method_exists('Closure', 'bind')) { $callback = Closure::bind($callback, $object); } return call_user_func($callback, $args, $object, $method); } } returnValues = $returnValues; } public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { $value = array_shift($this->returnValues); if (!count($this->returnValues)) { $this->returnValues[] = $value; } return $value; } } isAValidThrowable($exception)) { throw new InvalidArgumentException(sprintf( 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', $exception )); } } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) { throw new InvalidArgumentException(sprintf( 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', is_object($exception) ? get_class($exception) : gettype($exception) )); } $this->exception = $exception; } public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) { if (is_string($this->exception)) { $classname = $this->exception; $reflection = new ReflectionClass($classname); $constructor = $reflection->getConstructor(); if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { throw $reflection->newInstance(); } if (!$this->instantiator) { $this->instantiator = new Instantiator(); } throw $this->instantiator->instantiate($classname); } throw $this->exception; } private function isAValidThrowable($exception) { return is_a($exception, 'Exception', true) || is_a($exception, 'Throwable', true); } } registerClassPatch(new ClassPatch\SplFileInfoPatch); $doubler->registerClassPatch(new ClassPatch\TraversablePatch); $doubler->registerClassPatch(new ClassPatch\ThrowablePatch); $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch); $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch); $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch); $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch()); $doubler->registerClassPatch(new ClassPatch\MagicCallPatch); $doubler->registerClassPatch(new ClassPatch\KeywordPatch); } $this->doubler = $doubler; $this->revealer = $revealer ?: new Revealer; $this->util = $util ?: new StringUtil; } public function prophesize($classOrInterface = null) { $this->prophecies[] = $prophecy = new ObjectProphecy( new LazyDouble($this->doubler), new CallCenter($this->util), $this->revealer ); if ($classOrInterface && class_exists($classOrInterface)) { return $prophecy->willExtend($classOrInterface); } if ($classOrInterface && interface_exists($classOrInterface)) { return $prophecy->willImplement($classOrInterface); } return $prophecy; } public function getProphecies() { return $this->prophecies; } public function getDoubler() { return $this->doubler; } public function checkPredictions() { $exception = new AggregateException("Some predictions failed:\n"); foreach ($this->prophecies as $prophecy) { try { $prophecy->checkProphecyMethodsPredictions(); } catch (PredictionException $e) { $exception->append($e); } } if (count($exception->getExceptions())) { throw $exception; } } } name = $methodName; $this->value = $value; $this->util = $util ?: new StringUtil; $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } public function scoreArgument($argument) { if (is_object($argument) && method_exists($argument, $this->name)) { $actual = call_user_func(array($argument, $this->name)); $comparator = $this->comparatorFactory->getComparatorFor( $this->value, $actual ); try { $comparator->assertEquals($this->value, $actual); return 8; } catch (ComparisonFailure $failure) { return false; } } if (is_object($argument) && property_exists($argument, $this->name)) { return $argument->{$this->name} === $this->value ? 8 : false; } return false; } public function isLast() { return false; } public function __toString() { return sprintf('state(%s(), %s)', $this->name, $this->util->stringify($this->value) ); } } value = $value; } public function scoreArgument($argument) { if (!$argument instanceof \Traversable && !is_array($argument)) { return false; } $scores = array(); foreach ($argument as $key => $argumentEntry) { $scores[] = $this->value->scoreArgument($argumentEntry); } if (empty($scores) || in_array(false, $scores, true)) { return false; } return array_sum($scores) / count($scores); } public function isLast() { return false; } public function __toString() { return sprintf('[%s, ..., %s]', $this->value, $this->value); } public function getValue() { return $this->value; } } type = $type; } public function scoreArgument($argument) { $checker = "is_{$this->type}"; if (function_exists($checker)) { return call_user_func($checker, $argument) ? 5 : false; } return $argument instanceof $this->type ? 5 : false; } public function isLast() { return false; } public function __toString() { return sprintf('type(%s)', $this->type); } } value = $value; $this->precision = $precision; } public function scoreArgument($argument) { return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false; } public function isLast() { return false; } public function __toString() { return sprintf('≅%s', round($this->value, $this->precision)); } } callback = $callback; } public function scoreArgument($argument) { return call_user_func($this->callback, $argument) ? 7 : false; } public function isLast() { return false; } public function __toString() { return 'callback()'; } } value = $value; $this->util = $util ?: new StringUtil(); } public function scoreArgument($argument) { return $argument === $this->value ? 11 : false; } public function isLast() { return false; } public function __toString() { if (null === $this->string) { $this->string = sprintf('identical(%s)', $this->util->stringify($this->value)); } return $this->string; } } token = $value instanceof TokenInterface? $value : new ExactValueToken($value); } public function scoreArgument($argument) { return false === $this->token->scoreArgument($argument) ? 4 : false; } public function isLast() { return $this->token->isLast(); } public function getOriginatingToken() { return $this->token; } public function __toString() { return sprintf('not(%s)', $this->token); } } tokens[] = $argument; } } public function scoreArgument($argument) { if (0 === count($this->tokens)) { return false; } $maxScore = 0; foreach ($this->tokens as $token) { $score = $token->scoreArgument($argument); if (false === $score) { return false; } $maxScore = max($score, $maxScore); } return $maxScore; } public function isLast() { return false; } public function __toString() { return sprintf('bool(%s)', implode(' AND ', $this->tokens)); } } count = $value; } public function scoreArgument($argument) { return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false; } public function isLast() { return false; } public function __toString() { return sprintf('count(%s)', $this->count); } private function isCountable($argument) { return (is_array($argument) || $argument instanceof \Countable); } private function hasProperCount($argument) { return $this->count === count($argument); } } value = $value; } public function scoreArgument($argument) { return is_string($argument) && strpos($argument, $this->value) !== false ? 6 : false; } public function getValue() { return $this->value; } public function isLast() { return false; } public function __toString() { return sprintf('contains("%s")', $this->value); } } key = $this->wrapIntoExactValueToken($key); $this->value = $this->wrapIntoExactValueToken($value); } public function scoreArgument($argument) { if ($argument instanceof \Traversable) { $argument = iterator_to_array($argument); } if ($argument instanceof \ArrayAccess) { $argument = $this->convertArrayAccessToEntry($argument); } if (!is_array($argument) || empty($argument)) { return false; } $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument)); $valueScores = array_map(array($this->value,'scoreArgument'), $argument); $scoreEntry = function ($value, $key) { return $value && $key ? min(8, ($key + $value) / 2) : false; }; return max(array_map($scoreEntry, $valueScores, $keyScores)); } public function isLast() { return false; } public function __toString() { return sprintf('[..., %s => %s, ...]', $this->key, $this->value); } public function getKey() { return $this->key; } public function getValue() { return $this->value; } private function wrapIntoExactValueToken($value) { return $value instanceof TokenInterface ? $value : new ExactValueToken($value); } private function convertArrayAccessToEntry(\ArrayAccess $object) { if (!$this->key instanceof ExactValueToken) { throw new InvalidArgumentException(sprintf( 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL. 'But you used `%s`.', $this->key )); } $key = $this->key->getValue(); return $object->offsetExists($key) ? array($key => $object[$key]) : array(); } } value = $value; $this->util = $util ?: new StringUtil(); $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } public function scoreArgument($argument) { if (is_object($argument) && is_object($this->value)) { $comparator = $this->comparatorFactory->getComparatorFor( $argument, $this->value ); try { $comparator->assertEquals($argument, $this->value); return 10; } catch (ComparisonFailure $failure) { return false; } } if (is_object($argument) xor is_object($this->value)) { if (is_object($argument) && !method_exists($argument, '__toString')) { return false; } if (is_object($this->value) && !method_exists($this->value, '__toString')) { return false; } } elseif (is_numeric($argument) && is_numeric($this->value)) { } elseif (gettype($argument) !== gettype($this->value)) { return false; } return $argument == $this->value ? 10 : false; } public function getValue() { return $this->value; } public function isLast() { return false; } public function __toString() { if (null === $this->string) { $this->string = sprintf('exact(%s)', $this->util->stringify($this->value)); } return $this->string; } } tokens[] = $argument; } } public function scoreArguments(array $arguments) { if (0 == count($arguments) && 0 == count($this->tokens)) { return 1; } $arguments = array_values($arguments); $totalScore = 0; foreach ($this->tokens as $i => $token) { $argument = isset($arguments[$i]) ? $arguments[$i] : null; if (1 >= $score = $token->scoreArgument($argument)) { return false; } $totalScore += $score; if (true === $token->isLast()) { return $totalScore; } } if (count($arguments) > count($this->tokens)) { return false; } return $totalScore; } public function __toString() { if (null === $this->string) { $this->string = implode(', ', array_map(function ($token) { return (string) $token; }, $this->tokens)); } return $this->string; } public function getTokens() { return $this->tokens; } } reveal(); if (!method_exists($double, $methodName)) { throw new MethodNotFoundException(sprintf( 'Method `%s::%s()` is not defined.', get_class($double), $methodName ), get_class($double), $methodName, $arguments); } $this->objectProphecy = $objectProphecy; $this->methodName = $methodName; $reflectedMethod = new \ReflectionMethod($double, $methodName); if ($reflectedMethod->isFinal()) { throw new MethodProphecyException(sprintf( "Can not add prophecy for a method `%s::%s()`\n". "as it is a final method.", get_class($double), $methodName ), $this); } if (null !== $arguments) { $this->withArguments($arguments); } if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) { $type = PHP_VERSION_ID >= 70100 ? $reflectedMethod->getReturnType()->getName() : (string) $reflectedMethod->getReturnType(); if ('void' === $type) { $this->voidReturnType = true; } $this->will(function () use ($type) { switch ($type) { case 'void': return; case 'string': return ''; case 'float': return 0.0; case 'int': return 0; case 'bool': return false; case 'array': return array(); case 'callable': case 'Closure': return function () {}; case 'Traversable': case 'Generator': $generator = eval('return function () { yield; };'); return $generator(); default: $prophet = new Prophet; return $prophet->prophesize($type)->reveal(); } }); } } public function withArguments($arguments) { if (is_array($arguments)) { $arguments = new Argument\ArgumentsWildcard($arguments); } if (!$arguments instanceof Argument\ArgumentsWildcard) { throw new InvalidArgumentException(sprintf( "Either an array or an instance of ArgumentsWildcard expected as\n". 'a `MethodProphecy::withArguments()` argument, but got %s.', gettype($arguments) )); } $this->argumentsWildcard = $arguments; return $this; } public function will($promise) { if (is_callable($promise)) { $promise = new Promise\CallbackPromise($promise); } if (!$promise instanceof Promise\PromiseInterface) { throw new InvalidArgumentException(sprintf( 'Expected callable or instance of PromiseInterface, but got %s.', gettype($promise) )); } $this->bindToObjectProphecy(); $this->promise = $promise; return $this; } public function willReturn() { if ($this->voidReturnType) { throw new MethodProphecyException( "The method \"$this->methodName\" has a void return type, and so cannot return anything", $this ); } return $this->will(new Promise\ReturnPromise(func_get_args())); } public function willYield($items) { if ($this->voidReturnType) { throw new MethodProphecyException( "The method \"$this->methodName\" has a void return type, and so cannot yield anything", $this ); } if (!is_array($items)) { throw new InvalidArgumentException(sprintf( 'Expected array, but got %s.', gettype($items) )); } $generator = eval('return function() use ($items) { foreach ($items as $key => $value) { yield $key => $value; } };'); return $this->will($generator); } public function willReturnArgument($index = 0) { if ($this->voidReturnType) { throw new MethodProphecyException("The method \"$this->methodName\" has a void return type", $this); } return $this->will(new Promise\ReturnArgumentPromise($index)); } public function willThrow($exception) { return $this->will(new Promise\ThrowPromise($exception)); } public function should($prediction) { if (is_callable($prediction)) { $prediction = new Prediction\CallbackPrediction($prediction); } if (!$prediction instanceof Prediction\PredictionInterface) { throw new InvalidArgumentException(sprintf( 'Expected callable or instance of PredictionInterface, but got %s.', gettype($prediction) )); } $this->bindToObjectProphecy(); $this->prediction = $prediction; return $this; } public function shouldBeCalled() { return $this->should(new Prediction\CallPrediction); } public function shouldNotBeCalled() { return $this->should(new Prediction\NoCallsPrediction); } public function shouldBeCalledTimes($count) { return $this->should(new Prediction\CallTimesPrediction($count)); } public function shouldBeCalledOnce() { return $this->shouldBeCalledTimes(1); } public function shouldHave($prediction) { if (is_callable($prediction)) { $prediction = new Prediction\CallbackPrediction($prediction); } if (!$prediction instanceof Prediction\PredictionInterface) { throw new InvalidArgumentException(sprintf( 'Expected callable or instance of PredictionInterface, but got %s.', gettype($prediction) )); } if (null === $this->promise && !$this->voidReturnType) { $this->willReturn(); } $calls = $this->getObjectProphecy()->findProphecyMethodCalls( $this->getMethodName(), $this->getArgumentsWildcard() ); try { $prediction->check($calls, $this->getObjectProphecy(), $this); $this->checkedPredictions[] = $prediction; } catch (\Exception $e) { $this->checkedPredictions[] = $prediction; throw $e; } return $this; } public function shouldHaveBeenCalled() { return $this->shouldHave(new Prediction\CallPrediction); } public function shouldNotHaveBeenCalled() { return $this->shouldHave(new Prediction\NoCallsPrediction); } public function shouldNotBeenCalled() { return $this->shouldNotHaveBeenCalled(); } public function shouldHaveBeenCalledTimes($count) { return $this->shouldHave(new Prediction\CallTimesPrediction($count)); } public function shouldHaveBeenCalledOnce() { return $this->shouldHaveBeenCalledTimes(1); } public function checkPrediction() { if (null === $this->prediction) { return; } $this->shouldHave($this->prediction); } public function getPromise() { return $this->promise; } public function getPrediction() { return $this->prediction; } public function getCheckedPredictions() { return $this->checkedPredictions; } public function getObjectProphecy() { return $this->objectProphecy; } public function getMethodName() { return $this->methodName; } public function getArgumentsWildcard() { return $this->argumentsWildcard; } public function hasReturnVoid() { return $this->voidReturnType; } private function bindToObjectProphecy() { if ($this->bound) { return; } $this->getObjectProphecy()->addMethodProphecy($this); $this->bound = true; } } lazyDouble = $lazyDouble; $this->callCenter = $callCenter ?: new CallCenter; $this->revealer = $revealer ?: new Revealer; $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); } public function willExtend($class) { $this->lazyDouble->setParentClass($class); return $this; } public function willImplement($interface) { $this->lazyDouble->addInterface($interface); return $this; } public function willBeConstructedWith(array $arguments = null) { $this->lazyDouble->setArguments($arguments); return $this; } public function reveal() { $double = $this->lazyDouble->getInstance(); if (null === $double || !$double instanceof ProphecySubjectInterface) { throw new ObjectProphecyException( "Generated double must implement ProphecySubjectInterface, but it does not.\n". 'It seems you have wrongly configured doubler without required ClassPatch.', $this ); } $double->setProphecy($this); return $double; } public function addMethodProphecy(MethodProphecy $methodProphecy) { $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); if (null === $argumentsWildcard) { throw new MethodProphecyException(sprintf( "Can not add prophecy for a method `%s::%s()`\n". "as you did not specify arguments wildcard for it.", get_class($this->reveal()), $methodProphecy->getMethodName() ), $methodProphecy); } $methodName = strtolower($methodProphecy->getMethodName()); if (!isset($this->methodProphecies[$methodName])) { $this->methodProphecies[$methodName] = array(); } $this->methodProphecies[$methodName][] = $methodProphecy; } public function getMethodProphecies($methodName = null) { if (null === $methodName) { return $this->methodProphecies; } $methodName = strtolower($methodName); if (!isset($this->methodProphecies[$methodName])) { return array(); } return $this->methodProphecies[$methodName]; } public function makeProphecyMethodCall($methodName, array $arguments) { $arguments = $this->revealer->reveal($arguments); $return = $this->callCenter->makeCall($this, $methodName, $arguments); return $this->revealer->reveal($return); } public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) { return $this->callCenter->findCalls($methodName, $wildcard); } public function checkProphecyMethodsPredictions() { $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); $exception->setObjectProphecy($this); $this->callCenter->checkUnexpectedCalls(); foreach ($this->methodProphecies as $prophecies) { foreach ($prophecies as $prophecy) { try { $prophecy->checkPrediction(); } catch (PredictionException $e) { $exception->append($e); } } } if (count($exception->getExceptions())) { throw $exception; } } public function __call($methodName, array $arguments) { $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments)); foreach ($this->getMethodProphecies($methodName) as $prophecy) { $argumentsWildcard = $prophecy->getArgumentsWildcard(); $comparator = $this->comparatorFactory->getComparatorFor( $argumentsWildcard, $arguments ); try { $comparator->assertEquals($argumentsWildcard, $arguments); return $prophecy; } catch (ComparisonFailure $failure) {} } return new MethodProphecy($this, $methodName, $arguments); } public function __get($name) { return $this->reveal()->$name; } public function __set($name, $value) { $this->reveal()->$name = $this->revealer->reveal($value); } } reveal(); } return $value; } } getDocComment()); return $phpdoc->getTagsByName('method'); } } docBlockFactory = DocBlockFactory::createInstance(); $this->contextFactory = new ContextFactory(); } public function getTagList(\ReflectionClass $reflectionClass) { try { $phpdoc = $this->docBlockFactory->create( $reflectionClass, $this->contextFactory->createFromReflector($reflectionClass) ); $methods = array(); foreach ($phpdoc->getTagsByName('method') as $tag) { if ($tag instanceof Method) { $methods[] = $tag; } } return $methods; } catch (\InvalidArgumentException $e) { return array(); } } } classRetriever = $classRetriever; return; } $this->classRetriever = class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory') ? new ClassTagRetriever() : new LegacyClassTagRetriever() ; } public function getTagList(\ReflectionClass $reflectionClass) { return array_merge( $this->classRetriever->getTagList($reflectionClass), $this->getInterfacesTagList($reflectionClass) ); } private function getInterfacesTagList(\ReflectionClass $reflectionClass) { $interfaces = $reflectionClass->getInterfaces(); $tagList = array(); foreach($interfaces as $interface) { $tagList = array_merge($tagList, $this->classRetriever->getTagList($interface)); } return $tagList; } } util = $util ?: new StringUtil; $this->unexpectedCalls = new SplObjectStorage(); } public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) { if (PHP_VERSION_ID >= 50400) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $backtrace = debug_backtrace(); } $file = $line = null; if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { $file = $backtrace[2]['file']; $line = $backtrace[2]['line']; } if ('__destruct' === strtolower($methodName) || 0 == count($prophecy->getMethodProphecies())) { $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); return null; } $matches = $this->findMethodProphecies($prophecy, $methodName, $arguments); if (!count($matches)) { $this->unexpectedCalls->attach(new Call($methodName, $arguments, null, null, $file, $line), $prophecy); $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); return null; } @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; }); $score = $matches[0][0]; $methodProphecy = $matches[0][1]; $returnValue = null; $exception = null; if ($promise = $methodProphecy->getPromise()) { try { $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy); } catch (\Exception $e) { $exception = $e; } } if ($methodProphecy->hasReturnVoid() && $returnValue !== null) { throw new MethodProphecyException( "The method \"$methodName\" has a void return type, but the promise returned a value", $methodProphecy ); } $this->recordedCalls[] = $call = new Call( $methodName, $arguments, $returnValue, $exception, $file, $line ); $call->addScore($methodProphecy->getArgumentsWildcard(), $score); if (null !== $exception) { throw $exception; } return $returnValue; } public function findCalls($methodName, ArgumentsWildcard $wildcard) { $methodName = strtolower($methodName); return array_values( array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) { return $methodName === strtolower($call->getMethodName()) && 0 < $call->getScore($wildcard) ; }) ); } public function checkUnexpectedCalls() { foreach ($this->unexpectedCalls as $call) { $prophecy = $this->unexpectedCalls[$call]; if (!count($this->findMethodProphecies($prophecy, $call->getMethodName(), $call->getArguments()))) { throw $this->createUnexpectedCallException($prophecy, $call->getMethodName(), $call->getArguments()); } } } private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, array $arguments) { $classname = get_class($prophecy->reveal()); $indentationLength = 8; $argstring = implode( ",\n", $this->indentArguments( array_map(array($this->util, 'stringify'), $arguments), $indentationLength ) ); $expected = array(); foreach (call_user_func_array('array_merge', $prophecy->getMethodProphecies()) as $methodProphecy) { $expected[] = sprintf( " - %s(\n" . "%s\n" . " )", $methodProphecy->getMethodName(), implode( ",\n", $this->indentArguments( array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), $indentationLength ) ) ); } return new UnexpectedCallException( sprintf( "Unexpected method call on %s:\n". " - %s(\n". "%s\n". " )\n". "expected calls were:\n". "%s", $classname, $methodName, $argstring, implode("\n", $expected) ), $prophecy, $methodName, $arguments ); } private function indentArguments(array $arguments, $indentationLength) { return preg_replace_callback( '/^/m', function () use ($indentationLength) { return str_repeat(' ', $indentationLength); }, $arguments ); } private function findMethodProphecies(ObjectProphecy $prophecy, $methodName, array $arguments) { $matches = array(); foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { $matches[] = array($score, $methodProphecy); } } return $matches; } } methodName = $methodName; $this->arguments = $arguments; $this->returnValue = $returnValue; $this->exception = $exception; $this->scores = new \SplObjectStorage(); if ($file) { $this->file = $file; $this->line = intval($line); } } public function getMethodName() { return $this->methodName; } public function getArguments() { return $this->arguments; } public function getReturnValue() { return $this->returnValue; } public function getException() { return $this->exception; } public function getFile() { return $this->file; } public function getLine() { return $this->line; } public function getCallPlace() { if (null === $this->file) { return 'unknown'; } return sprintf('%s:%d', $this->file, $this->line); } public function addScore(ArgumentsWildcard $wildcard, $score) { $this->scores[$wildcard] = $score; return $this; } public function getScore(ArgumentsWildcard $wildcard) { if (isset($this->scores[$wildcard])) { return $this->scores[$wildcard]; } return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); } } getName(); } else { foreach ($interfaces as $interface) { $parts[] = $interface->getShortName(); } } if (!count($parts)) { $parts[] = 'stdClass'; } return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++); } } generator = $generator ?: new ClassCodeGenerator; } public function create($classname, Node\ClassNode $class) { $code = $this->generator->generate($classname, $class); $return = eval($code); if (!class_exists($classname, false)) { if (count($class->getInterfaces())) { throw new ClassCreatorException(sprintf( 'Could not double `%s` and implement interfaces: [%s].', $class->getParentClass(), implode(', ', $class->getInterfaces()) ), $class); } throw new ClassCreatorException( sprintf('Could not double `%s`.', $class->getParentClass()), $class ); } return $return; } } parentClass; } public function setParentClass($class) { $this->parentClass = $class ?: 'stdClass'; } public function getInterfaces() { return $this->interfaces; } public function addInterface($interface) { if ($this->hasInterface($interface)) { return; } array_unshift($this->interfaces, $interface); } public function hasInterface($interface) { return in_array($interface, $this->interfaces); } public function getProperties() { return $this->properties; } public function addProperty($name, $visibility = 'public') { $visibility = strtolower($visibility); if (!in_array($visibility, array('public', 'private', 'protected'))) { throw new InvalidArgumentException(sprintf( '`%s` property visibility is not supported.', $visibility )); } $this->properties[$name] = $visibility; } public function getMethods() { return $this->methods; } public function addMethod(MethodNode $method, $force = false) { if (!$this->isExtendable($method->getName())){ $message = sprintf( 'Method `%s` is not extendable, so can not be added.', $method->getName() ); throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); } if ($force || !isset($this->methods[$method->getName()])) { $this->methods[$method->getName()] = $method; } } public function removeMethod($name) { unset($this->methods[$name]); } public function getMethod($name) { return $this->hasMethod($name) ? $this->methods[$name] : null; } public function hasMethod($name) { return isset($this->methods[$name]); } public function getUnextendableMethods() { return $this->unextendableMethods; } public function addUnextendableMethod($unextendableMethod) { if (!$this->isExtendable($unextendableMethod)){ return; } $this->unextendableMethods[] = $unextendableMethod; } public function isExtendable($method) { return !in_array($method, $this->unextendableMethods); } } name = $name; $this->code = $code; $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); } public function getVisibility() { return $this->visibility; } public function setVisibility($visibility) { $visibility = strtolower($visibility); if (!in_array($visibility, array('public', 'private', 'protected'))) { throw new InvalidArgumentException(sprintf( '`%s` method visibility is not supported.', $visibility )); } $this->visibility = $visibility; } public function isStatic() { return $this->static; } public function setStatic($static = true) { $this->static = (bool) $static; } public function returnsReference() { return $this->returnsReference; } public function setReturnsReference() { $this->returnsReference = true; } public function getName() { return $this->name; } public function addArgument(ArgumentNode $argument) { $this->arguments[] = $argument; } public function getArguments() { return $this->arguments; } public function hasReturnType() { return null !== $this->returnType; } public function setReturnType($type = null) { if ($type === '' || $type === null) { $this->returnType = null; return; } $typeMap = array( 'double' => 'float', 'real' => 'float', 'boolean' => 'bool', 'integer' => 'int', ); if (isset($typeMap[$type])) { $type = $typeMap[$type]; } $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ? $type : '\\' . ltrim($type, '\\'); } public function getReturnType() { return $this->returnType; } public function setNullableReturnType($bool = true) { $this->nullableReturnType = (bool) $bool; } public function hasNullableReturnType() { return $this->nullableReturnType; } public function setCode($code) { $this->code = $code; } public function getCode() { if ($this->returnsReference) { return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; } return (string) $this->code; } public function useParentCode() { $this->code = sprintf( 'return parent::%s(%s);', $this->getName(), implode(', ', array_map(array($this, 'generateArgument'), $this->arguments) ) ); } private function generateArgument(ArgumentNode $arg) { $argument = '$'.$arg->getName(); if ($arg->isVariadic()) { $argument = '...'.$argument; } return $argument; } } name = $name; } public function getName() { return $this->name; } public function getTypeHint() { return $this->typeHint; } public function setTypeHint($typeHint = null) { $this->typeHint = $typeHint; } public function hasDefault() { return $this->isOptional() && !$this->isVariadic(); } public function getDefault() { return $this->default; } public function setDefault($default = null) { $this->optional = true; $this->default = $default; } public function isOptional() { return $this->optional; } public function setAsPassedByReference($byReference = true) { $this->byReference = $byReference; } public function isPassedByReference() { return $this->byReference; } public function setAsVariadic($isVariadic = true) { $this->isVariadic = $isVariadic; } public function isVariadic() { return $this->isVariadic; } public function isNullable() { return $this->isNullable; } public function setAsNullable($isNullable = true) { $this->isNullable = $isNullable; } } = 50400; case 'bool': case 'float': case 'int': case 'string': return PHP_VERSION_ID >= 70000; case 'iterable': return PHP_VERSION_ID >= 70100; case 'object': return PHP_VERSION_ID >= 70200; default: return false; } } public function isBuiltInReturnTypeHint($type) { if ($type === 'void') { return PHP_VERSION_ID >= 70100; } return $this->isBuiltInParamTypeHint($type); } } typeHintReference = $typeHintReference ?: new TypeHintReference(); } public function generate($classname, Node\ClassNode $class) { $parts = explode('\\', $classname); $classname = array_pop($parts); $namespace = implode('\\', $parts); $code = sprintf("class %s extends \%s implements %s {\n", $classname, $class->getParentClass(), implode(', ', array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces()) ) ); foreach ($class->getProperties() as $name => $visibility) { $code .= sprintf("%s \$%s;\n", $visibility, $name); } $code .= "\n"; foreach ($class->getMethods() as $method) { $code .= $this->generateMethod($method)."\n"; } $code .= "\n}"; return sprintf("namespace %s {\n%s\n}", $namespace, $code); } private function generateMethod(Node\MethodNode $method) { $php = sprintf("%s %s function %s%s(%s)%s {\n", $method->getVisibility(), $method->isStatic() ? 'static' : '', $method->returnsReference() ? '&':'', $method->getName(), implode(', ', $this->generateArguments($method->getArguments())), $this->getReturnType($method) ); $php .= $method->getCode()."\n"; return $php.'}'; } private function getReturnType(Node\MethodNode $method) { if (version_compare(PHP_VERSION, '7.1', '>=')) { if ($method->hasReturnType()) { return $method->hasNullableReturnType() ? sprintf(': ?%s', $method->getReturnType()) : sprintf(': %s', $method->getReturnType()); } } if (version_compare(PHP_VERSION, '7.0', '>=')) { return $method->hasReturnType() && $method->getReturnType() !== 'void' ? sprintf(': %s', $method->getReturnType()) : ''; } return ''; } private function generateArguments(array $arguments) { $typeHintReference = $this->typeHintReference; return array_map(function (Node\ArgumentNode $argument) use ($typeHintReference) { $php = ''; if (version_compare(PHP_VERSION, '7.1', '>=')) { $php .= $argument->isNullable() ? '?' : ''; } if ($hint = $argument->getTypeHint()) { $php .= $typeHintReference->isBuiltInParamTypeHint($hint) ? $hint : '\\'.$hint; } $php .= ' '.($argument->isPassedByReference() ? '&' : ''); $php .= $argument->isVariadic() ? '...' : ''; $php .= '$'.$argument->getName(); if ($argument->isOptional() && !$argument->isVariadic()) { $php .= ' = '.var_export($argument->getDefault(), true); } return $php; }, $arguments); } } isInterface()) { throw new InvalidArgumentException(sprintf( "Could not reflect %s as a class, because it\n". "is interface - use the second argument instead.", $class->getName() )); } $this->reflectClassToNode($class, $node); } foreach ($interfaces as $interface) { if (!$interface instanceof ReflectionClass) { throw new InvalidArgumentException(sprintf( "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". "a second argument to `ClassMirror::reflect(...)`, but got %s.", is_object($interface) ? get_class($interface).' class' : gettype($interface) )); } if (false === $interface->isInterface()) { throw new InvalidArgumentException(sprintf( "Could not reflect %s as an interface, because it\n". "is class - use the first argument instead.", $interface->getName() )); } $this->reflectInterfaceToNode($interface, $node); } $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface'); return $node; } private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node) { if (true === $class->isFinal()) { throw new ClassMirrorException(sprintf( 'Could not reflect class %s as it is marked final.', $class->getName() ), $class); } $node->setParentClass($class->getName()); foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { if (false === $method->isProtected()) { continue; } $this->reflectMethodToNode($method, $node); } foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { if (0 === strpos($method->getName(), '_') && !in_array($method->getName(), self::$reflectableMethods)) { continue; } if (true === $method->isFinal()) { $node->addUnextendableMethod($method->getName()); continue; } $this->reflectMethodToNode($method, $node); } } private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node) { $node->addInterface($interface->getName()); foreach ($interface->getMethods() as $method) { $this->reflectMethodToNode($method, $node); } } private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode) { $node = new Node\MethodNode($method->getName()); if (true === $method->isProtected()) { $node->setVisibility('protected'); } if (true === $method->isStatic()) { $node->setStatic(); } if (true === $method->returnsReference()) { $node->setReturnsReference(); } if (version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()) { $returnType = PHP_VERSION_ID >= 70100 ? $method->getReturnType()->getName() : (string) $method->getReturnType(); $returnTypeLower = strtolower($returnType); if ('self' === $returnTypeLower) { $returnType = $method->getDeclaringClass()->getName(); } if ('parent' === $returnTypeLower) { $returnType = $method->getDeclaringClass()->getParentClass()->getName(); } $node->setReturnType($returnType); if (version_compare(PHP_VERSION, '7.1', '>=') && $method->getReturnType()->allowsNull()) { $node->setNullableReturnType(true); } } if (is_array($params = $method->getParameters()) && count($params)) { foreach ($params as $param) { $this->reflectArgumentToNode($param, $node); } } $classNode->addMethod($node); } private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode) { $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); $node = new Node\ArgumentNode($name); $node->setTypeHint($this->getTypeHint($parameter)); if ($this->isVariadic($parameter)) { $node->setAsVariadic(); } if ($this->hasDefaultValue($parameter)) { $node->setDefault($this->getDefaultValue($parameter)); } if ($parameter->isPassedByReference()) { $node->setAsPassedByReference(); } $node->setAsNullable($this->isNullable($parameter)); $methodNode->addArgument($node); } private function hasDefaultValue(ReflectionParameter $parameter) { if ($this->isVariadic($parameter)) { return false; } if ($parameter->isDefaultValueAvailable()) { return true; } return $parameter->isOptional() || $this->isNullable($parameter); } private function getDefaultValue(ReflectionParameter $parameter) { if (!$parameter->isDefaultValueAvailable()) { return null; } return $parameter->getDefaultValue(); } private function getTypeHint(ReflectionParameter $parameter) { if (null !== $className = $this->getParameterClassName($parameter)) { return $className; } if (true === $parameter->isArray()) { return 'array'; } if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) { return 'callable'; } if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) { return PHP_VERSION_ID >= 70100 ? $parameter->getType()->getName() : (string) $parameter->getType(); } return null; } private function isVariadic(ReflectionParameter $parameter) { return PHP_VERSION_ID >= 50600 && $parameter->isVariadic(); } private function isNullable(ReflectionParameter $parameter) { return $parameter->allowsNull() && null !== $this->getTypeHint($parameter); } private function getParameterClassName(ReflectionParameter $parameter) { try { return $parameter->getClass() ? $parameter->getClass()->getName() : null; } catch (\ReflectionException $e) { preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches); return isset($matches[1]) ? $matches[1] : null; } } } generateClassId($class, $interfaces); if (isset(self::$classes[$classId])) { return self::$classes[$classId]; } return self::$classes[$classId] = parent::createDoubleClass($class, $interfaces); } private function generateClassId(ReflectionClass $class = null, array $interfaces) { $parts = array(); if (null !== $class) { $parts[] = $class->getName(); } foreach ($interfaces as $interface) { $parts[] = $interface->getName(); } foreach ($this->getClassPatches() as $patch) { $parts[] = get_class($patch); } sort($parts); return md5(implode('', $parts)); } public function resetCache() { self::$classes = array(); } } doubler = $doubler; } public function setParentClass($class) { if (null !== $this->double) { throw new DoubleException('Can not extend class with already instantiated double.'); } if (!$class instanceof ReflectionClass) { if (!class_exists($class)) { throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class); } $class = new ReflectionClass($class); } $this->class = $class; } public function addInterface($interface) { if (null !== $this->double) { throw new DoubleException( 'Can not implement interface with already instantiated double.' ); } if (!$interface instanceof ReflectionClass) { if (!interface_exists($interface)) { throw new InterfaceNotFoundException( sprintf('Interface %s not found.', $interface), $interface ); } $interface = new ReflectionClass($interface); } $this->interfaces[] = $interface; } public function setArguments(array $arguments = null) { $this->arguments = $arguments; } public function getInstance() { if (null === $this->double) { if (null !== $this->arguments) { return $this->double = $this->doubler->double( $this->class, $this->interfaces, $this->arguments ); } $this->double = $this->doubler->double($this->class, $this->interfaces); } return $this->double; } } mirror = $mirror ?: new ClassMirror; $this->creator = $creator ?: new ClassCreator; $this->namer = $namer ?: new NameGenerator; } public function getClassPatches() { return $this->patches; } public function registerClassPatch(ClassPatchInterface $patch) { $this->patches[] = $patch; @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { return $patch2->getPriority() - $patch1->getPriority(); }); } public function double(ReflectionClass $class = null, array $interfaces, array $args = null) { foreach ($interfaces as $interface) { if (!$interface instanceof ReflectionClass) { throw new InvalidArgumentException(sprintf( "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". "a second argument to `Doubler::double(...)`, but got %s.", is_object($interface) ? get_class($interface).' class' : gettype($interface) )); } } $classname = $this->createDoubleClass($class, $interfaces); $reflection = new ReflectionClass($classname); if (null !== $args) { return $reflection->newInstanceArgs($args); } if ((null === $constructor = $reflection->getConstructor()) || ($constructor->isPublic() && !$constructor->isFinal())) { return $reflection->newInstance(); } if (!$this->instantiator) { $this->instantiator = new Instantiator(); } return $this->instantiator->instantiate($classname); } protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) { $name = $this->namer->name($class, $interfaces); $node = $this->mirror->reflect($class, $interfaces); foreach ($this->patches as $patch) { if ($patch->supports($node)) { $patch->apply($node); } } $this->creator->create($name, $node); return $name; } } getMethods()); $methodsToRemove = array_intersect($methodNames, $this->getKeywords()); foreach ($methodsToRemove as $methodName) { $node->removeMethod($methodName); } } public function getPriority() { return 49; } private function getKeywords() { if (\PHP_VERSION_ID >= 70000) { return array('__halt_compiler'); } return array( '__halt_compiler', 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'finally', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor', 'yield', ); } } getParentClass(); } public function apply(ClassNode $node) { foreach ($node->getMethod('newInstance')->getArguments() as $argument) { $argument->setDefault(null); } } public function getPriority() { return 50; } } isExtendable('__construct')) { return; } if (!$node->hasMethod('__construct')) { $node->addMethod(new MethodNode('__construct', '')); return; } $constructor = $node->getMethod('__construct'); foreach ($constructor->getArguments() as $argument) { $argument->setDefault(null); } $constructor->setCode(<<getParentClass() || is_subclass_of($node->getParentClass(), 'Exception'); } public function apply(ClassNode $node) { if ($node->hasMethod('setTraceOptions')) { $node->getMethod('setTraceOptions')->useParentCode(); } if ($node->hasMethod('getTraceOptions')) { $node->getMethod('getTraceOptions')->useParentCode(); } } public function getPriority() { return -50; } } tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; } public function supports(ClassNode $node) { return true; } public function apply(ClassNode $node) { $types = array_filter($node->getInterfaces(), function ($interface) { return 0 !== strpos($interface, 'Prophecy\\'); }); $types[] = $node->getParentClass(); foreach ($types as $type) { $reflectionClass = new \ReflectionClass($type); while ($reflectionClass) { $tagList = $this->tagRetriever->getTagList($reflectionClass); foreach ($tagList as $tag) { $methodName = $tag->getMethodName(); if (empty($methodName)) { continue; } if (!$reflectionClass->hasMethod($methodName)) { $methodNode = new MethodNode($methodName); $methodNode->setStatic($tag->isStatic()); $node->addMethod($methodNode); } } $reflectionClass = $reflectionClass->getParentClass(); } } } public function getPriority() { return 50; } } addInterface('Prophecy\Prophecy\ProphecySubjectInterface'); $node->addProperty('objectProphecyClosure', 'private'); foreach ($node->getMethods() as $name => $method) { if ('__construct' === strtolower($name)) { continue; } if ($method->getReturnType() === 'void') { $method->setCode( '$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' ); } else { $method->setCode( 'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' ); } } $prophecySetter = new MethodNode('setProphecy'); $prophecyArgument = new ArgumentNode('prophecy'); $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface'); $prophecySetter->addArgument($prophecyArgument); $prophecySetter->setCode('$this->objectProphecyClosure = function () use ($prophecy) { return $prophecy; };'); $prophecyGetter = new MethodNode('getProphecy'); $prophecyGetter->setCode('return call_user_func($this->objectProphecyClosure);'); if ($node->hasMethod('__call')) { $__call = $node->getMethod('__call'); } else { $__call = new MethodNode('__call'); $__call->addArgument(new ArgumentNode('name')); $__call->addArgument(new ArgumentNode('arguments')); $node->addMethod($__call, true); } $__call->setCode(<<getProphecy(), func_get_arg(0) ); PHP ); $node->addMethod($prophecySetter, true); $node->addMethod($prophecyGetter, true); } public function getPriority() { return 0; } } getInterfaces())) { return false; } if (in_array('IteratorAggregate', $node->getInterfaces())) { return false; } foreach ($node->getInterfaces() as $interface) { if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) { continue; } if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) { continue; } if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) { continue; } return true; } return false; } public function apply(ClassNode $node) { $node->addInterface('Iterator'); $node->addMethod(new MethodNode('current')); $node->addMethod(new MethodNode('key')); $node->addMethod(new MethodNode('next')); $node->addMethod(new MethodNode('rewind')); $node->addMethod(new MethodNode('valid')); } public function getPriority() { return 100; } } implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); } private function implementsAThrowableInterface(ClassNode $node) { foreach ($node->getInterfaces() as $type) { if (is_a($type, 'Throwable', true)) { return true; } } return false; } private function doesNotExtendAThrowableClass(ClassNode $node) { return !is_a($node->getParentClass(), 'Throwable', true); } public function apply(ClassNode $node) { $this->checkItCanBeDoubled($node); $this->setParentClassToException($node); } private function checkItCanBeDoubled(ClassNode $node) { $className = $node->getParentClass(); if ($className !== 'stdClass') { throw new ClassCreatorException( sprintf( 'Cannot double concrete class %s as well as implement Traversable', $className ), $node ); } } private function setParentClassToException(ClassNode $node) { $node->setParentClass('Exception'); $node->removeMethod('getMessage'); $node->removeMethod('getCode'); $node->removeMethod('getFile'); $node->removeMethod('getLine'); $node->removeMethod('getTrace'); $node->removeMethod('getPrevious'); $node->removeMethod('getNext'); $node->removeMethod('getTraceAsString'); } public function getPriority() { return 100; } } getParentClass()) { return false; } return 'SplFileInfo' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'SplFileInfo') ; } public function apply(ClassNode $node) { if ($node->hasMethod('__construct')) { $constructor = $node->getMethod('__construct'); } else { $constructor = new MethodNode('__construct'); $node->addMethod($constructor); } if ($this->nodeIsDirectoryIterator($node)) { $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); return; } if ($this->nodeIsSplFileObject($node)) { $filePath = str_replace('\\','\\\\',__FILE__); $constructor->setCode('return parent::__construct("' . $filePath .'");'); return; } if ($this->nodeIsSymfonySplFileInfo($node)) { $filePath = str_replace('\\','\\\\',__FILE__); $constructor->setCode('return parent::__construct("' . $filePath .'", "", "");'); return; } $constructor->useParentCode(); } public function getPriority() { return 50; } private function nodeIsDirectoryIterator(ClassNode $node) { $parent = $node->getParentClass(); return 'DirectoryIterator' === $parent || is_subclass_of($parent, 'DirectoryIterator'); } private function nodeIsSplFileObject(ClassNode $node) { $parent = $node->getParentClass(); return 'SplFileObject' === $parent || is_subclass_of($parent, 'SplFileObject'); } private function nodeIsSymfonySplFileInfo(ClassNode $node) { $parent = $node->getParentClass(); return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent; } } expectedCount = intval($count); } public function getExpectedCount() { return $this->expectedCount; } } calls = $calls; } public function getCalls() { return $this->calls; } } getMessage(); $message = strtr($message, array("\n" => "\n "))."\n"; $message = empty($this->exceptions) ? $message : "\n" . $message; $this->message = rtrim($this->message.$message); $this->exceptions[] = $exception; } public function getExceptions() { return $this->exceptions; } public function setObjectProphecy(ObjectProphecy $objectProphecy) { $this->objectProphecy = $objectProphecy; } public function getObjectProphecy() { return $this->objectProphecy; } } getObjectProphecy()); $this->methodProphecy = $methodProphecy; } public function getMethodProphecy() { return $this->methodProphecy; } } objectProphecy = $objectProphecy; } public function getObjectProphecy() { return $this->objectProphecy; } } methodName = $methodName; $this->arguments = $arguments; } public function getMethodName() { return $this->methodName; } public function getArguments() { return $this->arguments; } } methodName = $methodName; $this->className = $className; } public function getMethodName() { return $this->methodName; } public function getClassName() { return $this->className; } } node = $node; } public function getClassNode() { return $this->node; } } class = $class; } public function getReflectedClass() { return $this->class; } } classname = $classname; $this->methodName = $methodName; } public function getClassname() { return $this->classname; } public function getMethodName() { return $this->methodName; } } getClassname(); } } classname = $classname; $this->methodName = $methodName; $this->arguments = $arguments; } public function getClassname() { return $this->classname; } public function getMethodName() { return $this->methodName; } public function getArguments() { return $this->arguments; } } classname = $classname; } public function getClassname() { return $this->classname; } } array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'reference' => '3597943bd0bee905e4b1cb099b7bd7345c9d5afb', 'name' => 'codeception/phar-file', 'dev' => true, ), 'versions' => array( 'behat/gherkin' => array( 'pretty_version' => 'v4.7.3', 'version' => '4.7.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../behat/gherkin', 'aliases' => array(), 'reference' => 'd5ae4616aeaa91daadbfb8446d9d17aae8d43cf7', 'dev_requirement' => false, ), 'codeception/codeception' => array( 'pretty_version' => '4.2.1', 'version' => '4.2.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/codeception', 'aliases' => array(), 'reference' => '77b3e2003fd4446b35826cb9dc397129c521c888', 'dev_requirement' => false, ), 'codeception/lib-asserts' => array( 'pretty_version' => '1.13.2', 'version' => '1.13.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/lib-asserts', 'aliases' => array(), 'reference' => '184231d5eab66bc69afd6b9429344d80c67a33b6', 'dev_requirement' => false, ), 'codeception/lib-innerbrowser' => array( 'pretty_version' => '1.5.1', 'version' => '1.5.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/lib-innerbrowser', 'aliases' => array(), 'reference' => '31b4b56ad53c3464fcb2c0a14d55a51a201bd3c2', 'dev_requirement' => false, ), 'codeception/module-amqp' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-amqp', 'aliases' => array(), 'reference' => 'be399360429aff06d6291c389e9c64aff567b041', 'dev_requirement' => false, ), 'codeception/module-apc' => array( 'pretty_version' => '1.0.2', 'version' => '1.0.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-apc', 'aliases' => array(), 'reference' => '07fbd6dd12b9d3f4b738bcac8266eecefb470365', 'dev_requirement' => false, ), 'codeception/module-asserts' => array( 'pretty_version' => '1.3.1', 'version' => '1.3.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-asserts', 'aliases' => array(), 'reference' => '59374f2fef0cabb9e8ddb53277e85cdca74328de', 'dev_requirement' => false, ), 'codeception/module-cli' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-cli', 'aliases' => array(), 'reference' => '1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f', 'dev_requirement' => false, ), 'codeception/module-db' => array( 'pretty_version' => '1.2.0', 'version' => '1.2.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-db', 'aliases' => array(), 'reference' => '04c3e66fbd3a3ced17fcccc49627f6393a97b04b', 'dev_requirement' => false, ), 'codeception/module-filesystem' => array( 'pretty_version' => '1.0.3', 'version' => '1.0.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-filesystem', 'aliases' => array(), 'reference' => '781be167fb1557bfc9b61e0a4eac60a32c534ec1', 'dev_requirement' => false, ), 'codeception/module-ftp' => array( 'pretty_version' => '1.0.2', 'version' => '1.0.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-ftp', 'aliases' => array(), 'reference' => 'eed215edf32998c212da6f375a6d1c5e7c3a01c0', 'dev_requirement' => false, ), 'codeception/module-memcache' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-memcache', 'aliases' => array(), 'reference' => '96d65b6cf80bf19a858d7fa21257b8af1fe806ee', 'dev_requirement' => false, ), 'codeception/module-mongodb' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-mongodb', 'aliases' => array(), 'reference' => '7bafcc9e7755b762436d742aa88c53726b5ddca5', 'dev_requirement' => false, ), 'codeception/module-phpbrowser' => array( 'pretty_version' => '1.0.3', 'version' => '1.0.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-phpbrowser', 'aliases' => array(), 'reference' => '8ba6bede11d0914e74d98691f427fd8f397f192e', 'dev_requirement' => false, ), 'codeception/module-queue' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-queue', 'aliases' => array(), 'reference' => 'cf4116ed143758c8f36ac24c787f6647787e40f8', 'dev_requirement' => false, ), 'codeception/module-redis' => array( 'pretty_version' => '1.1.0', 'version' => '1.1.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-redis', 'aliases' => array(), 'reference' => '83c6db34349f1bd8dbfe4b778d7645c9ffcacfb7', 'dev_requirement' => false, ), 'codeception/module-rest' => array( 'pretty_version' => '1.3.1', 'version' => '1.3.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-rest', 'aliases' => array(), 'reference' => '293a0103d5257b7c884ef276147a9a06914e878f', 'dev_requirement' => false, ), 'codeception/module-sequence' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-sequence', 'aliases' => array(), 'reference' => 'b75be26681ae90824cde8f8df785981f293667e1', 'dev_requirement' => false, ), 'codeception/module-soap' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-soap', 'aliases' => array(), 'reference' => '49d081cb0c455f405344a497fdbfda20168dcb9d', 'dev_requirement' => false, ), 'codeception/module-webdriver' => array( 'pretty_version' => '1.4.0', 'version' => '1.4.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-webdriver', 'aliases' => array(), 'reference' => 'baa18b7bf70aa024012f967b5ce5021e1faa9151', 'dev_requirement' => false, ), 'codeception/phar-file' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'reference' => '3597943bd0bee905e4b1cb099b7bd7345c9d5afb', 'dev_requirement' => false, ), 'codeception/phpunit-wrapper' => array( 'pretty_version' => '6.0.22', 'version' => '6.0.22.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/phpunit-wrapper', 'aliases' => array(), 'reference' => 'f90c0275e74c1d9812a800d3b7dd1a39c89d40d2', 'dev_requirement' => false, ), 'codeception/stub' => array( 'pretty_version' => '2.1.0', 'version' => '2.1.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/stub', 'aliases' => array(), 'reference' => '853657f988942f7afb69becf3fd0059f192c705a', 'dev_requirement' => false, ), 'composer/ca-bundle' => array( 'pretty_version' => '1.3.2', 'version' => '1.3.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/./ca-bundle', 'aliases' => array(), 'reference' => 'fd5dd441932a7e10ca6e5b490e272d34c8430640', 'dev_requirement' => false, ), 'doctrine/instantiator' => array( 'pretty_version' => '1.0.5', 'version' => '1.0.5.0', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/instantiator', 'aliases' => array(), 'reference' => '8e884e78f9f0eb1329e445619e04456e64d8051d', 'dev_requirement' => false, ), 'facebook/webdriver' => array( 'dev_requirement' => false, 'replaced' => array( 0 => '*', ), ), 'flow/jsonpath' => array( 'dev_requirement' => false, 'replaced' => array( 0 => '*', ), ), 'guzzlehttp/guzzle' => array( 'pretty_version' => '6.5.8', 'version' => '6.5.8.0', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'aliases' => array(), 'reference' => 'a52f0440530b54fa079ce76e8c5d196a42cad981', 'dev_requirement' => false, ), 'guzzlehttp/promises' => array( 'pretty_version' => '1.5.1', 'version' => '1.5.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/promises', 'aliases' => array(), 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da', 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( 'pretty_version' => '1.9.0', 'version' => '1.9.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), 'reference' => 'e98e3e6d4f86621a9b75f623996e6bbdeb4b9318', 'dev_requirement' => false, ), 'hoa/consistency' => array( 'pretty_version' => '1.17.05.02', 'version' => '1.17.05.02', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/consistency', 'aliases' => array(), 'reference' => 'fd7d0adc82410507f332516faf655b6ed22e4c2f', 'dev_requirement' => false, ), 'hoa/console' => array( 'pretty_version' => '3.17.05.02', 'version' => '3.17.05.02', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/console', 'aliases' => array(), 'reference' => 'e231fd3ea70e6d773576ae78de0bdc1daf331a66', 'dev_requirement' => false, ), 'hoa/event' => array( 'pretty_version' => '1.17.01.13', 'version' => '1.17.01.13', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/event', 'aliases' => array(), 'reference' => '6c0060dced212ffa3af0e34bb46624f990b29c54', 'dev_requirement' => false, ), 'hoa/exception' => array( 'pretty_version' => '1.17.01.16', 'version' => '1.17.01.16', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/exception', 'aliases' => array(), 'reference' => '091727d46420a3d7468ef0595651488bfc3a458f', 'dev_requirement' => false, ), 'hoa/file' => array( 'pretty_version' => '1.17.07.11', 'version' => '1.17.07.11', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/file', 'aliases' => array(), 'reference' => '35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca', 'dev_requirement' => false, ), 'hoa/iterator' => array( 'pretty_version' => '2.17.01.10', 'version' => '2.17.01.10', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/iterator', 'aliases' => array(), 'reference' => 'd1120ba09cb4ccd049c86d10058ab94af245f0cc', 'dev_requirement' => false, ), 'hoa/protocol' => array( 'pretty_version' => '1.17.01.14', 'version' => '1.17.01.14', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/protocol', 'aliases' => array(), 'reference' => '5c2cf972151c45f373230da170ea015deecf19e2', 'dev_requirement' => false, ), 'hoa/stream' => array( 'pretty_version' => '1.17.02.21', 'version' => '1.17.02.21', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/stream', 'aliases' => array(), 'reference' => '3293cfffca2de10525df51436adf88a559151d82', 'dev_requirement' => false, ), 'hoa/ustring' => array( 'pretty_version' => '4.17.01.16', 'version' => '4.17.01.16', 'type' => 'library', 'install_path' => __DIR__ . '/../hoa/ustring', 'aliases' => array(), 'reference' => 'e6326e2739178799b1fe3fdd92029f9517fa17a0', 'dev_requirement' => false, ), 'justinrainbow/json-schema' => array( 'pretty_version' => '5.2.12', 'version' => '5.2.12.0', 'type' => 'library', 'install_path' => __DIR__ . '/../justinrainbow/json-schema', 'aliases' => array(), 'reference' => 'ad87d5a5ca981228e0e205c2bc7dfb8e24559b60', 'dev_requirement' => false, ), 'myclabs/deep-copy' => array( 'pretty_version' => '1.7.0', 'version' => '1.7.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../myclabs/deep-copy', 'aliases' => array(), 'reference' => '3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e', 'dev_requirement' => false, ), 'padraic/humbug_get_contents' => array( 'pretty_version' => '1.1.2', 'version' => '1.1.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../padraic/humbug_get_contents', 'aliases' => array(), 'reference' => 'dcb086060c9dd6b2f51d8f7a895500307110b7a7', 'dev_requirement' => false, ), 'padraic/phar-updater' => array( 'pretty_version' => 'v1.0.6', 'version' => '1.0.6.0', 'type' => 'library', 'install_path' => __DIR__ . '/../padraic/phar-updater', 'aliases' => array(), 'reference' => 'd01d3b8f26e541ac9b9eeba1e18d005d852f7ff1', 'dev_requirement' => false, ), 'paragonie/random_compat' => array( 'pretty_version' => 'v2.0.21', 'version' => '2.0.21.0', 'type' => 'library', 'install_path' => __DIR__ . '/../paragonie/random_compat', 'aliases' => array(), 'reference' => '96c132c7f2f7bc3230723b66e89f8f150b29d5ae', 'dev_requirement' => false, ), 'php-amqplib/php-amqplib' => array( 'pretty_version' => 'v2.12.3', 'version' => '2.12.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../php-amqplib/php-amqplib', 'aliases' => array(), 'reference' => 'f746eb44df6d8f838173729867dd1d20b0265faa', 'dev_requirement' => false, ), 'php-webdriver/webdriver' => array( 'pretty_version' => '1.12.1', 'version' => '1.12.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../php-webdriver/webdriver', 'aliases' => array(), 'reference' => 'b27ddf458d273c7d4602106fcaf978aa0b7fe15a', 'dev_requirement' => false, ), 'phpdocumentor/reflection-common' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpdocumentor/reflection-common', 'aliases' => array(), 'reference' => '21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6', 'dev_requirement' => false, ), 'phpdocumentor/reflection-docblock' => array( 'pretty_version' => '3.3.2', 'version' => '3.3.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpdocumentor/reflection-docblock', 'aliases' => array(), 'reference' => 'bf329f6c1aadea3299f08ee804682b7c45b326a2', 'dev_requirement' => false, ), 'phpdocumentor/type-resolver' => array( 'pretty_version' => '0.4.0', 'version' => '0.4.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpdocumentor/type-resolver', 'aliases' => array(), 'reference' => '9c977708995954784726e25d0cd1dddf4e65b0f7', 'dev_requirement' => false, ), 'phpseclib/phpseclib' => array( 'pretty_version' => '2.0.37', 'version' => '2.0.37.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpseclib/phpseclib', 'aliases' => array(), 'reference' => 'c812fbb4d6b4d7f30235ab7298a12f09ba13b37c', 'dev_requirement' => false, ), 'phpspec/prophecy' => array( 'pretty_version' => 'v1.10.3', 'version' => '1.10.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpspec/prophecy', 'aliases' => array(), 'reference' => '451c3cd1418cf640de218914901e51b064abb093', 'dev_requirement' => false, ), 'phpunit/php-code-coverage' => array( 'pretty_version' => '4.0.8', 'version' => '4.0.8.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-code-coverage', 'aliases' => array(), 'reference' => 'ef7b2f56815df854e66ceaee8ebe9393ae36a40d', 'dev_requirement' => false, ), 'phpunit/php-file-iterator' => array( 'pretty_version' => '1.4.5', 'version' => '1.4.5.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-file-iterator', 'aliases' => array(), 'reference' => '730b01bc3e867237eaac355e06a36b85dd93a8b4', 'dev_requirement' => false, ), 'phpunit/php-text-template' => array( 'pretty_version' => '1.2.1', 'version' => '1.2.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-text-template', 'aliases' => array(), 'reference' => '31f8b717e51d9a2afca6c9f046f5d69fc27c8686', 'dev_requirement' => false, ), 'phpunit/php-timer' => array( 'pretty_version' => '1.0.9', 'version' => '1.0.9.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-timer', 'aliases' => array(), 'reference' => '3dcf38ca72b158baf0bc245e9184d3fdffa9c46f', 'dev_requirement' => false, ), 'phpunit/php-token-stream' => array( 'pretty_version' => '1.4.12', 'version' => '1.4.12.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-token-stream', 'aliases' => array(), 'reference' => '1ce90ba27c42e4e44e6d8458241466380b51fa16', 'dev_requirement' => false, ), 'phpunit/phpunit' => array( 'pretty_version' => '5.7.27', 'version' => '5.7.27.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/phpunit', 'aliases' => array(), 'reference' => 'b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c', 'dev_requirement' => false, ), 'phpunit/phpunit-mock-objects' => array( 'pretty_version' => '3.4.4', 'version' => '3.4.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/phpunit-mock-objects', 'aliases' => array(), 'reference' => 'a23b761686d50a560cc56233b9ecf49597cc9118', 'dev_requirement' => false, ), 'predis/predis' => array( 'pretty_version' => 'v1.1.10', 'version' => '1.1.10.0', 'type' => 'library', 'install_path' => __DIR__ . '/../predis/predis', 'aliases' => array(), 'reference' => 'a2fb02d738bedadcffdbb07efa3a5e7bd57f8d6e', 'dev_requirement' => false, ), 'psr/http-message' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', 'dev_requirement' => false, ), 'psr/http-message-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '1.0', ), ), 'psr/log' => array( 'pretty_version' => '1.1.4', 'version' => '1.1.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', 'dev_requirement' => false, ), 'psr/log-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '1.0', ), ), 'ralouphie/getallheaders' => array( 'pretty_version' => '3.0.3', 'version' => '3.0.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../ralouphie/getallheaders', 'aliases' => array(), 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'dev_requirement' => false, ), 'sebastian/code-unit-reverse-lookup' => array( 'pretty_version' => '1.0.2', 'version' => '1.0.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup', 'aliases' => array(), 'reference' => '1de8cd5c010cb153fcd68b8d0f64606f523f7619', 'dev_requirement' => false, ), 'sebastian/comparator' => array( 'pretty_version' => '1.2.4', 'version' => '1.2.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/comparator', 'aliases' => array(), 'reference' => '2b7424b55f5047b47ac6e5ccb20b2aea4011d9be', 'dev_requirement' => false, ), 'sebastian/diff' => array( 'pretty_version' => '1.4.3', 'version' => '1.4.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/diff', 'aliases' => array(), 'reference' => '7f066a26a962dbe58ddea9f72a4e82874a3975a4', 'dev_requirement' => false, ), 'sebastian/environment' => array( 'pretty_version' => '2.0.0', 'version' => '2.0.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/environment', 'aliases' => array(), 'reference' => '5795ffe5dc5b02460c3e34222fee8cbe245d8fac', 'dev_requirement' => false, ), 'sebastian/exporter' => array( 'pretty_version' => '2.0.0', 'version' => '2.0.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/exporter', 'aliases' => array(), 'reference' => 'ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4', 'dev_requirement' => false, ), 'sebastian/global-state' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/global-state', 'aliases' => array(), 'reference' => 'bc37d50fea7d017d3d340f230811c9f1d7280af4', 'dev_requirement' => false, ), 'sebastian/object-enumerator' => array( 'pretty_version' => '2.0.1', 'version' => '2.0.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/object-enumerator', 'aliases' => array(), 'reference' => '1311872ac850040a79c3c058bea3e22d0f09cbb7', 'dev_requirement' => false, ), 'sebastian/recursion-context' => array( 'pretty_version' => '2.0.0', 'version' => '2.0.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/recursion-context', 'aliases' => array(), 'reference' => '2c3ba150cbec723aa057506e73a8d33bdb286c9a', 'dev_requirement' => false, ), 'sebastian/resource-operations' => array( 'pretty_version' => '1.0.0', 'version' => '1.0.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/resource-operations', 'aliases' => array(), 'reference' => 'ce990bb21759f94aeafd30209e8cfcdfa8bc3f52', 'dev_requirement' => false, ), 'sebastian/version' => array( 'pretty_version' => '2.0.1', 'version' => '2.0.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/version', 'aliases' => array(), 'reference' => '99732be0ddb3361e16ad77b68ba41efc8e979019', 'dev_requirement' => false, ), 'softcreatr/jsonpath' => array( 'pretty_version' => '0.5.1', 'version' => '0.5.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../softcreatr/jsonpath', 'aliases' => array(), 'reference' => '758c56cdae8c58a599caac00f818eafbb1364e14', 'dev_requirement' => false, ), 'symfony/browser-kit' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/browser-kit', 'aliases' => array(), 'reference' => '9590bd3d3f9fa2f28d34b713ed4765a8cc8ad15c', 'dev_requirement' => false, ), 'symfony/console' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/console', 'aliases' => array(), 'reference' => 'a10b1da6fc93080c180bba7219b5ff5b7518fe81', 'dev_requirement' => false, ), 'symfony/css-selector' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/css-selector', 'aliases' => array(), 'reference' => 'da3d9da2ce0026771f5fe64cb332158f1bd2bc33', 'dev_requirement' => false, ), 'symfony/debug' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/debug', 'aliases' => array(), 'reference' => 'ab42889de57fdfcfcc0759ab102e2fd4ea72dcae', 'dev_requirement' => false, ), 'symfony/dom-crawler' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/dom-crawler', 'aliases' => array(), 'reference' => 'ef97bcfbae5b384b4ca6c8d57b617722f15241a6', 'dev_requirement' => false, ), 'symfony/event-dispatcher' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/event-dispatcher', 'aliases' => array(), 'reference' => '31fde73757b6bad247c54597beef974919ec6860', 'dev_requirement' => false, ), 'symfony/finder' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/finder', 'aliases' => array(), 'reference' => 'b6b6ad3db3edb1b4b1c1896b1975fb684994de6e', 'dev_requirement' => false, ), 'symfony/polyfill-ctype' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'aliases' => array(), 'reference' => 'aed596913b70fae57be53d86faa2e9ef85a2297b', 'dev_requirement' => false, ), 'symfony/polyfill-intl-idn' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn', 'aliases' => array(), 'reference' => '4ad5115c0f5d5172a9fe8147675ec6de266d8826', 'dev_requirement' => false, ), 'symfony/polyfill-intl-normalizer' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', 'aliases' => array(), 'reference' => '8db0ae7936b42feb370840cf24de1a144fb0ef27', 'dev_requirement' => false, ), 'symfony/polyfill-mbstring' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'aliases' => array(), 'reference' => 'b5f7b932ee6fa802fc792eabd77c4c88084517ce', 'dev_requirement' => false, ), 'symfony/polyfill-php70' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php70', 'aliases' => array(), 'reference' => '3fe414077251a81a1b15b1c709faf5c2fbae3d4e', 'dev_requirement' => false, ), 'symfony/polyfill-php72' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php72', 'aliases' => array(), 'reference' => 'beecef6b463b06954638f02378f52496cb84bacc', 'dev_requirement' => false, ), 'symfony/process' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/process', 'aliases' => array(), 'reference' => 'b8648cf1d5af12a44a51d07ef9bf980921f15fca', 'dev_requirement' => false, ), 'symfony/yaml' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/yaml', 'aliases' => array(), 'reference' => '88289caa3c166321883f67fe5130188ebbb47094', 'dev_requirement' => false, ), 'videlalvaro/php-amqplib' => array( 'dev_requirement' => false, 'replaced' => array( 0 => 'v2.12.3', ), ), 'webmozart/assert' => array( 'pretty_version' => '1.9.1', 'version' => '1.9.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../webmozart/assert', 'aliases' => array(), 'reference' => 'bafc69caeb4d49c39fd0779086c03a3738cbb389', 'dev_requirement' => false, ), ), ); $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', 'e88992873b7765f9b5710cab95ba5dd7' => $vendorDir . '/hoa/consistency/Prelude.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', 'e3b2795a8a512b6083af088fb53afe6c' => $vendorDir . '/codeception/codeception/functions.php', '3e76f7f02b41af8cea96018933f6b7e3' => $vendorDir . '/hoa/protocol/Wrapper.php', '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', 'd9d39f82a605ebe5918f683dd402334c' => $vendorDir . '/padraic/humbug_get_contents/src/function.php', '3a50d90d85c7fe889a94ae1114b921ce' => $vendorDir . '/padraic/humbug_get_contents/src/functions.php', '2a3c2110e8e0295330dc3d11a4cbc4cb' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php', ); = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit27e074e2281a7b29fcc5bab5b46fd4ef::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\ComposerStaticInit27e074e2281a7b29fcc5bab5b46fd4ef::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequire27e074e2281a7b29fcc5bab5b46fd4ef($fileIdentifier, $file); } return $loader; } } function composerRequire27e074e2281a7b29fcc5bab5b46fd4ef($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } } $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', 'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', 'Behat\\Gherkin\\Cache\\CacheInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php', 'Behat\\Gherkin\\Cache\\FileCache' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.php', 'Behat\\Gherkin\\Cache\\MemoryCache' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php', 'Behat\\Gherkin\\Exception\\CacheException' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Exception/CacheException.php', 'Behat\\Gherkin\\Exception\\Exception' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Exception/Exception.php', 'Behat\\Gherkin\\Exception\\LexerException' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Exception/LexerException.php', 'Behat\\Gherkin\\Exception\\NodeException' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Exception/NodeException.php', 'Behat\\Gherkin\\Exception\\ParserException' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Exception/ParserException.php', 'Behat\\Gherkin\\Filter\\ComplexFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilter.php', 'Behat\\Gherkin\\Filter\\ComplexFilterInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php', 'Behat\\Gherkin\\Filter\\FeatureFilterInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php', 'Behat\\Gherkin\\Filter\\FilterInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.php', 'Behat\\Gherkin\\Filter\\LineFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.php', 'Behat\\Gherkin\\Filter\\LineRangeFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php', 'Behat\\Gherkin\\Filter\\NameFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.php', 'Behat\\Gherkin\\Filter\\NarrativeFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php', 'Behat\\Gherkin\\Filter\\PathsFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php', 'Behat\\Gherkin\\Filter\\RoleFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.php', 'Behat\\Gherkin\\Filter\\SimpleFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/SimpleFilter.php', 'Behat\\Gherkin\\Filter\\TagFilter' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php', 'Behat\\Gherkin\\Gherkin' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Gherkin.php', 'Behat\\Gherkin\\Keywords\\ArrayKeywords' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.php', 'Behat\\Gherkin\\Keywords\\CachedArrayKeywords' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Keywords/CachedArrayKeywords.php', 'Behat\\Gherkin\\Keywords\\CucumberKeywords' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Keywords/CucumberKeywords.php', 'Behat\\Gherkin\\Keywords\\KeywordsDumper' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.php', 'Behat\\Gherkin\\Keywords\\KeywordsInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsInterface.php', 'Behat\\Gherkin\\Lexer' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Lexer.php', 'Behat\\Gherkin\\Loader\\AbstractFileLoader' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/AbstractFileLoader.php', 'Behat\\Gherkin\\Loader\\ArrayLoader' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php', 'Behat\\Gherkin\\Loader\\CucumberNDJsonAstLoader' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.php', 'Behat\\Gherkin\\Loader\\DirectoryLoader' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/DirectoryLoader.php', 'Behat\\Gherkin\\Loader\\FileLoaderInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/FileLoaderInterface.php', 'Behat\\Gherkin\\Loader\\GherkinFileLoader' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/GherkinFileLoader.php', 'Behat\\Gherkin\\Loader\\LoaderInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.php', 'Behat\\Gherkin\\Loader\\YamlFileLoader' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.php', 'Behat\\Gherkin\\Node\\ArgumentInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/ArgumentInterface.php', 'Behat\\Gherkin\\Node\\BackgroundNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php', 'Behat\\Gherkin\\Node\\ExampleNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php', 'Behat\\Gherkin\\Node\\ExampleTableNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php', 'Behat\\Gherkin\\Node\\FeatureNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.php', 'Behat\\Gherkin\\Node\\KeywordNodeInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/KeywordNodeInterface.php', 'Behat\\Gherkin\\Node\\NodeInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/NodeInterface.php', 'Behat\\Gherkin\\Node\\OutlineNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.php', 'Behat\\Gherkin\\Node\\PyStringNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/PyStringNode.php', 'Behat\\Gherkin\\Node\\ScenarioInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/ScenarioInterface.php', 'Behat\\Gherkin\\Node\\ScenarioLikeInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/ScenarioLikeInterface.php', 'Behat\\Gherkin\\Node\\ScenarioNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php', 'Behat\\Gherkin\\Node\\StepContainerInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php', 'Behat\\Gherkin\\Node\\StepNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/StepNode.php', 'Behat\\Gherkin\\Node\\TableNode' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php', 'Behat\\Gherkin\\Node\\TaggedNodeInterface' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php', 'Behat\\Gherkin\\Parser' => $vendorDir . '/behat/gherkin/src/Behat/Gherkin/Parser.php', 'Codeception\\Actor' => $vendorDir . '/codeception/codeception/src/Codeception/Actor.php', 'Codeception\\Application' => $vendorDir . '/codeception/codeception/src/Codeception/Application.php', 'Codeception\\Codecept' => $vendorDir . '/codeception/codeception/src/Codeception/Codecept.php', 'Codeception\\Command\\Bootstrap' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Bootstrap.php', 'Codeception\\Command\\Build' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Build.php', 'Codeception\\Command\\Clean' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Clean.php', 'Codeception\\Command\\Completion' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Completion.php', 'Codeception\\Command\\CompletionFallback' => $vendorDir . '/codeception/codeception/src/Codeception/Command/CompletionFallback.php', 'Codeception\\Command\\ConfigValidate' => $vendorDir . '/codeception/codeception/src/Codeception/Command/ConfigValidate.php', 'Codeception\\Command\\Console' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Console.php', 'Codeception\\Command\\DryRun' => $vendorDir . '/codeception/codeception/src/Codeception/Command/DryRun.php', 'Codeception\\Command\\GenerateCept' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateCept.php', 'Codeception\\Command\\GenerateCest' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateCest.php', 'Codeception\\Command\\GenerateEnvironment' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateEnvironment.php', 'Codeception\\Command\\GenerateFeature' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateFeature.php', 'Codeception\\Command\\GenerateGroup' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateGroup.php', 'Codeception\\Command\\GenerateHelper' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateHelper.php', 'Codeception\\Command\\GeneratePageObject' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GeneratePageObject.php', 'Codeception\\Command\\GenerateScenarios' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateScenarios.php', 'Codeception\\Command\\GenerateSnapshot' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateSnapshot.php', 'Codeception\\Command\\GenerateStepObject' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateStepObject.php', 'Codeception\\Command\\GenerateSuite' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateSuite.php', 'Codeception\\Command\\GenerateTest' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GenerateTest.php', 'Codeception\\Command\\GherkinSnippets' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GherkinSnippets.php', 'Codeception\\Command\\GherkinSteps' => $vendorDir . '/codeception/codeception/src/Codeception/Command/GherkinSteps.php', 'Codeception\\Command\\Init' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Init.php', 'Codeception\\Command\\Run' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Run.php', 'Codeception\\Command\\SelfUpdate' => $vendorDir . '/codeception/codeception/src/Codeception/Command/SelfUpdate.php', 'Codeception\\Command\\Shared\\Config' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Shared/Config.php', 'Codeception\\Command\\Shared\\FileSystem' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Shared/FileSystem.php', 'Codeception\\Command\\Shared\\Style' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Shared/Style.php', 'Codeception\\Configuration' => $vendorDir . '/codeception/codeception/src/Codeception/Configuration.php', 'Codeception\\Coverage\\Filter' => $vendorDir . '/codeception/codeception/src/Codeception/Coverage/Filter.php', 'Codeception\\Coverage\\PhpCodeCoverageFactory' => $vendorDir . '/codeception/codeception/src/Codeception/Coverage/PhpCodeCoverageFactory.php', 'Codeception\\Coverage\\Subscriber\\Local' => $vendorDir . '/codeception/codeception/src/Codeception/Coverage/Subscriber/Local.php', 'Codeception\\Coverage\\Subscriber\\LocalServer' => $vendorDir . '/codeception/codeception/src/Codeception/Coverage/Subscriber/LocalServer.php', 'Codeception\\Coverage\\Subscriber\\Printer' => $vendorDir . '/codeception/codeception/src/Codeception/Coverage/Subscriber/Printer.php', 'Codeception\\Coverage\\Subscriber\\RemoteServer' => $vendorDir . '/codeception/codeception/src/Codeception/Coverage/Subscriber/RemoteServer.php', 'Codeception\\Coverage\\SuiteSubscriber' => $vendorDir . '/codeception/codeception/src/Codeception/Coverage/SuiteSubscriber.php', 'Codeception\\CustomCommandInterface' => $vendorDir . '/codeception/codeception/src/Codeception/CustomCommandInterface.php', 'Codeception\\Event\\DispatcherWrapper' => $vendorDir . '/codeception/codeception/src/Codeception/Event/DispatcherWrapper.php', 'Codeception\\Event\\FailEvent' => $vendorDir . '/codeception/codeception/src/Codeception/Event/FailEvent.php', 'Codeception\\Event\\PrintResultEvent' => $vendorDir . '/codeception/codeception/src/Codeception/Event/PrintResultEvent.php', 'Codeception\\Event\\StepEvent' => $vendorDir . '/codeception/codeception/src/Codeception/Event/StepEvent.php', 'Codeception\\Event\\SuiteEvent' => $vendorDir . '/codeception/codeception/src/Codeception/Event/SuiteEvent.php', 'Codeception\\Event\\TestEvent' => $vendorDir . '/codeception/codeception/src/Codeception/Event/TestEvent.php', 'Codeception\\Events' => $vendorDir . '/codeception/codeception/src/Codeception/Events.php', 'Codeception\\Example' => $vendorDir . '/codeception/codeception/src/Codeception/Example.php', 'Codeception\\Exception\\ConditionalAssertionFailed' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ConditionalAssertionFailed.php', 'Codeception\\Exception\\ConfigurationException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ConfigurationException.php', 'Codeception\\Exception\\ConnectionException' => $vendorDir . '/codeception/module-webdriver/src/Codeception/Exception/ConnectionException.php', 'Codeception\\Exception\\ContentNotFound' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ContentNotFound.php', 'Codeception\\Exception\\ElementNotFound' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ElementNotFound.php', 'Codeception\\Exception\\ExtensionException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ExtensionException.php', 'Codeception\\Exception\\ExternalUrlException' => $vendorDir . '/codeception/lib-innerbrowser/src/Codeception/Exception/ExternalUrlException.php', 'Codeception\\Exception\\InjectionException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/InjectionException.php', 'Codeception\\Exception\\MalformedLocatorException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/MalformedLocatorException.php', 'Codeception\\Exception\\ModuleConfigException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ModuleConfigException.php', 'Codeception\\Exception\\ModuleConflictException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ModuleConflictException.php', 'Codeception\\Exception\\ModuleException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ModuleException.php', 'Codeception\\Exception\\ModuleRequireException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ModuleRequireException.php', 'Codeception\\Exception\\ParseException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ParseException.php', 'Codeception\\Exception\\RemoteException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/RemoteException.php', 'Codeception\\Exception\\TestParseException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/TestParseException.php', 'Codeception\\Exception\\TestRuntimeException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/TestRuntimeException.php', 'Codeception\\Extension' => $vendorDir . '/codeception/codeception/src/Codeception/Extension.php', 'Codeception\\Extension\\DotReporter' => $vendorDir . '/codeception/codeception/ext/DotReporter.php', 'Codeception\\Extension\\Logger' => $vendorDir . '/codeception/codeception/ext/Logger.php', 'Codeception\\Extension\\Recorder' => $vendorDir . '/codeception/codeception/ext/Recorder.php', 'Codeception\\Extension\\RunBefore' => $vendorDir . '/codeception/codeception/ext/RunBefore.php', 'Codeception\\Extension\\RunFailed' => $vendorDir . '/codeception/codeception/ext/RunFailed.php', 'Codeception\\Extension\\RunProcess' => $vendorDir . '/codeception/codeception/ext/RunProcess.php', 'Codeception\\Extension\\SimpleReporter' => $vendorDir . '/codeception/codeception/ext/SimpleReporter.php', 'Codeception\\GroupObject' => $vendorDir . '/codeception/codeception/src/Codeception/GroupObject.php', 'Codeception\\InitTemplate' => $vendorDir . '/codeception/codeception/src/Codeception/InitTemplate.php', 'Codeception\\Lib\\Actor\\Shared\\Comment' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Comment.php', 'Codeception\\Lib\\Actor\\Shared\\Friend' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Friend.php', 'Codeception\\Lib\\Actor\\Shared\\Pause' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Pause.php', 'Codeception\\Lib\\Actor\\Shared\\Retry' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Retry.php', 'Codeception\\Lib\\Connector\\Guzzle' => $vendorDir . '/codeception/module-phpbrowser/src/Codeception/Lib/Connector/Guzzle.php', 'Codeception\\Lib\\Connector\\Shared\\PhpSuperGlobalsConverter' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php', 'Codeception\\Lib\\Console\\Colorizer' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Console/Colorizer.php', 'Codeception\\Lib\\Console\\DiffFactory' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Console/DiffFactory.php', 'Codeception\\Lib\\Console\\Message' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Console/Message.php', 'Codeception\\Lib\\Console\\MessageFactory' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Console/MessageFactory.php', 'Codeception\\Lib\\Console\\Output' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Console/Output.php', 'Codeception\\Lib\\Console\\ReplHistory' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Console/ReplHistory.php', 'Codeception\\Lib\\DbPopulator' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/DbPopulator.php', 'Codeception\\Lib\\Di' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Di.php', 'Codeception\\Lib\\Driver\\AmazonSQS' => $vendorDir . '/codeception/module-queue/src/Codeception/Lib/Driver/AmazonSQS.php', 'Codeception\\Lib\\Driver\\Beanstalk' => $vendorDir . '/codeception/module-queue/src/Codeception/Lib/Driver/Beanstalk.php', 'Codeception\\Lib\\Driver\\Db' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/Driver/Db.php', 'Codeception\\Lib\\Driver\\Iron' => $vendorDir . '/codeception/module-queue/src/Codeception/Lib/Driver/Iron.php', 'Codeception\\Lib\\Driver\\MongoDb' => $vendorDir . '/codeception/module-mongodb/src/Codeception/Lib/Driver/MongoDb.php', 'Codeception\\Lib\\Driver\\MySql' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/Driver/MySql.php', 'Codeception\\Lib\\Driver\\Oci' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/Driver/Oci.php', 'Codeception\\Lib\\Driver\\Pheanstalk4' => $vendorDir . '/codeception/module-queue/src/Codeception/Lib/Driver/Pheanstalk4.php', 'Codeception\\Lib\\Driver\\PostgreSql' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/Driver/PostgreSql.php', 'Codeception\\Lib\\Driver\\SqlSrv' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/Driver/SqlSrv.php', 'Codeception\\Lib\\Driver\\Sqlite' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/Driver/Sqlite.php', 'Codeception\\Lib\\Framework' => $vendorDir . '/codeception/lib-innerbrowser/src/Codeception/Lib/Framework.php', 'Codeception\\Lib\\Friend' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Friend.php', 'Codeception\\Lib\\Generator\\Actions' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Actions.php', 'Codeception\\Lib\\Generator\\Actor' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Actor.php', 'Codeception\\Lib\\Generator\\Cept' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Cept.php', 'Codeception\\Lib\\Generator\\Cest' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Cest.php', 'Codeception\\Lib\\Generator\\Feature' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Feature.php', 'Codeception\\Lib\\Generator\\GherkinSnippets' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/GherkinSnippets.php', 'Codeception\\Lib\\Generator\\Group' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Group.php', 'Codeception\\Lib\\Generator\\Helper' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Helper.php', 'Codeception\\Lib\\Generator\\PageObject' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/PageObject.php', 'Codeception\\Lib\\Generator\\Shared\\Classname' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Shared/Classname.php', 'Codeception\\Lib\\Generator\\Snapshot' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Snapshot.php', 'Codeception\\Lib\\Generator\\StepObject' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/StepObject.php', 'Codeception\\Lib\\Generator\\Test' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Generator/Test.php', 'Codeception\\Lib\\GroupManager' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/GroupManager.php', 'Codeception\\Lib\\InnerBrowser' => $vendorDir . '/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.php', 'Codeception\\Lib\\Interfaces\\API' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/API.php', 'Codeception\\Lib\\Interfaces\\ActiveRecord' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/ActiveRecord.php', 'Codeception\\Lib\\Interfaces\\ConflictsWithModule' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/ConflictsWithModule.php', 'Codeception\\Lib\\Interfaces\\DataMapper' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/DataMapper.php', 'Codeception\\Lib\\Interfaces\\Db' => $vendorDir . '/codeception/module-db/src/Codeception/Lib/Interfaces/Db.php', 'Codeception\\Lib\\Interfaces\\DependsOnModule' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/DependsOnModule.php', 'Codeception\\Lib\\Interfaces\\DoctrineProvider' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/DoctrineProvider.php', 'Codeception\\Lib\\Interfaces\\ElementLocator' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/ElementLocator.php', 'Codeception\\Lib\\Interfaces\\MultiSession' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/MultiSession.php', 'Codeception\\Lib\\Interfaces\\ORM' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/ORM.php', 'Codeception\\Lib\\Interfaces\\PageSourceSaver' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/PageSourceSaver.php', 'Codeception\\Lib\\Interfaces\\PartedModule' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/PartedModule.php', 'Codeception\\Lib\\Interfaces\\Queue' => $vendorDir . '/codeception/module-queue/src/Codeception/Lib/Interfaces/Queue.php', 'Codeception\\Lib\\Interfaces\\Remote' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/Remote.php', 'Codeception\\Lib\\Interfaces\\RequiresPackage' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/RequiresPackage.php', 'Codeception\\Lib\\Interfaces\\ScreenshotSaver' => $vendorDir . '/codeception/module-webdriver/src/Codeception/Lib/Interfaces/ScreenshotSaver.php', 'Codeception\\Lib\\Interfaces\\SessionSnapshot' => $vendorDir . '/codeception/module-webdriver/src/Codeception/Lib/Interfaces/SessionSnapshot.php', 'Codeception\\Lib\\Interfaces\\Web' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/Web.php', 'Codeception\\Lib\\ModuleContainer' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/ModuleContainer.php', 'Codeception\\Lib\\Notification' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Notification.php', 'Codeception\\Lib\\ParamsLoader' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/ParamsLoader.php', 'Codeception\\Lib\\Parser' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Parser.php', 'Codeception\\Module' => $vendorDir . '/codeception/codeception/src/Codeception/Module.php', 'Codeception\\Module\\AMQP' => $vendorDir . '/codeception/module-amqp/src/Codeception/Module/AMQP.php', 'Codeception\\Module\\AbstractAsserts' => $vendorDir . '/codeception/module-asserts/src/Codeception/Module/AbstractAsserts.php', 'Codeception\\Module\\Apc' => $vendorDir . '/codeception/module-apc/src/Codeception/Module/Apc.php', 'Codeception\\Module\\Asserts' => $vendorDir . '/codeception/module-asserts/src/Codeception/Module/Asserts.php', 'Codeception\\Module\\Cli' => $vendorDir . '/codeception/module-cli/src/Codeception/Module/Cli.php', 'Codeception\\Module\\Db' => $vendorDir . '/codeception/module-db/src/Codeception/Module/Db.php', 'Codeception\\Module\\FTP' => $vendorDir . '/codeception/module-ftp/src/Codeception/Module/FTP.php', 'Codeception\\Module\\Filesystem' => $vendorDir . '/codeception/module-filesystem/src/Codeception/Module/Filesystem.php', 'Codeception\\Module\\Memcache' => $vendorDir . '/codeception/module-memcache/src/Codeception/Module/Memcache.php', 'Codeception\\Module\\MongoDb' => $vendorDir . '/codeception/module-mongodb/src/Codeception/Module/MongoDb.php', 'Codeception\\Module\\PhpBrowser' => $vendorDir . '/codeception/module-phpbrowser/src/Codeception/Module/PhpBrowser.php', 'Codeception\\Module\\Queue' => $vendorDir . '/codeception/module-queue/src/Codeception/Module/Queue.php', 'Codeception\\Module\\REST' => $vendorDir . '/codeception/module-rest/src/Codeception/Module/REST.php', 'Codeception\\Module\\Redis' => $vendorDir . '/codeception/module-redis/src/Codeception/Module/Redis.php', 'Codeception\\Module\\SOAP' => $vendorDir . '/codeception/module-soap/src/Codeception/Module/SOAP.php', 'Codeception\\Module\\Sequence' => $vendorDir . '/codeception/module-sequence/src/Codeception/Module/Sequence.php', 'Codeception\\Module\\WebDriver' => $vendorDir . '/codeception/module-webdriver/src/Codeception/Module/WebDriver.php', 'Codeception\\PHPUnit\\ConsolePrinter' => $vendorDir . '/codeception/phpunit-wrapper/src/ConsolePrinter.php', 'Codeception\\PHPUnit\\Constraint\\Crawler' => $vendorDir . '/codeception/phpunit-wrapper/src/Constraint/Crawler.php', 'Codeception\\PHPUnit\\Constraint\\CrawlerNot' => $vendorDir . '/codeception/phpunit-wrapper/src/Constraint/CrawlerNot.php', 'Codeception\\PHPUnit\\Constraint\\JsonContains' => $vendorDir . '/codeception/phpunit-wrapper/src/Constraint/JsonContains.php', 'Codeception\\PHPUnit\\Constraint\\JsonType' => $vendorDir . '/codeception/phpunit-wrapper/src/Constraint/JsonType.php', 'Codeception\\PHPUnit\\Constraint\\Page' => $vendorDir . '/codeception/phpunit-wrapper/src/Constraint/Page.php', 'Codeception\\PHPUnit\\Constraint\\WebDriver' => $vendorDir . '/codeception/phpunit-wrapper/src/Constraint/WebDriver.php', 'Codeception\\PHPUnit\\Constraint\\WebDriverNot' => $vendorDir . '/codeception/phpunit-wrapper/src/Constraint/WebDriverNot.php', 'Codeception\\PHPUnit\\DispatcherWrapper' => $vendorDir . '/codeception/phpunit-wrapper/src/DispatcherWrapper.php', 'Codeception\\PHPUnit\\FilterTest' => $vendorDir . '/codeception/phpunit-wrapper/src/FilterTest.php', 'Codeception\\PHPUnit\\Init' => $vendorDir . '/codeception/phpunit-wrapper/src/Init.php', 'Codeception\\PHPUnit\\Listener' => $vendorDir . '/codeception/phpunit-wrapper/src/Listener.php', 'Codeception\\PHPUnit\\Log\\JUnit' => $vendorDir . '/codeception/phpunit-wrapper/src/Log/JUnit.php', 'Codeception\\PHPUnit\\Log\\PhpUnit' => $vendorDir . '/codeception/phpunit-wrapper/src/Log/PhpUnit.php', 'Codeception\\PHPUnit\\ResultPrinter' => $vendorDir . '/codeception/phpunit-wrapper/src/ResultPrinter.php', 'Codeception\\PHPUnit\\ResultPrinter\\HTML' => $vendorDir . '/codeception/phpunit-wrapper/src/ResultPrinter/HTML.php', 'Codeception\\PHPUnit\\ResultPrinter\\Report' => $vendorDir . '/codeception/phpunit-wrapper/src/ResultPrinter/Report.php', 'Codeception\\PHPUnit\\ResultPrinter\\UI' => $vendorDir . '/codeception/phpunit-wrapper/src/ResultPrinter/UI.php', 'Codeception\\PHPUnit\\Runner' => $vendorDir . '/codeception/phpunit-wrapper/src/Runner.php', 'Codeception\\PHPUnit\\TestCase' => $vendorDir . '/codeception/phpunit-wrapper/src/TestCase.php', 'Codeception\\Scenario' => $vendorDir . '/codeception/codeception/src/Codeception/Scenario.php', 'Codeception\\Snapshot' => $vendorDir . '/codeception/codeception/src/Codeception/Snapshot.php', 'Codeception\\Step' => $vendorDir . '/codeception/codeception/src/Codeception/Step.php', 'Codeception\\Step\\Action' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Action.php', 'Codeception\\Step\\Argument\\FormattedOutput' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Argument/FormattedOutput.php', 'Codeception\\Step\\Argument\\PasswordArgument' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Argument/PasswordArgument.php', 'Codeception\\Step\\Assertion' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Assertion.php', 'Codeception\\Step\\Comment' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Comment.php', 'Codeception\\Step\\Condition' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Condition.php', 'Codeception\\Step\\ConditionalAssertion' => $vendorDir . '/codeception/codeception/src/Codeception/Step/ConditionalAssertion.php', 'Codeception\\Step\\Executor' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Executor.php', 'Codeception\\Step\\GeneratedStep' => $vendorDir . '/codeception/codeception/src/Codeception/Step/GeneratedStep.php', 'Codeception\\Step\\Incomplete' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Incomplete.php', 'Codeception\\Step\\Meta' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Meta.php', 'Codeception\\Step\\Retry' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Retry.php', 'Codeception\\Step\\Skip' => $vendorDir . '/codeception/codeception/src/Codeception/Step/Skip.php', 'Codeception\\Step\\TryTo' => $vendorDir . '/codeception/codeception/src/Codeception/Step/TryTo.php', 'Codeception\\Stub' => $vendorDir . '/codeception/stub/src/Stub.php', 'Codeception\\Stub\\ConsecutiveMap' => $vendorDir . '/codeception/stub/src/Stub/ConsecutiveMap.php', 'Codeception\\Stub\\Expected' => $vendorDir . '/codeception/stub/src/Stub/Expected.php', 'Codeception\\Stub\\StubMarshaler' => $vendorDir . '/codeception/stub/src/Stub/StubMarshaler.php', 'Codeception\\Subscriber\\AutoRebuild' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/AutoRebuild.php', 'Codeception\\Subscriber\\BeforeAfterTest' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/BeforeAfterTest.php', 'Codeception\\Subscriber\\Bootstrap' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/Bootstrap.php', 'Codeception\\Subscriber\\Console' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/Console.php', 'Codeception\\Subscriber\\Dependencies' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/Dependencies.php', 'Codeception\\Subscriber\\ErrorHandler' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/ErrorHandler.php', 'Codeception\\Subscriber\\ExtensionLoader' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/ExtensionLoader.php', 'Codeception\\Subscriber\\FailFast' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/FailFast.php', 'Codeception\\Subscriber\\GracefulTermination' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/GracefulTermination.php', 'Codeception\\Subscriber\\Module' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/Module.php', 'Codeception\\Subscriber\\PrepareTest' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/PrepareTest.php', 'Codeception\\Subscriber\\Shared\\StaticEvents' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/Shared/StaticEvents.php', 'Codeception\\Suite' => $vendorDir . '/codeception/codeception/src/Codeception/Suite.php', 'Codeception\\SuiteManager' => $vendorDir . '/codeception/codeception/src/Codeception/SuiteManager.php', 'Codeception\\Template\\Acceptance' => $vendorDir . '/codeception/codeception/src/Codeception/Template/Acceptance.php', 'Codeception\\Template\\Api' => $vendorDir . '/codeception/codeception/src/Codeception/Template/Api.php', 'Codeception\\Template\\Bootstrap' => $vendorDir . '/codeception/codeception/src/Codeception/Template/Bootstrap.php', 'Codeception\\Template\\Unit' => $vendorDir . '/codeception/codeception/src/Codeception/Template/Unit.php', 'Codeception\\Template\\Upgrade4' => $vendorDir . '/codeception/codeception/src/Codeception/Template/Upgrade4.php', 'Codeception\\TestInterface' => $vendorDir . '/codeception/codeception/src/Codeception/TestInterface.php', 'Codeception\\Test\\Cept' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Cept.php', 'Codeception\\Test\\Cest' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Cest.php', 'Codeception\\Test\\Descriptor' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Descriptor.php', 'Codeception\\Test\\Feature\\AssertionCounter' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Feature/AssertionCounter.php', 'Codeception\\Test\\Feature\\CodeCoverage' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Feature/CodeCoverage.php', 'Codeception\\Test\\Feature\\ErrorLogger' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Feature/ErrorLogger.php', 'Codeception\\Test\\Feature\\IgnoreIfMetadataBlocked' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Feature/IgnoreIfMetadataBlocked.php', 'Codeception\\Test\\Feature\\MetadataCollector' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Feature/MetadataCollector.php', 'Codeception\\Test\\Feature\\ScenarioLoader' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Feature/ScenarioLoader.php', 'Codeception\\Test\\Feature\\Stub' => $vendorDir . '/codeception/stub/src/Test/Feature/Stub.php', 'Codeception\\Test\\Gherkin' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Gherkin.php', 'Codeception\\Test\\Interfaces\\Dependent' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Interfaces/Dependent.php', 'Codeception\\Test\\Interfaces\\Descriptive' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Interfaces/Descriptive.php', 'Codeception\\Test\\Interfaces\\Plain' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Interfaces/Plain.php', 'Codeception\\Test\\Interfaces\\Reported' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Interfaces/Reported.php', 'Codeception\\Test\\Interfaces\\ScenarioDriven' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Interfaces/ScenarioDriven.php', 'Codeception\\Test\\Interfaces\\StrictCoverage' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Interfaces/StrictCoverage.php', 'Codeception\\Test\\Loader' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Loader.php', 'Codeception\\Test\\Loader\\Cept' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Loader/Cept.php', 'Codeception\\Test\\Loader\\Cest' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Loader/Cest.php', 'Codeception\\Test\\Loader\\Gherkin' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Loader/Gherkin.php', 'Codeception\\Test\\Loader\\LoaderInterface' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Loader/LoaderInterface.php', 'Codeception\\Test\\Loader\\Unit' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Loader/Unit.php', 'Codeception\\Test\\Metadata' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Metadata.php', 'Codeception\\Test\\Test' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Test.php', 'Codeception\\Test\\Unit' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Unit.php', 'Codeception\\Util\\ActionSequence' => $vendorDir . '/codeception/codeception/src/Codeception/Util/ActionSequence.php', 'Codeception\\Util\\Annotation' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Annotation.php', 'Codeception\\Util\\ArrayContainsComparator' => $vendorDir . '/codeception/codeception/src/Codeception/Util/ArrayContainsComparator.php', 'Codeception\\Util\\Autoload' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Autoload.php', 'Codeception\\Util\\Debug' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Debug.php', 'Codeception\\Util\\FileSystem' => $vendorDir . '/codeception/codeception/src/Codeception/Util/FileSystem.php', 'Codeception\\Util\\Fixtures' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Fixtures.php', 'Codeception\\Util\\HttpCode' => $vendorDir . '/codeception/lib-innerbrowser/src/Codeception/Util/HttpCode.php', 'Codeception\\Util\\JsonArray' => $vendorDir . '/codeception/module-rest/src/Codeception/Util/JsonArray.php', 'Codeception\\Util\\JsonType' => $vendorDir . '/codeception/module-rest/src/Codeception/Util/JsonType.php', 'Codeception\\Util\\Locator' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Locator.php', 'Codeception\\Util\\Maybe' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Maybe.php', 'Codeception\\Util\\PathResolver' => $vendorDir . '/codeception/codeception/src/Codeception/Util/PathResolver.php', 'Codeception\\Util\\ReflectionHelper' => $vendorDir . '/codeception/codeception/src/Codeception/Util/ReflectionHelper.php', 'Codeception\\Util\\ReflectionPropertyAccessor' => $vendorDir . '/codeception/codeception/src/Codeception/Util/ReflectionPropertyAccessor.php', 'Codeception\\Util\\Shared\\Asserts' => $vendorDir . '/codeception/lib-asserts/src/Codeception/Util/Shared/Asserts.php', 'Codeception\\Util\\Shared\\InheritedAsserts' => $vendorDir . '/codeception/lib-asserts/src/Codeception/Util/Shared/InheritedAsserts.php', 'Codeception\\Util\\Shared\\Namespaces' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Shared/Namespaces.php', 'Codeception\\Util\\Soap' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Soap.php', 'Codeception\\Util\\Stub' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Stub.php', 'Codeception\\Util\\Template' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Template.php', 'Codeception\\Util\\Uri' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Uri.php', 'Codeception\\Util\\Xml' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Xml.php', 'Codeception\\Util\\XmlBuilder' => $vendorDir . '/codeception/codeception/src/Codeception/Util/XmlBuilder.php', 'Codeception\\Util\\XmlStructure' => $vendorDir . '/codeception/codeception/src/Codeception/Util/XmlStructure.php', 'Composer\\CaBundle\\CaBundle' => $vendorDir . '/composer/ca-bundle/src/CaBundle.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'DeepCopy\\DeepCopy' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/DeepCopy.php', 'DeepCopy\\Exception\\CloneException' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php', 'DeepCopy\\Exception\\PropertyException' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php', 'DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', 'DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', 'DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', 'DeepCopy\\Filter\\Filter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php', 'DeepCopy\\Filter\\KeepFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php', 'DeepCopy\\Filter\\ReplaceFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php', 'DeepCopy\\Filter\\SetNullFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php', 'DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', 'DeepCopy\\Matcher\\Matcher' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php', 'DeepCopy\\Matcher\\PropertyMatcher' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php', 'DeepCopy\\Matcher\\PropertyNameMatcher' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php', 'DeepCopy\\Matcher\\PropertyTypeMatcher' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php', 'DeepCopy\\Reflection\\ReflectionHelper' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php', 'DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', 'DeepCopy\\TypeFilter\\ReplaceFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php', 'DeepCopy\\TypeFilter\\ShallowCopyFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php', 'DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', 'DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', 'DeepCopy\\TypeFilter\\TypeFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php', 'DeepCopy\\TypeMatcher\\TypeMatcher' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php', 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', 'Doctrine\\Instantiator\\Exception\\ExceptionInterface' => $vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php', 'Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => $vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php', 'Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => $vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php', 'Doctrine\\Instantiator\\Instantiator' => $vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php', 'Doctrine\\Instantiator\\InstantiatorInterface' => $vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php', 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'Facebook\\WebDriver\\AbstractWebDriverCheckboxOrRadio' => $vendorDir . '/php-webdriver/webdriver/lib/AbstractWebDriverCheckboxOrRadio.php', 'Facebook\\WebDriver\\Chrome\\ChromeDevToolsDriver' => $vendorDir . '/php-webdriver/webdriver/lib/Chrome/ChromeDevToolsDriver.php', 'Facebook\\WebDriver\\Chrome\\ChromeDriver' => $vendorDir . '/php-webdriver/webdriver/lib/Chrome/ChromeDriver.php', 'Facebook\\WebDriver\\Chrome\\ChromeDriverService' => $vendorDir . '/php-webdriver/webdriver/lib/Chrome/ChromeDriverService.php', 'Facebook\\WebDriver\\Chrome\\ChromeOptions' => $vendorDir . '/php-webdriver/webdriver/lib/Chrome/ChromeOptions.php', 'Facebook\\WebDriver\\Cookie' => $vendorDir . '/php-webdriver/webdriver/lib/Cookie.php', 'Facebook\\WebDriver\\Exception\\DriverServerDiedException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/DriverServerDiedException.php', 'Facebook\\WebDriver\\Exception\\ElementClickInterceptedException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/ElementClickInterceptedException.php', 'Facebook\\WebDriver\\Exception\\ElementNotInteractableException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/ElementNotInteractableException.php', 'Facebook\\WebDriver\\Exception\\ElementNotSelectableException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/ElementNotSelectableException.php', 'Facebook\\WebDriver\\Exception\\ElementNotVisibleException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/ElementNotVisibleException.php', 'Facebook\\WebDriver\\Exception\\ExpectedException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/ExpectedException.php', 'Facebook\\WebDriver\\Exception\\IMEEngineActivationFailedException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/IMEEngineActivationFailedException.php', 'Facebook\\WebDriver\\Exception\\IMENotAvailableException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/IMENotAvailableException.php', 'Facebook\\WebDriver\\Exception\\IndexOutOfBoundsException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/IndexOutOfBoundsException.php', 'Facebook\\WebDriver\\Exception\\InsecureCertificateException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/InsecureCertificateException.php', 'Facebook\\WebDriver\\Exception\\InvalidArgumentException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/InvalidArgumentException.php', 'Facebook\\WebDriver\\Exception\\InvalidCookieDomainException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/InvalidCookieDomainException.php', 'Facebook\\WebDriver\\Exception\\InvalidCoordinatesException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/InvalidCoordinatesException.php', 'Facebook\\WebDriver\\Exception\\InvalidElementStateException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/InvalidElementStateException.php', 'Facebook\\WebDriver\\Exception\\InvalidSelectorException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/InvalidSelectorException.php', 'Facebook\\WebDriver\\Exception\\InvalidSessionIdException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/InvalidSessionIdException.php', 'Facebook\\WebDriver\\Exception\\JavascriptErrorException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/JavascriptErrorException.php', 'Facebook\\WebDriver\\Exception\\MoveTargetOutOfBoundsException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/MoveTargetOutOfBoundsException.php', 'Facebook\\WebDriver\\Exception\\NoAlertOpenException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoAlertOpenException.php', 'Facebook\\WebDriver\\Exception\\NoCollectionException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoCollectionException.php', 'Facebook\\WebDriver\\Exception\\NoScriptResultException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoScriptResultException.php', 'Facebook\\WebDriver\\Exception\\NoStringException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoStringException.php', 'Facebook\\WebDriver\\Exception\\NoStringLengthException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoStringLengthException.php', 'Facebook\\WebDriver\\Exception\\NoStringWrapperException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoStringWrapperException.php', 'Facebook\\WebDriver\\Exception\\NoSuchAlertException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchAlertException.php', 'Facebook\\WebDriver\\Exception\\NoSuchCollectionException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchCollectionException.php', 'Facebook\\WebDriver\\Exception\\NoSuchCookieException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchCookieException.php', 'Facebook\\WebDriver\\Exception\\NoSuchDocumentException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchDocumentException.php', 'Facebook\\WebDriver\\Exception\\NoSuchDriverException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchDriverException.php', 'Facebook\\WebDriver\\Exception\\NoSuchElementException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchElementException.php', 'Facebook\\WebDriver\\Exception\\NoSuchFrameException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchFrameException.php', 'Facebook\\WebDriver\\Exception\\NoSuchWindowException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchWindowException.php', 'Facebook\\WebDriver\\Exception\\NullPointerException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NullPointerException.php', 'Facebook\\WebDriver\\Exception\\ScriptTimeoutException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/ScriptTimeoutException.php', 'Facebook\\WebDriver\\Exception\\SessionNotCreatedException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/SessionNotCreatedException.php', 'Facebook\\WebDriver\\Exception\\StaleElementReferenceException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/StaleElementReferenceException.php', 'Facebook\\WebDriver\\Exception\\TimeoutException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php', 'Facebook\\WebDriver\\Exception\\UnableToCaptureScreenException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnableToCaptureScreenException.php', 'Facebook\\WebDriver\\Exception\\UnableToSetCookieException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnableToSetCookieException.php', 'Facebook\\WebDriver\\Exception\\UnexpectedAlertOpenException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnexpectedAlertOpenException.php', 'Facebook\\WebDriver\\Exception\\UnexpectedJavascriptException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnexpectedJavascriptException.php', 'Facebook\\WebDriver\\Exception\\UnexpectedTagNameException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnexpectedTagNameException.php', 'Facebook\\WebDriver\\Exception\\UnknownCommandException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnknownCommandException.php', 'Facebook\\WebDriver\\Exception\\UnknownErrorException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnknownErrorException.php', 'Facebook\\WebDriver\\Exception\\UnknownMethodException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnknownMethodException.php', 'Facebook\\WebDriver\\Exception\\UnknownServerException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnknownServerException.php', 'Facebook\\WebDriver\\Exception\\UnrecognizedExceptionException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnrecognizedExceptionException.php', 'Facebook\\WebDriver\\Exception\\UnsupportedOperationException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/UnsupportedOperationException.php', 'Facebook\\WebDriver\\Exception\\WebDriverCurlException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/WebDriverCurlException.php', 'Facebook\\WebDriver\\Exception\\WebDriverException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/WebDriverException.php', 'Facebook\\WebDriver\\Exception\\XPathLookupException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/XPathLookupException.php', 'Facebook\\WebDriver\\Firefox\\FirefoxDriver' => $vendorDir . '/php-webdriver/webdriver/lib/Firefox/FirefoxDriver.php', 'Facebook\\WebDriver\\Firefox\\FirefoxDriverService' => $vendorDir . '/php-webdriver/webdriver/lib/Firefox/FirefoxDriverService.php', 'Facebook\\WebDriver\\Firefox\\FirefoxOptions' => $vendorDir . '/php-webdriver/webdriver/lib/Firefox/FirefoxOptions.php', 'Facebook\\WebDriver\\Firefox\\FirefoxPreferences' => $vendorDir . '/php-webdriver/webdriver/lib/Firefox/FirefoxPreferences.php', 'Facebook\\WebDriver\\Firefox\\FirefoxProfile' => $vendorDir . '/php-webdriver/webdriver/lib/Firefox/FirefoxProfile.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverButtonReleaseAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverButtonReleaseAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverClickAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverClickAndHoldAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAndHoldAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverContextClickAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverContextClickAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverCoordinates' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverCoordinates.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverDoubleClickAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverDoubleClickAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverKeyDownAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyDownAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverKeyUpAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyUpAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverKeysRelatedAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeysRelatedAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverMouseAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverMouseMoveAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseMoveAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverMoveToOffsetAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverSendKeysAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSendKeysAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverSingleKeyAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSingleKeyAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverDoubleTapAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDoubleTapAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverDownAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDownAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverFlickAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverFlickFromElementAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickFromElementAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverLongPressAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverLongPressAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverMoveAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverMoveAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverScrollAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverScrollFromElementAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollFromElementAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverTapAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTapAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverTouchAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverTouchScreen' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchScreen.php', 'Facebook\\WebDriver\\Interactions\\WebDriverActions' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/WebDriverActions.php', 'Facebook\\WebDriver\\Interactions\\WebDriverCompositeAction' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/WebDriverCompositeAction.php', 'Facebook\\WebDriver\\Interactions\\WebDriverTouchActions' => $vendorDir . '/php-webdriver/webdriver/lib/Interactions/WebDriverTouchActions.php', 'Facebook\\WebDriver\\Internal\\WebDriverLocatable' => $vendorDir . '/php-webdriver/webdriver/lib/Internal/WebDriverLocatable.php', 'Facebook\\WebDriver\\JavaScriptExecutor' => $vendorDir . '/php-webdriver/webdriver/lib/JavaScriptExecutor.php', 'Facebook\\WebDriver\\Local\\LocalWebDriver' => $vendorDir . '/php-webdriver/webdriver/lib/Local/LocalWebDriver.php', 'Facebook\\WebDriver\\Net\\URLChecker' => $vendorDir . '/php-webdriver/webdriver/lib/Net/URLChecker.php', 'Facebook\\WebDriver\\Remote\\CustomWebDriverCommand' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/CustomWebDriverCommand.php', 'Facebook\\WebDriver\\Remote\\DesiredCapabilities' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/DesiredCapabilities.php', 'Facebook\\WebDriver\\Remote\\DriverCommand' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/DriverCommand.php', 'Facebook\\WebDriver\\Remote\\ExecuteMethod' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/ExecuteMethod.php', 'Facebook\\WebDriver\\Remote\\FileDetector' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/FileDetector.php', 'Facebook\\WebDriver\\Remote\\HttpCommandExecutor' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/HttpCommandExecutor.php', 'Facebook\\WebDriver\\Remote\\JsonWireCompat' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/JsonWireCompat.php', 'Facebook\\WebDriver\\Remote\\LocalFileDetector' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/LocalFileDetector.php', 'Facebook\\WebDriver\\Remote\\RemoteExecuteMethod' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteExecuteMethod.php', 'Facebook\\WebDriver\\Remote\\RemoteKeyboard' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteKeyboard.php', 'Facebook\\WebDriver\\Remote\\RemoteMouse' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteMouse.php', 'Facebook\\WebDriver\\Remote\\RemoteStatus' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteStatus.php', 'Facebook\\WebDriver\\Remote\\RemoteTargetLocator' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteTargetLocator.php', 'Facebook\\WebDriver\\Remote\\RemoteTouchScreen' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteTouchScreen.php', 'Facebook\\WebDriver\\Remote\\RemoteWebDriver' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteWebDriver.php', 'Facebook\\WebDriver\\Remote\\RemoteWebElement' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/RemoteWebElement.php', 'Facebook\\WebDriver\\Remote\\Service\\DriverCommandExecutor' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/Service/DriverCommandExecutor.php', 'Facebook\\WebDriver\\Remote\\Service\\DriverService' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/Service/DriverService.php', 'Facebook\\WebDriver\\Remote\\UselessFileDetector' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/UselessFileDetector.php', 'Facebook\\WebDriver\\Remote\\WebDriverBrowserType' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/WebDriverBrowserType.php', 'Facebook\\WebDriver\\Remote\\WebDriverCapabilityType' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/WebDriverCapabilityType.php', 'Facebook\\WebDriver\\Remote\\WebDriverCommand' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/WebDriverCommand.php', 'Facebook\\WebDriver\\Remote\\WebDriverResponse' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/WebDriverResponse.php', 'Facebook\\WebDriver\\Support\\Events\\EventFiringWebDriver' => $vendorDir . '/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriver.php', 'Facebook\\WebDriver\\Support\\Events\\EventFiringWebDriverNavigation' => $vendorDir . '/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriverNavigation.php', 'Facebook\\WebDriver\\Support\\Events\\EventFiringWebElement' => $vendorDir . '/php-webdriver/webdriver/lib/Support/Events/EventFiringWebElement.php', 'Facebook\\WebDriver\\Support\\XPathEscaper' => $vendorDir . '/php-webdriver/webdriver/lib/Support/XPathEscaper.php', 'Facebook\\WebDriver\\WebDriver' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriver.php', 'Facebook\\WebDriver\\WebDriverAction' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverAction.php', 'Facebook\\WebDriver\\WebDriverAlert' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverAlert.php', 'Facebook\\WebDriver\\WebDriverBy' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverBy.php', 'Facebook\\WebDriver\\WebDriverCapabilities' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverCapabilities.php', 'Facebook\\WebDriver\\WebDriverCheckboxes' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverCheckboxes.php', 'Facebook\\WebDriver\\WebDriverCommandExecutor' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverCommandExecutor.php', 'Facebook\\WebDriver\\WebDriverDimension' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverDimension.php', 'Facebook\\WebDriver\\WebDriverDispatcher' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverDispatcher.php', 'Facebook\\WebDriver\\WebDriverElement' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverElement.php', 'Facebook\\WebDriver\\WebDriverEventListener' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverEventListener.php', 'Facebook\\WebDriver\\WebDriverExpectedCondition' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverExpectedCondition.php', 'Facebook\\WebDriver\\WebDriverHasInputDevices' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverHasInputDevices.php', 'Facebook\\WebDriver\\WebDriverKeyboard' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverKeyboard.php', 'Facebook\\WebDriver\\WebDriverKeys' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverKeys.php', 'Facebook\\WebDriver\\WebDriverMouse' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverMouse.php', 'Facebook\\WebDriver\\WebDriverNavigation' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverNavigation.php', 'Facebook\\WebDriver\\WebDriverNavigationInterface' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverNavigationInterface.php', 'Facebook\\WebDriver\\WebDriverOptions' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverOptions.php', 'Facebook\\WebDriver\\WebDriverPlatform' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverPlatform.php', 'Facebook\\WebDriver\\WebDriverPoint' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverPoint.php', 'Facebook\\WebDriver\\WebDriverRadios' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverRadios.php', 'Facebook\\WebDriver\\WebDriverSearchContext' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverSearchContext.php', 'Facebook\\WebDriver\\WebDriverSelect' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverSelect.php', 'Facebook\\WebDriver\\WebDriverSelectInterface' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverSelectInterface.php', 'Facebook\\WebDriver\\WebDriverTargetLocator' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverTargetLocator.php', 'Facebook\\WebDriver\\WebDriverTimeouts' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverTimeouts.php', 'Facebook\\WebDriver\\WebDriverUpAction' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverUpAction.php', 'Facebook\\WebDriver\\WebDriverWait' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverWait.php', 'Facebook\\WebDriver\\WebDriverWindow' => $vendorDir . '/php-webdriver/webdriver/lib/WebDriverWindow.php', 'File_Iterator' => $vendorDir . '/phpunit/php-file-iterator/src/Iterator.php', 'File_Iterator_Facade' => $vendorDir . '/phpunit/php-file-iterator/src/Facade.php', 'File_Iterator_Factory' => $vendorDir . '/phpunit/php-file-iterator/src/Factory.php', 'Flow\\JSONPath\\AccessHelper' => $vendorDir . '/softcreatr/jsonpath/src/AccessHelper.php', 'Flow\\JSONPath\\Filters\\AbstractFilter' => $vendorDir . '/softcreatr/jsonpath/src/Filters/AbstractFilter.php', 'Flow\\JSONPath\\Filters\\IndexFilter' => $vendorDir . '/softcreatr/jsonpath/src/Filters/IndexFilter.php', 'Flow\\JSONPath\\Filters\\IndexesFilter' => $vendorDir . '/softcreatr/jsonpath/src/Filters/IndexesFilter.php', 'Flow\\JSONPath\\Filters\\QueryMatchFilter' => $vendorDir . '/softcreatr/jsonpath/src/Filters/QueryMatchFilter.php', 'Flow\\JSONPath\\Filters\\QueryResultFilter' => $vendorDir . '/softcreatr/jsonpath/src/Filters/QueryResultFilter.php', 'Flow\\JSONPath\\Filters\\RecursiveFilter' => $vendorDir . '/softcreatr/jsonpath/src/Filters/RecursiveFilter.php', 'Flow\\JSONPath\\Filters\\SliceFilter' => $vendorDir . '/softcreatr/jsonpath/src/Filters/SliceFilter.php', 'Flow\\JSONPath\\JSONPath' => $vendorDir . '/softcreatr/jsonpath/src/JSONPath.php', 'Flow\\JSONPath\\JSONPathException' => $vendorDir . '/softcreatr/jsonpath/src/JSONPathException.php', 'Flow\\JSONPath\\JSONPathLexer' => $vendorDir . '/softcreatr/jsonpath/src/JSONPathLexer.php', 'Flow\\JSONPath\\JSONPathToken' => $vendorDir . '/softcreatr/jsonpath/src/JSONPathToken.php', 'GuzzleHttp\\Client' => $vendorDir . '/guzzlehttp/guzzle/src/Client.php', 'GuzzleHttp\\ClientInterface' => $vendorDir . '/guzzlehttp/guzzle/src/ClientInterface.php', 'GuzzleHttp\\Cookie\\CookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php', 'GuzzleHttp\\Cookie\\CookieJarInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php', 'GuzzleHttp\\Cookie\\FileCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php', 'GuzzleHttp\\Cookie\\SessionCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php', 'GuzzleHttp\\Cookie\\SetCookie' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php', 'GuzzleHttp\\Exception\\BadResponseException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php', 'GuzzleHttp\\Exception\\ClientException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ClientException.php', 'GuzzleHttp\\Exception\\ConnectException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ConnectException.php', 'GuzzleHttp\\Exception\\GuzzleException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/GuzzleException.php', 'GuzzleHttp\\Exception\\InvalidArgumentException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.php', 'GuzzleHttp\\Exception\\RequestException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/RequestException.php', 'GuzzleHttp\\Exception\\SeekException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/SeekException.php', 'GuzzleHttp\\Exception\\ServerException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ServerException.php', 'GuzzleHttp\\Exception\\TooManyRedirectsException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php', 'GuzzleHttp\\Exception\\TransferException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TransferException.php', 'GuzzleHttp\\HandlerStack' => $vendorDir . '/guzzlehttp/guzzle/src/HandlerStack.php', 'GuzzleHttp\\Handler\\CurlFactory' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlFactory.php', 'GuzzleHttp\\Handler\\CurlFactoryInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php', 'GuzzleHttp\\Handler\\CurlHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php', 'GuzzleHttp\\Handler\\CurlMultiHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php', 'GuzzleHttp\\Handler\\EasyHandle' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php', 'GuzzleHttp\\Handler\\MockHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/MockHandler.php', 'GuzzleHttp\\Handler\\Proxy' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/Proxy.php', 'GuzzleHttp\\Handler\\StreamHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php', 'GuzzleHttp\\MessageFormatter' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatter.php', 'GuzzleHttp\\Middleware' => $vendorDir . '/guzzlehttp/guzzle/src/Middleware.php', 'GuzzleHttp\\Pool' => $vendorDir . '/guzzlehttp/guzzle/src/Pool.php', 'GuzzleHttp\\PrepareBodyMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php', 'GuzzleHttp\\Promise\\AggregateException' => $vendorDir . '/guzzlehttp/promises/src/AggregateException.php', 'GuzzleHttp\\Promise\\CancellationException' => $vendorDir . '/guzzlehttp/promises/src/CancellationException.php', 'GuzzleHttp\\Promise\\Coroutine' => $vendorDir . '/guzzlehttp/promises/src/Coroutine.php', 'GuzzleHttp\\Promise\\Create' => $vendorDir . '/guzzlehttp/promises/src/Create.php', 'GuzzleHttp\\Promise\\Each' => $vendorDir . '/guzzlehttp/promises/src/Each.php', 'GuzzleHttp\\Promise\\EachPromise' => $vendorDir . '/guzzlehttp/promises/src/EachPromise.php', 'GuzzleHttp\\Promise\\FulfilledPromise' => $vendorDir . '/guzzlehttp/promises/src/FulfilledPromise.php', 'GuzzleHttp\\Promise\\Is' => $vendorDir . '/guzzlehttp/promises/src/Is.php', 'GuzzleHttp\\Promise\\Promise' => $vendorDir . '/guzzlehttp/promises/src/Promise.php', 'GuzzleHttp\\Promise\\PromiseInterface' => $vendorDir . '/guzzlehttp/promises/src/PromiseInterface.php', 'GuzzleHttp\\Promise\\PromisorInterface' => $vendorDir . '/guzzlehttp/promises/src/PromisorInterface.php', 'GuzzleHttp\\Promise\\RejectedPromise' => $vendorDir . '/guzzlehttp/promises/src/RejectedPromise.php', 'GuzzleHttp\\Promise\\RejectionException' => $vendorDir . '/guzzlehttp/promises/src/RejectionException.php', 'GuzzleHttp\\Promise\\TaskQueue' => $vendorDir . '/guzzlehttp/promises/src/TaskQueue.php', 'GuzzleHttp\\Promise\\TaskQueueInterface' => $vendorDir . '/guzzlehttp/promises/src/TaskQueueInterface.php', 'GuzzleHttp\\Promise\\Utils' => $vendorDir . '/guzzlehttp/promises/src/Utils.php', 'GuzzleHttp\\Psr7\\AppendStream' => $vendorDir . '/guzzlehttp/psr7/src/AppendStream.php', 'GuzzleHttp\\Psr7\\BufferStream' => $vendorDir . '/guzzlehttp/psr7/src/BufferStream.php', 'GuzzleHttp\\Psr7\\CachingStream' => $vendorDir . '/guzzlehttp/psr7/src/CachingStream.php', 'GuzzleHttp\\Psr7\\DroppingStream' => $vendorDir . '/guzzlehttp/psr7/src/DroppingStream.php', 'GuzzleHttp\\Psr7\\FnStream' => $vendorDir . '/guzzlehttp/psr7/src/FnStream.php', 'GuzzleHttp\\Psr7\\Header' => $vendorDir . '/guzzlehttp/psr7/src/Header.php', 'GuzzleHttp\\Psr7\\InflateStream' => $vendorDir . '/guzzlehttp/psr7/src/InflateStream.php', 'GuzzleHttp\\Psr7\\LazyOpenStream' => $vendorDir . '/guzzlehttp/psr7/src/LazyOpenStream.php', 'GuzzleHttp\\Psr7\\LimitStream' => $vendorDir . '/guzzlehttp/psr7/src/LimitStream.php', 'GuzzleHttp\\Psr7\\Message' => $vendorDir . '/guzzlehttp/psr7/src/Message.php', 'GuzzleHttp\\Psr7\\MessageTrait' => $vendorDir . '/guzzlehttp/psr7/src/MessageTrait.php', 'GuzzleHttp\\Psr7\\MimeType' => $vendorDir . '/guzzlehttp/psr7/src/MimeType.php', 'GuzzleHttp\\Psr7\\MultipartStream' => $vendorDir . '/guzzlehttp/psr7/src/MultipartStream.php', 'GuzzleHttp\\Psr7\\NoSeekStream' => $vendorDir . '/guzzlehttp/psr7/src/NoSeekStream.php', 'GuzzleHttp\\Psr7\\PumpStream' => $vendorDir . '/guzzlehttp/psr7/src/PumpStream.php', 'GuzzleHttp\\Psr7\\Query' => $vendorDir . '/guzzlehttp/psr7/src/Query.php', 'GuzzleHttp\\Psr7\\Request' => $vendorDir . '/guzzlehttp/psr7/src/Request.php', 'GuzzleHttp\\Psr7\\Response' => $vendorDir . '/guzzlehttp/psr7/src/Response.php', 'GuzzleHttp\\Psr7\\Rfc7230' => $vendorDir . '/guzzlehttp/psr7/src/Rfc7230.php', 'GuzzleHttp\\Psr7\\ServerRequest' => $vendorDir . '/guzzlehttp/psr7/src/ServerRequest.php', 'GuzzleHttp\\Psr7\\Stream' => $vendorDir . '/guzzlehttp/psr7/src/Stream.php', 'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => $vendorDir . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php', 'GuzzleHttp\\Psr7\\StreamWrapper' => $vendorDir . '/guzzlehttp/psr7/src/StreamWrapper.php', 'GuzzleHttp\\Psr7\\UploadedFile' => $vendorDir . '/guzzlehttp/psr7/src/UploadedFile.php', 'GuzzleHttp\\Psr7\\Uri' => $vendorDir . '/guzzlehttp/psr7/src/Uri.php', 'GuzzleHttp\\Psr7\\UriComparator' => $vendorDir . '/guzzlehttp/psr7/src/UriComparator.php', 'GuzzleHttp\\Psr7\\UriNormalizer' => $vendorDir . '/guzzlehttp/psr7/src/UriNormalizer.php', 'GuzzleHttp\\Psr7\\UriResolver' => $vendorDir . '/guzzlehttp/psr7/src/UriResolver.php', 'GuzzleHttp\\Psr7\\Utils' => $vendorDir . '/guzzlehttp/psr7/src/Utils.php', 'GuzzleHttp\\RedirectMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RedirectMiddleware.php', 'GuzzleHttp\\RequestOptions' => $vendorDir . '/guzzlehttp/guzzle/src/RequestOptions.php', 'GuzzleHttp\\RetryMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RetryMiddleware.php', 'GuzzleHttp\\TransferStats' => $vendorDir . '/guzzlehttp/guzzle/src/TransferStats.php', 'GuzzleHttp\\UriTemplate' => $vendorDir . '/guzzlehttp/guzzle/src/UriTemplate.php', 'GuzzleHttp\\Utils' => $vendorDir . '/guzzlehttp/guzzle/src/Utils.php', 'Hoa\\Consistency\\Autoloader' => $vendorDir . '/hoa/consistency/Autoloader.php', 'Hoa\\Consistency\\Consistency' => $vendorDir . '/hoa/consistency/Consistency.php', 'Hoa\\Consistency\\Exception' => $vendorDir . '/hoa/consistency/Exception.php', 'Hoa\\Consistency\\Test\\Unit\\Autoloader' => $vendorDir . '/hoa/consistency/Test/Unit/Autoloader.php', 'Hoa\\Consistency\\Test\\Unit\\Consistency' => $vendorDir . '/hoa/consistency/Test/Unit/Consistency.php', 'Hoa\\Consistency\\Test\\Unit\\Exception' => $vendorDir . '/hoa/consistency/Test/Unit/Exception.php', 'Hoa\\Consistency\\Test\\Unit\\Xcallable' => $vendorDir . '/hoa/consistency/Test/Unit/Xcallable.php', 'Hoa\\Consistency\\Xcallable' => $vendorDir . '/hoa/consistency/Xcallable.php', 'Hoa\\Console\\Bin\\Termcap' => $vendorDir . '/hoa/console/Bin/Termcap.php', 'Hoa\\Console\\Chrome\\Editor' => $vendorDir . '/hoa/console/Chrome/Editor.php', 'Hoa\\Console\\Chrome\\Exception' => $vendorDir . '/hoa/console/Chrome/Exception.php', 'Hoa\\Console\\Chrome\\Pager' => $vendorDir . '/hoa/console/Chrome/Pager.php', 'Hoa\\Console\\Chrome\\Text' => $vendorDir . '/hoa/console/Chrome/Text.php', 'Hoa\\Console\\Console' => $vendorDir . '/hoa/console/Console.php', 'Hoa\\Console\\Cursor' => $vendorDir . '/hoa/console/Cursor.php', 'Hoa\\Console\\Dispatcher\\Kit' => $vendorDir . '/hoa/console/Dispatcher/Kit.php', 'Hoa\\Console\\Exception' => $vendorDir . '/hoa/console/Exception.php', 'Hoa\\Console\\GetOption' => $vendorDir . '/hoa/console/GetOption.php', 'Hoa\\Console\\Input' => $vendorDir . '/hoa/console/Input.php', 'Hoa\\Console\\Mouse' => $vendorDir . '/hoa/console/Mouse.php', 'Hoa\\Console\\Output' => $vendorDir . '/hoa/console/Output.php', 'Hoa\\Console\\Parser' => $vendorDir . '/hoa/console/Parser.php', 'Hoa\\Console\\Processus' => $vendorDir . '/hoa/console/Processus.php', 'Hoa\\Console\\Readline\\Autocompleter\\Aggregate' => $vendorDir . '/hoa/console/Readline/Autocompleter/Aggregate.php', 'Hoa\\Console\\Readline\\Autocompleter\\Autocompleter' => $vendorDir . '/hoa/console/Readline/Autocompleter/Autocompleter.php', 'Hoa\\Console\\Readline\\Autocompleter\\Path' => $vendorDir . '/hoa/console/Readline/Autocompleter/Path.php', 'Hoa\\Console\\Readline\\Autocompleter\\Word' => $vendorDir . '/hoa/console/Readline/Autocompleter/Word.php', 'Hoa\\Console\\Readline\\Password' => $vendorDir . '/hoa/console/Readline/Password.php', 'Hoa\\Console\\Readline\\Readline' => $vendorDir . '/hoa/console/Readline/Readline.php', 'Hoa\\Console\\Test\\Unit\\Console' => $vendorDir . '/hoa/console/Test/Unit/Console.php', 'Hoa\\Console\\Test\\Unit\\Cursor' => $vendorDir . '/hoa/console/Test/Unit/Cursor.php', 'Hoa\\Console\\Test\\Unit\\GetOption' => $vendorDir . '/hoa/console/Test/Unit/GetOption.php', 'Hoa\\Console\\Test\\Unit\\Input' => $vendorDir . '/hoa/console/Test/Unit/Input.php', 'Hoa\\Console\\Test\\Unit\\Mouse' => $vendorDir . '/hoa/console/Test/Unit/Mouse.php', 'Hoa\\Console\\Test\\Unit\\Output' => $vendorDir . '/hoa/console/Test/Unit/Output.php', 'Hoa\\Console\\Test\\Unit\\Parser' => $vendorDir . '/hoa/console/Test/Unit/Parser.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Autocompleter\\Aggregate' => $vendorDir . '/hoa/console/Test/Unit/Readline/Autocompleter/Aggregate.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Autocompleter\\Path' => $vendorDir . '/hoa/console/Test/Unit/Readline/Autocompleter/Path.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Autocompleter\\Word' => $vendorDir . '/hoa/console/Test/Unit/Readline/Autocompleter/Word.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Password' => $vendorDir . '/hoa/console/Test/Unit/Readline/Password.php', 'Hoa\\Console\\Test\\Unit\\Tput' => $vendorDir . '/hoa/console/Test/Unit/Tput.php', 'Hoa\\Console\\Test\\Unit\\Window' => $vendorDir . '/hoa/console/Test/Unit/Window.php', 'Hoa\\Console\\Tput' => $vendorDir . '/hoa/console/Tput.php', 'Hoa\\Console\\Window' => $vendorDir . '/hoa/console/Window.php', 'Hoa\\Event\\Bucket' => $vendorDir . '/hoa/event/Bucket.php', 'Hoa\\Event\\Event' => $vendorDir . '/hoa/event/Event.php', 'Hoa\\Event\\Exception' => $vendorDir . '/hoa/event/Exception.php', 'Hoa\\Event\\Listenable' => $vendorDir . '/hoa/event/Listenable.php', 'Hoa\\Event\\Listener' => $vendorDir . '/hoa/event/Listener.php', 'Hoa\\Event\\Listens' => $vendorDir . '/hoa/event/Listens.php', 'Hoa\\Event\\Source' => $vendorDir . '/hoa/event/Source.php', 'Hoa\\Event\\Test\\Unit\\Bucket' => $vendorDir . '/hoa/event/Test/Unit/Bucket.php', 'Hoa\\Event\\Test\\Unit\\Event' => $vendorDir . '/hoa/event/Test/Unit/Event.php', 'Hoa\\Event\\Test\\Unit\\Exception' => $vendorDir . '/hoa/event/Test/Unit/Exception.php', 'Hoa\\Event\\Test\\Unit\\Listenable' => $vendorDir . '/hoa/event/Test/Unit/Listenable.php', 'Hoa\\Event\\Test\\Unit\\Listener' => $vendorDir . '/hoa/event/Test/Unit/Listener.php', 'Hoa\\Event\\Test\\Unit\\Listens' => $vendorDir . '/hoa/event/Test/Unit/Listens.php', 'Hoa\\Event\\Test\\Unit\\Source' => $vendorDir . '/hoa/event/Test/Unit/Source.php', 'Hoa\\Exception\\Error' => $vendorDir . '/hoa/exception/Error.php', 'Hoa\\Exception\\Exception' => $vendorDir . '/hoa/exception/Exception.php', 'Hoa\\Exception\\Group' => $vendorDir . '/hoa/exception/Group.php', 'Hoa\\Exception\\Idle' => $vendorDir . '/hoa/exception/Idle.php', 'Hoa\\Exception\\Test\\Unit\\Error' => $vendorDir . '/hoa/exception/Test/Unit/Error.php', 'Hoa\\Exception\\Test\\Unit\\Exception' => $vendorDir . '/hoa/exception/Test/Unit/Exception.php', 'Hoa\\Exception\\Test\\Unit\\Group' => $vendorDir . '/hoa/exception/Test/Unit/Group.php', 'Hoa\\Exception\\Test\\Unit\\Idle' => $vendorDir . '/hoa/exception/Test/Unit/Idle.php', 'Hoa\\File\\Directory' => $vendorDir . '/hoa/file/Directory.php', 'Hoa\\File\\Exception\\Exception' => $vendorDir . '/hoa/file/Exception/Exception.php', 'Hoa\\File\\Exception\\FileDoesNotExist' => $vendorDir . '/hoa/file/Exception/FileDoesNotExist.php', 'Hoa\\File\\File' => $vendorDir . '/hoa/file/File.php', 'Hoa\\File\\Finder' => $vendorDir . '/hoa/file/Finder.php', 'Hoa\\File\\Generic' => $vendorDir . '/hoa/file/Generic.php', 'Hoa\\File\\Link\\Link' => $vendorDir . '/hoa/file/Link/Link.php', 'Hoa\\File\\Link\\Read' => $vendorDir . '/hoa/file/Link/Read.php', 'Hoa\\File\\Link\\ReadWrite' => $vendorDir . '/hoa/file/Link/ReadWrite.php', 'Hoa\\File\\Link\\Write' => $vendorDir . '/hoa/file/Link/Write.php', 'Hoa\\File\\Read' => $vendorDir . '/hoa/file/Read.php', 'Hoa\\File\\ReadWrite' => $vendorDir . '/hoa/file/ReadWrite.php', 'Hoa\\File\\SplFileInfo' => $vendorDir . '/hoa/file/SplFileInfo.php', 'Hoa\\File\\Temporary\\Read' => $vendorDir . '/hoa/file/Temporary/Read.php', 'Hoa\\File\\Temporary\\ReadWrite' => $vendorDir . '/hoa/file/Temporary/ReadWrite.php', 'Hoa\\File\\Temporary\\Temporary' => $vendorDir . '/hoa/file/Temporary/Temporary.php', 'Hoa\\File\\Temporary\\Write' => $vendorDir . '/hoa/file/Temporary/Write.php', 'Hoa\\File\\Watcher' => $vendorDir . '/hoa/file/Watcher.php', 'Hoa\\File\\Write' => $vendorDir . '/hoa/file/Write.php', 'Hoa\\Iterator\\Aggregate' => $vendorDir . '/hoa/iterator/Aggregate.php', 'Hoa\\Iterator\\Append' => $vendorDir . '/hoa/iterator/Append.php', 'Hoa\\Iterator\\Buffer' => $vendorDir . '/hoa/iterator/Buffer.php', 'Hoa\\Iterator\\CallbackFilter' => $vendorDir . '/hoa/iterator/CallbackFilter.php', 'Hoa\\Iterator\\CallbackGenerator' => $vendorDir . '/hoa/iterator/CallbackGenerator.php', 'Hoa\\Iterator\\Counter' => $vendorDir . '/hoa/iterator/Counter.php', 'Hoa\\Iterator\\Demultiplexer' => $vendorDir . '/hoa/iterator/Demultiplexer.php', 'Hoa\\Iterator\\Directory' => $vendorDir . '/hoa/iterator/Directory.php', 'Hoa\\Iterator\\Exception' => $vendorDir . '/hoa/iterator/Exception.php', 'Hoa\\Iterator\\FileSystem' => $vendorDir . '/hoa/iterator/FileSystem.php', 'Hoa\\Iterator\\Filter' => $vendorDir . '/hoa/iterator/Filter.php', 'Hoa\\Iterator\\Glob' => $vendorDir . '/hoa/iterator/Glob.php', 'Hoa\\Iterator\\Infinite' => $vendorDir . '/hoa/iterator/Infinite.php', 'Hoa\\Iterator\\Iterator' => $vendorDir . '/hoa/iterator/Iterator.php', 'Hoa\\Iterator\\IteratorIterator' => $vendorDir . '/hoa/iterator/IteratorIterator.php', 'Hoa\\Iterator\\Limit' => $vendorDir . '/hoa/iterator/Limit.php', 'Hoa\\Iterator\\Lookahead' => $vendorDir . '/hoa/iterator/Lookahead.php', 'Hoa\\Iterator\\Lookbehind' => $vendorDir . '/hoa/iterator/Lookbehind.php', 'Hoa\\Iterator\\Map' => $vendorDir . '/hoa/iterator/Map.php', 'Hoa\\Iterator\\Mock' => $vendorDir . '/hoa/iterator/Mock.php', 'Hoa\\Iterator\\Multiple' => $vendorDir . '/hoa/iterator/Multiple.php', 'Hoa\\Iterator\\NoRewind' => $vendorDir . '/hoa/iterator/NoRewind.php', 'Hoa\\Iterator\\Outer' => $vendorDir . '/hoa/iterator/Outer.php', 'Hoa\\Iterator\\Recursive\\CallbackFilter' => $vendorDir . '/hoa/iterator/Recursive/CallbackFilter.php', 'Hoa\\Iterator\\Recursive\\Directory' => $vendorDir . '/hoa/iterator/Recursive/Directory.php', 'Hoa\\Iterator\\Recursive\\Filter' => $vendorDir . '/hoa/iterator/Recursive/Filter.php', 'Hoa\\Iterator\\Recursive\\Iterator' => $vendorDir . '/hoa/iterator/Recursive/Iterator.php', 'Hoa\\Iterator\\Recursive\\Map' => $vendorDir . '/hoa/iterator/Recursive/Map.php', 'Hoa\\Iterator\\Recursive\\Mock' => $vendorDir . '/hoa/iterator/Recursive/Mock.php', 'Hoa\\Iterator\\Recursive\\Recursive' => $vendorDir . '/hoa/iterator/Recursive/Recursive.php', 'Hoa\\Iterator\\Recursive\\RegularExpression' => $vendorDir . '/hoa/iterator/Recursive/RegularExpression.php', 'Hoa\\Iterator\\RegularExpression' => $vendorDir . '/hoa/iterator/RegularExpression.php', 'Hoa\\Iterator\\Repeater' => $vendorDir . '/hoa/iterator/Repeater.php', 'Hoa\\Iterator\\Seekable' => $vendorDir . '/hoa/iterator/Seekable.php', 'Hoa\\Iterator\\SplFileInfo' => $vendorDir . '/hoa/iterator/SplFileInfo.php', 'Hoa\\Iterator\\Test\\Unit\\Append' => $vendorDir . '/hoa/iterator/Test/Unit/Append.php', 'Hoa\\Iterator\\Test\\Unit\\Buffer' => $vendorDir . '/hoa/iterator/Test/Unit/Buffer.php', 'Hoa\\Iterator\\Test\\Unit\\CallbackFilter' => $vendorDir . '/hoa/iterator/Test/Unit/CallbackFilter.php', 'Hoa\\Iterator\\Test\\Unit\\CallbackGenerator' => $vendorDir . '/hoa/iterator/Test/Unit/CallbackGenerator.php', 'Hoa\\Iterator\\Test\\Unit\\Counter' => $vendorDir . '/hoa/iterator/Test/Unit/Counter.php', 'Hoa\\Iterator\\Test\\Unit\\Demultiplexer' => $vendorDir . '/hoa/iterator/Test/Unit/Demultiplexer.php', 'Hoa\\Iterator\\Test\\Unit\\Directory' => $vendorDir . '/hoa/iterator/Test/Unit/Directory.php', 'Hoa\\Iterator\\Test\\Unit\\FileSystem' => $vendorDir . '/hoa/iterator/Test/Unit/FileSystem.php', 'Hoa\\Iterator\\Test\\Unit\\Filter' => $vendorDir . '/hoa/iterator/Test/Unit/Filter.php', 'Hoa\\Iterator\\Test\\Unit\\Infinite' => $vendorDir . '/hoa/iterator/Test/Unit/Infinite.php', 'Hoa\\Iterator\\Test\\Unit\\IteratorIterator' => $vendorDir . '/hoa/iterator/Test/Unit/IteratorIterator.php', 'Hoa\\Iterator\\Test\\Unit\\Limit' => $vendorDir . '/hoa/iterator/Test/Unit/Limit.php', 'Hoa\\Iterator\\Test\\Unit\\Lookahead' => $vendorDir . '/hoa/iterator/Test/Unit/Lookahead.php', 'Hoa\\Iterator\\Test\\Unit\\Lookbehind' => $vendorDir . '/hoa/iterator/Test/Unit/Lookbehind.php', 'Hoa\\Iterator\\Test\\Unit\\Map' => $vendorDir . '/hoa/iterator/Test/Unit/Map.php', 'Hoa\\Iterator\\Test\\Unit\\Mock' => $vendorDir . '/hoa/iterator/Test/Unit/Mock.php', 'Hoa\\Iterator\\Test\\Unit\\Multiple' => $vendorDir . '/hoa/iterator/Test/Unit/Multiple.php', 'Hoa\\Iterator\\Test\\Unit\\NoRewind' => $vendorDir . '/hoa/iterator/Test/Unit/NoRewind.php', 'Hoa\\Iterator\\Test\\Unit\\RegularExpression' => $vendorDir . '/hoa/iterator/Test/Unit/RegularExpression.php', 'Hoa\\Iterator\\Test\\Unit\\Repeater' => $vendorDir . '/hoa/iterator/Test/Unit/Repeater.php', 'Hoa\\Iterator\\Test\\Unit\\SplFileInfo' => $vendorDir . '/hoa/iterator/Test/Unit/SplFileInfo.php', 'Hoa\\Protocol\\Bin\\Resolve' => $vendorDir . '/hoa/protocol/Bin/Resolve.php', 'Hoa\\Protocol\\Exception' => $vendorDir . '/hoa/protocol/Exception.php', 'Hoa\\Protocol\\Node\\Library' => $vendorDir . '/hoa/protocol/Node/Library.php', 'Hoa\\Protocol\\Node\\Node' => $vendorDir . '/hoa/protocol/Node/Node.php', 'Hoa\\Protocol\\Protocol' => $vendorDir . '/hoa/protocol/Protocol.php', 'Hoa\\Protocol\\Test\\Unit\\Exception' => $vendorDir . '/hoa/protocol/Test/Unit/Exception.php', 'Hoa\\Protocol\\Test\\Unit\\Node\\Library' => $vendorDir . '/hoa/protocol/Test/Unit/Node/Library.php', 'Hoa\\Protocol\\Test\\Unit\\Node\\Node' => $vendorDir . '/hoa/protocol/Test/Unit/Node/Node.php', 'Hoa\\Protocol\\Test\\Unit\\Protocol' => $vendorDir . '/hoa/protocol/Test/Unit/Protocol.php', 'Hoa\\Protocol\\Test\\Unit\\Wrapper' => $vendorDir . '/hoa/protocol/Test/Unit/Wrapper.php', 'Hoa\\Protocol\\Wrapper' => $vendorDir . '/hoa/protocol/Wrapper.php', 'Hoa\\Stream\\Bucket' => $vendorDir . '/hoa/stream/Bucket.php', 'Hoa\\Stream\\Composite' => $vendorDir . '/hoa/stream/Composite.php', 'Hoa\\Stream\\Context' => $vendorDir . '/hoa/stream/Context.php', 'Hoa\\Stream\\Exception' => $vendorDir . '/hoa/stream/Exception.php', 'Hoa\\Stream\\Filter\\Basic' => $vendorDir . '/hoa/stream/Filter/Basic.php', 'Hoa\\Stream\\Filter\\Exception' => $vendorDir . '/hoa/stream/Filter/Exception.php', 'Hoa\\Stream\\Filter\\Filter' => $vendorDir . '/hoa/stream/Filter/Filter.php', 'Hoa\\Stream\\Filter\\LateComputed' => $vendorDir . '/hoa/stream/Filter/LateComputed.php', 'Hoa\\Stream\\IStream\\Bufferable' => $vendorDir . '/hoa/stream/IStream/Bufferable.php', 'Hoa\\Stream\\IStream\\In' => $vendorDir . '/hoa/stream/IStream/In.php', 'Hoa\\Stream\\IStream\\Lockable' => $vendorDir . '/hoa/stream/IStream/Lockable.php', 'Hoa\\Stream\\IStream\\Out' => $vendorDir . '/hoa/stream/IStream/Out.php', 'Hoa\\Stream\\IStream\\Pathable' => $vendorDir . '/hoa/stream/IStream/Pathable.php', 'Hoa\\Stream\\IStream\\Pointable' => $vendorDir . '/hoa/stream/IStream/Pointable.php', 'Hoa\\Stream\\IStream\\Statable' => $vendorDir . '/hoa/stream/IStream/Statable.php', 'Hoa\\Stream\\IStream\\Stream' => $vendorDir . '/hoa/stream/IStream/Stream.php', 'Hoa\\Stream\\IStream\\Structural' => $vendorDir . '/hoa/stream/IStream/Structural.php', 'Hoa\\Stream\\IStream\\Touchable' => $vendorDir . '/hoa/stream/IStream/Touchable.php', 'Hoa\\Stream\\Stream' => $vendorDir . '/hoa/stream/Stream.php', 'Hoa\\Stream\\Test\\Integration\\Filter\\Filter' => $vendorDir . '/hoa/stream/Test/Integration/Filter/Filter.php', 'Hoa\\Stream\\Test\\Integration\\Filter\\LateComputed' => $vendorDir . '/hoa/stream/Test/Integration/Filter/LateComputed.php', 'Hoa\\Stream\\Test\\Integration\\Stream' => $vendorDir . '/hoa/stream/Test/Integration/Stream.php', 'Hoa\\Stream\\Test\\Unit\\Bucket' => $vendorDir . '/hoa/stream/Test/Unit/Bucket.php', 'Hoa\\Stream\\Test\\Unit\\Composite' => $vendorDir . '/hoa/stream/Test/Unit/Composite.php', 'Hoa\\Stream\\Test\\Unit\\Context' => $vendorDir . '/hoa/stream/Test/Unit/Context.php', 'Hoa\\Stream\\Test\\Unit\\Exception' => $vendorDir . '/hoa/stream/Test/Unit/Exception.php', 'Hoa\\Stream\\Test\\Unit\\Filter\\Basic' => $vendorDir . '/hoa/stream/Test/Unit/Filter/Basic.php', 'Hoa\\Stream\\Test\\Unit\\Filter\\Exception' => $vendorDir . '/hoa/stream/Test/Unit/Filter/Exception.php', 'Hoa\\Stream\\Test\\Unit\\Filter\\Filter' => $vendorDir . '/hoa/stream/Test/Unit/Filter/Filter.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Bufferable' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Bufferable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\In' => $vendorDir . '/hoa/stream/Test/Unit/IStream/In.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Lockable' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Lockable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Out' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Out.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Pathable' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Pathable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Pointable' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Pointable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Statable' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Statable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Stream' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Stream.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Structural' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Structural.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Touchable' => $vendorDir . '/hoa/stream/Test/Unit/IStream/Touchable.php', 'Hoa\\Stream\\Test\\Unit\\Stream' => $vendorDir . '/hoa/stream/Test/Unit/Stream.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\Exception' => $vendorDir . '/hoa/stream/Test/Unit/Wrapper/Exception.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\IWrapper\\File' => $vendorDir . '/hoa/stream/Test/Unit/Wrapper/IWrapper/File.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\IWrapper\\IWrapper' => $vendorDir . '/hoa/stream/Test/Unit/Wrapper/IWrapper/IWrapper.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\IWrapper\\Stream' => $vendorDir . '/hoa/stream/Test/Unit/Wrapper/IWrapper/Stream.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\Wrapper' => $vendorDir . '/hoa/stream/Test/Unit/Wrapper/Wrapper.php', 'Hoa\\Stream\\Wrapper\\Exception' => $vendorDir . '/hoa/stream/Wrapper/Exception.php', 'Hoa\\Stream\\Wrapper\\IWrapper\\File' => $vendorDir . '/hoa/stream/Wrapper/IWrapper/File.php', 'Hoa\\Stream\\Wrapper\\IWrapper\\IWrapper' => $vendorDir . '/hoa/stream/Wrapper/IWrapper/IWrapper.php', 'Hoa\\Stream\\Wrapper\\IWrapper\\Stream' => $vendorDir . '/hoa/stream/Wrapper/IWrapper/Stream.php', 'Hoa\\Stream\\Wrapper\\Wrapper' => $vendorDir . '/hoa/stream/Wrapper/Wrapper.php', 'Hoa\\Ustring\\Bin\\Fromcode' => $vendorDir . '/hoa/ustring/Bin/Fromcode.php', 'Hoa\\Ustring\\Bin\\Tocode' => $vendorDir . '/hoa/ustring/Bin/Tocode.php', 'Hoa\\Ustring\\Exception' => $vendorDir . '/hoa/ustring/Exception.php', 'Hoa\\Ustring\\Search' => $vendorDir . '/hoa/ustring/Search.php', 'Hoa\\Ustring\\Test\\Unit\\Issue' => $vendorDir . '/hoa/ustring/Test/Unit/Issue.php', 'Hoa\\Ustring\\Test\\Unit\\Search' => $vendorDir . '/hoa/ustring/Test/Unit/Search.php', 'Hoa\\Ustring\\Test\\Unit\\Ustring' => $vendorDir . '/hoa/ustring/Test/Unit/Ustring.php', 'Hoa\\Ustring\\Ustring' => $vendorDir . '/hoa/ustring/Ustring.php', 'Humbug\\FileGetContents' => $vendorDir . '/padraic/humbug_get_contents/src/FileGetContents.php', 'Humbug\\SelfUpdate\\Exception\\ExceptionInterface' => $vendorDir . '/padraic/phar-updater/src/Exception/ExceptionInterface.php', 'Humbug\\SelfUpdate\\Exception\\FilesystemException' => $vendorDir . '/padraic/phar-updater/src/Exception/FilesystemException.php', 'Humbug\\SelfUpdate\\Exception\\HttpRequestException' => $vendorDir . '/padraic/phar-updater/src/Exception/HttpRequestException.php', 'Humbug\\SelfUpdate\\Exception\\InvalidArgumentException' => $vendorDir . '/padraic/phar-updater/src/Exception/InvalidArgumentException.php', 'Humbug\\SelfUpdate\\Exception\\JsonParsingException' => $vendorDir . '/padraic/phar-updater/src/Exception/JsonParsingException.php', 'Humbug\\SelfUpdate\\Exception\\NoSignatureException' => $vendorDir . '/padraic/phar-updater/src/Exception/NoSignatureException.php', 'Humbug\\SelfUpdate\\Exception\\RuntimeException' => $vendorDir . '/padraic/phar-updater/src/Exception/RuntimeException.php', 'Humbug\\SelfUpdate\\Strategy\\GithubStrategy' => $vendorDir . '/padraic/phar-updater/src/Strategy/GithubStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\ShaStrategy' => $vendorDir . '/padraic/phar-updater/src/Strategy/ShaStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\StrategyInterface' => $vendorDir . '/padraic/phar-updater/src/Strategy/StrategyInterface.php', 'Humbug\\SelfUpdate\\Updater' => $vendorDir . '/padraic/phar-updater/src/Updater.php', 'Humbug\\SelfUpdate\\VersionParser' => $vendorDir . '/padraic/phar-updater/src/VersionParser.php', 'JsonSchema\\Constraints\\BaseConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php', 'JsonSchema\\Constraints\\CollectionConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php', 'JsonSchema\\Constraints\\Constraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php', 'JsonSchema\\Constraints\\ConstraintInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php', 'JsonSchema\\Constraints\\EnumConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php', 'JsonSchema\\Constraints\\Factory' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php', 'JsonSchema\\Constraints\\FormatConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php', 'JsonSchema\\Constraints\\NumberConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php', 'JsonSchema\\Constraints\\ObjectConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php', 'JsonSchema\\Constraints\\SchemaConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php', 'JsonSchema\\Constraints\\StringConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php', 'JsonSchema\\Constraints\\TypeCheck\\LooseTypeCheck' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php', 'JsonSchema\\Constraints\\TypeCheck\\StrictTypeCheck' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php', 'JsonSchema\\Constraints\\TypeCheck\\TypeCheckInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php', 'JsonSchema\\Constraints\\TypeConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php', 'JsonSchema\\Constraints\\UndefinedConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php', 'JsonSchema\\Entity\\JsonPointer' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php', 'JsonSchema\\Exception\\ExceptionInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php', 'JsonSchema\\Exception\\InvalidArgumentException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php', 'JsonSchema\\Exception\\InvalidConfigException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidConfigException.php', 'JsonSchema\\Exception\\InvalidSchemaException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaException.php', 'JsonSchema\\Exception\\InvalidSchemaMediaTypeException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php', 'JsonSchema\\Exception\\InvalidSourceUriException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSourceUriException.php', 'JsonSchema\\Exception\\JsonDecodingException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php', 'JsonSchema\\Exception\\ResourceNotFoundException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php', 'JsonSchema\\Exception\\RuntimeException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php', 'JsonSchema\\Exception\\UnresolvableJsonPointerException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php', 'JsonSchema\\Exception\\UriResolverException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php', 'JsonSchema\\Exception\\ValidationException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/ValidationException.php', 'JsonSchema\\Iterator\\ObjectIterator' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php', 'JsonSchema\\Rfc3339' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php', 'JsonSchema\\SchemaStorage' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php', 'JsonSchema\\SchemaStorageInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.php', 'JsonSchema\\UriResolverInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php', 'JsonSchema\\UriRetrieverInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php', 'JsonSchema\\Uri\\Retrievers\\AbstractRetriever' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/AbstractRetriever.php', 'JsonSchema\\Uri\\Retrievers\\Curl' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php', 'JsonSchema\\Uri\\Retrievers\\FileGetContents' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php', 'JsonSchema\\Uri\\Retrievers\\PredefinedArray' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/PredefinedArray.php', 'JsonSchema\\Uri\\Retrievers\\UriRetrieverInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php', 'JsonSchema\\Uri\\UriResolver' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php', 'JsonSchema\\Uri\\UriRetriever' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php', 'JsonSchema\\Validator' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Validator.php', 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.php', 'PHPUnit\\Framework\\BaseTestListener' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.php', 'PHPUnit\\Framework\\Test' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/Test.php', 'PHPUnit\\Framework\\TestCase' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestCase.php', 'PHPUnit\\Framework\\TestListener' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestListener.php', 'PHPUnit\\Framework\\TestSuite' => $vendorDir . '/phpunit/phpunit/src/ForwardCompatibility/TestSuite.php', 'PHPUnit_Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php', 'PHPUnit_Extensions_GroupTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/GroupTestSuite.php', 'PHPUnit_Extensions_PhptTestCase' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestCase.php', 'PHPUnit_Extensions_PhptTestSuite' => $vendorDir . '/phpunit/phpunit/src/Extensions/PhptTestSuite.php', 'PHPUnit_Extensions_RepeatedTest' => $vendorDir . '/phpunit/phpunit/src/Extensions/RepeatedTest.php', 'PHPUnit_Extensions_TestDecorator' => $vendorDir . '/phpunit/phpunit/src/Extensions/TestDecorator.php', 'PHPUnit_Extensions_TicketListener' => $vendorDir . '/phpunit/phpunit/src/Extensions/TicketListener.php', 'PHPUnit_Framework_Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php', 'PHPUnit_Framework_AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/Framework/AssertionFailedError.php', 'PHPUnit_Framework_BaseTestListener' => $vendorDir . '/phpunit/phpunit/src/Framework/BaseTestListener.php', 'PHPUnit_Framework_CodeCoverageException' => $vendorDir . '/phpunit/phpunit/src/Framework/CodeCoverageException.php', 'PHPUnit_Framework_Constraint' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint.php', 'PHPUnit_Framework_Constraint_And' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/And.php', 'PHPUnit_Framework_Constraint_ArrayHasKey' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php', 'PHPUnit_Framework_Constraint_ArraySubset' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php', 'PHPUnit_Framework_Constraint_Attribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php', 'PHPUnit_Framework_Constraint_Callback' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Callback.php', 'PHPUnit_Framework_Constraint_ClassHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php', 'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php', 'PHPUnit_Framework_Constraint_Composite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Composite.php', 'PHPUnit_Framework_Constraint_Count' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Count.php', 'PHPUnit_Framework_Constraint_DirectoryExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php', 'PHPUnit_Framework_Constraint_Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception.php', 'PHPUnit_Framework_Constraint_ExceptionCode' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php', 'PHPUnit_Framework_Constraint_ExceptionMessage' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php', 'PHPUnit_Framework_Constraint_ExceptionMessageRegExp' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php', 'PHPUnit_Framework_Constraint_FileExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php', 'PHPUnit_Framework_Constraint_GreaterThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php', 'PHPUnit_Framework_Constraint_IsAnything' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php', 'PHPUnit_Framework_Constraint_IsEmpty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php', 'PHPUnit_Framework_Constraint_IsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php', 'PHPUnit_Framework_Constraint_IsFalse' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php', 'PHPUnit_Framework_Constraint_IsFinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php', 'PHPUnit_Framework_Constraint_IsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php', 'PHPUnit_Framework_Constraint_IsInfinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php', 'PHPUnit_Framework_Constraint_IsInstanceOf' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php', 'PHPUnit_Framework_Constraint_IsJson' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php', 'PHPUnit_Framework_Constraint_IsNan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsNan.php', 'PHPUnit_Framework_Constraint_IsNull' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php', 'PHPUnit_Framework_Constraint_IsReadable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php', 'PHPUnit_Framework_Constraint_IsTrue' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php', 'PHPUnit_Framework_Constraint_IsType' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsType.php', 'PHPUnit_Framework_Constraint_IsWritable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php', 'PHPUnit_Framework_Constraint_JsonMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php', 'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.php', 'PHPUnit_Framework_Constraint_LessThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php', 'PHPUnit_Framework_Constraint_Not' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Not.php', 'PHPUnit_Framework_Constraint_ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php', 'PHPUnit_Framework_Constraint_Or' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Or.php', 'PHPUnit_Framework_Constraint_PCREMatch' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/PCREMatch.php', 'PHPUnit_Framework_Constraint_SameSize' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php', 'PHPUnit_Framework_Constraint_StringContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php', 'PHPUnit_Framework_Constraint_StringEndsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php', 'PHPUnit_Framework_Constraint_StringMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringMatches.php', 'PHPUnit_Framework_Constraint_StringStartsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php', 'PHPUnit_Framework_Constraint_TraversableContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php', 'PHPUnit_Framework_Constraint_TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php', 'PHPUnit_Framework_Constraint_Xor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Xor.php', 'PHPUnit_Framework_CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/phpunit/src/Framework/CoveredCodeNotExecutedException.php', 'PHPUnit_Framework_Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Error.php', 'PHPUnit_Framework_Error_Deprecated' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Deprecated.php', 'PHPUnit_Framework_Error_Notice' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Notice.php', 'PHPUnit_Framework_Error_Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Warning.php', 'PHPUnit_Framework_Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception.php', 'PHPUnit_Framework_ExceptionWrapper' => $vendorDir . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php', 'PHPUnit_Framework_ExpectationFailedException' => $vendorDir . '/phpunit/phpunit/src/Framework/ExpectationFailedException.php', 'PHPUnit_Framework_IncompleteTest' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTest.php', 'PHPUnit_Framework_IncompleteTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php', 'PHPUnit_Framework_IncompleteTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/IncompleteTestError.php', 'PHPUnit_Framework_InvalidCoversTargetException' => $vendorDir . '/phpunit/phpunit/src/Framework/InvalidCoversTargetException.php', 'PHPUnit_Framework_MissingCoversAnnotationException' => $vendorDir . '/phpunit/phpunit/src/Framework/MissingCoversAnnotationException.php', 'PHPUnit_Framework_MockObject_BadMethodCallException' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/BadMethodCallException.php', 'PHPUnit_Framework_MockObject_Builder_Identity' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Identity.php', 'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/InvocationMocker.php', 'PHPUnit_Framework_MockObject_Builder_Match' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Match.php', 'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/MethodNameMatch.php', 'PHPUnit_Framework_MockObject_Builder_Namespace' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Namespace.php', 'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/ParametersMatch.php', 'PHPUnit_Framework_MockObject_Builder_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Stub.php', 'PHPUnit_Framework_MockObject_Exception' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/Exception.php', 'PHPUnit_Framework_MockObject_Generator' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php', 'PHPUnit_Framework_MockObject_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation.php', 'PHPUnit_Framework_MockObject_InvocationMocker' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/InvocationMocker.php', 'PHPUnit_Framework_MockObject_Invocation_Object' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Object.php', 'PHPUnit_Framework_MockObject_Invocation_Static' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Static.php', 'PHPUnit_Framework_MockObject_Invokable' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invokable.php', 'PHPUnit_Framework_MockObject_Matcher' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher.php', 'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyInvokedCount.php', 'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyParameters.php', 'PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/ConsecutiveParameters.php', 'PHPUnit_Framework_MockObject_Matcher_Invocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Invocation.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtIndex.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtMostCount.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedCount.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedRecorder.php', 'PHPUnit_Framework_MockObject_Matcher_MethodName' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/MethodName.php', 'PHPUnit_Framework_MockObject_Matcher_Parameters' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Parameters.php', 'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/StatelessInvocation.php', 'PHPUnit_Framework_MockObject_MockBuilder' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockBuilder.php', 'PHPUnit_Framework_MockObject_MockObject' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockObject.php', 'PHPUnit_Framework_MockObject_RuntimeException' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/RuntimeException.php', 'PHPUnit_Framework_MockObject_Stub' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub.php', 'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ConsecutiveCalls.php', 'PHPUnit_Framework_MockObject_Stub_Exception' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Exception.php', 'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/MatcherCollection.php', 'PHPUnit_Framework_MockObject_Stub_Return' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Return.php', 'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnArgument.php', 'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnCallback.php', 'PHPUnit_Framework_MockObject_Stub_ReturnReference' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnReference.php', 'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnSelf.php', 'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnValueMap.php', 'PHPUnit_Framework_MockObject_Verifiable' => $vendorDir . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Verifiable.php', 'PHPUnit_Framework_OutputError' => $vendorDir . '/phpunit/phpunit/src/Framework/OutputError.php', 'PHPUnit_Framework_RiskyTest' => $vendorDir . '/phpunit/phpunit/src/Framework/RiskyTest.php', 'PHPUnit_Framework_RiskyTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/RiskyTestError.php', 'PHPUnit_Framework_SelfDescribing' => $vendorDir . '/phpunit/phpunit/src/Framework/SelfDescribing.php', 'PHPUnit_Framework_SkippedTest' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTest.php', 'PHPUnit_Framework_SkippedTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestCase.php', 'PHPUnit_Framework_SkippedTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestError.php', 'PHPUnit_Framework_SkippedTestSuiteError' => $vendorDir . '/phpunit/phpunit/src/Framework/SkippedTestSuiteError.php', 'PHPUnit_Framework_SyntheticError' => $vendorDir . '/phpunit/phpunit/src/Framework/SyntheticError.php', 'PHPUnit_Framework_Test' => $vendorDir . '/phpunit/phpunit/src/Framework/Test.php', 'PHPUnit_Framework_TestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/TestCase.php', 'PHPUnit_Framework_TestFailure' => $vendorDir . '/phpunit/phpunit/src/Framework/TestFailure.php', 'PHPUnit_Framework_TestListener' => $vendorDir . '/phpunit/phpunit/src/Framework/TestListener.php', 'PHPUnit_Framework_TestResult' => $vendorDir . '/phpunit/phpunit/src/Framework/TestResult.php', 'PHPUnit_Framework_TestSuite' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuite.php', 'PHPUnit_Framework_TestSuite_DataProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuite/DataProvider.php', 'PHPUnit_Framework_UnintentionallyCoveredCodeError' => $vendorDir . '/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.php', 'PHPUnit_Framework_Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Warning.php', 'PHPUnit_Framework_WarningTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/WarningTestCase.php', 'PHPUnit_Runner_BaseTestRunner' => $vendorDir . '/phpunit/phpunit/src/Runner/BaseTestRunner.php', 'PHPUnit_Runner_Exception' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception.php', 'PHPUnit_Runner_Filter_Factory' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Factory.php', 'PHPUnit_Runner_Filter_GroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group.php', 'PHPUnit_Runner_Filter_Group_Exclude' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group/Exclude.php', 'PHPUnit_Runner_Filter_Group_Include' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Group/Include.php', 'PHPUnit_Runner_Filter_Test' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Test.php', 'PHPUnit_Runner_StandardTestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php', 'PHPUnit_Runner_TestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php', 'PHPUnit_Runner_Version' => $vendorDir . '/phpunit/phpunit/src/Runner/Version.php', 'PHPUnit_TextUI_Command' => $vendorDir . '/phpunit/phpunit/src/TextUI/Command.php', 'PHPUnit_TextUI_ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/TextUI/ResultPrinter.php', 'PHPUnit_TextUI_TestRunner' => $vendorDir . '/phpunit/phpunit/src/TextUI/TestRunner.php', 'PHPUnit_Util_Blacklist' => $vendorDir . '/phpunit/phpunit/src/Util/Blacklist.php', 'PHPUnit_Util_Configuration' => $vendorDir . '/phpunit/phpunit/src/Util/Configuration.php', 'PHPUnit_Util_ConfigurationGenerator' => $vendorDir . '/phpunit/phpunit/src/Util/ConfigurationGenerator.php', 'PHPUnit_Util_ErrorHandler' => $vendorDir . '/phpunit/phpunit/src/Util/ErrorHandler.php', 'PHPUnit_Util_Fileloader' => $vendorDir . '/phpunit/phpunit/src/Util/Fileloader.php', 'PHPUnit_Util_Filesystem' => $vendorDir . '/phpunit/phpunit/src/Util/Filesystem.php', 'PHPUnit_Util_Filter' => $vendorDir . '/phpunit/phpunit/src/Util/Filter.php', 'PHPUnit_Util_Getopt' => $vendorDir . '/phpunit/phpunit/src/Util/Getopt.php', 'PHPUnit_Util_GlobalState' => $vendorDir . '/phpunit/phpunit/src/Util/GlobalState.php', 'PHPUnit_Util_InvalidArgumentHelper' => $vendorDir . '/phpunit/phpunit/src/Util/InvalidArgumentHelper.php', 'PHPUnit_Util_Log_JSON' => $vendorDir . '/phpunit/phpunit/src/Util/Log/JSON.php', 'PHPUnit_Util_Log_JUnit' => $vendorDir . '/phpunit/phpunit/src/Util/Log/JUnit.php', 'PHPUnit_Util_Log_TAP' => $vendorDir . '/phpunit/phpunit/src/Util/Log/TAP.php', 'PHPUnit_Util_Log_TeamCity' => $vendorDir . '/phpunit/phpunit/src/Util/Log/TeamCity.php', 'PHPUnit_Util_PHP' => $vendorDir . '/phpunit/phpunit/src/Util/PHP.php', 'PHPUnit_Util_PHP_Default' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/Default.php', 'PHPUnit_Util_PHP_Windows' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/Windows.php', 'PHPUnit_Util_Printer' => $vendorDir . '/phpunit/phpunit/src/Util/Printer.php', 'PHPUnit_Util_Regex' => $vendorDir . '/phpunit/phpunit/src/Util/Regex.php', 'PHPUnit_Util_String' => $vendorDir . '/phpunit/phpunit/src/Util/String.php', 'PHPUnit_Util_Test' => $vendorDir . '/phpunit/phpunit/src/Util/Test.php', 'PHPUnit_Util_TestDox_NamePrettifier' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php', 'PHPUnit_Util_TestDox_ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter.php', 'PHPUnit_Util_TestDox_ResultPrinter_HTML' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.php', 'PHPUnit_Util_TestDox_ResultPrinter_Text' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.php', 'PHPUnit_Util_TestDox_ResultPrinter_XML' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/XML.php', 'PHPUnit_Util_TestSuiteIterator' => $vendorDir . '/phpunit/phpunit/src/Util/TestSuiteIterator.php', 'PHPUnit_Util_Type' => $vendorDir . '/phpunit/phpunit/src/Util/Type.php', 'PHPUnit_Util_XML' => $vendorDir . '/phpunit/phpunit/src/Util/XML.php', 'PHP_Timer' => $vendorDir . '/phpunit/php-timer/src/Timer.php', 'PHP_Token' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_TokenWithScope' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_TokenWithScopeAndVisibility' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ABSTRACT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AMPERSAND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AND_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ARRAY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ARRAY_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ASYNC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AWAIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BACKTICK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BAD_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BOOLEAN_AND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BOOLEAN_OR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BOOL_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BREAK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CALLABLE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CARET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CASE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CATCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CHARACTER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLASS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLASS_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLASS_NAME_CONSTANT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLONE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_TAG' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COALESCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COMMA' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COMPILER_HALT_OFFSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONCAT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONSTANT_ENCAPSED_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONTINUE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CURLY_OPEN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DEC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DEFAULT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DIR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DIV' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DIV_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOC_COMMENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOLLAR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_ARROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_COLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_QUOTES' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ELLIPSIS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ELSE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ELSEIF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EMPTY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENCAPSED_AND_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDDECLARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDFOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDFOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDIF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDSWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDWHILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_END_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENUM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EQUALS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EVAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EXCLAMATION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EXIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EXTENDS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FINAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FINALLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FOREACH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FUNC_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_GLOBAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_GOTO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_HALT_COMPILER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IMPLEMENTS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INCLUDE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INCLUDE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INLINE_HTML' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INSTANCEOF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INSTEADOF' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INTERFACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ISSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_GREATER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_NOT_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_NOT_IDENTICAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_SMALLER_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_Includes' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_JOIN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LAMBDA_ARROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LAMBDA_CP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LAMBDA_OP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LINE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LIST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LNUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LOGICAL_AND' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LOGICAL_OR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LOGICAL_XOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_METHOD_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MINUS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MINUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MOD_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MULT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MUL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NAMESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NEW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NS_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NS_SEPARATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NULLSAFE_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NUM_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OBJECT_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OBJECT_OPERATOR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ONUMBER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_BRACKET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_CURLY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_SQUARE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_TAG' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_TAG_WITH_ECHO' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PERCENT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PIPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PLUS' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PLUS_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_POW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_POW_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PRINT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PRIVATE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PROTECTED' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PUBLIC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_QUESTION_MARK' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_REQUIRE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_REQUIRE_ONCE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_RETURN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SEMICOLON' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SHAPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SL_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SPACESHIP' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_START_HEREDOC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STATIC' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STRING' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STRING_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STRING_VARNAME' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SUPER' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SWITCH' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_Stream' => $vendorDir . '/phpunit/php-token-stream/src/Token/Stream.php', 'PHP_Token_Stream_CachingFactory' => $vendorDir . '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php', 'PHP_Token_THROW' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TILDE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TRAIT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TRAIT_C' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TRY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TYPE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TYPELIST_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TYPELIST_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_UNSET' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_UNSET_CAST' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_USE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_USE_FUNCTION' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_VAR' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_VARIABLE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_WHERE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_WHILE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_WHITESPACE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_ATTRIBUTE' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_CATEGORY' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_CATEGORY_LABEL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_CHILDREN' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_LABEL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_REQUIRED' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_TAG_GT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_TAG_LT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_TEXT' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XOR_EQUAL' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_YIELD' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_YIELD_FROM' => $vendorDir . '/phpunit/php-token-stream/src/Token.php', 'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.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\\Connection\\Heartbeat\\PCNTLHeartbeatSender' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.php', 'PhpAmqpLib\\Exception\\AMQPBasicCancelException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.php', 'PhpAmqpLib\\Exception\\AMQPChannelClosedException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelClosedException.php', 'PhpAmqpLib\\Exception\\AMQPChannelException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionBlockedException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionClosedException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionClosedException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionException.php', 'PhpAmqpLib\\Exception\\AMQPDataReadException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPDataReadException.php', 'PhpAmqpLib\\Exception\\AMQPEmptyDeliveryTagException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPEmptyDeliveryTagException.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\\AMQPHeartbeatMissedException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPHeartbeatMissedException.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\\AMQPInvalidFrameException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidFrameException.php', 'PhpAmqpLib\\Exception\\AMQPLogicException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.php', 'PhpAmqpLib\\Exception\\AMQPNoDataException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNoDataException.php', 'PhpAmqpLib\\Exception\\AMQPNotImplementedException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNotImplementedException.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\\AMQPSocketException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPSocketException.php', 'PhpAmqpLib\\Exception\\AMQPTimeoutException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.php', 'PhpAmqpLib\\Exchange\\AMQPExchangeType' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exchange/AMQPExchangeType.php', 'PhpAmqpLib\\Helper\\Assert' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Assert.php', 'PhpAmqpLib\\Helper\\BigInteger' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/BigInteger.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\\Helper\\SocketConstants' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/SocketConstants.php', 'PhpAmqpLib\\Message\\AMQPMessage' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.php', 'PhpAmqpLib\\Package' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Package.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\\Constants' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants.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\\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', 'Predis\\Autoloader' => $vendorDir . '/predis/predis/src/Autoloader.php', 'Predis\\Client' => $vendorDir . '/predis/predis/src/Client.php', 'Predis\\ClientContextInterface' => $vendorDir . '/predis/predis/src/ClientContextInterface.php', 'Predis\\ClientException' => $vendorDir . '/predis/predis/src/ClientException.php', 'Predis\\ClientInterface' => $vendorDir . '/predis/predis/src/ClientInterface.php', 'Predis\\Cluster\\ClusterStrategy' => $vendorDir . '/predis/predis/src/Cluster/ClusterStrategy.php', 'Predis\\Cluster\\Distributor\\DistributorInterface' => $vendorDir . '/predis/predis/src/Cluster/Distributor/DistributorInterface.php', 'Predis\\Cluster\\Distributor\\EmptyRingException' => $vendorDir . '/predis/predis/src/Cluster/Distributor/EmptyRingException.php', 'Predis\\Cluster\\Distributor\\HashRing' => $vendorDir . '/predis/predis/src/Cluster/Distributor/HashRing.php', 'Predis\\Cluster\\Distributor\\KetamaRing' => $vendorDir . '/predis/predis/src/Cluster/Distributor/KetamaRing.php', 'Predis\\Cluster\\Hash\\CRC16' => $vendorDir . '/predis/predis/src/Cluster/Hash/CRC16.php', 'Predis\\Cluster\\Hash\\HashGeneratorInterface' => $vendorDir . '/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php', 'Predis\\Cluster\\PredisStrategy' => $vendorDir . '/predis/predis/src/Cluster/PredisStrategy.php', 'Predis\\Cluster\\RedisStrategy' => $vendorDir . '/predis/predis/src/Cluster/RedisStrategy.php', 'Predis\\Cluster\\StrategyInterface' => $vendorDir . '/predis/predis/src/Cluster/StrategyInterface.php', 'Predis\\Collection\\Iterator\\CursorBasedIterator' => $vendorDir . '/predis/predis/src/Collection/Iterator/CursorBasedIterator.php', 'Predis\\Collection\\Iterator\\HashKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/HashKey.php', 'Predis\\Collection\\Iterator\\Keyspace' => $vendorDir . '/predis/predis/src/Collection/Iterator/Keyspace.php', 'Predis\\Collection\\Iterator\\ListKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/ListKey.php', 'Predis\\Collection\\Iterator\\SetKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/SetKey.php', 'Predis\\Collection\\Iterator\\SortedSetKey' => $vendorDir . '/predis/predis/src/Collection/Iterator/SortedSetKey.php', 'Predis\\Command\\Command' => $vendorDir . '/predis/predis/src/Command/Command.php', 'Predis\\Command\\CommandInterface' => $vendorDir . '/predis/predis/src/Command/CommandInterface.php', 'Predis\\Command\\ConnectionAuth' => $vendorDir . '/predis/predis/src/Command/ConnectionAuth.php', 'Predis\\Command\\ConnectionEcho' => $vendorDir . '/predis/predis/src/Command/ConnectionEcho.php', 'Predis\\Command\\ConnectionPing' => $vendorDir . '/predis/predis/src/Command/ConnectionPing.php', 'Predis\\Command\\ConnectionQuit' => $vendorDir . '/predis/predis/src/Command/ConnectionQuit.php', 'Predis\\Command\\ConnectionSelect' => $vendorDir . '/predis/predis/src/Command/ConnectionSelect.php', 'Predis\\Command\\GeospatialGeoAdd' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoAdd.php', 'Predis\\Command\\GeospatialGeoDist' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoDist.php', 'Predis\\Command\\GeospatialGeoHash' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoHash.php', 'Predis\\Command\\GeospatialGeoPos' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoPos.php', 'Predis\\Command\\GeospatialGeoRadius' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoRadius.php', 'Predis\\Command\\GeospatialGeoRadiusByMember' => $vendorDir . '/predis/predis/src/Command/GeospatialGeoRadiusByMember.php', 'Predis\\Command\\HashDelete' => $vendorDir . '/predis/predis/src/Command/HashDelete.php', 'Predis\\Command\\HashExists' => $vendorDir . '/predis/predis/src/Command/HashExists.php', 'Predis\\Command\\HashGet' => $vendorDir . '/predis/predis/src/Command/HashGet.php', 'Predis\\Command\\HashGetAll' => $vendorDir . '/predis/predis/src/Command/HashGetAll.php', 'Predis\\Command\\HashGetMultiple' => $vendorDir . '/predis/predis/src/Command/HashGetMultiple.php', 'Predis\\Command\\HashIncrementBy' => $vendorDir . '/predis/predis/src/Command/HashIncrementBy.php', 'Predis\\Command\\HashIncrementByFloat' => $vendorDir . '/predis/predis/src/Command/HashIncrementByFloat.php', 'Predis\\Command\\HashKeys' => $vendorDir . '/predis/predis/src/Command/HashKeys.php', 'Predis\\Command\\HashLength' => $vendorDir . '/predis/predis/src/Command/HashLength.php', 'Predis\\Command\\HashScan' => $vendorDir . '/predis/predis/src/Command/HashScan.php', 'Predis\\Command\\HashSet' => $vendorDir . '/predis/predis/src/Command/HashSet.php', 'Predis\\Command\\HashSetMultiple' => $vendorDir . '/predis/predis/src/Command/HashSetMultiple.php', 'Predis\\Command\\HashSetPreserve' => $vendorDir . '/predis/predis/src/Command/HashSetPreserve.php', 'Predis\\Command\\HashStringLength' => $vendorDir . '/predis/predis/src/Command/HashStringLength.php', 'Predis\\Command\\HashValues' => $vendorDir . '/predis/predis/src/Command/HashValues.php', 'Predis\\Command\\HyperLogLogAdd' => $vendorDir . '/predis/predis/src/Command/HyperLogLogAdd.php', 'Predis\\Command\\HyperLogLogCount' => $vendorDir . '/predis/predis/src/Command/HyperLogLogCount.php', 'Predis\\Command\\HyperLogLogMerge' => $vendorDir . '/predis/predis/src/Command/HyperLogLogMerge.php', 'Predis\\Command\\KeyDelete' => $vendorDir . '/predis/predis/src/Command/KeyDelete.php', 'Predis\\Command\\KeyDump' => $vendorDir . '/predis/predis/src/Command/KeyDump.php', 'Predis\\Command\\KeyExists' => $vendorDir . '/predis/predis/src/Command/KeyExists.php', 'Predis\\Command\\KeyExpire' => $vendorDir . '/predis/predis/src/Command/KeyExpire.php', 'Predis\\Command\\KeyExpireAt' => $vendorDir . '/predis/predis/src/Command/KeyExpireAt.php', 'Predis\\Command\\KeyKeys' => $vendorDir . '/predis/predis/src/Command/KeyKeys.php', 'Predis\\Command\\KeyMigrate' => $vendorDir . '/predis/predis/src/Command/KeyMigrate.php', 'Predis\\Command\\KeyMove' => $vendorDir . '/predis/predis/src/Command/KeyMove.php', 'Predis\\Command\\KeyPersist' => $vendorDir . '/predis/predis/src/Command/KeyPersist.php', 'Predis\\Command\\KeyPreciseExpire' => $vendorDir . '/predis/predis/src/Command/KeyPreciseExpire.php', 'Predis\\Command\\KeyPreciseExpireAt' => $vendorDir . '/predis/predis/src/Command/KeyPreciseExpireAt.php', 'Predis\\Command\\KeyPreciseTimeToLive' => $vendorDir . '/predis/predis/src/Command/KeyPreciseTimeToLive.php', 'Predis\\Command\\KeyRandom' => $vendorDir . '/predis/predis/src/Command/KeyRandom.php', 'Predis\\Command\\KeyRename' => $vendorDir . '/predis/predis/src/Command/KeyRename.php', 'Predis\\Command\\KeyRenamePreserve' => $vendorDir . '/predis/predis/src/Command/KeyRenamePreserve.php', 'Predis\\Command\\KeyRestore' => $vendorDir . '/predis/predis/src/Command/KeyRestore.php', 'Predis\\Command\\KeyScan' => $vendorDir . '/predis/predis/src/Command/KeyScan.php', 'Predis\\Command\\KeySort' => $vendorDir . '/predis/predis/src/Command/KeySort.php', 'Predis\\Command\\KeyTimeToLive' => $vendorDir . '/predis/predis/src/Command/KeyTimeToLive.php', 'Predis\\Command\\KeyType' => $vendorDir . '/predis/predis/src/Command/KeyType.php', 'Predis\\Command\\ListIndex' => $vendorDir . '/predis/predis/src/Command/ListIndex.php', 'Predis\\Command\\ListInsert' => $vendorDir . '/predis/predis/src/Command/ListInsert.php', 'Predis\\Command\\ListLength' => $vendorDir . '/predis/predis/src/Command/ListLength.php', 'Predis\\Command\\ListPopFirst' => $vendorDir . '/predis/predis/src/Command/ListPopFirst.php', 'Predis\\Command\\ListPopFirstBlocking' => $vendorDir . '/predis/predis/src/Command/ListPopFirstBlocking.php', 'Predis\\Command\\ListPopLast' => $vendorDir . '/predis/predis/src/Command/ListPopLast.php', 'Predis\\Command\\ListPopLastBlocking' => $vendorDir . '/predis/predis/src/Command/ListPopLastBlocking.php', 'Predis\\Command\\ListPopLastPushHead' => $vendorDir . '/predis/predis/src/Command/ListPopLastPushHead.php', 'Predis\\Command\\ListPopLastPushHeadBlocking' => $vendorDir . '/predis/predis/src/Command/ListPopLastPushHeadBlocking.php', 'Predis\\Command\\ListPushHead' => $vendorDir . '/predis/predis/src/Command/ListPushHead.php', 'Predis\\Command\\ListPushHeadX' => $vendorDir . '/predis/predis/src/Command/ListPushHeadX.php', 'Predis\\Command\\ListPushTail' => $vendorDir . '/predis/predis/src/Command/ListPushTail.php', 'Predis\\Command\\ListPushTailX' => $vendorDir . '/predis/predis/src/Command/ListPushTailX.php', 'Predis\\Command\\ListRange' => $vendorDir . '/predis/predis/src/Command/ListRange.php', 'Predis\\Command\\ListRemove' => $vendorDir . '/predis/predis/src/Command/ListRemove.php', 'Predis\\Command\\ListSet' => $vendorDir . '/predis/predis/src/Command/ListSet.php', 'Predis\\Command\\ListTrim' => $vendorDir . '/predis/predis/src/Command/ListTrim.php', 'Predis\\Command\\PrefixableCommandInterface' => $vendorDir . '/predis/predis/src/Command/PrefixableCommandInterface.php', 'Predis\\Command\\Processor\\KeyPrefixProcessor' => $vendorDir . '/predis/predis/src/Command/Processor/KeyPrefixProcessor.php', 'Predis\\Command\\Processor\\ProcessorChain' => $vendorDir . '/predis/predis/src/Command/Processor/ProcessorChain.php', 'Predis\\Command\\Processor\\ProcessorInterface' => $vendorDir . '/predis/predis/src/Command/Processor/ProcessorInterface.php', 'Predis\\Command\\PubSubPublish' => $vendorDir . '/predis/predis/src/Command/PubSubPublish.php', 'Predis\\Command\\PubSubPubsub' => $vendorDir . '/predis/predis/src/Command/PubSubPubsub.php', 'Predis\\Command\\PubSubSubscribe' => $vendorDir . '/predis/predis/src/Command/PubSubSubscribe.php', 'Predis\\Command\\PubSubSubscribeByPattern' => $vendorDir . '/predis/predis/src/Command/PubSubSubscribeByPattern.php', 'Predis\\Command\\PubSubUnsubscribe' => $vendorDir . '/predis/predis/src/Command/PubSubUnsubscribe.php', 'Predis\\Command\\PubSubUnsubscribeByPattern' => $vendorDir . '/predis/predis/src/Command/PubSubUnsubscribeByPattern.php', 'Predis\\Command\\RawCommand' => $vendorDir . '/predis/predis/src/Command/RawCommand.php', 'Predis\\Command\\ScriptCommand' => $vendorDir . '/predis/predis/src/Command/ScriptCommand.php', 'Predis\\Command\\ServerBackgroundRewriteAOF' => $vendorDir . '/predis/predis/src/Command/ServerBackgroundRewriteAOF.php', 'Predis\\Command\\ServerBackgroundSave' => $vendorDir . '/predis/predis/src/Command/ServerBackgroundSave.php', 'Predis\\Command\\ServerClient' => $vendorDir . '/predis/predis/src/Command/ServerClient.php', 'Predis\\Command\\ServerCommand' => $vendorDir . '/predis/predis/src/Command/ServerCommand.php', 'Predis\\Command\\ServerConfig' => $vendorDir . '/predis/predis/src/Command/ServerConfig.php', 'Predis\\Command\\ServerDatabaseSize' => $vendorDir . '/predis/predis/src/Command/ServerDatabaseSize.php', 'Predis\\Command\\ServerEval' => $vendorDir . '/predis/predis/src/Command/ServerEval.php', 'Predis\\Command\\ServerEvalSHA' => $vendorDir . '/predis/predis/src/Command/ServerEvalSHA.php', 'Predis\\Command\\ServerFlushAll' => $vendorDir . '/predis/predis/src/Command/ServerFlushAll.php', 'Predis\\Command\\ServerFlushDatabase' => $vendorDir . '/predis/predis/src/Command/ServerFlushDatabase.php', 'Predis\\Command\\ServerInfo' => $vendorDir . '/predis/predis/src/Command/ServerInfo.php', 'Predis\\Command\\ServerInfoV26x' => $vendorDir . '/predis/predis/src/Command/ServerInfoV26x.php', 'Predis\\Command\\ServerLastSave' => $vendorDir . '/predis/predis/src/Command/ServerLastSave.php', 'Predis\\Command\\ServerMonitor' => $vendorDir . '/predis/predis/src/Command/ServerMonitor.php', 'Predis\\Command\\ServerObject' => $vendorDir . '/predis/predis/src/Command/ServerObject.php', 'Predis\\Command\\ServerSave' => $vendorDir . '/predis/predis/src/Command/ServerSave.php', 'Predis\\Command\\ServerScript' => $vendorDir . '/predis/predis/src/Command/ServerScript.php', 'Predis\\Command\\ServerSentinel' => $vendorDir . '/predis/predis/src/Command/ServerSentinel.php', 'Predis\\Command\\ServerShutdown' => $vendorDir . '/predis/predis/src/Command/ServerShutdown.php', 'Predis\\Command\\ServerSlaveOf' => $vendorDir . '/predis/predis/src/Command/ServerSlaveOf.php', 'Predis\\Command\\ServerSlowlog' => $vendorDir . '/predis/predis/src/Command/ServerSlowlog.php', 'Predis\\Command\\ServerTime' => $vendorDir . '/predis/predis/src/Command/ServerTime.php', 'Predis\\Command\\SetAdd' => $vendorDir . '/predis/predis/src/Command/SetAdd.php', 'Predis\\Command\\SetCardinality' => $vendorDir . '/predis/predis/src/Command/SetCardinality.php', 'Predis\\Command\\SetDifference' => $vendorDir . '/predis/predis/src/Command/SetDifference.php', 'Predis\\Command\\SetDifferenceStore' => $vendorDir . '/predis/predis/src/Command/SetDifferenceStore.php', 'Predis\\Command\\SetIntersection' => $vendorDir . '/predis/predis/src/Command/SetIntersection.php', 'Predis\\Command\\SetIntersectionStore' => $vendorDir . '/predis/predis/src/Command/SetIntersectionStore.php', 'Predis\\Command\\SetIsMember' => $vendorDir . '/predis/predis/src/Command/SetIsMember.php', 'Predis\\Command\\SetMembers' => $vendorDir . '/predis/predis/src/Command/SetMembers.php', 'Predis\\Command\\SetMove' => $vendorDir . '/predis/predis/src/Command/SetMove.php', 'Predis\\Command\\SetPop' => $vendorDir . '/predis/predis/src/Command/SetPop.php', 'Predis\\Command\\SetRandomMember' => $vendorDir . '/predis/predis/src/Command/SetRandomMember.php', 'Predis\\Command\\SetRemove' => $vendorDir . '/predis/predis/src/Command/SetRemove.php', 'Predis\\Command\\SetScan' => $vendorDir . '/predis/predis/src/Command/SetScan.php', 'Predis\\Command\\SetUnion' => $vendorDir . '/predis/predis/src/Command/SetUnion.php', 'Predis\\Command\\SetUnionStore' => $vendorDir . '/predis/predis/src/Command/SetUnionStore.php', 'Predis\\Command\\StringAppend' => $vendorDir . '/predis/predis/src/Command/StringAppend.php', 'Predis\\Command\\StringBitCount' => $vendorDir . '/predis/predis/src/Command/StringBitCount.php', 'Predis\\Command\\StringBitField' => $vendorDir . '/predis/predis/src/Command/StringBitField.php', 'Predis\\Command\\StringBitOp' => $vendorDir . '/predis/predis/src/Command/StringBitOp.php', 'Predis\\Command\\StringBitPos' => $vendorDir . '/predis/predis/src/Command/StringBitPos.php', 'Predis\\Command\\StringDecrement' => $vendorDir . '/predis/predis/src/Command/StringDecrement.php', 'Predis\\Command\\StringDecrementBy' => $vendorDir . '/predis/predis/src/Command/StringDecrementBy.php', 'Predis\\Command\\StringGet' => $vendorDir . '/predis/predis/src/Command/StringGet.php', 'Predis\\Command\\StringGetBit' => $vendorDir . '/predis/predis/src/Command/StringGetBit.php', 'Predis\\Command\\StringGetMultiple' => $vendorDir . '/predis/predis/src/Command/StringGetMultiple.php', 'Predis\\Command\\StringGetRange' => $vendorDir . '/predis/predis/src/Command/StringGetRange.php', 'Predis\\Command\\StringGetSet' => $vendorDir . '/predis/predis/src/Command/StringGetSet.php', 'Predis\\Command\\StringIncrement' => $vendorDir . '/predis/predis/src/Command/StringIncrement.php', 'Predis\\Command\\StringIncrementBy' => $vendorDir . '/predis/predis/src/Command/StringIncrementBy.php', 'Predis\\Command\\StringIncrementByFloat' => $vendorDir . '/predis/predis/src/Command/StringIncrementByFloat.php', 'Predis\\Command\\StringPreciseSetExpire' => $vendorDir . '/predis/predis/src/Command/StringPreciseSetExpire.php', 'Predis\\Command\\StringSet' => $vendorDir . '/predis/predis/src/Command/StringSet.php', 'Predis\\Command\\StringSetBit' => $vendorDir . '/predis/predis/src/Command/StringSetBit.php', 'Predis\\Command\\StringSetExpire' => $vendorDir . '/predis/predis/src/Command/StringSetExpire.php', 'Predis\\Command\\StringSetMultiple' => $vendorDir . '/predis/predis/src/Command/StringSetMultiple.php', 'Predis\\Command\\StringSetMultiplePreserve' => $vendorDir . '/predis/predis/src/Command/StringSetMultiplePreserve.php', 'Predis\\Command\\StringSetPreserve' => $vendorDir . '/predis/predis/src/Command/StringSetPreserve.php', 'Predis\\Command\\StringSetRange' => $vendorDir . '/predis/predis/src/Command/StringSetRange.php', 'Predis\\Command\\StringStrlen' => $vendorDir . '/predis/predis/src/Command/StringStrlen.php', 'Predis\\Command\\StringSubstr' => $vendorDir . '/predis/predis/src/Command/StringSubstr.php', 'Predis\\Command\\TransactionDiscard' => $vendorDir . '/predis/predis/src/Command/TransactionDiscard.php', 'Predis\\Command\\TransactionExec' => $vendorDir . '/predis/predis/src/Command/TransactionExec.php', 'Predis\\Command\\TransactionMulti' => $vendorDir . '/predis/predis/src/Command/TransactionMulti.php', 'Predis\\Command\\TransactionUnwatch' => $vendorDir . '/predis/predis/src/Command/TransactionUnwatch.php', 'Predis\\Command\\TransactionWatch' => $vendorDir . '/predis/predis/src/Command/TransactionWatch.php', 'Predis\\Command\\ZSetAdd' => $vendorDir . '/predis/predis/src/Command/ZSetAdd.php', 'Predis\\Command\\ZSetCardinality' => $vendorDir . '/predis/predis/src/Command/ZSetCardinality.php', 'Predis\\Command\\ZSetCount' => $vendorDir . '/predis/predis/src/Command/ZSetCount.php', 'Predis\\Command\\ZSetIncrementBy' => $vendorDir . '/predis/predis/src/Command/ZSetIncrementBy.php', 'Predis\\Command\\ZSetIntersectionStore' => $vendorDir . '/predis/predis/src/Command/ZSetIntersectionStore.php', 'Predis\\Command\\ZSetLexCount' => $vendorDir . '/predis/predis/src/Command/ZSetLexCount.php', 'Predis\\Command\\ZSetRange' => $vendorDir . '/predis/predis/src/Command/ZSetRange.php', 'Predis\\Command\\ZSetRangeByLex' => $vendorDir . '/predis/predis/src/Command/ZSetRangeByLex.php', 'Predis\\Command\\ZSetRangeByScore' => $vendorDir . '/predis/predis/src/Command/ZSetRangeByScore.php', 'Predis\\Command\\ZSetRank' => $vendorDir . '/predis/predis/src/Command/ZSetRank.php', 'Predis\\Command\\ZSetRemove' => $vendorDir . '/predis/predis/src/Command/ZSetRemove.php', 'Predis\\Command\\ZSetRemoveRangeByLex' => $vendorDir . '/predis/predis/src/Command/ZSetRemoveRangeByLex.php', 'Predis\\Command\\ZSetRemoveRangeByRank' => $vendorDir . '/predis/predis/src/Command/ZSetRemoveRangeByRank.php', 'Predis\\Command\\ZSetRemoveRangeByScore' => $vendorDir . '/predis/predis/src/Command/ZSetRemoveRangeByScore.php', 'Predis\\Command\\ZSetReverseRange' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRange.php', 'Predis\\Command\\ZSetReverseRangeByLex' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRangeByLex.php', 'Predis\\Command\\ZSetReverseRangeByScore' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRangeByScore.php', 'Predis\\Command\\ZSetReverseRank' => $vendorDir . '/predis/predis/src/Command/ZSetReverseRank.php', 'Predis\\Command\\ZSetScan' => $vendorDir . '/predis/predis/src/Command/ZSetScan.php', 'Predis\\Command\\ZSetScore' => $vendorDir . '/predis/predis/src/Command/ZSetScore.php', 'Predis\\Command\\ZSetUnionStore' => $vendorDir . '/predis/predis/src/Command/ZSetUnionStore.php', 'Predis\\CommunicationException' => $vendorDir . '/predis/predis/src/CommunicationException.php', 'Predis\\Configuration\\ClusterOption' => $vendorDir . '/predis/predis/src/Configuration/ClusterOption.php', 'Predis\\Configuration\\ConnectionFactoryOption' => $vendorDir . '/predis/predis/src/Configuration/ConnectionFactoryOption.php', 'Predis\\Configuration\\ExceptionsOption' => $vendorDir . '/predis/predis/src/Configuration/ExceptionsOption.php', 'Predis\\Configuration\\OptionInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionInterface.php', 'Predis\\Configuration\\Options' => $vendorDir . '/predis/predis/src/Configuration/Options.php', 'Predis\\Configuration\\OptionsInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionsInterface.php', 'Predis\\Configuration\\PrefixOption' => $vendorDir . '/predis/predis/src/Configuration/PrefixOption.php', 'Predis\\Configuration\\ProfileOption' => $vendorDir . '/predis/predis/src/Configuration/ProfileOption.php', 'Predis\\Configuration\\ReplicationOption' => $vendorDir . '/predis/predis/src/Configuration/ReplicationOption.php', 'Predis\\Connection\\AbstractConnection' => $vendorDir . '/predis/predis/src/Connection/AbstractConnection.php', 'Predis\\Connection\\AggregateConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/AggregateConnectionInterface.php', 'Predis\\Connection\\Aggregate\\ClusterInterface' => $vendorDir . '/predis/predis/src/Connection/Aggregate/ClusterInterface.php', 'Predis\\Connection\\Aggregate\\MasterSlaveReplication' => $vendorDir . '/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php', 'Predis\\Connection\\Aggregate\\PredisCluster' => $vendorDir . '/predis/predis/src/Connection/Aggregate/PredisCluster.php', 'Predis\\Connection\\Aggregate\\RedisCluster' => $vendorDir . '/predis/predis/src/Connection/Aggregate/RedisCluster.php', 'Predis\\Connection\\Aggregate\\ReplicationInterface' => $vendorDir . '/predis/predis/src/Connection/Aggregate/ReplicationInterface.php', 'Predis\\Connection\\Aggregate\\SentinelReplication' => $vendorDir . '/predis/predis/src/Connection/Aggregate/SentinelReplication.php', 'Predis\\Connection\\CompositeConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/CompositeConnectionInterface.php', 'Predis\\Connection\\CompositeStreamConnection' => $vendorDir . '/predis/predis/src/Connection/CompositeStreamConnection.php', 'Predis\\Connection\\ConnectionException' => $vendorDir . '/predis/predis/src/Connection/ConnectionException.php', 'Predis\\Connection\\ConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/ConnectionInterface.php', 'Predis\\Connection\\Factory' => $vendorDir . '/predis/predis/src/Connection/Factory.php', 'Predis\\Connection\\FactoryInterface' => $vendorDir . '/predis/predis/src/Connection/FactoryInterface.php', 'Predis\\Connection\\NodeConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/NodeConnectionInterface.php', 'Predis\\Connection\\Parameters' => $vendorDir . '/predis/predis/src/Connection/Parameters.php', 'Predis\\Connection\\ParametersInterface' => $vendorDir . '/predis/predis/src/Connection/ParametersInterface.php', 'Predis\\Connection\\PhpiredisSocketConnection' => $vendorDir . '/predis/predis/src/Connection/PhpiredisSocketConnection.php', 'Predis\\Connection\\PhpiredisStreamConnection' => $vendorDir . '/predis/predis/src/Connection/PhpiredisStreamConnection.php', 'Predis\\Connection\\StreamConnection' => $vendorDir . '/predis/predis/src/Connection/StreamConnection.php', 'Predis\\Connection\\WebdisConnection' => $vendorDir . '/predis/predis/src/Connection/WebdisConnection.php', 'Predis\\Monitor\\Consumer' => $vendorDir . '/predis/predis/src/Monitor/Consumer.php', 'Predis\\NotSupportedException' => $vendorDir . '/predis/predis/src/NotSupportedException.php', 'Predis\\Pipeline\\Atomic' => $vendorDir . '/predis/predis/src/Pipeline/Atomic.php', 'Predis\\Pipeline\\ConnectionErrorProof' => $vendorDir . '/predis/predis/src/Pipeline/ConnectionErrorProof.php', 'Predis\\Pipeline\\FireAndForget' => $vendorDir . '/predis/predis/src/Pipeline/FireAndForget.php', 'Predis\\Pipeline\\Pipeline' => $vendorDir . '/predis/predis/src/Pipeline/Pipeline.php', 'Predis\\PredisException' => $vendorDir . '/predis/predis/src/PredisException.php', 'Predis\\Profile\\Factory' => $vendorDir . '/predis/predis/src/Profile/Factory.php', 'Predis\\Profile\\ProfileInterface' => $vendorDir . '/predis/predis/src/Profile/ProfileInterface.php', 'Predis\\Profile\\RedisProfile' => $vendorDir . '/predis/predis/src/Profile/RedisProfile.php', 'Predis\\Profile\\RedisUnstable' => $vendorDir . '/predis/predis/src/Profile/RedisUnstable.php', 'Predis\\Profile\\RedisVersion200' => $vendorDir . '/predis/predis/src/Profile/RedisVersion200.php', 'Predis\\Profile\\RedisVersion220' => $vendorDir . '/predis/predis/src/Profile/RedisVersion220.php', 'Predis\\Profile\\RedisVersion240' => $vendorDir . '/predis/predis/src/Profile/RedisVersion240.php', 'Predis\\Profile\\RedisVersion260' => $vendorDir . '/predis/predis/src/Profile/RedisVersion260.php', 'Predis\\Profile\\RedisVersion280' => $vendorDir . '/predis/predis/src/Profile/RedisVersion280.php', 'Predis\\Profile\\RedisVersion300' => $vendorDir . '/predis/predis/src/Profile/RedisVersion300.php', 'Predis\\Profile\\RedisVersion320' => $vendorDir . '/predis/predis/src/Profile/RedisVersion320.php', 'Predis\\Protocol\\ProtocolException' => $vendorDir . '/predis/predis/src/Protocol/ProtocolException.php', 'Predis\\Protocol\\ProtocolProcessorInterface' => $vendorDir . '/predis/predis/src/Protocol/ProtocolProcessorInterface.php', 'Predis\\Protocol\\RequestSerializerInterface' => $vendorDir . '/predis/predis/src/Protocol/RequestSerializerInterface.php', 'Predis\\Protocol\\ResponseReaderInterface' => $vendorDir . '/predis/predis/src/Protocol/ResponseReaderInterface.php', 'Predis\\Protocol\\Text\\CompositeProtocolProcessor' => $vendorDir . '/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php', 'Predis\\Protocol\\Text\\Handler\\BulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/BulkResponse.php', 'Predis\\Protocol\\Text\\Handler\\ErrorResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php', 'Predis\\Protocol\\Text\\Handler\\IntegerResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php', 'Predis\\Protocol\\Text\\Handler\\MultiBulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php', 'Predis\\Protocol\\Text\\Handler\\ResponseHandlerInterface' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php', 'Predis\\Protocol\\Text\\Handler\\StatusResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/StatusResponse.php', 'Predis\\Protocol\\Text\\Handler\\StreamableMultiBulkResponse' => $vendorDir . '/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php', 'Predis\\Protocol\\Text\\ProtocolProcessor' => $vendorDir . '/predis/predis/src/Protocol/Text/ProtocolProcessor.php', 'Predis\\Protocol\\Text\\RequestSerializer' => $vendorDir . '/predis/predis/src/Protocol/Text/RequestSerializer.php', 'Predis\\Protocol\\Text\\ResponseReader' => $vendorDir . '/predis/predis/src/Protocol/Text/ResponseReader.php', 'Predis\\PubSub\\AbstractConsumer' => $vendorDir . '/predis/predis/src/PubSub/AbstractConsumer.php', 'Predis\\PubSub\\Consumer' => $vendorDir . '/predis/predis/src/PubSub/Consumer.php', 'Predis\\PubSub\\DispatcherLoop' => $vendorDir . '/predis/predis/src/PubSub/DispatcherLoop.php', 'Predis\\Replication\\MissingMasterException' => $vendorDir . '/predis/predis/src/Replication/MissingMasterException.php', 'Predis\\Replication\\ReplicationStrategy' => $vendorDir . '/predis/predis/src/Replication/ReplicationStrategy.php', 'Predis\\Replication\\RoleException' => $vendorDir . '/predis/predis/src/Replication/RoleException.php', 'Predis\\Response\\Error' => $vendorDir . '/predis/predis/src/Response/Error.php', 'Predis\\Response\\ErrorInterface' => $vendorDir . '/predis/predis/src/Response/ErrorInterface.php', 'Predis\\Response\\Iterator\\MultiBulk' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulk.php', 'Predis\\Response\\Iterator\\MultiBulkIterator' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulkIterator.php', 'Predis\\Response\\Iterator\\MultiBulkTuple' => $vendorDir . '/predis/predis/src/Response/Iterator/MultiBulkTuple.php', 'Predis\\Response\\ResponseInterface' => $vendorDir . '/predis/predis/src/Response/ResponseInterface.php', 'Predis\\Response\\ServerException' => $vendorDir . '/predis/predis/src/Response/ServerException.php', 'Predis\\Response\\Status' => $vendorDir . '/predis/predis/src/Response/Status.php', 'Predis\\Session\\Handler' => $vendorDir . '/predis/predis/src/Session/Handler.php', 'Predis\\Transaction\\AbortedMultiExecException' => $vendorDir . '/predis/predis/src/Transaction/AbortedMultiExecException.php', 'Predis\\Transaction\\MultiExec' => $vendorDir . '/predis/predis/src/Transaction/MultiExec.php', 'Predis\\Transaction\\MultiExecState' => $vendorDir . '/predis/predis/src/Transaction/MultiExecState.php', 'Prophecy\\Argument' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument.php', 'Prophecy\\Argument\\ArgumentsWildcard' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php', 'Prophecy\\Argument\\Token\\AnyValueToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php', 'Prophecy\\Argument\\Token\\AnyValuesToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.php', 'Prophecy\\Argument\\Token\\ApproximateValueToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/ApproximateValueToken.php', 'Prophecy\\Argument\\Token\\ArrayCountToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.php', 'Prophecy\\Argument\\Token\\ArrayEntryToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.php', 'Prophecy\\Argument\\Token\\ArrayEveryEntryToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php', 'Prophecy\\Argument\\Token\\CallbackToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.php', 'Prophecy\\Argument\\Token\\ExactValueToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.php', 'Prophecy\\Argument\\Token\\IdenticalValueToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.php', 'Prophecy\\Argument\\Token\\LogicalAndToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.php', 'Prophecy\\Argument\\Token\\LogicalNotToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.php', 'Prophecy\\Argument\\Token\\ObjectStateToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php', 'Prophecy\\Argument\\Token\\StringContainsToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php', 'Prophecy\\Argument\\Token\\TokenInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.php', 'Prophecy\\Argument\\Token\\TypeToken' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.php', 'Prophecy\\Call\\Call' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Call/Call.php', 'Prophecy\\Call\\CallCenter' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Call/CallCenter.php', 'Prophecy\\Comparator\\ClosureComparator' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.php', 'Prophecy\\Comparator\\Factory' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Comparator/Factory.php', 'Prophecy\\Comparator\\ProphecyComparator' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Comparator/ProphecyComparator.php', 'Prophecy\\Doubler\\CachedDoubler' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.php', 'Prophecy\\Doubler\\ClassPatch\\ClassPatchInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php', 'Prophecy\\Doubler\\ClassPatch\\DisableConstructorPatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php', 'Prophecy\\Doubler\\ClassPatch\\HhvmExceptionPatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php', 'Prophecy\\Doubler\\ClassPatch\\KeywordPatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php', 'Prophecy\\Doubler\\ClassPatch\\MagicCallPatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php', 'Prophecy\\Doubler\\ClassPatch\\ProphecySubjectPatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php', 'Prophecy\\Doubler\\ClassPatch\\ReflectionClassNewInstancePatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php', 'Prophecy\\Doubler\\ClassPatch\\SplFileInfoPatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php', 'Prophecy\\Doubler\\ClassPatch\\ThrowablePatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ThrowablePatch.php', 'Prophecy\\Doubler\\ClassPatch\\TraversablePatch' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php', 'Prophecy\\Doubler\\DoubleInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.php', 'Prophecy\\Doubler\\Doubler' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php', 'Prophecy\\Doubler\\Generator\\ClassCodeGenerator' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php', 'Prophecy\\Doubler\\Generator\\ClassCreator' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php', 'Prophecy\\Doubler\\Generator\\ClassMirror' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.php', 'Prophecy\\Doubler\\Generator\\Node\\ArgumentNode' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.php', 'Prophecy\\Doubler\\Generator\\Node\\ClassNode' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.php', 'Prophecy\\Doubler\\Generator\\Node\\MethodNode' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php', 'Prophecy\\Doubler\\Generator\\ReflectionInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php', 'Prophecy\\Doubler\\Generator\\TypeHintReference' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/TypeHintReference.php', 'Prophecy\\Doubler\\LazyDouble' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php', 'Prophecy\\Doubler\\NameGenerator' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.php', 'Prophecy\\Exception\\Call\\UnexpectedCallException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.php', 'Prophecy\\Exception\\Doubler\\ClassCreatorException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.php', 'Prophecy\\Exception\\Doubler\\ClassMirrorException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.php', 'Prophecy\\Exception\\Doubler\\ClassNotFoundException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php', 'Prophecy\\Exception\\Doubler\\DoubleException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.php', 'Prophecy\\Exception\\Doubler\\DoublerException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.php', 'Prophecy\\Exception\\Doubler\\InterfaceNotFoundException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.php', 'Prophecy\\Exception\\Doubler\\MethodNotExtendableException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotExtendableException.php', 'Prophecy\\Exception\\Doubler\\MethodNotFoundException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.php', 'Prophecy\\Exception\\Doubler\\ReturnByReferenceException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.php', 'Prophecy\\Exception\\Exception' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Exception.php', 'Prophecy\\Exception\\InvalidArgumentException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.php', 'Prophecy\\Exception\\Prediction\\AggregateException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.php', 'Prophecy\\Exception\\Prediction\\FailedPredictionException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.php', 'Prophecy\\Exception\\Prediction\\NoCallsException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.php', 'Prophecy\\Exception\\Prediction\\PredictionException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.php', 'Prophecy\\Exception\\Prediction\\UnexpectedCallsCountException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php', 'Prophecy\\Exception\\Prediction\\UnexpectedCallsException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.php', 'Prophecy\\Exception\\Prophecy\\MethodProphecyException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.php', 'Prophecy\\Exception\\Prophecy\\ObjectProphecyException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.php', 'Prophecy\\Exception\\Prophecy\\ProphecyException' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.php', 'Prophecy\\PhpDocumentor\\ClassAndInterfaceTagRetriever' => $vendorDir . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php', 'Prophecy\\PhpDocumentor\\ClassTagRetriever' => $vendorDir . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassTagRetriever.php', 'Prophecy\\PhpDocumentor\\LegacyClassTagRetriever' => $vendorDir . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php', 'Prophecy\\PhpDocumentor\\MethodTagRetrieverInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php', 'Prophecy\\Prediction\\CallPrediction' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.php', 'Prophecy\\Prediction\\CallTimesPrediction' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.php', 'Prophecy\\Prediction\\CallbackPrediction' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.php', 'Prophecy\\Prediction\\NoCallsPrediction' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.php', 'Prophecy\\Prediction\\PredictionInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.php', 'Prophecy\\Promise\\CallbackPromise' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php', 'Prophecy\\Promise\\PromiseInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.php', 'Prophecy\\Promise\\ReturnArgumentPromise' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.php', 'Prophecy\\Promise\\ReturnPromise' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.php', 'Prophecy\\Promise\\ThrowPromise' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.php', 'Prophecy\\Prophecy\\MethodProphecy' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.php', 'Prophecy\\Prophecy\\ObjectProphecy' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php', 'Prophecy\\Prophecy\\ProphecyInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.php', 'Prophecy\\Prophecy\\ProphecySubjectInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.php', 'Prophecy\\Prophecy\\Revealer' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.php', 'Prophecy\\Prophecy\\RevealerInterface' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.php', 'Prophecy\\Prophet' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Prophet.php', 'Prophecy\\Util\\ExportUtil' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Util/ExportUtil.php', 'Prophecy\\Util\\StringUtil' => $vendorDir . '/phpspec/prophecy/src/Prophecy/Util/StringUtil.php', 'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php', 'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php', 'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php', 'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php', 'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php', 'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php', 'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.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/DummyTest.php', 'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', 'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage.php', 'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\HHVM' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/HHVM.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PHPDBG' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PHPDBG.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Xdebug.php', 'SebastianBergmann\\CodeCoverage\\Exception' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/Exception.php', 'SebastianBergmann\\CodeCoverage\\Filter' => $vendorDir . '/phpunit/php-code-coverage/src/Filter.php', 'SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php', 'SebastianBergmann\\CodeCoverage\\MissingCoversAnnotationException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php', 'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => $vendorDir . '/phpunit/php-code-coverage/src/Node/AbstractNode.php', 'SebastianBergmann\\CodeCoverage\\Node\\Builder' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Builder.php', 'SebastianBergmann\\CodeCoverage\\Node\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Directory.php', 'SebastianBergmann\\CodeCoverage\\Node\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Node/File.php', 'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => $vendorDir . '/phpunit/php-code-coverage/src/Node/Iterator.php', 'SebastianBergmann\\CodeCoverage\\Report\\Clover' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Clover.php', 'SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Crap4j.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Facade.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Html/Renderer.php', 'SebastianBergmann\\CodeCoverage\\Report\\PHP' => $vendorDir . '/phpunit/php-code-coverage/src/Report/PHP.php', 'SebastianBergmann\\CodeCoverage\\Report\\Text' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Text.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Coverage.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Directory.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Facade.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/File.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Method.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Node.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Project.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Report.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Tests.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Totals.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php', 'SebastianBergmann\\CodeCoverage\\RuntimeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/RuntimeException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', 'SebastianBergmann\\CodeCoverage\\Util' => $vendorDir . '/phpunit/php-code-coverage/src/Util.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => $vendorDir . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', 'SebastianBergmann\\Comparator\\ArrayComparator' => $vendorDir . '/sebastian/comparator/src/ArrayComparator.php', 'SebastianBergmann\\Comparator\\Comparator' => $vendorDir . '/sebastian/comparator/src/Comparator.php', 'SebastianBergmann\\Comparator\\ComparisonFailure' => $vendorDir . '/sebastian/comparator/src/ComparisonFailure.php', 'SebastianBergmann\\Comparator\\DOMNodeComparator' => $vendorDir . '/sebastian/comparator/src/DOMNodeComparator.php', 'SebastianBergmann\\Comparator\\DateTimeComparator' => $vendorDir . '/sebastian/comparator/src/DateTimeComparator.php', 'SebastianBergmann\\Comparator\\DoubleComparator' => $vendorDir . '/sebastian/comparator/src/DoubleComparator.php', 'SebastianBergmann\\Comparator\\ExceptionComparator' => $vendorDir . '/sebastian/comparator/src/ExceptionComparator.php', 'SebastianBergmann\\Comparator\\Factory' => $vendorDir . '/sebastian/comparator/src/Factory.php', 'SebastianBergmann\\Comparator\\MockObjectComparator' => $vendorDir . '/sebastian/comparator/src/MockObjectComparator.php', 'SebastianBergmann\\Comparator\\NumericComparator' => $vendorDir . '/sebastian/comparator/src/NumericComparator.php', 'SebastianBergmann\\Comparator\\ObjectComparator' => $vendorDir . '/sebastian/comparator/src/ObjectComparator.php', 'SebastianBergmann\\Comparator\\ResourceComparator' => $vendorDir . '/sebastian/comparator/src/ResourceComparator.php', 'SebastianBergmann\\Comparator\\ScalarComparator' => $vendorDir . '/sebastian/comparator/src/ScalarComparator.php', 'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => $vendorDir . '/sebastian/comparator/src/SplObjectStorageComparator.php', 'SebastianBergmann\\Comparator\\TypeComparator' => $vendorDir . '/sebastian/comparator/src/TypeComparator.php', 'SebastianBergmann\\Diff\\Chunk' => $vendorDir . '/sebastian/diff/src/Chunk.php', 'SebastianBergmann\\Diff\\Diff' => $vendorDir . '/sebastian/diff/src/Diff.php', 'SebastianBergmann\\Diff\\Differ' => $vendorDir . '/sebastian/diff/src/Differ.php', 'SebastianBergmann\\Diff\\LCS\\LongestCommonSubsequence' => $vendorDir . '/sebastian/diff/src/LCS/LongestCommonSubsequence.php', 'SebastianBergmann\\Diff\\LCS\\MemoryEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php', 'SebastianBergmann\\Diff\\LCS\\TimeEfficientImplementation' => $vendorDir . '/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php', 'SebastianBergmann\\Diff\\Line' => $vendorDir . '/sebastian/diff/src/Line.php', 'SebastianBergmann\\Diff\\Parser' => $vendorDir . '/sebastian/diff/src/Parser.php', 'SebastianBergmann\\Environment\\Console' => $vendorDir . '/sebastian/environment/src/Console.php', 'SebastianBergmann\\Environment\\Runtime' => $vendorDir . '/sebastian/environment/src/Runtime.php', 'SebastianBergmann\\Exporter\\Exporter' => $vendorDir . '/sebastian/exporter/src/Exporter.php', 'SebastianBergmann\\GlobalState\\Blacklist' => $vendorDir . '/sebastian/global-state/src/Blacklist.php', 'SebastianBergmann\\GlobalState\\CodeExporter' => $vendorDir . '/sebastian/global-state/src/CodeExporter.php', 'SebastianBergmann\\GlobalState\\Exception' => $vendorDir . '/sebastian/global-state/src/Exception.php', 'SebastianBergmann\\GlobalState\\Restorer' => $vendorDir . '/sebastian/global-state/src/Restorer.php', 'SebastianBergmann\\GlobalState\\RuntimeException' => $vendorDir . '/sebastian/global-state/src/RuntimeException.php', 'SebastianBergmann\\GlobalState\\Snapshot' => $vendorDir . '/sebastian/global-state/src/Snapshot.php', 'SebastianBergmann\\ObjectEnumerator\\Enumerator' => $vendorDir . '/sebastian/object-enumerator/src/Enumerator.php', 'SebastianBergmann\\ObjectEnumerator\\Exception' => $vendorDir . '/sebastian/object-enumerator/src/Exception.php', 'SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => $vendorDir . '/sebastian/object-enumerator/src/InvalidArgumentException.php', 'SebastianBergmann\\RecursionContext\\Context' => $vendorDir . '/sebastian/recursion-context/src/Context.php', 'SebastianBergmann\\RecursionContext\\Exception' => $vendorDir . '/sebastian/recursion-context/src/Exception.php', 'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => $vendorDir . '/sebastian/recursion-context/src/InvalidArgumentException.php', 'SebastianBergmann\\ResourceOperations\\ResourceOperations' => $vendorDir . '/sebastian/resource-operations/src/ResourceOperations.php', 'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php', 'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', 'Symfony\\Component\\BrowserKit\\Client' => $vendorDir . '/symfony/browser-kit/Client.php', 'Symfony\\Component\\BrowserKit\\Cookie' => $vendorDir . '/symfony/browser-kit/Cookie.php', 'Symfony\\Component\\BrowserKit\\CookieJar' => $vendorDir . '/symfony/browser-kit/CookieJar.php', 'Symfony\\Component\\BrowserKit\\History' => $vendorDir . '/symfony/browser-kit/History.php', 'Symfony\\Component\\BrowserKit\\Request' => $vendorDir . '/symfony/browser-kit/Request.php', 'Symfony\\Component\\BrowserKit\\Response' => $vendorDir . '/symfony/browser-kit/Response.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\\CssSelector\\CssSelectorConverter' => $vendorDir . '/symfony/css-selector/CssSelectorConverter.php', 'Symfony\\Component\\CssSelector\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/css-selector/Exception/ExceptionInterface.php', 'Symfony\\Component\\CssSelector\\Exception\\ExpressionErrorException' => $vendorDir . '/symfony/css-selector/Exception/ExpressionErrorException.php', 'Symfony\\Component\\CssSelector\\Exception\\InternalErrorException' => $vendorDir . '/symfony/css-selector/Exception/InternalErrorException.php', 'Symfony\\Component\\CssSelector\\Exception\\ParseException' => $vendorDir . '/symfony/css-selector/Exception/ParseException.php', 'Symfony\\Component\\CssSelector\\Exception\\SyntaxErrorException' => $vendorDir . '/symfony/css-selector/Exception/SyntaxErrorException.php', 'Symfony\\Component\\CssSelector\\Node\\AbstractNode' => $vendorDir . '/symfony/css-selector/Node/AbstractNode.php', 'Symfony\\Component\\CssSelector\\Node\\AttributeNode' => $vendorDir . '/symfony/css-selector/Node/AttributeNode.php', 'Symfony\\Component\\CssSelector\\Node\\ClassNode' => $vendorDir . '/symfony/css-selector/Node/ClassNode.php', 'Symfony\\Component\\CssSelector\\Node\\CombinedSelectorNode' => $vendorDir . '/symfony/css-selector/Node/CombinedSelectorNode.php', 'Symfony\\Component\\CssSelector\\Node\\ElementNode' => $vendorDir . '/symfony/css-selector/Node/ElementNode.php', 'Symfony\\Component\\CssSelector\\Node\\FunctionNode' => $vendorDir . '/symfony/css-selector/Node/FunctionNode.php', 'Symfony\\Component\\CssSelector\\Node\\HashNode' => $vendorDir . '/symfony/css-selector/Node/HashNode.php', 'Symfony\\Component\\CssSelector\\Node\\NegationNode' => $vendorDir . '/symfony/css-selector/Node/NegationNode.php', 'Symfony\\Component\\CssSelector\\Node\\NodeInterface' => $vendorDir . '/symfony/css-selector/Node/NodeInterface.php', 'Symfony\\Component\\CssSelector\\Node\\PseudoNode' => $vendorDir . '/symfony/css-selector/Node/PseudoNode.php', 'Symfony\\Component\\CssSelector\\Node\\SelectorNode' => $vendorDir . '/symfony/css-selector/Node/SelectorNode.php', 'Symfony\\Component\\CssSelector\\Node\\Specificity' => $vendorDir . '/symfony/css-selector/Node/Specificity.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\CommentHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/CommentHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\HandlerInterface' => $vendorDir . '/symfony/css-selector/Parser/Handler/HandlerInterface.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\HashHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/HashHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\IdentifierHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/IdentifierHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\NumberHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/NumberHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\StringHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/StringHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\WhitespaceHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/WhitespaceHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Parser' => $vendorDir . '/symfony/css-selector/Parser/Parser.php', 'Symfony\\Component\\CssSelector\\Parser\\ParserInterface' => $vendorDir . '/symfony/css-selector/Parser/ParserInterface.php', 'Symfony\\Component\\CssSelector\\Parser\\Reader' => $vendorDir . '/symfony/css-selector/Parser/Reader.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ClassParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/ClassParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ElementParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/ElementParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\EmptyStringParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\HashParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/HashParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Token' => $vendorDir . '/symfony/css-selector/Parser/Token.php', 'Symfony\\Component\\CssSelector\\Parser\\TokenStream' => $vendorDir . '/symfony/css-selector/Parser/TokenStream.php', 'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\Tokenizer' => $vendorDir . '/symfony/css-selector/Parser/Tokenizer/Tokenizer.php', 'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerEscaping' => $vendorDir . '/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php', 'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerPatterns' => $vendorDir . '/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\AbstractExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/AbstractExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\AttributeMatchingExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\CombinationExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/CombinationExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\ExtensionInterface' => $vendorDir . '/symfony/css-selector/XPath/Extension/ExtensionInterface.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\FunctionExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/FunctionExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\HtmlExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/HtmlExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\NodeExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/NodeExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\PseudoClassExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/PseudoClassExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Translator' => $vendorDir . '/symfony/css-selector/XPath/Translator.php', 'Symfony\\Component\\CssSelector\\XPath\\TranslatorInterface' => $vendorDir . '/symfony/css-selector/XPath/TranslatorInterface.php', 'Symfony\\Component\\CssSelector\\XPath\\XPathExpr' => $vendorDir . '/symfony/css-selector/XPath/XPathExpr.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\\DomCrawler\\AbstractUriElement' => $vendorDir . '/symfony/dom-crawler/AbstractUriElement.php', 'Symfony\\Component\\DomCrawler\\Crawler' => $vendorDir . '/symfony/dom-crawler/Crawler.php', 'Symfony\\Component\\DomCrawler\\Field\\ChoiceFormField' => $vendorDir . '/symfony/dom-crawler/Field/ChoiceFormField.php', 'Symfony\\Component\\DomCrawler\\Field\\FileFormField' => $vendorDir . '/symfony/dom-crawler/Field/FileFormField.php', 'Symfony\\Component\\DomCrawler\\Field\\FormField' => $vendorDir . '/symfony/dom-crawler/Field/FormField.php', 'Symfony\\Component\\DomCrawler\\Field\\InputFormField' => $vendorDir . '/symfony/dom-crawler/Field/InputFormField.php', 'Symfony\\Component\\DomCrawler\\Field\\TextareaFormField' => $vendorDir . '/symfony/dom-crawler/Field/TextareaFormField.php', 'Symfony\\Component\\DomCrawler\\Form' => $vendorDir . '/symfony/dom-crawler/Form.php', 'Symfony\\Component\\DomCrawler\\FormFieldRegistry' => $vendorDir . '/symfony/dom-crawler/FormFieldRegistry.php', 'Symfony\\Component\\DomCrawler\\Image' => $vendorDir . '/symfony/dom-crawler/Image.php', 'Symfony\\Component\\DomCrawler\\Link' => $vendorDir . '/symfony/dom-crawler/Link.php', 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Debug/WrappedListener.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', 'Symfony\\Component\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher/Event.php', 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/EventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/EventDispatcherInterface.php', 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php', 'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php', 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', 'Symfony\\Component\\Finder\\Comparator\\Comparator' => $vendorDir . '/symfony/finder/Comparator/Comparator.php', 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => $vendorDir . '/symfony/finder/Comparator/DateComparator.php', 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => $vendorDir . '/symfony/finder/Comparator/NumberComparator.php', 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => $vendorDir . '/symfony/finder/Exception/AccessDeniedException.php', 'Symfony\\Component\\Finder\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/finder/Exception/ExceptionInterface.php', 'Symfony\\Component\\Finder\\Finder' => $vendorDir . '/symfony/finder/Finder.php', 'Symfony\\Component\\Finder\\Glob' => $vendorDir . '/symfony/finder/Glob.php', 'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => $vendorDir . '/symfony/finder/Iterator/CustomFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/DateRangeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/DepthRangeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator' => $vendorDir . '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FileTypeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FileTypeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilecontentFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilenameFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => $vendorDir . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => $vendorDir . '/symfony/finder/Iterator/PathFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => $vendorDir . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php', 'Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/SizeRangeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\SortableIterator' => $vendorDir . '/symfony/finder/Iterator/SortableIterator.php', 'Symfony\\Component\\Finder\\SplFileInfo' => $vendorDir . '/symfony/finder/SplFileInfo.php', 'Symfony\\Component\\Process\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/process/Exception/ExceptionInterface.php', 'Symfony\\Component\\Process\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/process/Exception/InvalidArgumentException.php', 'Symfony\\Component\\Process\\Exception\\LogicException' => $vendorDir . '/symfony/process/Exception/LogicException.php', 'Symfony\\Component\\Process\\Exception\\ProcessFailedException' => $vendorDir . '/symfony/process/Exception/ProcessFailedException.php', 'Symfony\\Component\\Process\\Exception\\ProcessTimedOutException' => $vendorDir . '/symfony/process/Exception/ProcessTimedOutException.php', 'Symfony\\Component\\Process\\Exception\\RuntimeException' => $vendorDir . '/symfony/process/Exception/RuntimeException.php', 'Symfony\\Component\\Process\\ExecutableFinder' => $vendorDir . '/symfony/process/ExecutableFinder.php', 'Symfony\\Component\\Process\\InputStream' => $vendorDir . '/symfony/process/InputStream.php', 'Symfony\\Component\\Process\\PhpExecutableFinder' => $vendorDir . '/symfony/process/PhpExecutableFinder.php', 'Symfony\\Component\\Process\\PhpProcess' => $vendorDir . '/symfony/process/PhpProcess.php', 'Symfony\\Component\\Process\\Pipes\\AbstractPipes' => $vendorDir . '/symfony/process/Pipes/AbstractPipes.php', 'Symfony\\Component\\Process\\Pipes\\PipesInterface' => $vendorDir . '/symfony/process/Pipes/PipesInterface.php', 'Symfony\\Component\\Process\\Pipes\\UnixPipes' => $vendorDir . '/symfony/process/Pipes/UnixPipes.php', 'Symfony\\Component\\Process\\Pipes\\WindowsPipes' => $vendorDir . '/symfony/process/Pipes/WindowsPipes.php', 'Symfony\\Component\\Process\\Process' => $vendorDir . '/symfony/process/Process.php', 'Symfony\\Component\\Process\\ProcessBuilder' => $vendorDir . '/symfony/process/ProcessBuilder.php', 'Symfony\\Component\\Process\\ProcessUtils' => $vendorDir . '/symfony/process/ProcessUtils.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\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php', 'Symfony\\Polyfill\\Intl\\Idn\\Idn' => $vendorDir . '/symfony/polyfill-intl-idn/Idn.php', 'Symfony\\Polyfill\\Intl\\Idn\\Info' => $vendorDir . '/symfony/polyfill-intl-idn/Info.php', 'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\DisallowedRanges' => $vendorDir . '/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php', 'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\Regex' => $vendorDir . '/symfony/polyfill-intl-idn/Resources/unidata/Regex.php', 'Symfony\\Polyfill\\Intl\\Normalizer\\Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Normalizer.php', 'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', 'Symfony\\Polyfill\\Php70\\Php70' => $vendorDir . '/symfony/polyfill-php70/Php70.php', 'Symfony\\Polyfill\\Php72\\Php72' => $vendorDir . '/symfony/polyfill-php72/Php72.php', 'Text_Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php', 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', 'Webmozart\\Assert\\Assert' => $vendorDir . '/webmozart/assert/src/Assert.php', 'Webmozart\\Assert\\Mixin' => $vendorDir . '/webmozart/assert/src/Mixin.php', 'phpDocumentor\\Reflection\\DocBlock' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock.php', 'phpDocumentor\\Reflection\\DocBlockFactory' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlockFactory.php', 'phpDocumentor\\Reflection\\DocBlockFactoryInterface' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php', 'phpDocumentor\\Reflection\\DocBlock\\Description' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Description.php', 'phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php', 'phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php', 'phpDocumentor\\Reflection\\DocBlock\\Serializer' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php', 'phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php', 'phpDocumentor\\Reflection\\DocBlock\\Tag' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tag.php', 'phpDocumentor\\Reflection\\DocBlock\\TagFactory' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\Strategy' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Strategy.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Reference.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => $vendorDir . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php', 'phpDocumentor\\Reflection\\Element' => $vendorDir . '/phpdocumentor/reflection-common/src/Element.php', 'phpDocumentor\\Reflection\\File' => $vendorDir . '/phpdocumentor/reflection-common/src/File.php', 'phpDocumentor\\Reflection\\Fqsen' => $vendorDir . '/phpdocumentor/reflection-common/src/Fqsen.php', 'phpDocumentor\\Reflection\\FqsenResolver' => $vendorDir . '/phpdocumentor/type-resolver/src/FqsenResolver.php', 'phpDocumentor\\Reflection\\Location' => $vendorDir . '/phpdocumentor/reflection-common/src/Location.php', 'phpDocumentor\\Reflection\\Project' => $vendorDir . '/phpdocumentor/reflection-common/src/Project.php', 'phpDocumentor\\Reflection\\ProjectFactory' => $vendorDir . '/phpdocumentor/reflection-common/src/ProjectFactory.php', 'phpDocumentor\\Reflection\\Type' => $vendorDir . '/phpdocumentor/type-resolver/src/Type.php', 'phpDocumentor\\Reflection\\TypeResolver' => $vendorDir . '/phpdocumentor/type-resolver/src/TypeResolver.php', 'phpDocumentor\\Reflection\\Types\\Array_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Array_.php', 'phpDocumentor\\Reflection\\Types\\Boolean' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Boolean.php', 'phpDocumentor\\Reflection\\Types\\Callable_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Callable_.php', 'phpDocumentor\\Reflection\\Types\\Compound' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Compound.php', 'phpDocumentor\\Reflection\\Types\\Context' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Context.php', 'phpDocumentor\\Reflection\\Types\\ContextFactory' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/ContextFactory.php', 'phpDocumentor\\Reflection\\Types\\Float_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Float_.php', 'phpDocumentor\\Reflection\\Types\\Integer' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Integer.php', 'phpDocumentor\\Reflection\\Types\\Iterable_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Iterable_.php', 'phpDocumentor\\Reflection\\Types\\Mixed_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Mixed_.php', 'phpDocumentor\\Reflection\\Types\\Null_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Null_.php', 'phpDocumentor\\Reflection\\Types\\Nullable' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Nullable.php', 'phpDocumentor\\Reflection\\Types\\Object_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Object_.php', 'phpDocumentor\\Reflection\\Types\\Parent_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Parent_.php', 'phpDocumentor\\Reflection\\Types\\Resource_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Resource_.php', 'phpDocumentor\\Reflection\\Types\\Scalar' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Scalar.php', 'phpDocumentor\\Reflection\\Types\\Self_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Self_.php', 'phpDocumentor\\Reflection\\Types\\Static_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Static_.php', 'phpDocumentor\\Reflection\\Types\\String_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/String_.php', 'phpDocumentor\\Reflection\\Types\\This' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/This.php', 'phpDocumentor\\Reflection\\Types\\Void_' => $vendorDir . '/phpdocumentor/type-resolver/src/Types/Void_.php', 'phpseclib\\Crypt\\AES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php', 'phpseclib\\Crypt\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Base.php', 'phpseclib\\Crypt\\Blowfish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php', 'phpseclib\\Crypt\\DES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php', 'phpseclib\\Crypt\\Hash' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php', 'phpseclib\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php', 'phpseclib\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php', 'phpseclib\\Crypt\\RSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php', 'phpseclib\\Crypt\\Random' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php', 'phpseclib\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php', 'phpseclib\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php', 'phpseclib\\Crypt\\Twofish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php', 'phpseclib\\File\\ANSI' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ANSI.php', 'phpseclib\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1.php', 'phpseclib\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php', 'phpseclib\\File\\X509' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/X509.php', 'phpseclib\\Math\\BigInteger' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php', 'phpseclib\\Net\\SCP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SCP.php', 'phpseclib\\Net\\SFTP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php', 'phpseclib\\Net\\SFTP\\Stream' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php', 'phpseclib\\Net\\SSH1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH1.php', 'phpseclib\\Net\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php', 'phpseclib\\System\\SSH\\Agent' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php', 'phpseclib\\System\\SSH\\Agent\\Identity' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php', ); vendorDir = $vendorDir; } public function getPrefixes() { if (!empty($this->prefixesPsr0)) { return call_user_func_array('array_merge', array_values($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; } public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } } 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 ); } } public function addPsr4($prefix, $paths, $prepend = false) { if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr4 = array_merge( (array) $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, (array) $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { $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) { $this->prefixDirsPsr4[$prefix] = array_merge( (array) $paths, $this->prefixDirsPsr4[$prefix] ); } else { $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], (array) $paths ); } } public function set($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr0 = (array) $paths; } else { $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } } 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; } } public function setUseIncludePath($useIncludePath) { $this->useIncludePath = $useIncludePath; } public function getUseIncludePath() { return $this->useIncludePath; } public function setClassMapAuthoritative($classMapAuthoritative) { $this->classMapAuthoritative = $classMapAuthoritative; } public function isClassMapAuthoritative() { return $this->classMapAuthoritative; } public function setApcuPrefix($apcuPrefix) { $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } public function getApcuPrefix() { return $this->apcuPrefix; } public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); if (null === $this->vendorDir) { return; } if ($prepend) { self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; } else { unset(self::$registeredLoaders[$this->vendorDir]); self::$registeredLoaders[$this->vendorDir] = $this; } } public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); if (null !== $this->vendorDir) { unset(self::$registeredLoaders[$this->vendorDir]); } } public function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file); return true; } return null; } public function findFile($class) { 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'); if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if (null !== $this->apcuPrefix) { apcu_add($this->apcuPrefix.$class, $file); } if (false === $file) { $this->missingClasses[$class] = true; } return $file; } public static function getRegisteredLoaders() { return self::$registeredLoaders; } private function findFileWithExtension($class, $ext) { $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])) { $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { if (file_exists($file = $dir . $pathEnd)) { return $file; } } } } } foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } if (false !== $pos = strrpos($class, '\\')) { $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { $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; } } } } } foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } return false; } } function includeFile($file) { include $file; } __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', 'e88992873b7765f9b5710cab95ba5dd7' => __DIR__ . '/..' . '/hoa/consistency/Prelude.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', 'e3b2795a8a512b6083af088fb53afe6c' => __DIR__ . '/..' . '/codeception/codeception/functions.php', '3e76f7f02b41af8cea96018933f6b7e3' => __DIR__ . '/..' . '/hoa/protocol/Wrapper.php', '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', 'd9d39f82a605ebe5918f683dd402334c' => __DIR__ . '/..' . '/padraic/humbug_get_contents/src/function.php', '3a50d90d85c7fe889a94ae1114b921ce' => __DIR__ . '/..' . '/padraic/humbug_get_contents/src/functions.php', '2a3c2110e8e0295330dc3d11a4cbc4cb' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php', ); public static $prefixLengthsPsr4 = array ( 'p' => array ( 'phpseclib\\' => 10, 'phpDocumentor\\Reflection\\' => 25, ), 'W' => array ( 'Webmozart\\Assert\\' => 17, ), 'S' => array ( 'Symfony\\Polyfill\\Php72\\' => 23, 'Symfony\\Polyfill\\Php70\\' => 23, 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33, 'Symfony\\Polyfill\\Intl\\Idn\\' => 26, 'Symfony\\Polyfill\\Ctype\\' => 23, 'Symfony\\Component\\Yaml\\' => 23, 'Symfony\\Component\\Process\\' => 26, 'Symfony\\Component\\Finder\\' => 25, 'Symfony\\Component\\EventDispatcher\\' => 34, 'Symfony\\Component\\DomCrawler\\' => 29, 'Symfony\\Component\\Debug\\' => 24, 'Symfony\\Component\\CssSelector\\' => 30, 'Symfony\\Component\\Console\\' => 26, 'Symfony\\Component\\BrowserKit\\' => 29, ), 'P' => array ( 'Psr\\Log\\' => 8, 'Psr\\Http\\Message\\' => 17, 'Prophecy\\' => 9, 'Predis\\' => 7, 'PhpAmqpLib\\' => 11, ), 'J' => array ( 'JsonSchema\\' => 11, ), 'H' => array ( 'Humbug\\SelfUpdate\\' => 18, 'Humbug\\' => 7, 'Hoa\\Ustring\\' => 12, 'Hoa\\Stream\\' => 11, 'Hoa\\Protocol\\' => 13, 'Hoa\\Iterator\\' => 13, 'Hoa\\File\\' => 9, 'Hoa\\Exception\\' => 14, 'Hoa\\Event\\' => 10, 'Hoa\\Console\\' => 12, 'Hoa\\Consistency\\' => 16, ), 'G' => array ( 'GuzzleHttp\\Psr7\\' => 16, 'GuzzleHttp\\Promise\\' => 19, 'GuzzleHttp\\' => 11, ), 'F' => array ( 'Flow\\JSONPath\\' => 14, 'Facebook\\WebDriver\\' => 19, ), 'D' => array ( 'Doctrine\\Instantiator\\' => 22, 'DeepCopy\\' => 9, ), 'C' => array ( 'Composer\\CaBundle\\' => 18, 'Codeception\\PHPUnit\\' => 20, 'Codeception\\Extension\\' => 22, 'Codeception\\' => 12, ), ); public static $prefixDirsPsr4 = array ( 'phpseclib\\' => array ( 0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib', ), 'phpDocumentor\\Reflection\\' => array ( 0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src', 1 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src', 2 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src', ), 'Webmozart\\Assert\\' => array ( 0 => __DIR__ . '/..' . '/webmozart/assert/src', ), 'Symfony\\Polyfill\\Php72\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php72', ), 'Symfony\\Polyfill\\Php70\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', ), 'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ), 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer', ), 'Symfony\\Polyfill\\Intl\\Idn\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-idn', ), 'Symfony\\Polyfill\\Ctype\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', ), 'Symfony\\Component\\Yaml\\' => array ( 0 => __DIR__ . '/..' . '/symfony/yaml', ), 'Symfony\\Component\\Process\\' => array ( 0 => __DIR__ . '/..' . '/symfony/process', ), 'Symfony\\Component\\Finder\\' => array ( 0 => __DIR__ . '/..' . '/symfony/finder', ), 'Symfony\\Component\\EventDispatcher\\' => array ( 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', ), 'Symfony\\Component\\DomCrawler\\' => array ( 0 => __DIR__ . '/..' . '/symfony/dom-crawler', ), 'Symfony\\Component\\Debug\\' => array ( 0 => __DIR__ . '/..' . '/symfony/debug', ), 'Symfony\\Component\\CssSelector\\' => array ( 0 => __DIR__ . '/..' . '/symfony/css-selector', ), 'Symfony\\Component\\Console\\' => array ( 0 => __DIR__ . '/..' . '/symfony/console', ), 'Symfony\\Component\\BrowserKit\\' => array ( 0 => __DIR__ . '/..' . '/symfony/browser-kit', ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', ), 'Psr\\Http\\Message\\' => array ( 0 => __DIR__ . '/..' . '/psr/http-message/src', ), 'Prophecy\\' => array ( 0 => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy', ), 'Predis\\' => array ( 0 => __DIR__ . '/..' . '/predis/predis/src', ), 'PhpAmqpLib\\' => array ( 0 => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib', ), 'JsonSchema\\' => array ( 0 => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema', ), 'Humbug\\SelfUpdate\\' => array ( 0 => __DIR__ . '/..' . '/padraic/phar-updater/src', ), 'Humbug\\' => array ( 0 => __DIR__ . '/..' . '/padraic/humbug_get_contents/src', ), 'Hoa\\Ustring\\' => array ( 0 => __DIR__ . '/..' . '/hoa/ustring', ), 'Hoa\\Stream\\' => array ( 0 => __DIR__ . '/..' . '/hoa/stream', ), 'Hoa\\Protocol\\' => array ( 0 => __DIR__ . '/..' . '/hoa/protocol', ), 'Hoa\\Iterator\\' => array ( 0 => __DIR__ . '/..' . '/hoa/iterator', ), 'Hoa\\File\\' => array ( 0 => __DIR__ . '/..' . '/hoa/file', ), 'Hoa\\Exception\\' => array ( 0 => __DIR__ . '/..' . '/hoa/exception', ), 'Hoa\\Event\\' => array ( 0 => __DIR__ . '/..' . '/hoa/event', ), 'Hoa\\Console\\' => array ( 0 => __DIR__ . '/..' . '/hoa/console', ), 'Hoa\\Consistency\\' => array ( 0 => __DIR__ . '/..' . '/hoa/consistency', ), 'GuzzleHttp\\Psr7\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', ), 'GuzzleHttp\\Promise\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/promises/src', ), 'GuzzleHttp\\' => array ( 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src', ), 'Flow\\JSONPath\\' => array ( 0 => __DIR__ . '/..' . '/softcreatr/jsonpath/src', ), 'Facebook\\WebDriver\\' => array ( 0 => __DIR__ . '/..' . '/php-webdriver/webdriver/lib', ), 'Doctrine\\Instantiator\\' => array ( 0 => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator', ), 'DeepCopy\\' => array ( 0 => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy', ), 'Composer\\CaBundle\\' => array ( 0 => __DIR__ . '/..' . '/composer/ca-bundle/src', ), 'Codeception\\PHPUnit\\' => array ( 0 => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src', ), 'Codeception\\Extension\\' => array ( 0 => __DIR__ . '/..' . '/codeception/codeception/ext', ), 'Codeception\\' => array ( 0 => __DIR__ . '/..' . '/codeception/stub/src', 1 => __DIR__ . '/..' . '/codeception/codeception/src/Codeception', ), ); public static $prefixesPsr0 = array ( 'B' => array ( 'Behat\\Gherkin' => array ( 0 => __DIR__ . '/..' . '/behat/gherkin/src', ), ), ); public static $classMap = array ( 'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', 'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', 'Behat\\Gherkin\\Cache\\CacheInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php', 'Behat\\Gherkin\\Cache\\FileCache' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.php', 'Behat\\Gherkin\\Cache\\MemoryCache' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php', 'Behat\\Gherkin\\Exception\\CacheException' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Exception/CacheException.php', 'Behat\\Gherkin\\Exception\\Exception' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Exception/Exception.php', 'Behat\\Gherkin\\Exception\\LexerException' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Exception/LexerException.php', 'Behat\\Gherkin\\Exception\\NodeException' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Exception/NodeException.php', 'Behat\\Gherkin\\Exception\\ParserException' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Exception/ParserException.php', 'Behat\\Gherkin\\Filter\\ComplexFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilter.php', 'Behat\\Gherkin\\Filter\\ComplexFilterInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php', 'Behat\\Gherkin\\Filter\\FeatureFilterInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php', 'Behat\\Gherkin\\Filter\\FilterInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.php', 'Behat\\Gherkin\\Filter\\LineFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.php', 'Behat\\Gherkin\\Filter\\LineRangeFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php', 'Behat\\Gherkin\\Filter\\NameFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.php', 'Behat\\Gherkin\\Filter\\NarrativeFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php', 'Behat\\Gherkin\\Filter\\PathsFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php', 'Behat\\Gherkin\\Filter\\RoleFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.php', 'Behat\\Gherkin\\Filter\\SimpleFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/SimpleFilter.php', 'Behat\\Gherkin\\Filter\\TagFilter' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php', 'Behat\\Gherkin\\Gherkin' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Gherkin.php', 'Behat\\Gherkin\\Keywords\\ArrayKeywords' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.php', 'Behat\\Gherkin\\Keywords\\CachedArrayKeywords' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Keywords/CachedArrayKeywords.php', 'Behat\\Gherkin\\Keywords\\CucumberKeywords' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Keywords/CucumberKeywords.php', 'Behat\\Gherkin\\Keywords\\KeywordsDumper' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.php', 'Behat\\Gherkin\\Keywords\\KeywordsInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsInterface.php', 'Behat\\Gherkin\\Lexer' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Lexer.php', 'Behat\\Gherkin\\Loader\\AbstractFileLoader' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/AbstractFileLoader.php', 'Behat\\Gherkin\\Loader\\ArrayLoader' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php', 'Behat\\Gherkin\\Loader\\CucumberNDJsonAstLoader' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.php', 'Behat\\Gherkin\\Loader\\DirectoryLoader' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/DirectoryLoader.php', 'Behat\\Gherkin\\Loader\\FileLoaderInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/FileLoaderInterface.php', 'Behat\\Gherkin\\Loader\\GherkinFileLoader' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/GherkinFileLoader.php', 'Behat\\Gherkin\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.php', 'Behat\\Gherkin\\Loader\\YamlFileLoader' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.php', 'Behat\\Gherkin\\Node\\ArgumentInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/ArgumentInterface.php', 'Behat\\Gherkin\\Node\\BackgroundNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php', 'Behat\\Gherkin\\Node\\ExampleNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php', 'Behat\\Gherkin\\Node\\ExampleTableNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php', 'Behat\\Gherkin\\Node\\FeatureNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.php', 'Behat\\Gherkin\\Node\\KeywordNodeInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/KeywordNodeInterface.php', 'Behat\\Gherkin\\Node\\NodeInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/NodeInterface.php', 'Behat\\Gherkin\\Node\\OutlineNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.php', 'Behat\\Gherkin\\Node\\PyStringNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/PyStringNode.php', 'Behat\\Gherkin\\Node\\ScenarioInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/ScenarioInterface.php', 'Behat\\Gherkin\\Node\\ScenarioLikeInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/ScenarioLikeInterface.php', 'Behat\\Gherkin\\Node\\ScenarioNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php', 'Behat\\Gherkin\\Node\\StepContainerInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php', 'Behat\\Gherkin\\Node\\StepNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/StepNode.php', 'Behat\\Gherkin\\Node\\TableNode' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php', 'Behat\\Gherkin\\Node\\TaggedNodeInterface' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php', 'Behat\\Gherkin\\Parser' => __DIR__ . '/..' . '/behat/gherkin/src/Behat/Gherkin/Parser.php', 'Codeception\\Actor' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Actor.php', 'Codeception\\Application' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Application.php', 'Codeception\\Codecept' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Codecept.php', 'Codeception\\Command\\Bootstrap' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Bootstrap.php', 'Codeception\\Command\\Build' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Build.php', 'Codeception\\Command\\Clean' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Clean.php', 'Codeception\\Command\\Completion' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Completion.php', 'Codeception\\Command\\CompletionFallback' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/CompletionFallback.php', 'Codeception\\Command\\ConfigValidate' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/ConfigValidate.php', 'Codeception\\Command\\Console' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Console.php', 'Codeception\\Command\\DryRun' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/DryRun.php', 'Codeception\\Command\\GenerateCept' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateCept.php', 'Codeception\\Command\\GenerateCest' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateCest.php', 'Codeception\\Command\\GenerateEnvironment' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateEnvironment.php', 'Codeception\\Command\\GenerateFeature' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateFeature.php', 'Codeception\\Command\\GenerateGroup' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateGroup.php', 'Codeception\\Command\\GenerateHelper' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateHelper.php', 'Codeception\\Command\\GeneratePageObject' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GeneratePageObject.php', 'Codeception\\Command\\GenerateScenarios' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateScenarios.php', 'Codeception\\Command\\GenerateSnapshot' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateSnapshot.php', 'Codeception\\Command\\GenerateStepObject' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateStepObject.php', 'Codeception\\Command\\GenerateSuite' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateSuite.php', 'Codeception\\Command\\GenerateTest' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GenerateTest.php', 'Codeception\\Command\\GherkinSnippets' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GherkinSnippets.php', 'Codeception\\Command\\GherkinSteps' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/GherkinSteps.php', 'Codeception\\Command\\Init' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Init.php', 'Codeception\\Command\\Run' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Run.php', 'Codeception\\Command\\SelfUpdate' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/SelfUpdate.php', 'Codeception\\Command\\Shared\\Config' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Shared/Config.php', 'Codeception\\Command\\Shared\\FileSystem' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Shared/FileSystem.php', 'Codeception\\Command\\Shared\\Style' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Shared/Style.php', 'Codeception\\Configuration' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Configuration.php', 'Codeception\\Coverage\\Filter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Coverage/Filter.php', 'Codeception\\Coverage\\PhpCodeCoverageFactory' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Coverage/PhpCodeCoverageFactory.php', 'Codeception\\Coverage\\Subscriber\\Local' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Coverage/Subscriber/Local.php', 'Codeception\\Coverage\\Subscriber\\LocalServer' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Coverage/Subscriber/LocalServer.php', 'Codeception\\Coverage\\Subscriber\\Printer' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Coverage/Subscriber/Printer.php', 'Codeception\\Coverage\\Subscriber\\RemoteServer' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Coverage/Subscriber/RemoteServer.php', 'Codeception\\Coverage\\SuiteSubscriber' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Coverage/SuiteSubscriber.php', 'Codeception\\CustomCommandInterface' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/CustomCommandInterface.php', 'Codeception\\Event\\DispatcherWrapper' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Event/DispatcherWrapper.php', 'Codeception\\Event\\FailEvent' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Event/FailEvent.php', 'Codeception\\Event\\PrintResultEvent' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Event/PrintResultEvent.php', 'Codeception\\Event\\StepEvent' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Event/StepEvent.php', 'Codeception\\Event\\SuiteEvent' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Event/SuiteEvent.php', 'Codeception\\Event\\TestEvent' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Event/TestEvent.php', 'Codeception\\Events' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Events.php', 'Codeception\\Example' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Example.php', 'Codeception\\Exception\\ConditionalAssertionFailed' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ConditionalAssertionFailed.php', 'Codeception\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ConfigurationException.php', 'Codeception\\Exception\\ConnectionException' => __DIR__ . '/..' . '/codeception/module-webdriver/src/Codeception/Exception/ConnectionException.php', 'Codeception\\Exception\\ContentNotFound' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ContentNotFound.php', 'Codeception\\Exception\\ElementNotFound' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ElementNotFound.php', 'Codeception\\Exception\\ExtensionException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ExtensionException.php', 'Codeception\\Exception\\ExternalUrlException' => __DIR__ . '/..' . '/codeception/lib-innerbrowser/src/Codeception/Exception/ExternalUrlException.php', 'Codeception\\Exception\\InjectionException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/InjectionException.php', 'Codeception\\Exception\\MalformedLocatorException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/MalformedLocatorException.php', 'Codeception\\Exception\\ModuleConfigException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ModuleConfigException.php', 'Codeception\\Exception\\ModuleConflictException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ModuleConflictException.php', 'Codeception\\Exception\\ModuleException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ModuleException.php', 'Codeception\\Exception\\ModuleRequireException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ModuleRequireException.php', 'Codeception\\Exception\\ParseException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ParseException.php', 'Codeception\\Exception\\RemoteException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/RemoteException.php', 'Codeception\\Exception\\TestParseException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/TestParseException.php', 'Codeception\\Exception\\TestRuntimeException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/TestRuntimeException.php', 'Codeception\\Extension' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Extension.php', 'Codeception\\Extension\\DotReporter' => __DIR__ . '/..' . '/codeception/codeception/ext/DotReporter.php', 'Codeception\\Extension\\Logger' => __DIR__ . '/..' . '/codeception/codeception/ext/Logger.php', 'Codeception\\Extension\\Recorder' => __DIR__ . '/..' . '/codeception/codeception/ext/Recorder.php', 'Codeception\\Extension\\RunBefore' => __DIR__ . '/..' . '/codeception/codeception/ext/RunBefore.php', 'Codeception\\Extension\\RunFailed' => __DIR__ . '/..' . '/codeception/codeception/ext/RunFailed.php', 'Codeception\\Extension\\RunProcess' => __DIR__ . '/..' . '/codeception/codeception/ext/RunProcess.php', 'Codeception\\Extension\\SimpleReporter' => __DIR__ . '/..' . '/codeception/codeception/ext/SimpleReporter.php', 'Codeception\\GroupObject' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/GroupObject.php', 'Codeception\\InitTemplate' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/InitTemplate.php', 'Codeception\\Lib\\Actor\\Shared\\Comment' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Comment.php', 'Codeception\\Lib\\Actor\\Shared\\Friend' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Friend.php', 'Codeception\\Lib\\Actor\\Shared\\Pause' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Pause.php', 'Codeception\\Lib\\Actor\\Shared\\Retry' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Actor/Shared/Retry.php', 'Codeception\\Lib\\Connector\\Guzzle' => __DIR__ . '/..' . '/codeception/module-phpbrowser/src/Codeception/Lib/Connector/Guzzle.php', 'Codeception\\Lib\\Connector\\Shared\\PhpSuperGlobalsConverter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php', 'Codeception\\Lib\\Console\\Colorizer' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Console/Colorizer.php', 'Codeception\\Lib\\Console\\DiffFactory' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Console/DiffFactory.php', 'Codeception\\Lib\\Console\\Message' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Console/Message.php', 'Codeception\\Lib\\Console\\MessageFactory' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Console/MessageFactory.php', 'Codeception\\Lib\\Console\\Output' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Console/Output.php', 'Codeception\\Lib\\Console\\ReplHistory' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Console/ReplHistory.php', 'Codeception\\Lib\\DbPopulator' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/DbPopulator.php', 'Codeception\\Lib\\Di' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Di.php', 'Codeception\\Lib\\Driver\\AmazonSQS' => __DIR__ . '/..' . '/codeception/module-queue/src/Codeception/Lib/Driver/AmazonSQS.php', 'Codeception\\Lib\\Driver\\Beanstalk' => __DIR__ . '/..' . '/codeception/module-queue/src/Codeception/Lib/Driver/Beanstalk.php', 'Codeception\\Lib\\Driver\\Db' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/Driver/Db.php', 'Codeception\\Lib\\Driver\\Iron' => __DIR__ . '/..' . '/codeception/module-queue/src/Codeception/Lib/Driver/Iron.php', 'Codeception\\Lib\\Driver\\MongoDb' => __DIR__ . '/..' . '/codeception/module-mongodb/src/Codeception/Lib/Driver/MongoDb.php', 'Codeception\\Lib\\Driver\\MySql' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/Driver/MySql.php', 'Codeception\\Lib\\Driver\\Oci' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/Driver/Oci.php', 'Codeception\\Lib\\Driver\\Pheanstalk4' => __DIR__ . '/..' . '/codeception/module-queue/src/Codeception/Lib/Driver/Pheanstalk4.php', 'Codeception\\Lib\\Driver\\PostgreSql' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/Driver/PostgreSql.php', 'Codeception\\Lib\\Driver\\SqlSrv' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/Driver/SqlSrv.php', 'Codeception\\Lib\\Driver\\Sqlite' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/Driver/Sqlite.php', 'Codeception\\Lib\\Framework' => __DIR__ . '/..' . '/codeception/lib-innerbrowser/src/Codeception/Lib/Framework.php', 'Codeception\\Lib\\Friend' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Friend.php', 'Codeception\\Lib\\Generator\\Actions' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Actions.php', 'Codeception\\Lib\\Generator\\Actor' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Actor.php', 'Codeception\\Lib\\Generator\\Cept' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Cept.php', 'Codeception\\Lib\\Generator\\Cest' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Cest.php', 'Codeception\\Lib\\Generator\\Feature' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Feature.php', 'Codeception\\Lib\\Generator\\GherkinSnippets' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/GherkinSnippets.php', 'Codeception\\Lib\\Generator\\Group' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Group.php', 'Codeception\\Lib\\Generator\\Helper' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Helper.php', 'Codeception\\Lib\\Generator\\PageObject' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/PageObject.php', 'Codeception\\Lib\\Generator\\Shared\\Classname' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Shared/Classname.php', 'Codeception\\Lib\\Generator\\Snapshot' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Snapshot.php', 'Codeception\\Lib\\Generator\\StepObject' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/StepObject.php', 'Codeception\\Lib\\Generator\\Test' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Generator/Test.php', 'Codeception\\Lib\\GroupManager' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/GroupManager.php', 'Codeception\\Lib\\InnerBrowser' => __DIR__ . '/..' . '/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.php', 'Codeception\\Lib\\Interfaces\\API' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/API.php', 'Codeception\\Lib\\Interfaces\\ActiveRecord' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/ActiveRecord.php', 'Codeception\\Lib\\Interfaces\\ConflictsWithModule' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/ConflictsWithModule.php', 'Codeception\\Lib\\Interfaces\\DataMapper' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/DataMapper.php', 'Codeception\\Lib\\Interfaces\\Db' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Lib/Interfaces/Db.php', 'Codeception\\Lib\\Interfaces\\DependsOnModule' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/DependsOnModule.php', 'Codeception\\Lib\\Interfaces\\DoctrineProvider' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/DoctrineProvider.php', 'Codeception\\Lib\\Interfaces\\ElementLocator' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/ElementLocator.php', 'Codeception\\Lib\\Interfaces\\MultiSession' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/MultiSession.php', 'Codeception\\Lib\\Interfaces\\ORM' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/ORM.php', 'Codeception\\Lib\\Interfaces\\PageSourceSaver' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/PageSourceSaver.php', 'Codeception\\Lib\\Interfaces\\PartedModule' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/PartedModule.php', 'Codeception\\Lib\\Interfaces\\Queue' => __DIR__ . '/..' . '/codeception/module-queue/src/Codeception/Lib/Interfaces/Queue.php', 'Codeception\\Lib\\Interfaces\\Remote' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/Remote.php', 'Codeception\\Lib\\Interfaces\\RequiresPackage' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/RequiresPackage.php', 'Codeception\\Lib\\Interfaces\\ScreenshotSaver' => __DIR__ . '/..' . '/codeception/module-webdriver/src/Codeception/Lib/Interfaces/ScreenshotSaver.php', 'Codeception\\Lib\\Interfaces\\SessionSnapshot' => __DIR__ . '/..' . '/codeception/module-webdriver/src/Codeception/Lib/Interfaces/SessionSnapshot.php', 'Codeception\\Lib\\Interfaces\\Web' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/Web.php', 'Codeception\\Lib\\ModuleContainer' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/ModuleContainer.php', 'Codeception\\Lib\\Notification' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Notification.php', 'Codeception\\Lib\\ParamsLoader' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/ParamsLoader.php', 'Codeception\\Lib\\Parser' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Parser.php', 'Codeception\\Module' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Module.php', 'Codeception\\Module\\AMQP' => __DIR__ . '/..' . '/codeception/module-amqp/src/Codeception/Module/AMQP.php', 'Codeception\\Module\\AbstractAsserts' => __DIR__ . '/..' . '/codeception/module-asserts/src/Codeception/Module/AbstractAsserts.php', 'Codeception\\Module\\Apc' => __DIR__ . '/..' . '/codeception/module-apc/src/Codeception/Module/Apc.php', 'Codeception\\Module\\Asserts' => __DIR__ . '/..' . '/codeception/module-asserts/src/Codeception/Module/Asserts.php', 'Codeception\\Module\\Cli' => __DIR__ . '/..' . '/codeception/module-cli/src/Codeception/Module/Cli.php', 'Codeception\\Module\\Db' => __DIR__ . '/..' . '/codeception/module-db/src/Codeception/Module/Db.php', 'Codeception\\Module\\FTP' => __DIR__ . '/..' . '/codeception/module-ftp/src/Codeception/Module/FTP.php', 'Codeception\\Module\\Filesystem' => __DIR__ . '/..' . '/codeception/module-filesystem/src/Codeception/Module/Filesystem.php', 'Codeception\\Module\\Memcache' => __DIR__ . '/..' . '/codeception/module-memcache/src/Codeception/Module/Memcache.php', 'Codeception\\Module\\MongoDb' => __DIR__ . '/..' . '/codeception/module-mongodb/src/Codeception/Module/MongoDb.php', 'Codeception\\Module\\PhpBrowser' => __DIR__ . '/..' . '/codeception/module-phpbrowser/src/Codeception/Module/PhpBrowser.php', 'Codeception\\Module\\Queue' => __DIR__ . '/..' . '/codeception/module-queue/src/Codeception/Module/Queue.php', 'Codeception\\Module\\REST' => __DIR__ . '/..' . '/codeception/module-rest/src/Codeception/Module/REST.php', 'Codeception\\Module\\Redis' => __DIR__ . '/..' . '/codeception/module-redis/src/Codeception/Module/Redis.php', 'Codeception\\Module\\SOAP' => __DIR__ . '/..' . '/codeception/module-soap/src/Codeception/Module/SOAP.php', 'Codeception\\Module\\Sequence' => __DIR__ . '/..' . '/codeception/module-sequence/src/Codeception/Module/Sequence.php', 'Codeception\\Module\\WebDriver' => __DIR__ . '/..' . '/codeception/module-webdriver/src/Codeception/Module/WebDriver.php', 'Codeception\\PHPUnit\\ConsolePrinter' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/ConsolePrinter.php', 'Codeception\\PHPUnit\\Constraint\\Crawler' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Constraint/Crawler.php', 'Codeception\\PHPUnit\\Constraint\\CrawlerNot' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Constraint/CrawlerNot.php', 'Codeception\\PHPUnit\\Constraint\\JsonContains' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Constraint/JsonContains.php', 'Codeception\\PHPUnit\\Constraint\\JsonType' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Constraint/JsonType.php', 'Codeception\\PHPUnit\\Constraint\\Page' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Constraint/Page.php', 'Codeception\\PHPUnit\\Constraint\\WebDriver' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Constraint/WebDriver.php', 'Codeception\\PHPUnit\\Constraint\\WebDriverNot' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Constraint/WebDriverNot.php', 'Codeception\\PHPUnit\\DispatcherWrapper' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/DispatcherWrapper.php', 'Codeception\\PHPUnit\\FilterTest' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/FilterTest.php', 'Codeception\\PHPUnit\\Init' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Init.php', 'Codeception\\PHPUnit\\Listener' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Listener.php', 'Codeception\\PHPUnit\\Log\\JUnit' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Log/JUnit.php', 'Codeception\\PHPUnit\\Log\\PhpUnit' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Log/PhpUnit.php', 'Codeception\\PHPUnit\\ResultPrinter' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/ResultPrinter.php', 'Codeception\\PHPUnit\\ResultPrinter\\HTML' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/ResultPrinter/HTML.php', 'Codeception\\PHPUnit\\ResultPrinter\\Report' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/ResultPrinter/Report.php', 'Codeception\\PHPUnit\\ResultPrinter\\UI' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/ResultPrinter/UI.php', 'Codeception\\PHPUnit\\Runner' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/Runner.php', 'Codeception\\PHPUnit\\TestCase' => __DIR__ . '/..' . '/codeception/phpunit-wrapper/src/TestCase.php', 'Codeception\\Scenario' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Scenario.php', 'Codeception\\Snapshot' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Snapshot.php', 'Codeception\\Step' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step.php', 'Codeception\\Step\\Action' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Action.php', 'Codeception\\Step\\Argument\\FormattedOutput' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Argument/FormattedOutput.php', 'Codeception\\Step\\Argument\\PasswordArgument' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Argument/PasswordArgument.php', 'Codeception\\Step\\Assertion' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Assertion.php', 'Codeception\\Step\\Comment' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Comment.php', 'Codeception\\Step\\Condition' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Condition.php', 'Codeception\\Step\\ConditionalAssertion' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/ConditionalAssertion.php', 'Codeception\\Step\\Executor' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Executor.php', 'Codeception\\Step\\GeneratedStep' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/GeneratedStep.php', 'Codeception\\Step\\Incomplete' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Incomplete.php', 'Codeception\\Step\\Meta' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Meta.php', 'Codeception\\Step\\Retry' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Retry.php', 'Codeception\\Step\\Skip' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/Skip.php', 'Codeception\\Step\\TryTo' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Step/TryTo.php', 'Codeception\\Stub' => __DIR__ . '/..' . '/codeception/stub/src/Stub.php', 'Codeception\\Stub\\ConsecutiveMap' => __DIR__ . '/..' . '/codeception/stub/src/Stub/ConsecutiveMap.php', 'Codeception\\Stub\\Expected' => __DIR__ . '/..' . '/codeception/stub/src/Stub/Expected.php', 'Codeception\\Stub\\StubMarshaler' => __DIR__ . '/..' . '/codeception/stub/src/Stub/StubMarshaler.php', 'Codeception\\Subscriber\\AutoRebuild' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/AutoRebuild.php', 'Codeception\\Subscriber\\BeforeAfterTest' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/BeforeAfterTest.php', 'Codeception\\Subscriber\\Bootstrap' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/Bootstrap.php', 'Codeception\\Subscriber\\Console' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/Console.php', 'Codeception\\Subscriber\\Dependencies' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/Dependencies.php', 'Codeception\\Subscriber\\ErrorHandler' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/ErrorHandler.php', 'Codeception\\Subscriber\\ExtensionLoader' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/ExtensionLoader.php', 'Codeception\\Subscriber\\FailFast' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/FailFast.php', 'Codeception\\Subscriber\\GracefulTermination' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/GracefulTermination.php', 'Codeception\\Subscriber\\Module' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/Module.php', 'Codeception\\Subscriber\\PrepareTest' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/PrepareTest.php', 'Codeception\\Subscriber\\Shared\\StaticEvents' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/Shared/StaticEvents.php', 'Codeception\\Suite' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Suite.php', 'Codeception\\SuiteManager' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/SuiteManager.php', 'Codeception\\Template\\Acceptance' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Template/Acceptance.php', 'Codeception\\Template\\Api' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Template/Api.php', 'Codeception\\Template\\Bootstrap' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Template/Bootstrap.php', 'Codeception\\Template\\Unit' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Template/Unit.php', 'Codeception\\Template\\Upgrade4' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Template/Upgrade4.php', 'Codeception\\TestInterface' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/TestInterface.php', 'Codeception\\Test\\Cept' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Cept.php', 'Codeception\\Test\\Cest' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Cest.php', 'Codeception\\Test\\Descriptor' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Descriptor.php', 'Codeception\\Test\\Feature\\AssertionCounter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Feature/AssertionCounter.php', 'Codeception\\Test\\Feature\\CodeCoverage' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Feature/CodeCoverage.php', 'Codeception\\Test\\Feature\\ErrorLogger' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Feature/ErrorLogger.php', 'Codeception\\Test\\Feature\\IgnoreIfMetadataBlocked' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Feature/IgnoreIfMetadataBlocked.php', 'Codeception\\Test\\Feature\\MetadataCollector' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Feature/MetadataCollector.php', 'Codeception\\Test\\Feature\\ScenarioLoader' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Feature/ScenarioLoader.php', 'Codeception\\Test\\Feature\\Stub' => __DIR__ . '/..' . '/codeception/stub/src/Test/Feature/Stub.php', 'Codeception\\Test\\Gherkin' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Gherkin.php', 'Codeception\\Test\\Interfaces\\Dependent' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Interfaces/Dependent.php', 'Codeception\\Test\\Interfaces\\Descriptive' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Interfaces/Descriptive.php', 'Codeception\\Test\\Interfaces\\Plain' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Interfaces/Plain.php', 'Codeception\\Test\\Interfaces\\Reported' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Interfaces/Reported.php', 'Codeception\\Test\\Interfaces\\ScenarioDriven' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Interfaces/ScenarioDriven.php', 'Codeception\\Test\\Interfaces\\StrictCoverage' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Interfaces/StrictCoverage.php', 'Codeception\\Test\\Loader' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Loader.php', 'Codeception\\Test\\Loader\\Cept' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Loader/Cept.php', 'Codeception\\Test\\Loader\\Cest' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Loader/Cest.php', 'Codeception\\Test\\Loader\\Gherkin' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Loader/Gherkin.php', 'Codeception\\Test\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Loader/LoaderInterface.php', 'Codeception\\Test\\Loader\\Unit' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Loader/Unit.php', 'Codeception\\Test\\Metadata' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Metadata.php', 'Codeception\\Test\\Test' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Test.php', 'Codeception\\Test\\Unit' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Unit.php', 'Codeception\\Util\\ActionSequence' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/ActionSequence.php', 'Codeception\\Util\\Annotation' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Annotation.php', 'Codeception\\Util\\ArrayContainsComparator' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/ArrayContainsComparator.php', 'Codeception\\Util\\Autoload' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Autoload.php', 'Codeception\\Util\\Debug' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Debug.php', 'Codeception\\Util\\FileSystem' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/FileSystem.php', 'Codeception\\Util\\Fixtures' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Fixtures.php', 'Codeception\\Util\\HttpCode' => __DIR__ . '/..' . '/codeception/lib-innerbrowser/src/Codeception/Util/HttpCode.php', 'Codeception\\Util\\JsonArray' => __DIR__ . '/..' . '/codeception/module-rest/src/Codeception/Util/JsonArray.php', 'Codeception\\Util\\JsonType' => __DIR__ . '/..' . '/codeception/module-rest/src/Codeception/Util/JsonType.php', 'Codeception\\Util\\Locator' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Locator.php', 'Codeception\\Util\\Maybe' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Maybe.php', 'Codeception\\Util\\PathResolver' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/PathResolver.php', 'Codeception\\Util\\ReflectionHelper' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/ReflectionHelper.php', 'Codeception\\Util\\ReflectionPropertyAccessor' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/ReflectionPropertyAccessor.php', 'Codeception\\Util\\Shared\\Asserts' => __DIR__ . '/..' . '/codeception/lib-asserts/src/Codeception/Util/Shared/Asserts.php', 'Codeception\\Util\\Shared\\InheritedAsserts' => __DIR__ . '/..' . '/codeception/lib-asserts/src/Codeception/Util/Shared/InheritedAsserts.php', 'Codeception\\Util\\Shared\\Namespaces' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Shared/Namespaces.php', 'Codeception\\Util\\Soap' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Soap.php', 'Codeception\\Util\\Stub' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Stub.php', 'Codeception\\Util\\Template' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Template.php', 'Codeception\\Util\\Uri' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Uri.php', 'Codeception\\Util\\Xml' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Xml.php', 'Codeception\\Util\\XmlBuilder' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/XmlBuilder.php', 'Codeception\\Util\\XmlStructure' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/XmlStructure.php', 'Composer\\CaBundle\\CaBundle' => __DIR__ . '/..' . '/composer/ca-bundle/src/CaBundle.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'DeepCopy\\DeepCopy' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/DeepCopy.php', 'DeepCopy\\Exception\\CloneException' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php', 'DeepCopy\\Exception\\PropertyException' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php', 'DeepCopy\\Filter\\Doctrine\\DoctrineCollectionFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php', 'DeepCopy\\Filter\\Doctrine\\DoctrineEmptyCollectionFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php', 'DeepCopy\\Filter\\Doctrine\\DoctrineProxyFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php', 'DeepCopy\\Filter\\Filter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php', 'DeepCopy\\Filter\\KeepFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php', 'DeepCopy\\Filter\\ReplaceFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php', 'DeepCopy\\Filter\\SetNullFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php', 'DeepCopy\\Matcher\\Doctrine\\DoctrineProxyMatcher' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php', 'DeepCopy\\Matcher\\Matcher' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php', 'DeepCopy\\Matcher\\PropertyMatcher' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php', 'DeepCopy\\Matcher\\PropertyNameMatcher' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php', 'DeepCopy\\Matcher\\PropertyTypeMatcher' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php', 'DeepCopy\\Reflection\\ReflectionHelper' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php', 'DeepCopy\\TypeFilter\\Date\\DateIntervalFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php', 'DeepCopy\\TypeFilter\\ReplaceFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php', 'DeepCopy\\TypeFilter\\ShallowCopyFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php', 'DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedList' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php', 'DeepCopy\\TypeFilter\\Spl\\SplDoublyLinkedListFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php', 'DeepCopy\\TypeFilter\\TypeFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php', 'DeepCopy\\TypeMatcher\\TypeMatcher' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php', 'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', 'Doctrine\\Instantiator\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.php', 'Doctrine\\Instantiator\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.php', 'Doctrine\\Instantiator\\Exception\\UnexpectedValueException' => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.php', 'Doctrine\\Instantiator\\Instantiator' => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.php', 'Doctrine\\Instantiator\\InstantiatorInterface' => __DIR__ . '/..' . '/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php', 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php', 'Facebook\\WebDriver\\AbstractWebDriverCheckboxOrRadio' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/AbstractWebDriverCheckboxOrRadio.php', 'Facebook\\WebDriver\\Chrome\\ChromeDevToolsDriver' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Chrome/ChromeDevToolsDriver.php', 'Facebook\\WebDriver\\Chrome\\ChromeDriver' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Chrome/ChromeDriver.php', 'Facebook\\WebDriver\\Chrome\\ChromeDriverService' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Chrome/ChromeDriverService.php', 'Facebook\\WebDriver\\Chrome\\ChromeOptions' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Chrome/ChromeOptions.php', 'Facebook\\WebDriver\\Cookie' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Cookie.php', 'Facebook\\WebDriver\\Exception\\DriverServerDiedException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/DriverServerDiedException.php', 'Facebook\\WebDriver\\Exception\\ElementClickInterceptedException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/ElementClickInterceptedException.php', 'Facebook\\WebDriver\\Exception\\ElementNotInteractableException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/ElementNotInteractableException.php', 'Facebook\\WebDriver\\Exception\\ElementNotSelectableException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/ElementNotSelectableException.php', 'Facebook\\WebDriver\\Exception\\ElementNotVisibleException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/ElementNotVisibleException.php', 'Facebook\\WebDriver\\Exception\\ExpectedException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/ExpectedException.php', 'Facebook\\WebDriver\\Exception\\IMEEngineActivationFailedException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/IMEEngineActivationFailedException.php', 'Facebook\\WebDriver\\Exception\\IMENotAvailableException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/IMENotAvailableException.php', 'Facebook\\WebDriver\\Exception\\IndexOutOfBoundsException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/IndexOutOfBoundsException.php', 'Facebook\\WebDriver\\Exception\\InsecureCertificateException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/InsecureCertificateException.php', 'Facebook\\WebDriver\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/InvalidArgumentException.php', 'Facebook\\WebDriver\\Exception\\InvalidCookieDomainException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/InvalidCookieDomainException.php', 'Facebook\\WebDriver\\Exception\\InvalidCoordinatesException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/InvalidCoordinatesException.php', 'Facebook\\WebDriver\\Exception\\InvalidElementStateException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/InvalidElementStateException.php', 'Facebook\\WebDriver\\Exception\\InvalidSelectorException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/InvalidSelectorException.php', 'Facebook\\WebDriver\\Exception\\InvalidSessionIdException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/InvalidSessionIdException.php', 'Facebook\\WebDriver\\Exception\\JavascriptErrorException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/JavascriptErrorException.php', 'Facebook\\WebDriver\\Exception\\MoveTargetOutOfBoundsException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/MoveTargetOutOfBoundsException.php', 'Facebook\\WebDriver\\Exception\\NoAlertOpenException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoAlertOpenException.php', 'Facebook\\WebDriver\\Exception\\NoCollectionException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoCollectionException.php', 'Facebook\\WebDriver\\Exception\\NoScriptResultException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoScriptResultException.php', 'Facebook\\WebDriver\\Exception\\NoStringException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoStringException.php', 'Facebook\\WebDriver\\Exception\\NoStringLengthException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoStringLengthException.php', 'Facebook\\WebDriver\\Exception\\NoStringWrapperException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoStringWrapperException.php', 'Facebook\\WebDriver\\Exception\\NoSuchAlertException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchAlertException.php', 'Facebook\\WebDriver\\Exception\\NoSuchCollectionException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchCollectionException.php', 'Facebook\\WebDriver\\Exception\\NoSuchCookieException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchCookieException.php', 'Facebook\\WebDriver\\Exception\\NoSuchDocumentException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchDocumentException.php', 'Facebook\\WebDriver\\Exception\\NoSuchDriverException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchDriverException.php', 'Facebook\\WebDriver\\Exception\\NoSuchElementException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchElementException.php', 'Facebook\\WebDriver\\Exception\\NoSuchFrameException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchFrameException.php', 'Facebook\\WebDriver\\Exception\\NoSuchWindowException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchWindowException.php', 'Facebook\\WebDriver\\Exception\\NullPointerException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NullPointerException.php', 'Facebook\\WebDriver\\Exception\\ScriptTimeoutException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/ScriptTimeoutException.php', 'Facebook\\WebDriver\\Exception\\SessionNotCreatedException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/SessionNotCreatedException.php', 'Facebook\\WebDriver\\Exception\\StaleElementReferenceException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/StaleElementReferenceException.php', 'Facebook\\WebDriver\\Exception\\TimeoutException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php', 'Facebook\\WebDriver\\Exception\\UnableToCaptureScreenException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnableToCaptureScreenException.php', 'Facebook\\WebDriver\\Exception\\UnableToSetCookieException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnableToSetCookieException.php', 'Facebook\\WebDriver\\Exception\\UnexpectedAlertOpenException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnexpectedAlertOpenException.php', 'Facebook\\WebDriver\\Exception\\UnexpectedJavascriptException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnexpectedJavascriptException.php', 'Facebook\\WebDriver\\Exception\\UnexpectedTagNameException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnexpectedTagNameException.php', 'Facebook\\WebDriver\\Exception\\UnknownCommandException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnknownCommandException.php', 'Facebook\\WebDriver\\Exception\\UnknownErrorException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnknownErrorException.php', 'Facebook\\WebDriver\\Exception\\UnknownMethodException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnknownMethodException.php', 'Facebook\\WebDriver\\Exception\\UnknownServerException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnknownServerException.php', 'Facebook\\WebDriver\\Exception\\UnrecognizedExceptionException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnrecognizedExceptionException.php', 'Facebook\\WebDriver\\Exception\\UnsupportedOperationException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/UnsupportedOperationException.php', 'Facebook\\WebDriver\\Exception\\WebDriverCurlException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/WebDriverCurlException.php', 'Facebook\\WebDriver\\Exception\\WebDriverException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/WebDriverException.php', 'Facebook\\WebDriver\\Exception\\XPathLookupException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/XPathLookupException.php', 'Facebook\\WebDriver\\Firefox\\FirefoxDriver' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Firefox/FirefoxDriver.php', 'Facebook\\WebDriver\\Firefox\\FirefoxDriverService' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Firefox/FirefoxDriverService.php', 'Facebook\\WebDriver\\Firefox\\FirefoxOptions' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Firefox/FirefoxOptions.php', 'Facebook\\WebDriver\\Firefox\\FirefoxPreferences' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Firefox/FirefoxPreferences.php', 'Facebook\\WebDriver\\Firefox\\FirefoxProfile' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Firefox/FirefoxProfile.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverButtonReleaseAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverButtonReleaseAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverClickAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverClickAndHoldAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAndHoldAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverContextClickAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverContextClickAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverCoordinates' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverCoordinates.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverDoubleClickAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverDoubleClickAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverKeyDownAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyDownAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverKeyUpAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyUpAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverKeysRelatedAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeysRelatedAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverMouseAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverMouseMoveAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseMoveAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverMoveToOffsetAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverSendKeysAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSendKeysAction.php', 'Facebook\\WebDriver\\Interactions\\Internal\\WebDriverSingleKeyAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSingleKeyAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverDoubleTapAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDoubleTapAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverDownAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDownAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverFlickAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverFlickFromElementAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickFromElementAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverLongPressAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverLongPressAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverMoveAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverMoveAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverScrollAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverScrollFromElementAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollFromElementAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverTapAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTapAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverTouchAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchAction.php', 'Facebook\\WebDriver\\Interactions\\Touch\\WebDriverTouchScreen' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchScreen.php', 'Facebook\\WebDriver\\Interactions\\WebDriverActions' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/WebDriverActions.php', 'Facebook\\WebDriver\\Interactions\\WebDriverCompositeAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/WebDriverCompositeAction.php', 'Facebook\\WebDriver\\Interactions\\WebDriverTouchActions' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Interactions/WebDriverTouchActions.php', 'Facebook\\WebDriver\\Internal\\WebDriverLocatable' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Internal/WebDriverLocatable.php', 'Facebook\\WebDriver\\JavaScriptExecutor' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/JavaScriptExecutor.php', 'Facebook\\WebDriver\\Local\\LocalWebDriver' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Local/LocalWebDriver.php', 'Facebook\\WebDriver\\Net\\URLChecker' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Net/URLChecker.php', 'Facebook\\WebDriver\\Remote\\CustomWebDriverCommand' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/CustomWebDriverCommand.php', 'Facebook\\WebDriver\\Remote\\DesiredCapabilities' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/DesiredCapabilities.php', 'Facebook\\WebDriver\\Remote\\DriverCommand' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/DriverCommand.php', 'Facebook\\WebDriver\\Remote\\ExecuteMethod' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/ExecuteMethod.php', 'Facebook\\WebDriver\\Remote\\FileDetector' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/FileDetector.php', 'Facebook\\WebDriver\\Remote\\HttpCommandExecutor' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/HttpCommandExecutor.php', 'Facebook\\WebDriver\\Remote\\JsonWireCompat' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/JsonWireCompat.php', 'Facebook\\WebDriver\\Remote\\LocalFileDetector' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/LocalFileDetector.php', 'Facebook\\WebDriver\\Remote\\RemoteExecuteMethod' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteExecuteMethod.php', 'Facebook\\WebDriver\\Remote\\RemoteKeyboard' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteKeyboard.php', 'Facebook\\WebDriver\\Remote\\RemoteMouse' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteMouse.php', 'Facebook\\WebDriver\\Remote\\RemoteStatus' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteStatus.php', 'Facebook\\WebDriver\\Remote\\RemoteTargetLocator' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteTargetLocator.php', 'Facebook\\WebDriver\\Remote\\RemoteTouchScreen' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteTouchScreen.php', 'Facebook\\WebDriver\\Remote\\RemoteWebDriver' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteWebDriver.php', 'Facebook\\WebDriver\\Remote\\RemoteWebElement' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/RemoteWebElement.php', 'Facebook\\WebDriver\\Remote\\Service\\DriverCommandExecutor' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/Service/DriverCommandExecutor.php', 'Facebook\\WebDriver\\Remote\\Service\\DriverService' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/Service/DriverService.php', 'Facebook\\WebDriver\\Remote\\UselessFileDetector' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/UselessFileDetector.php', 'Facebook\\WebDriver\\Remote\\WebDriverBrowserType' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/WebDriverBrowserType.php', 'Facebook\\WebDriver\\Remote\\WebDriverCapabilityType' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/WebDriverCapabilityType.php', 'Facebook\\WebDriver\\Remote\\WebDriverCommand' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/WebDriverCommand.php', 'Facebook\\WebDriver\\Remote\\WebDriverResponse' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/WebDriverResponse.php', 'Facebook\\WebDriver\\Support\\Events\\EventFiringWebDriver' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriver.php', 'Facebook\\WebDriver\\Support\\Events\\EventFiringWebDriverNavigation' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriverNavigation.php', 'Facebook\\WebDriver\\Support\\Events\\EventFiringWebElement' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Support/Events/EventFiringWebElement.php', 'Facebook\\WebDriver\\Support\\XPathEscaper' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Support/XPathEscaper.php', 'Facebook\\WebDriver\\WebDriver' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriver.php', 'Facebook\\WebDriver\\WebDriverAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverAction.php', 'Facebook\\WebDriver\\WebDriverAlert' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverAlert.php', 'Facebook\\WebDriver\\WebDriverBy' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverBy.php', 'Facebook\\WebDriver\\WebDriverCapabilities' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverCapabilities.php', 'Facebook\\WebDriver\\WebDriverCheckboxes' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverCheckboxes.php', 'Facebook\\WebDriver\\WebDriverCommandExecutor' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverCommandExecutor.php', 'Facebook\\WebDriver\\WebDriverDimension' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverDimension.php', 'Facebook\\WebDriver\\WebDriverDispatcher' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverDispatcher.php', 'Facebook\\WebDriver\\WebDriverElement' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverElement.php', 'Facebook\\WebDriver\\WebDriverEventListener' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverEventListener.php', 'Facebook\\WebDriver\\WebDriverExpectedCondition' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverExpectedCondition.php', 'Facebook\\WebDriver\\WebDriverHasInputDevices' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverHasInputDevices.php', 'Facebook\\WebDriver\\WebDriverKeyboard' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverKeyboard.php', 'Facebook\\WebDriver\\WebDriverKeys' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverKeys.php', 'Facebook\\WebDriver\\WebDriverMouse' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverMouse.php', 'Facebook\\WebDriver\\WebDriverNavigation' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverNavigation.php', 'Facebook\\WebDriver\\WebDriverNavigationInterface' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverNavigationInterface.php', 'Facebook\\WebDriver\\WebDriverOptions' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverOptions.php', 'Facebook\\WebDriver\\WebDriverPlatform' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverPlatform.php', 'Facebook\\WebDriver\\WebDriverPoint' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverPoint.php', 'Facebook\\WebDriver\\WebDriverRadios' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverRadios.php', 'Facebook\\WebDriver\\WebDriverSearchContext' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverSearchContext.php', 'Facebook\\WebDriver\\WebDriverSelect' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverSelect.php', 'Facebook\\WebDriver\\WebDriverSelectInterface' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverSelectInterface.php', 'Facebook\\WebDriver\\WebDriverTargetLocator' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverTargetLocator.php', 'Facebook\\WebDriver\\WebDriverTimeouts' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverTimeouts.php', 'Facebook\\WebDriver\\WebDriverUpAction' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverUpAction.php', 'Facebook\\WebDriver\\WebDriverWait' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverWait.php', 'Facebook\\WebDriver\\WebDriverWindow' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/WebDriverWindow.php', 'File_Iterator' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Iterator.php', 'File_Iterator_Facade' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Facade.php', 'File_Iterator_Factory' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Factory.php', 'Flow\\JSONPath\\AccessHelper' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/AccessHelper.php', 'Flow\\JSONPath\\Filters\\AbstractFilter' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/Filters/AbstractFilter.php', 'Flow\\JSONPath\\Filters\\IndexFilter' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/Filters/IndexFilter.php', 'Flow\\JSONPath\\Filters\\IndexesFilter' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/Filters/IndexesFilter.php', 'Flow\\JSONPath\\Filters\\QueryMatchFilter' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/Filters/QueryMatchFilter.php', 'Flow\\JSONPath\\Filters\\QueryResultFilter' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/Filters/QueryResultFilter.php', 'Flow\\JSONPath\\Filters\\RecursiveFilter' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/Filters/RecursiveFilter.php', 'Flow\\JSONPath\\Filters\\SliceFilter' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/Filters/SliceFilter.php', 'Flow\\JSONPath\\JSONPath' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/JSONPath.php', 'Flow\\JSONPath\\JSONPathException' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/JSONPathException.php', 'Flow\\JSONPath\\JSONPathLexer' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/JSONPathLexer.php', 'Flow\\JSONPath\\JSONPathToken' => __DIR__ . '/..' . '/softcreatr/jsonpath/src/JSONPathToken.php', 'GuzzleHttp\\Client' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Client.php', 'GuzzleHttp\\ClientInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ClientInterface.php', 'GuzzleHttp\\Cookie\\CookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php', 'GuzzleHttp\\Cookie\\CookieJarInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php', 'GuzzleHttp\\Cookie\\FileCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php', 'GuzzleHttp\\Cookie\\SessionCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php', 'GuzzleHttp\\Cookie\\SetCookie' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php', 'GuzzleHttp\\Exception\\BadResponseException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php', 'GuzzleHttp\\Exception\\ClientException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ClientException.php', 'GuzzleHttp\\Exception\\ConnectException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ConnectException.php', 'GuzzleHttp\\Exception\\GuzzleException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/GuzzleException.php', 'GuzzleHttp\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.php', 'GuzzleHttp\\Exception\\RequestException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/RequestException.php', 'GuzzleHttp\\Exception\\SeekException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/SeekException.php', 'GuzzleHttp\\Exception\\ServerException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ServerException.php', 'GuzzleHttp\\Exception\\TooManyRedirectsException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php', 'GuzzleHttp\\Exception\\TransferException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TransferException.php', 'GuzzleHttp\\HandlerStack' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/HandlerStack.php', 'GuzzleHttp\\Handler\\CurlFactory' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlFactory.php', 'GuzzleHttp\\Handler\\CurlFactoryInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php', 'GuzzleHttp\\Handler\\CurlHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php', 'GuzzleHttp\\Handler\\CurlMultiHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php', 'GuzzleHttp\\Handler\\EasyHandle' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php', 'GuzzleHttp\\Handler\\MockHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/MockHandler.php', 'GuzzleHttp\\Handler\\Proxy' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/Proxy.php', 'GuzzleHttp\\Handler\\StreamHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php', 'GuzzleHttp\\MessageFormatter' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatter.php', 'GuzzleHttp\\Middleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Middleware.php', 'GuzzleHttp\\Pool' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Pool.php', 'GuzzleHttp\\PrepareBodyMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php', 'GuzzleHttp\\Promise\\AggregateException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/AggregateException.php', 'GuzzleHttp\\Promise\\CancellationException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/CancellationException.php', 'GuzzleHttp\\Promise\\Coroutine' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Coroutine.php', 'GuzzleHttp\\Promise\\Create' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Create.php', 'GuzzleHttp\\Promise\\Each' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Each.php', 'GuzzleHttp\\Promise\\EachPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/EachPromise.php', 'GuzzleHttp\\Promise\\FulfilledPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/FulfilledPromise.php', 'GuzzleHttp\\Promise\\Is' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Is.php', 'GuzzleHttp\\Promise\\Promise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Promise.php', 'GuzzleHttp\\Promise\\PromiseInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromiseInterface.php', 'GuzzleHttp\\Promise\\PromisorInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromisorInterface.php', 'GuzzleHttp\\Promise\\RejectedPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/RejectedPromise.php', 'GuzzleHttp\\Promise\\RejectionException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/RejectionException.php', 'GuzzleHttp\\Promise\\TaskQueue' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueue.php', 'GuzzleHttp\\Promise\\TaskQueueInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueueInterface.php', 'GuzzleHttp\\Promise\\Utils' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Utils.php', 'GuzzleHttp\\Psr7\\AppendStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/AppendStream.php', 'GuzzleHttp\\Psr7\\BufferStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/BufferStream.php', 'GuzzleHttp\\Psr7\\CachingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/CachingStream.php', 'GuzzleHttp\\Psr7\\DroppingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/DroppingStream.php', 'GuzzleHttp\\Psr7\\FnStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/FnStream.php', 'GuzzleHttp\\Psr7\\Header' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Header.php', 'GuzzleHttp\\Psr7\\InflateStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/InflateStream.php', 'GuzzleHttp\\Psr7\\LazyOpenStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LazyOpenStream.php', 'GuzzleHttp\\Psr7\\LimitStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LimitStream.php', 'GuzzleHttp\\Psr7\\Message' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Message.php', 'GuzzleHttp\\Psr7\\MessageTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MessageTrait.php', 'GuzzleHttp\\Psr7\\MimeType' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MimeType.php', 'GuzzleHttp\\Psr7\\MultipartStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MultipartStream.php', 'GuzzleHttp\\Psr7\\NoSeekStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/NoSeekStream.php', 'GuzzleHttp\\Psr7\\PumpStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/PumpStream.php', 'GuzzleHttp\\Psr7\\Query' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Query.php', 'GuzzleHttp\\Psr7\\Request' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Request.php', 'GuzzleHttp\\Psr7\\Response' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Response.php', 'GuzzleHttp\\Psr7\\Rfc7230' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Rfc7230.php', 'GuzzleHttp\\Psr7\\ServerRequest' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/ServerRequest.php', 'GuzzleHttp\\Psr7\\Stream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Stream.php', 'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php', 'GuzzleHttp\\Psr7\\StreamWrapper' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamWrapper.php', 'GuzzleHttp\\Psr7\\UploadedFile' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UploadedFile.php', 'GuzzleHttp\\Psr7\\Uri' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Uri.php', 'GuzzleHttp\\Psr7\\UriComparator' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriComparator.php', 'GuzzleHttp\\Psr7\\UriNormalizer' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriNormalizer.php', 'GuzzleHttp\\Psr7\\UriResolver' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriResolver.php', 'GuzzleHttp\\Psr7\\Utils' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Utils.php', 'GuzzleHttp\\RedirectMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RedirectMiddleware.php', 'GuzzleHttp\\RequestOptions' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RequestOptions.php', 'GuzzleHttp\\RetryMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RetryMiddleware.php', 'GuzzleHttp\\TransferStats' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/TransferStats.php', 'GuzzleHttp\\UriTemplate' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/UriTemplate.php', 'GuzzleHttp\\Utils' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Utils.php', 'Hoa\\Consistency\\Autoloader' => __DIR__ . '/..' . '/hoa/consistency/Autoloader.php', 'Hoa\\Consistency\\Consistency' => __DIR__ . '/..' . '/hoa/consistency/Consistency.php', 'Hoa\\Consistency\\Exception' => __DIR__ . '/..' . '/hoa/consistency/Exception.php', 'Hoa\\Consistency\\Test\\Unit\\Autoloader' => __DIR__ . '/..' . '/hoa/consistency/Test/Unit/Autoloader.php', 'Hoa\\Consistency\\Test\\Unit\\Consistency' => __DIR__ . '/..' . '/hoa/consistency/Test/Unit/Consistency.php', 'Hoa\\Consistency\\Test\\Unit\\Exception' => __DIR__ . '/..' . '/hoa/consistency/Test/Unit/Exception.php', 'Hoa\\Consistency\\Test\\Unit\\Xcallable' => __DIR__ . '/..' . '/hoa/consistency/Test/Unit/Xcallable.php', 'Hoa\\Consistency\\Xcallable' => __DIR__ . '/..' . '/hoa/consistency/Xcallable.php', 'Hoa\\Console\\Bin\\Termcap' => __DIR__ . '/..' . '/hoa/console/Bin/Termcap.php', 'Hoa\\Console\\Chrome\\Editor' => __DIR__ . '/..' . '/hoa/console/Chrome/Editor.php', 'Hoa\\Console\\Chrome\\Exception' => __DIR__ . '/..' . '/hoa/console/Chrome/Exception.php', 'Hoa\\Console\\Chrome\\Pager' => __DIR__ . '/..' . '/hoa/console/Chrome/Pager.php', 'Hoa\\Console\\Chrome\\Text' => __DIR__ . '/..' . '/hoa/console/Chrome/Text.php', 'Hoa\\Console\\Console' => __DIR__ . '/..' . '/hoa/console/Console.php', 'Hoa\\Console\\Cursor' => __DIR__ . '/..' . '/hoa/console/Cursor.php', 'Hoa\\Console\\Dispatcher\\Kit' => __DIR__ . '/..' . '/hoa/console/Dispatcher/Kit.php', 'Hoa\\Console\\Exception' => __DIR__ . '/..' . '/hoa/console/Exception.php', 'Hoa\\Console\\GetOption' => __DIR__ . '/..' . '/hoa/console/GetOption.php', 'Hoa\\Console\\Input' => __DIR__ . '/..' . '/hoa/console/Input.php', 'Hoa\\Console\\Mouse' => __DIR__ . '/..' . '/hoa/console/Mouse.php', 'Hoa\\Console\\Output' => __DIR__ . '/..' . '/hoa/console/Output.php', 'Hoa\\Console\\Parser' => __DIR__ . '/..' . '/hoa/console/Parser.php', 'Hoa\\Console\\Processus' => __DIR__ . '/..' . '/hoa/console/Processus.php', 'Hoa\\Console\\Readline\\Autocompleter\\Aggregate' => __DIR__ . '/..' . '/hoa/console/Readline/Autocompleter/Aggregate.php', 'Hoa\\Console\\Readline\\Autocompleter\\Autocompleter' => __DIR__ . '/..' . '/hoa/console/Readline/Autocompleter/Autocompleter.php', 'Hoa\\Console\\Readline\\Autocompleter\\Path' => __DIR__ . '/..' . '/hoa/console/Readline/Autocompleter/Path.php', 'Hoa\\Console\\Readline\\Autocompleter\\Word' => __DIR__ . '/..' . '/hoa/console/Readline/Autocompleter/Word.php', 'Hoa\\Console\\Readline\\Password' => __DIR__ . '/..' . '/hoa/console/Readline/Password.php', 'Hoa\\Console\\Readline\\Readline' => __DIR__ . '/..' . '/hoa/console/Readline/Readline.php', 'Hoa\\Console\\Test\\Unit\\Console' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Console.php', 'Hoa\\Console\\Test\\Unit\\Cursor' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Cursor.php', 'Hoa\\Console\\Test\\Unit\\GetOption' => __DIR__ . '/..' . '/hoa/console/Test/Unit/GetOption.php', 'Hoa\\Console\\Test\\Unit\\Input' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Input.php', 'Hoa\\Console\\Test\\Unit\\Mouse' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Mouse.php', 'Hoa\\Console\\Test\\Unit\\Output' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Output.php', 'Hoa\\Console\\Test\\Unit\\Parser' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Parser.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Autocompleter\\Aggregate' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Readline/Autocompleter/Aggregate.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Autocompleter\\Path' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Readline/Autocompleter/Path.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Autocompleter\\Word' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Readline/Autocompleter/Word.php', 'Hoa\\Console\\Test\\Unit\\Readline\\Password' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Readline/Password.php', 'Hoa\\Console\\Test\\Unit\\Tput' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Tput.php', 'Hoa\\Console\\Test\\Unit\\Window' => __DIR__ . '/..' . '/hoa/console/Test/Unit/Window.php', 'Hoa\\Console\\Tput' => __DIR__ . '/..' . '/hoa/console/Tput.php', 'Hoa\\Console\\Window' => __DIR__ . '/..' . '/hoa/console/Window.php', 'Hoa\\Event\\Bucket' => __DIR__ . '/..' . '/hoa/event/Bucket.php', 'Hoa\\Event\\Event' => __DIR__ . '/..' . '/hoa/event/Event.php', 'Hoa\\Event\\Exception' => __DIR__ . '/..' . '/hoa/event/Exception.php', 'Hoa\\Event\\Listenable' => __DIR__ . '/..' . '/hoa/event/Listenable.php', 'Hoa\\Event\\Listener' => __DIR__ . '/..' . '/hoa/event/Listener.php', 'Hoa\\Event\\Listens' => __DIR__ . '/..' . '/hoa/event/Listens.php', 'Hoa\\Event\\Source' => __DIR__ . '/..' . '/hoa/event/Source.php', 'Hoa\\Event\\Test\\Unit\\Bucket' => __DIR__ . '/..' . '/hoa/event/Test/Unit/Bucket.php', 'Hoa\\Event\\Test\\Unit\\Event' => __DIR__ . '/..' . '/hoa/event/Test/Unit/Event.php', 'Hoa\\Event\\Test\\Unit\\Exception' => __DIR__ . '/..' . '/hoa/event/Test/Unit/Exception.php', 'Hoa\\Event\\Test\\Unit\\Listenable' => __DIR__ . '/..' . '/hoa/event/Test/Unit/Listenable.php', 'Hoa\\Event\\Test\\Unit\\Listener' => __DIR__ . '/..' . '/hoa/event/Test/Unit/Listener.php', 'Hoa\\Event\\Test\\Unit\\Listens' => __DIR__ . '/..' . '/hoa/event/Test/Unit/Listens.php', 'Hoa\\Event\\Test\\Unit\\Source' => __DIR__ . '/..' . '/hoa/event/Test/Unit/Source.php', 'Hoa\\Exception\\Error' => __DIR__ . '/..' . '/hoa/exception/Error.php', 'Hoa\\Exception\\Exception' => __DIR__ . '/..' . '/hoa/exception/Exception.php', 'Hoa\\Exception\\Group' => __DIR__ . '/..' . '/hoa/exception/Group.php', 'Hoa\\Exception\\Idle' => __DIR__ . '/..' . '/hoa/exception/Idle.php', 'Hoa\\Exception\\Test\\Unit\\Error' => __DIR__ . '/..' . '/hoa/exception/Test/Unit/Error.php', 'Hoa\\Exception\\Test\\Unit\\Exception' => __DIR__ . '/..' . '/hoa/exception/Test/Unit/Exception.php', 'Hoa\\Exception\\Test\\Unit\\Group' => __DIR__ . '/..' . '/hoa/exception/Test/Unit/Group.php', 'Hoa\\Exception\\Test\\Unit\\Idle' => __DIR__ . '/..' . '/hoa/exception/Test/Unit/Idle.php', 'Hoa\\File\\Directory' => __DIR__ . '/..' . '/hoa/file/Directory.php', 'Hoa\\File\\Exception\\Exception' => __DIR__ . '/..' . '/hoa/file/Exception/Exception.php', 'Hoa\\File\\Exception\\FileDoesNotExist' => __DIR__ . '/..' . '/hoa/file/Exception/FileDoesNotExist.php', 'Hoa\\File\\File' => __DIR__ . '/..' . '/hoa/file/File.php', 'Hoa\\File\\Finder' => __DIR__ . '/..' . '/hoa/file/Finder.php', 'Hoa\\File\\Generic' => __DIR__ . '/..' . '/hoa/file/Generic.php', 'Hoa\\File\\Link\\Link' => __DIR__ . '/..' . '/hoa/file/Link/Link.php', 'Hoa\\File\\Link\\Read' => __DIR__ . '/..' . '/hoa/file/Link/Read.php', 'Hoa\\File\\Link\\ReadWrite' => __DIR__ . '/..' . '/hoa/file/Link/ReadWrite.php', 'Hoa\\File\\Link\\Write' => __DIR__ . '/..' . '/hoa/file/Link/Write.php', 'Hoa\\File\\Read' => __DIR__ . '/..' . '/hoa/file/Read.php', 'Hoa\\File\\ReadWrite' => __DIR__ . '/..' . '/hoa/file/ReadWrite.php', 'Hoa\\File\\SplFileInfo' => __DIR__ . '/..' . '/hoa/file/SplFileInfo.php', 'Hoa\\File\\Temporary\\Read' => __DIR__ . '/..' . '/hoa/file/Temporary/Read.php', 'Hoa\\File\\Temporary\\ReadWrite' => __DIR__ . '/..' . '/hoa/file/Temporary/ReadWrite.php', 'Hoa\\File\\Temporary\\Temporary' => __DIR__ . '/..' . '/hoa/file/Temporary/Temporary.php', 'Hoa\\File\\Temporary\\Write' => __DIR__ . '/..' . '/hoa/file/Temporary/Write.php', 'Hoa\\File\\Watcher' => __DIR__ . '/..' . '/hoa/file/Watcher.php', 'Hoa\\File\\Write' => __DIR__ . '/..' . '/hoa/file/Write.php', 'Hoa\\Iterator\\Aggregate' => __DIR__ . '/..' . '/hoa/iterator/Aggregate.php', 'Hoa\\Iterator\\Append' => __DIR__ . '/..' . '/hoa/iterator/Append.php', 'Hoa\\Iterator\\Buffer' => __DIR__ . '/..' . '/hoa/iterator/Buffer.php', 'Hoa\\Iterator\\CallbackFilter' => __DIR__ . '/..' . '/hoa/iterator/CallbackFilter.php', 'Hoa\\Iterator\\CallbackGenerator' => __DIR__ . '/..' . '/hoa/iterator/CallbackGenerator.php', 'Hoa\\Iterator\\Counter' => __DIR__ . '/..' . '/hoa/iterator/Counter.php', 'Hoa\\Iterator\\Demultiplexer' => __DIR__ . '/..' . '/hoa/iterator/Demultiplexer.php', 'Hoa\\Iterator\\Directory' => __DIR__ . '/..' . '/hoa/iterator/Directory.php', 'Hoa\\Iterator\\Exception' => __DIR__ . '/..' . '/hoa/iterator/Exception.php', 'Hoa\\Iterator\\FileSystem' => __DIR__ . '/..' . '/hoa/iterator/FileSystem.php', 'Hoa\\Iterator\\Filter' => __DIR__ . '/..' . '/hoa/iterator/Filter.php', 'Hoa\\Iterator\\Glob' => __DIR__ . '/..' . '/hoa/iterator/Glob.php', 'Hoa\\Iterator\\Infinite' => __DIR__ . '/..' . '/hoa/iterator/Infinite.php', 'Hoa\\Iterator\\Iterator' => __DIR__ . '/..' . '/hoa/iterator/Iterator.php', 'Hoa\\Iterator\\IteratorIterator' => __DIR__ . '/..' . '/hoa/iterator/IteratorIterator.php', 'Hoa\\Iterator\\Limit' => __DIR__ . '/..' . '/hoa/iterator/Limit.php', 'Hoa\\Iterator\\Lookahead' => __DIR__ . '/..' . '/hoa/iterator/Lookahead.php', 'Hoa\\Iterator\\Lookbehind' => __DIR__ . '/..' . '/hoa/iterator/Lookbehind.php', 'Hoa\\Iterator\\Map' => __DIR__ . '/..' . '/hoa/iterator/Map.php', 'Hoa\\Iterator\\Mock' => __DIR__ . '/..' . '/hoa/iterator/Mock.php', 'Hoa\\Iterator\\Multiple' => __DIR__ . '/..' . '/hoa/iterator/Multiple.php', 'Hoa\\Iterator\\NoRewind' => __DIR__ . '/..' . '/hoa/iterator/NoRewind.php', 'Hoa\\Iterator\\Outer' => __DIR__ . '/..' . '/hoa/iterator/Outer.php', 'Hoa\\Iterator\\Recursive\\CallbackFilter' => __DIR__ . '/..' . '/hoa/iterator/Recursive/CallbackFilter.php', 'Hoa\\Iterator\\Recursive\\Directory' => __DIR__ . '/..' . '/hoa/iterator/Recursive/Directory.php', 'Hoa\\Iterator\\Recursive\\Filter' => __DIR__ . '/..' . '/hoa/iterator/Recursive/Filter.php', 'Hoa\\Iterator\\Recursive\\Iterator' => __DIR__ . '/..' . '/hoa/iterator/Recursive/Iterator.php', 'Hoa\\Iterator\\Recursive\\Map' => __DIR__ . '/..' . '/hoa/iterator/Recursive/Map.php', 'Hoa\\Iterator\\Recursive\\Mock' => __DIR__ . '/..' . '/hoa/iterator/Recursive/Mock.php', 'Hoa\\Iterator\\Recursive\\Recursive' => __DIR__ . '/..' . '/hoa/iterator/Recursive/Recursive.php', 'Hoa\\Iterator\\Recursive\\RegularExpression' => __DIR__ . '/..' . '/hoa/iterator/Recursive/RegularExpression.php', 'Hoa\\Iterator\\RegularExpression' => __DIR__ . '/..' . '/hoa/iterator/RegularExpression.php', 'Hoa\\Iterator\\Repeater' => __DIR__ . '/..' . '/hoa/iterator/Repeater.php', 'Hoa\\Iterator\\Seekable' => __DIR__ . '/..' . '/hoa/iterator/Seekable.php', 'Hoa\\Iterator\\SplFileInfo' => __DIR__ . '/..' . '/hoa/iterator/SplFileInfo.php', 'Hoa\\Iterator\\Test\\Unit\\Append' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Append.php', 'Hoa\\Iterator\\Test\\Unit\\Buffer' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Buffer.php', 'Hoa\\Iterator\\Test\\Unit\\CallbackFilter' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/CallbackFilter.php', 'Hoa\\Iterator\\Test\\Unit\\CallbackGenerator' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/CallbackGenerator.php', 'Hoa\\Iterator\\Test\\Unit\\Counter' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Counter.php', 'Hoa\\Iterator\\Test\\Unit\\Demultiplexer' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Demultiplexer.php', 'Hoa\\Iterator\\Test\\Unit\\Directory' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Directory.php', 'Hoa\\Iterator\\Test\\Unit\\FileSystem' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/FileSystem.php', 'Hoa\\Iterator\\Test\\Unit\\Filter' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Filter.php', 'Hoa\\Iterator\\Test\\Unit\\Infinite' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Infinite.php', 'Hoa\\Iterator\\Test\\Unit\\IteratorIterator' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/IteratorIterator.php', 'Hoa\\Iterator\\Test\\Unit\\Limit' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Limit.php', 'Hoa\\Iterator\\Test\\Unit\\Lookahead' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Lookahead.php', 'Hoa\\Iterator\\Test\\Unit\\Lookbehind' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Lookbehind.php', 'Hoa\\Iterator\\Test\\Unit\\Map' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Map.php', 'Hoa\\Iterator\\Test\\Unit\\Mock' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Mock.php', 'Hoa\\Iterator\\Test\\Unit\\Multiple' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Multiple.php', 'Hoa\\Iterator\\Test\\Unit\\NoRewind' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/NoRewind.php', 'Hoa\\Iterator\\Test\\Unit\\RegularExpression' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/RegularExpression.php', 'Hoa\\Iterator\\Test\\Unit\\Repeater' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/Repeater.php', 'Hoa\\Iterator\\Test\\Unit\\SplFileInfo' => __DIR__ . '/..' . '/hoa/iterator/Test/Unit/SplFileInfo.php', 'Hoa\\Protocol\\Bin\\Resolve' => __DIR__ . '/..' . '/hoa/protocol/Bin/Resolve.php', 'Hoa\\Protocol\\Exception' => __DIR__ . '/..' . '/hoa/protocol/Exception.php', 'Hoa\\Protocol\\Node\\Library' => __DIR__ . '/..' . '/hoa/protocol/Node/Library.php', 'Hoa\\Protocol\\Node\\Node' => __DIR__ . '/..' . '/hoa/protocol/Node/Node.php', 'Hoa\\Protocol\\Protocol' => __DIR__ . '/..' . '/hoa/protocol/Protocol.php', 'Hoa\\Protocol\\Test\\Unit\\Exception' => __DIR__ . '/..' . '/hoa/protocol/Test/Unit/Exception.php', 'Hoa\\Protocol\\Test\\Unit\\Node\\Library' => __DIR__ . '/..' . '/hoa/protocol/Test/Unit/Node/Library.php', 'Hoa\\Protocol\\Test\\Unit\\Node\\Node' => __DIR__ . '/..' . '/hoa/protocol/Test/Unit/Node/Node.php', 'Hoa\\Protocol\\Test\\Unit\\Protocol' => __DIR__ . '/..' . '/hoa/protocol/Test/Unit/Protocol.php', 'Hoa\\Protocol\\Test\\Unit\\Wrapper' => __DIR__ . '/..' . '/hoa/protocol/Test/Unit/Wrapper.php', 'Hoa\\Protocol\\Wrapper' => __DIR__ . '/..' . '/hoa/protocol/Wrapper.php', 'Hoa\\Stream\\Bucket' => __DIR__ . '/..' . '/hoa/stream/Bucket.php', 'Hoa\\Stream\\Composite' => __DIR__ . '/..' . '/hoa/stream/Composite.php', 'Hoa\\Stream\\Context' => __DIR__ . '/..' . '/hoa/stream/Context.php', 'Hoa\\Stream\\Exception' => __DIR__ . '/..' . '/hoa/stream/Exception.php', 'Hoa\\Stream\\Filter\\Basic' => __DIR__ . '/..' . '/hoa/stream/Filter/Basic.php', 'Hoa\\Stream\\Filter\\Exception' => __DIR__ . '/..' . '/hoa/stream/Filter/Exception.php', 'Hoa\\Stream\\Filter\\Filter' => __DIR__ . '/..' . '/hoa/stream/Filter/Filter.php', 'Hoa\\Stream\\Filter\\LateComputed' => __DIR__ . '/..' . '/hoa/stream/Filter/LateComputed.php', 'Hoa\\Stream\\IStream\\Bufferable' => __DIR__ . '/..' . '/hoa/stream/IStream/Bufferable.php', 'Hoa\\Stream\\IStream\\In' => __DIR__ . '/..' . '/hoa/stream/IStream/In.php', 'Hoa\\Stream\\IStream\\Lockable' => __DIR__ . '/..' . '/hoa/stream/IStream/Lockable.php', 'Hoa\\Stream\\IStream\\Out' => __DIR__ . '/..' . '/hoa/stream/IStream/Out.php', 'Hoa\\Stream\\IStream\\Pathable' => __DIR__ . '/..' . '/hoa/stream/IStream/Pathable.php', 'Hoa\\Stream\\IStream\\Pointable' => __DIR__ . '/..' . '/hoa/stream/IStream/Pointable.php', 'Hoa\\Stream\\IStream\\Statable' => __DIR__ . '/..' . '/hoa/stream/IStream/Statable.php', 'Hoa\\Stream\\IStream\\Stream' => __DIR__ . '/..' . '/hoa/stream/IStream/Stream.php', 'Hoa\\Stream\\IStream\\Structural' => __DIR__ . '/..' . '/hoa/stream/IStream/Structural.php', 'Hoa\\Stream\\IStream\\Touchable' => __DIR__ . '/..' . '/hoa/stream/IStream/Touchable.php', 'Hoa\\Stream\\Stream' => __DIR__ . '/..' . '/hoa/stream/Stream.php', 'Hoa\\Stream\\Test\\Integration\\Filter\\Filter' => __DIR__ . '/..' . '/hoa/stream/Test/Integration/Filter/Filter.php', 'Hoa\\Stream\\Test\\Integration\\Filter\\LateComputed' => __DIR__ . '/..' . '/hoa/stream/Test/Integration/Filter/LateComputed.php', 'Hoa\\Stream\\Test\\Integration\\Stream' => __DIR__ . '/..' . '/hoa/stream/Test/Integration/Stream.php', 'Hoa\\Stream\\Test\\Unit\\Bucket' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Bucket.php', 'Hoa\\Stream\\Test\\Unit\\Composite' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Composite.php', 'Hoa\\Stream\\Test\\Unit\\Context' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Context.php', 'Hoa\\Stream\\Test\\Unit\\Exception' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Exception.php', 'Hoa\\Stream\\Test\\Unit\\Filter\\Basic' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Filter/Basic.php', 'Hoa\\Stream\\Test\\Unit\\Filter\\Exception' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Filter/Exception.php', 'Hoa\\Stream\\Test\\Unit\\Filter\\Filter' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Filter/Filter.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Bufferable' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Bufferable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\In' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/In.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Lockable' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Lockable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Out' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Out.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Pathable' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Pathable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Pointable' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Pointable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Statable' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Statable.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Stream' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Stream.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Structural' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Structural.php', 'Hoa\\Stream\\Test\\Unit\\IStream\\Touchable' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/IStream/Touchable.php', 'Hoa\\Stream\\Test\\Unit\\Stream' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Stream.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\Exception' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Wrapper/Exception.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\IWrapper\\File' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Wrapper/IWrapper/File.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\IWrapper\\IWrapper' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Wrapper/IWrapper/IWrapper.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\IWrapper\\Stream' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Wrapper/IWrapper/Stream.php', 'Hoa\\Stream\\Test\\Unit\\Wrapper\\Wrapper' => __DIR__ . '/..' . '/hoa/stream/Test/Unit/Wrapper/Wrapper.php', 'Hoa\\Stream\\Wrapper\\Exception' => __DIR__ . '/..' . '/hoa/stream/Wrapper/Exception.php', 'Hoa\\Stream\\Wrapper\\IWrapper\\File' => __DIR__ . '/..' . '/hoa/stream/Wrapper/IWrapper/File.php', 'Hoa\\Stream\\Wrapper\\IWrapper\\IWrapper' => __DIR__ . '/..' . '/hoa/stream/Wrapper/IWrapper/IWrapper.php', 'Hoa\\Stream\\Wrapper\\IWrapper\\Stream' => __DIR__ . '/..' . '/hoa/stream/Wrapper/IWrapper/Stream.php', 'Hoa\\Stream\\Wrapper\\Wrapper' => __DIR__ . '/..' . '/hoa/stream/Wrapper/Wrapper.php', 'Hoa\\Ustring\\Bin\\Fromcode' => __DIR__ . '/..' . '/hoa/ustring/Bin/Fromcode.php', 'Hoa\\Ustring\\Bin\\Tocode' => __DIR__ . '/..' . '/hoa/ustring/Bin/Tocode.php', 'Hoa\\Ustring\\Exception' => __DIR__ . '/..' . '/hoa/ustring/Exception.php', 'Hoa\\Ustring\\Search' => __DIR__ . '/..' . '/hoa/ustring/Search.php', 'Hoa\\Ustring\\Test\\Unit\\Issue' => __DIR__ . '/..' . '/hoa/ustring/Test/Unit/Issue.php', 'Hoa\\Ustring\\Test\\Unit\\Search' => __DIR__ . '/..' . '/hoa/ustring/Test/Unit/Search.php', 'Hoa\\Ustring\\Test\\Unit\\Ustring' => __DIR__ . '/..' . '/hoa/ustring/Test/Unit/Ustring.php', 'Hoa\\Ustring\\Ustring' => __DIR__ . '/..' . '/hoa/ustring/Ustring.php', 'Humbug\\FileGetContents' => __DIR__ . '/..' . '/padraic/humbug_get_contents/src/FileGetContents.php', 'Humbug\\SelfUpdate\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/padraic/phar-updater/src/Exception/ExceptionInterface.php', 'Humbug\\SelfUpdate\\Exception\\FilesystemException' => __DIR__ . '/..' . '/padraic/phar-updater/src/Exception/FilesystemException.php', 'Humbug\\SelfUpdate\\Exception\\HttpRequestException' => __DIR__ . '/..' . '/padraic/phar-updater/src/Exception/HttpRequestException.php', 'Humbug\\SelfUpdate\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/padraic/phar-updater/src/Exception/InvalidArgumentException.php', 'Humbug\\SelfUpdate\\Exception\\JsonParsingException' => __DIR__ . '/..' . '/padraic/phar-updater/src/Exception/JsonParsingException.php', 'Humbug\\SelfUpdate\\Exception\\NoSignatureException' => __DIR__ . '/..' . '/padraic/phar-updater/src/Exception/NoSignatureException.php', 'Humbug\\SelfUpdate\\Exception\\RuntimeException' => __DIR__ . '/..' . '/padraic/phar-updater/src/Exception/RuntimeException.php', 'Humbug\\SelfUpdate\\Strategy\\GithubStrategy' => __DIR__ . '/..' . '/padraic/phar-updater/src/Strategy/GithubStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\ShaStrategy' => __DIR__ . '/..' . '/padraic/phar-updater/src/Strategy/ShaStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\StrategyInterface' => __DIR__ . '/..' . '/padraic/phar-updater/src/Strategy/StrategyInterface.php', 'Humbug\\SelfUpdate\\Updater' => __DIR__ . '/..' . '/padraic/phar-updater/src/Updater.php', 'Humbug\\SelfUpdate\\VersionParser' => __DIR__ . '/..' . '/padraic/phar-updater/src/VersionParser.php', 'JsonSchema\\Constraints\\BaseConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php', 'JsonSchema\\Constraints\\CollectionConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php', 'JsonSchema\\Constraints\\Constraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php', 'JsonSchema\\Constraints\\ConstraintInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php', 'JsonSchema\\Constraints\\EnumConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php', 'JsonSchema\\Constraints\\Factory' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php', 'JsonSchema\\Constraints\\FormatConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php', 'JsonSchema\\Constraints\\NumberConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php', 'JsonSchema\\Constraints\\ObjectConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php', 'JsonSchema\\Constraints\\SchemaConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php', 'JsonSchema\\Constraints\\StringConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php', 'JsonSchema\\Constraints\\TypeCheck\\LooseTypeCheck' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php', 'JsonSchema\\Constraints\\TypeCheck\\StrictTypeCheck' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php', 'JsonSchema\\Constraints\\TypeCheck\\TypeCheckInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php', 'JsonSchema\\Constraints\\TypeConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php', 'JsonSchema\\Constraints\\UndefinedConstraint' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php', 'JsonSchema\\Entity\\JsonPointer' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php', 'JsonSchema\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php', 'JsonSchema\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php', 'JsonSchema\\Exception\\InvalidConfigException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidConfigException.php', 'JsonSchema\\Exception\\InvalidSchemaException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaException.php', 'JsonSchema\\Exception\\InvalidSchemaMediaTypeException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php', 'JsonSchema\\Exception\\InvalidSourceUriException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSourceUriException.php', 'JsonSchema\\Exception\\JsonDecodingException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php', 'JsonSchema\\Exception\\ResourceNotFoundException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php', 'JsonSchema\\Exception\\RuntimeException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php', 'JsonSchema\\Exception\\UnresolvableJsonPointerException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php', 'JsonSchema\\Exception\\UriResolverException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php', 'JsonSchema\\Exception\\ValidationException' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Exception/ValidationException.php', 'JsonSchema\\Iterator\\ObjectIterator' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php', 'JsonSchema\\Rfc3339' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php', 'JsonSchema\\SchemaStorage' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php', 'JsonSchema\\SchemaStorageInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.php', 'JsonSchema\\UriResolverInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php', 'JsonSchema\\UriRetrieverInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php', 'JsonSchema\\Uri\\Retrievers\\AbstractRetriever' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/AbstractRetriever.php', 'JsonSchema\\Uri\\Retrievers\\Curl' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php', 'JsonSchema\\Uri\\Retrievers\\FileGetContents' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php', 'JsonSchema\\Uri\\Retrievers\\PredefinedArray' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/PredefinedArray.php', 'JsonSchema\\Uri\\Retrievers\\UriRetrieverInterface' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php', 'JsonSchema\\Uri\\UriResolver' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php', 'JsonSchema\\Uri\\UriRetriever' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php', 'JsonSchema\\Validator' => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema/Validator.php', 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/AssertionFailedError.php', 'PHPUnit\\Framework\\BaseTestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/BaseTestListener.php', 'PHPUnit\\Framework\\Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/Test.php', 'PHPUnit\\Framework\\TestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestCase.php', 'PHPUnit\\Framework\\TestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestListener.php', 'PHPUnit\\Framework\\TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/ForwardCompatibility/TestSuite.php', 'PHPUnit_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php', 'PHPUnit_Extensions_GroupTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/GroupTestSuite.php', 'PHPUnit_Extensions_PhptTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/PhptTestCase.php', 'PHPUnit_Extensions_PhptTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/PhptTestSuite.php', 'PHPUnit_Extensions_RepeatedTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/RepeatedTest.php', 'PHPUnit_Extensions_TestDecorator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/TestDecorator.php', 'PHPUnit_Extensions_TicketListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Extensions/TicketListener.php', 'PHPUnit_Framework_Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php', 'PHPUnit_Framework_AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/AssertionFailedError.php', 'PHPUnit_Framework_BaseTestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/BaseTestListener.php', 'PHPUnit_Framework_CodeCoverageException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/CodeCoverageException.php', 'PHPUnit_Framework_Constraint' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint.php', 'PHPUnit_Framework_Constraint_And' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/And.php', 'PHPUnit_Framework_Constraint_ArrayHasKey' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArrayHasKey.php', 'PHPUnit_Framework_Constraint_ArraySubset' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ArraySubset.php', 'PHPUnit_Framework_Constraint_Attribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Attribute.php', 'PHPUnit_Framework_Constraint_Callback' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Callback.php', 'PHPUnit_Framework_Constraint_ClassHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasAttribute.php', 'PHPUnit_Framework_Constraint_ClassHasStaticAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ClassHasStaticAttribute.php', 'PHPUnit_Framework_Constraint_Composite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Composite.php', 'PHPUnit_Framework_Constraint_Count' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Count.php', 'PHPUnit_Framework_Constraint_DirectoryExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/DirectoryExists.php', 'PHPUnit_Framework_Constraint_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception.php', 'PHPUnit_Framework_Constraint_ExceptionCode' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionCode.php', 'PHPUnit_Framework_Constraint_ExceptionMessage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessage.php', 'PHPUnit_Framework_Constraint_ExceptionMessageRegExp' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ExceptionMessageRegExp.php', 'PHPUnit_Framework_Constraint_FileExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/FileExists.php', 'PHPUnit_Framework_Constraint_GreaterThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/GreaterThan.php', 'PHPUnit_Framework_Constraint_IsAnything' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php', 'PHPUnit_Framework_Constraint_IsEmpty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEmpty.php', 'PHPUnit_Framework_Constraint_IsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsEqual.php', 'PHPUnit_Framework_Constraint_IsFalse' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsFalse.php', 'PHPUnit_Framework_Constraint_IsFinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsFinite.php', 'PHPUnit_Framework_Constraint_IsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php', 'PHPUnit_Framework_Constraint_IsInfinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsInfinite.php', 'PHPUnit_Framework_Constraint_IsInstanceOf' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsInstanceOf.php', 'PHPUnit_Framework_Constraint_IsJson' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsJson.php', 'PHPUnit_Framework_Constraint_IsNan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsNan.php', 'PHPUnit_Framework_Constraint_IsNull' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsNull.php', 'PHPUnit_Framework_Constraint_IsReadable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsReadable.php', 'PHPUnit_Framework_Constraint_IsTrue' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsTrue.php', 'PHPUnit_Framework_Constraint_IsType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsType.php', 'PHPUnit_Framework_Constraint_IsWritable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsWritable.php', 'PHPUnit_Framework_Constraint_JsonMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php', 'PHPUnit_Framework_Constraint_JsonMatches_ErrorMessageProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches/ErrorMessageProvider.php', 'PHPUnit_Framework_Constraint_LessThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/LessThan.php', 'PHPUnit_Framework_Constraint_Not' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Not.php', 'PHPUnit_Framework_Constraint_ObjectHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/ObjectHasAttribute.php', 'PHPUnit_Framework_Constraint_Or' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Or.php', 'PHPUnit_Framework_Constraint_PCREMatch' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/PCREMatch.php', 'PHPUnit_Framework_Constraint_SameSize' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/SameSize.php', 'PHPUnit_Framework_Constraint_StringContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringContains.php', 'PHPUnit_Framework_Constraint_StringEndsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringEndsWith.php', 'PHPUnit_Framework_Constraint_StringMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringMatches.php', 'PHPUnit_Framework_Constraint_StringStartsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/StringStartsWith.php', 'PHPUnit_Framework_Constraint_TraversableContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContains.php', 'PHPUnit_Framework_Constraint_TraversableContainsOnly' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/TraversableContainsOnly.php', 'PHPUnit_Framework_Constraint_Xor' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Xor.php', 'PHPUnit_Framework_CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/CoveredCodeNotExecutedException.php', 'PHPUnit_Framework_Error' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error.php', 'PHPUnit_Framework_Error_Deprecated' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Deprecated.php', 'PHPUnit_Framework_Error_Notice' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Notice.php', 'PHPUnit_Framework_Error_Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Warning.php', 'PHPUnit_Framework_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception.php', 'PHPUnit_Framework_ExceptionWrapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php', 'PHPUnit_Framework_ExpectationFailedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExpectationFailedException.php', 'PHPUnit_Framework_IncompleteTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTest.php', 'PHPUnit_Framework_IncompleteTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTestCase.php', 'PHPUnit_Framework_IncompleteTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/IncompleteTestError.php', 'PHPUnit_Framework_InvalidCoversTargetException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/InvalidCoversTargetException.php', 'PHPUnit_Framework_MissingCoversAnnotationException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MissingCoversAnnotationException.php', 'PHPUnit_Framework_MockObject_BadMethodCallException' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/BadMethodCallException.php', 'PHPUnit_Framework_MockObject_Builder_Identity' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Identity.php', 'PHPUnit_Framework_MockObject_Builder_InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/InvocationMocker.php', 'PHPUnit_Framework_MockObject_Builder_Match' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Match.php', 'PHPUnit_Framework_MockObject_Builder_MethodNameMatch' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/MethodNameMatch.php', 'PHPUnit_Framework_MockObject_Builder_Namespace' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Namespace.php', 'PHPUnit_Framework_MockObject_Builder_ParametersMatch' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/ParametersMatch.php', 'PHPUnit_Framework_MockObject_Builder_Stub' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Builder/Stub.php', 'PHPUnit_Framework_MockObject_Exception' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/Exception.php', 'PHPUnit_Framework_MockObject_Generator' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php', 'PHPUnit_Framework_MockObject_Invocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation.php', 'PHPUnit_Framework_MockObject_InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/InvocationMocker.php', 'PHPUnit_Framework_MockObject_Invocation_Object' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Object.php', 'PHPUnit_Framework_MockObject_Invocation_Static' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invocation/Static.php', 'PHPUnit_Framework_MockObject_Invokable' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Invokable.php', 'PHPUnit_Framework_MockObject_Matcher' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher.php', 'PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyInvokedCount.php', 'PHPUnit_Framework_MockObject_Matcher_AnyParameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/AnyParameters.php', 'PHPUnit_Framework_MockObject_Matcher_ConsecutiveParameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/ConsecutiveParameters.php', 'PHPUnit_Framework_MockObject_Matcher_Invocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Invocation.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtIndex.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedAtMostCount.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedCount.php', 'PHPUnit_Framework_MockObject_Matcher_InvokedRecorder' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/InvokedRecorder.php', 'PHPUnit_Framework_MockObject_Matcher_MethodName' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/MethodName.php', 'PHPUnit_Framework_MockObject_Matcher_Parameters' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/Parameters.php', 'PHPUnit_Framework_MockObject_Matcher_StatelessInvocation' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Matcher/StatelessInvocation.php', 'PHPUnit_Framework_MockObject_MockBuilder' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockBuilder.php', 'PHPUnit_Framework_MockObject_MockObject' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/MockObject.php', 'PHPUnit_Framework_MockObject_RuntimeException' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Exception/RuntimeException.php', 'PHPUnit_Framework_MockObject_Stub' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub.php', 'PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ConsecutiveCalls.php', 'PHPUnit_Framework_MockObject_Stub_Exception' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Exception.php', 'PHPUnit_Framework_MockObject_Stub_MatcherCollection' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/MatcherCollection.php', 'PHPUnit_Framework_MockObject_Stub_Return' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/Return.php', 'PHPUnit_Framework_MockObject_Stub_ReturnArgument' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnArgument.php', 'PHPUnit_Framework_MockObject_Stub_ReturnCallback' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnCallback.php', 'PHPUnit_Framework_MockObject_Stub_ReturnReference' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnReference.php', 'PHPUnit_Framework_MockObject_Stub_ReturnSelf' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnSelf.php', 'PHPUnit_Framework_MockObject_Stub_ReturnValueMap' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Stub/ReturnValueMap.php', 'PHPUnit_Framework_MockObject_Verifiable' => __DIR__ . '/..' . '/phpunit/phpunit-mock-objects/src/Framework/MockObject/Verifiable.php', 'PHPUnit_Framework_OutputError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/OutputError.php', 'PHPUnit_Framework_RiskyTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/RiskyTest.php', 'PHPUnit_Framework_RiskyTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/RiskyTestError.php', 'PHPUnit_Framework_SelfDescribing' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SelfDescribing.php', 'PHPUnit_Framework_SkippedTest' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTest.php', 'PHPUnit_Framework_SkippedTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestCase.php', 'PHPUnit_Framework_SkippedTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestError.php', 'PHPUnit_Framework_SkippedTestSuiteError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SkippedTestSuiteError.php', 'PHPUnit_Framework_SyntheticError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/SyntheticError.php', 'PHPUnit_Framework_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Test.php', 'PHPUnit_Framework_TestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestCase.php', 'PHPUnit_Framework_TestFailure' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestFailure.php', 'PHPUnit_Framework_TestListener' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestListener.php', 'PHPUnit_Framework_TestResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestResult.php', 'PHPUnit_Framework_TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuite.php', 'PHPUnit_Framework_TestSuite_DataProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuite/DataProvider.php', 'PHPUnit_Framework_UnintentionallyCoveredCodeError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/UnintentionallyCoveredCodeError.php', 'PHPUnit_Framework_Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Warning.php', 'PHPUnit_Framework_WarningTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/WarningTestCase.php', 'PHPUnit_Runner_BaseTestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/BaseTestRunner.php', 'PHPUnit_Runner_Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception.php', 'PHPUnit_Runner_Filter_Factory' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Factory.php', 'PHPUnit_Runner_Filter_GroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group.php', 'PHPUnit_Runner_Filter_Group_Exclude' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group/Exclude.php', 'PHPUnit_Runner_Filter_Group_Include' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Group/Include.php', 'PHPUnit_Runner_Filter_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Test.php', 'PHPUnit_Runner_StandardTestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php', 'PHPUnit_Runner_TestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php', 'PHPUnit_Runner_Version' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Version.php', 'PHPUnit_TextUI_Command' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Command.php', 'PHPUnit_TextUI_ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/ResultPrinter.php', 'PHPUnit_TextUI_TestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/TestRunner.php', 'PHPUnit_Util_Blacklist' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Blacklist.php', 'PHPUnit_Util_Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Configuration.php', 'PHPUnit_Util_ConfigurationGenerator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ConfigurationGenerator.php', 'PHPUnit_Util_ErrorHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ErrorHandler.php', 'PHPUnit_Util_Fileloader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Fileloader.php', 'PHPUnit_Util_Filesystem' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filesystem.php', 'PHPUnit_Util_Filter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Filter.php', 'PHPUnit_Util_Getopt' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Getopt.php', 'PHPUnit_Util_GlobalState' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/GlobalState.php', 'PHPUnit_Util_InvalidArgumentHelper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/InvalidArgumentHelper.php', 'PHPUnit_Util_Log_JSON' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/JSON.php', 'PHPUnit_Util_Log_JUnit' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/JUnit.php', 'PHPUnit_Util_Log_TAP' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/TAP.php', 'PHPUnit_Util_Log_TeamCity' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/TeamCity.php', 'PHPUnit_Util_PHP' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP.php', 'PHPUnit_Util_PHP_Default' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/Default.php', 'PHPUnit_Util_PHP_Windows' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/Windows.php', 'PHPUnit_Util_Printer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Printer.php', 'PHPUnit_Util_Regex' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Regex.php', 'PHPUnit_Util_String' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/String.php', 'PHPUnit_Util_Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Test.php', 'PHPUnit_Util_TestDox_NamePrettifier' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/NamePrettifier.php', 'PHPUnit_Util_TestDox_ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter.php', 'PHPUnit_Util_TestDox_ResultPrinter_HTML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/HTML.php', 'PHPUnit_Util_TestDox_ResultPrinter_Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/Text.php', 'PHPUnit_Util_TestDox_ResultPrinter_XML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/ResultPrinter/XML.php', 'PHPUnit_Util_TestSuiteIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestSuiteIterator.php', 'PHPUnit_Util_Type' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Type.php', 'PHPUnit_Util_XML' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/XML.php', 'PHP_Timer' => __DIR__ . '/..' . '/phpunit/php-timer/src/Timer.php', 'PHP_Token' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_TokenWithScope' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_TokenWithScopeAndVisibility' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ABSTRACT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AMPERSAND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AND_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ARRAY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ARRAY_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ASYNC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_AWAIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BACKTICK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BAD_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BOOLEAN_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BOOLEAN_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BOOL_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_BREAK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CALLABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CARET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CASE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CATCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CHARACTER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLASS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLASS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLASS_NAME_CONSTANT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLONE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CLOSE_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COALESCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COMMA' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_COMPILER_HALT_OFFSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONCAT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONSTANT_ENCAPSED_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CONTINUE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_CURLY_OPEN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DEC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DEFAULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DIR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DIV' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DIV_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOC_COMMENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOLLAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOLLAR_OPEN_CURLY_BRACES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_ARROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_COLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_DOUBLE_QUOTES' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ELLIPSIS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ELSE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ELSEIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EMPTY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENCAPSED_AND_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDDECLARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDFOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDFOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDIF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDSWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENDWHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_END_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ENUM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EQUALS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EVAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EXCLAMATION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EXIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_EXTENDS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FINAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FINALLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FOREACH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_FUNC_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_GLOBAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_GOTO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_HALT_COMPILER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IMPLEMENTS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INCLUDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INCLUDE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INLINE_HTML' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INSTANCEOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INSTEADOF' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INTERFACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_INT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ISSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_GREATER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_NOT_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_NOT_IDENTICAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_IS_SMALLER_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_Includes' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_JOIN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LAMBDA_ARROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LAMBDA_CP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LAMBDA_OP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LINE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LIST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LNUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LOGICAL_AND' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LOGICAL_OR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LOGICAL_XOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_METHOD_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MINUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MINUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MOD_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MULT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_MUL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NAMESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NEW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NS_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NS_SEPARATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NULLSAFE_OBJECT_OPERATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_NUM_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OBJECT_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OBJECT_OPERATOR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_ONUMBER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_BRACKET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_CURLY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_SQUARE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_TAG' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OPEN_TAG_WITH_ECHO' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_OR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PAAMAYIM_NEKUDOTAYIM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PERCENT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PIPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PLUS' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PLUS_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_POW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_POW_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PRINT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PRIVATE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PROTECTED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_PUBLIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_QUESTION_MARK' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_REQUIRE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_REQUIRE_ONCE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_RETURN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SEMICOLON' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SHAPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SL_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SPACESHIP' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_START_HEREDOC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STATIC' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STRING' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STRING_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_STRING_VARNAME' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SUPER' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_SWITCH' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_Stream' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token/Stream.php', 'PHP_Token_Stream_CachingFactory' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token/Stream/CachingFactory.php', 'PHP_Token_THROW' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TILDE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TRAIT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TRAIT_C' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TRY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TYPE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TYPELIST_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_TYPELIST_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_UNSET' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_UNSET_CAST' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_USE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_USE_FUNCTION' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_VAR' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_VARIABLE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_WHERE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_WHILE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_WHITESPACE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_ATTRIBUTE' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_CATEGORY' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_CATEGORY_LABEL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_CHILDREN' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_LABEL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_REQUIRED' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_TAG_GT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_TAG_LT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XHP_TEXT' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_XOR_EQUAL' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_YIELD' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'PHP_Token_YIELD_FROM' => __DIR__ . '/..' . '/phpunit/php-token-stream/src/Token.php', 'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.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\\Connection\\Heartbeat\\PCNTLHeartbeatSender' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.php', 'PhpAmqpLib\\Exception\\AMQPBasicCancelException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.php', 'PhpAmqpLib\\Exception\\AMQPChannelClosedException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelClosedException.php', 'PhpAmqpLib\\Exception\\AMQPChannelException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionBlockedException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionClosedException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionClosedException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionException.php', 'PhpAmqpLib\\Exception\\AMQPDataReadException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPDataReadException.php', 'PhpAmqpLib\\Exception\\AMQPEmptyDeliveryTagException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPEmptyDeliveryTagException.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\\AMQPHeartbeatMissedException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPHeartbeatMissedException.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\\AMQPInvalidFrameException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidFrameException.php', 'PhpAmqpLib\\Exception\\AMQPLogicException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.php', 'PhpAmqpLib\\Exception\\AMQPNoDataException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNoDataException.php', 'PhpAmqpLib\\Exception\\AMQPNotImplementedException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNotImplementedException.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\\AMQPSocketException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPSocketException.php', 'PhpAmqpLib\\Exception\\AMQPTimeoutException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.php', 'PhpAmqpLib\\Exchange\\AMQPExchangeType' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exchange/AMQPExchangeType.php', 'PhpAmqpLib\\Helper\\Assert' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Assert.php', 'PhpAmqpLib\\Helper\\BigInteger' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/BigInteger.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\\Helper\\SocketConstants' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Helper/SocketConstants.php', 'PhpAmqpLib\\Message\\AMQPMessage' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.php', 'PhpAmqpLib\\Package' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Package.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\\Constants' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants.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\\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', 'Predis\\Autoloader' => __DIR__ . '/..' . '/predis/predis/src/Autoloader.php', 'Predis\\Client' => __DIR__ . '/..' . '/predis/predis/src/Client.php', 'Predis\\ClientContextInterface' => __DIR__ . '/..' . '/predis/predis/src/ClientContextInterface.php', 'Predis\\ClientException' => __DIR__ . '/..' . '/predis/predis/src/ClientException.php', 'Predis\\ClientInterface' => __DIR__ . '/..' . '/predis/predis/src/ClientInterface.php', 'Predis\\Cluster\\ClusterStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/ClusterStrategy.php', 'Predis\\Cluster\\Distributor\\DistributorInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/DistributorInterface.php', 'Predis\\Cluster\\Distributor\\EmptyRingException' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/EmptyRingException.php', 'Predis\\Cluster\\Distributor\\HashRing' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/HashRing.php', 'Predis\\Cluster\\Distributor\\KetamaRing' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Distributor/KetamaRing.php', 'Predis\\Cluster\\Hash\\CRC16' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/CRC16.php', 'Predis\\Cluster\\Hash\\HashGeneratorInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php', 'Predis\\Cluster\\PredisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/PredisStrategy.php', 'Predis\\Cluster\\RedisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/RedisStrategy.php', 'Predis\\Cluster\\StrategyInterface' => __DIR__ . '/..' . '/predis/predis/src/Cluster/StrategyInterface.php', 'Predis\\Collection\\Iterator\\CursorBasedIterator' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/CursorBasedIterator.php', 'Predis\\Collection\\Iterator\\HashKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/HashKey.php', 'Predis\\Collection\\Iterator\\Keyspace' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/Keyspace.php', 'Predis\\Collection\\Iterator\\ListKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/ListKey.php', 'Predis\\Collection\\Iterator\\SetKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/SetKey.php', 'Predis\\Collection\\Iterator\\SortedSetKey' => __DIR__ . '/..' . '/predis/predis/src/Collection/Iterator/SortedSetKey.php', 'Predis\\Command\\Command' => __DIR__ . '/..' . '/predis/predis/src/Command/Command.php', 'Predis\\Command\\CommandInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/CommandInterface.php', 'Predis\\Command\\ConnectionAuth' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionAuth.php', 'Predis\\Command\\ConnectionEcho' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionEcho.php', 'Predis\\Command\\ConnectionPing' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionPing.php', 'Predis\\Command\\ConnectionQuit' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionQuit.php', 'Predis\\Command\\ConnectionSelect' => __DIR__ . '/..' . '/predis/predis/src/Command/ConnectionSelect.php', 'Predis\\Command\\GeospatialGeoAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoAdd.php', 'Predis\\Command\\GeospatialGeoDist' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoDist.php', 'Predis\\Command\\GeospatialGeoHash' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoHash.php', 'Predis\\Command\\GeospatialGeoPos' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoPos.php', 'Predis\\Command\\GeospatialGeoRadius' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoRadius.php', 'Predis\\Command\\GeospatialGeoRadiusByMember' => __DIR__ . '/..' . '/predis/predis/src/Command/GeospatialGeoRadiusByMember.php', 'Predis\\Command\\HashDelete' => __DIR__ . '/..' . '/predis/predis/src/Command/HashDelete.php', 'Predis\\Command\\HashExists' => __DIR__ . '/..' . '/predis/predis/src/Command/HashExists.php', 'Predis\\Command\\HashGet' => __DIR__ . '/..' . '/predis/predis/src/Command/HashGet.php', 'Predis\\Command\\HashGetAll' => __DIR__ . '/..' . '/predis/predis/src/Command/HashGetAll.php', 'Predis\\Command\\HashGetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/HashGetMultiple.php', 'Predis\\Command\\HashIncrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/HashIncrementBy.php', 'Predis\\Command\\HashIncrementByFloat' => __DIR__ . '/..' . '/predis/predis/src/Command/HashIncrementByFloat.php', 'Predis\\Command\\HashKeys' => __DIR__ . '/..' . '/predis/predis/src/Command/HashKeys.php', 'Predis\\Command\\HashLength' => __DIR__ . '/..' . '/predis/predis/src/Command/HashLength.php', 'Predis\\Command\\HashScan' => __DIR__ . '/..' . '/predis/predis/src/Command/HashScan.php', 'Predis\\Command\\HashSet' => __DIR__ . '/..' . '/predis/predis/src/Command/HashSet.php', 'Predis\\Command\\HashSetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/HashSetMultiple.php', 'Predis\\Command\\HashSetPreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/HashSetPreserve.php', 'Predis\\Command\\HashStringLength' => __DIR__ . '/..' . '/predis/predis/src/Command/HashStringLength.php', 'Predis\\Command\\HashValues' => __DIR__ . '/..' . '/predis/predis/src/Command/HashValues.php', 'Predis\\Command\\HyperLogLogAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/HyperLogLogAdd.php', 'Predis\\Command\\HyperLogLogCount' => __DIR__ . '/..' . '/predis/predis/src/Command/HyperLogLogCount.php', 'Predis\\Command\\HyperLogLogMerge' => __DIR__ . '/..' . '/predis/predis/src/Command/HyperLogLogMerge.php', 'Predis\\Command\\KeyDelete' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyDelete.php', 'Predis\\Command\\KeyDump' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyDump.php', 'Predis\\Command\\KeyExists' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyExists.php', 'Predis\\Command\\KeyExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyExpire.php', 'Predis\\Command\\KeyExpireAt' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyExpireAt.php', 'Predis\\Command\\KeyKeys' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyKeys.php', 'Predis\\Command\\KeyMigrate' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyMigrate.php', 'Predis\\Command\\KeyMove' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyMove.php', 'Predis\\Command\\KeyPersist' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPersist.php', 'Predis\\Command\\KeyPreciseExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPreciseExpire.php', 'Predis\\Command\\KeyPreciseExpireAt' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPreciseExpireAt.php', 'Predis\\Command\\KeyPreciseTimeToLive' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyPreciseTimeToLive.php', 'Predis\\Command\\KeyRandom' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRandom.php', 'Predis\\Command\\KeyRename' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRename.php', 'Predis\\Command\\KeyRenamePreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRenamePreserve.php', 'Predis\\Command\\KeyRestore' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyRestore.php', 'Predis\\Command\\KeyScan' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyScan.php', 'Predis\\Command\\KeySort' => __DIR__ . '/..' . '/predis/predis/src/Command/KeySort.php', 'Predis\\Command\\KeyTimeToLive' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyTimeToLive.php', 'Predis\\Command\\KeyType' => __DIR__ . '/..' . '/predis/predis/src/Command/KeyType.php', 'Predis\\Command\\ListIndex' => __DIR__ . '/..' . '/predis/predis/src/Command/ListIndex.php', 'Predis\\Command\\ListInsert' => __DIR__ . '/..' . '/predis/predis/src/Command/ListInsert.php', 'Predis\\Command\\ListLength' => __DIR__ . '/..' . '/predis/predis/src/Command/ListLength.php', 'Predis\\Command\\ListPopFirst' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopFirst.php', 'Predis\\Command\\ListPopFirstBlocking' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopFirstBlocking.php', 'Predis\\Command\\ListPopLast' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLast.php', 'Predis\\Command\\ListPopLastBlocking' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLastBlocking.php', 'Predis\\Command\\ListPopLastPushHead' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLastPushHead.php', 'Predis\\Command\\ListPopLastPushHeadBlocking' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPopLastPushHeadBlocking.php', 'Predis\\Command\\ListPushHead' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushHead.php', 'Predis\\Command\\ListPushHeadX' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushHeadX.php', 'Predis\\Command\\ListPushTail' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushTail.php', 'Predis\\Command\\ListPushTailX' => __DIR__ . '/..' . '/predis/predis/src/Command/ListPushTailX.php', 'Predis\\Command\\ListRange' => __DIR__ . '/..' . '/predis/predis/src/Command/ListRange.php', 'Predis\\Command\\ListRemove' => __DIR__ . '/..' . '/predis/predis/src/Command/ListRemove.php', 'Predis\\Command\\ListSet' => __DIR__ . '/..' . '/predis/predis/src/Command/ListSet.php', 'Predis\\Command\\ListTrim' => __DIR__ . '/..' . '/predis/predis/src/Command/ListTrim.php', 'Predis\\Command\\PrefixableCommandInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/PrefixableCommandInterface.php', 'Predis\\Command\\Processor\\KeyPrefixProcessor' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/KeyPrefixProcessor.php', 'Predis\\Command\\Processor\\ProcessorChain' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/ProcessorChain.php', 'Predis\\Command\\Processor\\ProcessorInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Processor/ProcessorInterface.php', 'Predis\\Command\\PubSubPublish' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubPublish.php', 'Predis\\Command\\PubSubPubsub' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubPubsub.php', 'Predis\\Command\\PubSubSubscribe' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubSubscribe.php', 'Predis\\Command\\PubSubSubscribeByPattern' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubSubscribeByPattern.php', 'Predis\\Command\\PubSubUnsubscribe' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubUnsubscribe.php', 'Predis\\Command\\PubSubUnsubscribeByPattern' => __DIR__ . '/..' . '/predis/predis/src/Command/PubSubUnsubscribeByPattern.php', 'Predis\\Command\\RawCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/RawCommand.php', 'Predis\\Command\\ScriptCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/ScriptCommand.php', 'Predis\\Command\\ServerBackgroundRewriteAOF' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerBackgroundRewriteAOF.php', 'Predis\\Command\\ServerBackgroundSave' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerBackgroundSave.php', 'Predis\\Command\\ServerClient' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerClient.php', 'Predis\\Command\\ServerCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerCommand.php', 'Predis\\Command\\ServerConfig' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerConfig.php', 'Predis\\Command\\ServerDatabaseSize' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerDatabaseSize.php', 'Predis\\Command\\ServerEval' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerEval.php', 'Predis\\Command\\ServerEvalSHA' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerEvalSHA.php', 'Predis\\Command\\ServerFlushAll' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerFlushAll.php', 'Predis\\Command\\ServerFlushDatabase' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerFlushDatabase.php', 'Predis\\Command\\ServerInfo' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerInfo.php', 'Predis\\Command\\ServerInfoV26x' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerInfoV26x.php', 'Predis\\Command\\ServerLastSave' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerLastSave.php', 'Predis\\Command\\ServerMonitor' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerMonitor.php', 'Predis\\Command\\ServerObject' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerObject.php', 'Predis\\Command\\ServerSave' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSave.php', 'Predis\\Command\\ServerScript' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerScript.php', 'Predis\\Command\\ServerSentinel' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSentinel.php', 'Predis\\Command\\ServerShutdown' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerShutdown.php', 'Predis\\Command\\ServerSlaveOf' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSlaveOf.php', 'Predis\\Command\\ServerSlowlog' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerSlowlog.php', 'Predis\\Command\\ServerTime' => __DIR__ . '/..' . '/predis/predis/src/Command/ServerTime.php', 'Predis\\Command\\SetAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/SetAdd.php', 'Predis\\Command\\SetCardinality' => __DIR__ . '/..' . '/predis/predis/src/Command/SetCardinality.php', 'Predis\\Command\\SetDifference' => __DIR__ . '/..' . '/predis/predis/src/Command/SetDifference.php', 'Predis\\Command\\SetDifferenceStore' => __DIR__ . '/..' . '/predis/predis/src/Command/SetDifferenceStore.php', 'Predis\\Command\\SetIntersection' => __DIR__ . '/..' . '/predis/predis/src/Command/SetIntersection.php', 'Predis\\Command\\SetIntersectionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/SetIntersectionStore.php', 'Predis\\Command\\SetIsMember' => __DIR__ . '/..' . '/predis/predis/src/Command/SetIsMember.php', 'Predis\\Command\\SetMembers' => __DIR__ . '/..' . '/predis/predis/src/Command/SetMembers.php', 'Predis\\Command\\SetMove' => __DIR__ . '/..' . '/predis/predis/src/Command/SetMove.php', 'Predis\\Command\\SetPop' => __DIR__ . '/..' . '/predis/predis/src/Command/SetPop.php', 'Predis\\Command\\SetRandomMember' => __DIR__ . '/..' . '/predis/predis/src/Command/SetRandomMember.php', 'Predis\\Command\\SetRemove' => __DIR__ . '/..' . '/predis/predis/src/Command/SetRemove.php', 'Predis\\Command\\SetScan' => __DIR__ . '/..' . '/predis/predis/src/Command/SetScan.php', 'Predis\\Command\\SetUnion' => __DIR__ . '/..' . '/predis/predis/src/Command/SetUnion.php', 'Predis\\Command\\SetUnionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/SetUnionStore.php', 'Predis\\Command\\StringAppend' => __DIR__ . '/..' . '/predis/predis/src/Command/StringAppend.php', 'Predis\\Command\\StringBitCount' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitCount.php', 'Predis\\Command\\StringBitField' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitField.php', 'Predis\\Command\\StringBitOp' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitOp.php', 'Predis\\Command\\StringBitPos' => __DIR__ . '/..' . '/predis/predis/src/Command/StringBitPos.php', 'Predis\\Command\\StringDecrement' => __DIR__ . '/..' . '/predis/predis/src/Command/StringDecrement.php', 'Predis\\Command\\StringDecrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/StringDecrementBy.php', 'Predis\\Command\\StringGet' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGet.php', 'Predis\\Command\\StringGetBit' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetBit.php', 'Predis\\Command\\StringGetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetMultiple.php', 'Predis\\Command\\StringGetRange' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetRange.php', 'Predis\\Command\\StringGetSet' => __DIR__ . '/..' . '/predis/predis/src/Command/StringGetSet.php', 'Predis\\Command\\StringIncrement' => __DIR__ . '/..' . '/predis/predis/src/Command/StringIncrement.php', 'Predis\\Command\\StringIncrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/StringIncrementBy.php', 'Predis\\Command\\StringIncrementByFloat' => __DIR__ . '/..' . '/predis/predis/src/Command/StringIncrementByFloat.php', 'Predis\\Command\\StringPreciseSetExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/StringPreciseSetExpire.php', 'Predis\\Command\\StringSet' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSet.php', 'Predis\\Command\\StringSetBit' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetBit.php', 'Predis\\Command\\StringSetExpire' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetExpire.php', 'Predis\\Command\\StringSetMultiple' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetMultiple.php', 'Predis\\Command\\StringSetMultiplePreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetMultiplePreserve.php', 'Predis\\Command\\StringSetPreserve' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetPreserve.php', 'Predis\\Command\\StringSetRange' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSetRange.php', 'Predis\\Command\\StringStrlen' => __DIR__ . '/..' . '/predis/predis/src/Command/StringStrlen.php', 'Predis\\Command\\StringSubstr' => __DIR__ . '/..' . '/predis/predis/src/Command/StringSubstr.php', 'Predis\\Command\\TransactionDiscard' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionDiscard.php', 'Predis\\Command\\TransactionExec' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionExec.php', 'Predis\\Command\\TransactionMulti' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionMulti.php', 'Predis\\Command\\TransactionUnwatch' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionUnwatch.php', 'Predis\\Command\\TransactionWatch' => __DIR__ . '/..' . '/predis/predis/src/Command/TransactionWatch.php', 'Predis\\Command\\ZSetAdd' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetAdd.php', 'Predis\\Command\\ZSetCardinality' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetCardinality.php', 'Predis\\Command\\ZSetCount' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetCount.php', 'Predis\\Command\\ZSetIncrementBy' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetIncrementBy.php', 'Predis\\Command\\ZSetIntersectionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetIntersectionStore.php', 'Predis\\Command\\ZSetLexCount' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetLexCount.php', 'Predis\\Command\\ZSetRange' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRange.php', 'Predis\\Command\\ZSetRangeByLex' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRangeByLex.php', 'Predis\\Command\\ZSetRangeByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRangeByScore.php', 'Predis\\Command\\ZSetRank' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRank.php', 'Predis\\Command\\ZSetRemove' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemove.php', 'Predis\\Command\\ZSetRemoveRangeByLex' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemoveRangeByLex.php', 'Predis\\Command\\ZSetRemoveRangeByRank' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemoveRangeByRank.php', 'Predis\\Command\\ZSetRemoveRangeByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetRemoveRangeByScore.php', 'Predis\\Command\\ZSetReverseRange' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRange.php', 'Predis\\Command\\ZSetReverseRangeByLex' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRangeByLex.php', 'Predis\\Command\\ZSetReverseRangeByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRangeByScore.php', 'Predis\\Command\\ZSetReverseRank' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetReverseRank.php', 'Predis\\Command\\ZSetScan' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetScan.php', 'Predis\\Command\\ZSetScore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetScore.php', 'Predis\\Command\\ZSetUnionStore' => __DIR__ . '/..' . '/predis/predis/src/Command/ZSetUnionStore.php', 'Predis\\CommunicationException' => __DIR__ . '/..' . '/predis/predis/src/CommunicationException.php', 'Predis\\Configuration\\ClusterOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ClusterOption.php', 'Predis\\Configuration\\ConnectionFactoryOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ConnectionFactoryOption.php', 'Predis\\Configuration\\ExceptionsOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ExceptionsOption.php', 'Predis\\Configuration\\OptionInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionInterface.php', 'Predis\\Configuration\\Options' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Options.php', 'Predis\\Configuration\\OptionsInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionsInterface.php', 'Predis\\Configuration\\PrefixOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/PrefixOption.php', 'Predis\\Configuration\\ProfileOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ProfileOption.php', 'Predis\\Configuration\\ReplicationOption' => __DIR__ . '/..' . '/predis/predis/src/Configuration/ReplicationOption.php', 'Predis\\Connection\\AbstractConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/AbstractConnection.php', 'Predis\\Connection\\AggregateConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/AggregateConnectionInterface.php', 'Predis\\Connection\\Aggregate\\ClusterInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/ClusterInterface.php', 'Predis\\Connection\\Aggregate\\MasterSlaveReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php', 'Predis\\Connection\\Aggregate\\PredisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/PredisCluster.php', 'Predis\\Connection\\Aggregate\\RedisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/RedisCluster.php', 'Predis\\Connection\\Aggregate\\ReplicationInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/ReplicationInterface.php', 'Predis\\Connection\\Aggregate\\SentinelReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Aggregate/SentinelReplication.php', 'Predis\\Connection\\CompositeConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/CompositeConnectionInterface.php', 'Predis\\Connection\\CompositeStreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/CompositeStreamConnection.php', 'Predis\\Connection\\ConnectionException' => __DIR__ . '/..' . '/predis/predis/src/Connection/ConnectionException.php', 'Predis\\Connection\\ConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/ConnectionInterface.php', 'Predis\\Connection\\Factory' => __DIR__ . '/..' . '/predis/predis/src/Connection/Factory.php', 'Predis\\Connection\\FactoryInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/FactoryInterface.php', 'Predis\\Connection\\NodeConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/NodeConnectionInterface.php', 'Predis\\Connection\\Parameters' => __DIR__ . '/..' . '/predis/predis/src/Connection/Parameters.php', 'Predis\\Connection\\ParametersInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/ParametersInterface.php', 'Predis\\Connection\\PhpiredisSocketConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/PhpiredisSocketConnection.php', 'Predis\\Connection\\PhpiredisStreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/PhpiredisStreamConnection.php', 'Predis\\Connection\\StreamConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/StreamConnection.php', 'Predis\\Connection\\WebdisConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/WebdisConnection.php', 'Predis\\Monitor\\Consumer' => __DIR__ . '/..' . '/predis/predis/src/Monitor/Consumer.php', 'Predis\\NotSupportedException' => __DIR__ . '/..' . '/predis/predis/src/NotSupportedException.php', 'Predis\\Pipeline\\Atomic' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/Atomic.php', 'Predis\\Pipeline\\ConnectionErrorProof' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/ConnectionErrorProof.php', 'Predis\\Pipeline\\FireAndForget' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/FireAndForget.php', 'Predis\\Pipeline\\Pipeline' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/Pipeline.php', 'Predis\\PredisException' => __DIR__ . '/..' . '/predis/predis/src/PredisException.php', 'Predis\\Profile\\Factory' => __DIR__ . '/..' . '/predis/predis/src/Profile/Factory.php', 'Predis\\Profile\\ProfileInterface' => __DIR__ . '/..' . '/predis/predis/src/Profile/ProfileInterface.php', 'Predis\\Profile\\RedisProfile' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisProfile.php', 'Predis\\Profile\\RedisUnstable' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisUnstable.php', 'Predis\\Profile\\RedisVersion200' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion200.php', 'Predis\\Profile\\RedisVersion220' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion220.php', 'Predis\\Profile\\RedisVersion240' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion240.php', 'Predis\\Profile\\RedisVersion260' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion260.php', 'Predis\\Profile\\RedisVersion280' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion280.php', 'Predis\\Profile\\RedisVersion300' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion300.php', 'Predis\\Profile\\RedisVersion320' => __DIR__ . '/..' . '/predis/predis/src/Profile/RedisVersion320.php', 'Predis\\Protocol\\ProtocolException' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ProtocolException.php', 'Predis\\Protocol\\ProtocolProcessorInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ProtocolProcessorInterface.php', 'Predis\\Protocol\\RequestSerializerInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/RequestSerializerInterface.php', 'Predis\\Protocol\\ResponseReaderInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/ResponseReaderInterface.php', 'Predis\\Protocol\\Text\\CompositeProtocolProcessor' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php', 'Predis\\Protocol\\Text\\Handler\\BulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/BulkResponse.php', 'Predis\\Protocol\\Text\\Handler\\ErrorResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php', 'Predis\\Protocol\\Text\\Handler\\IntegerResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php', 'Predis\\Protocol\\Text\\Handler\\MultiBulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php', 'Predis\\Protocol\\Text\\Handler\\ResponseHandlerInterface' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php', 'Predis\\Protocol\\Text\\Handler\\StatusResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/StatusResponse.php', 'Predis\\Protocol\\Text\\Handler\\StreamableMultiBulkResponse' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php', 'Predis\\Protocol\\Text\\ProtocolProcessor' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/ProtocolProcessor.php', 'Predis\\Protocol\\Text\\RequestSerializer' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/RequestSerializer.php', 'Predis\\Protocol\\Text\\ResponseReader' => __DIR__ . '/..' . '/predis/predis/src/Protocol/Text/ResponseReader.php', 'Predis\\PubSub\\AbstractConsumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/AbstractConsumer.php', 'Predis\\PubSub\\Consumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/Consumer.php', 'Predis\\PubSub\\DispatcherLoop' => __DIR__ . '/..' . '/predis/predis/src/PubSub/DispatcherLoop.php', 'Predis\\Replication\\MissingMasterException' => __DIR__ . '/..' . '/predis/predis/src/Replication/MissingMasterException.php', 'Predis\\Replication\\ReplicationStrategy' => __DIR__ . '/..' . '/predis/predis/src/Replication/ReplicationStrategy.php', 'Predis\\Replication\\RoleException' => __DIR__ . '/..' . '/predis/predis/src/Replication/RoleException.php', 'Predis\\Response\\Error' => __DIR__ . '/..' . '/predis/predis/src/Response/Error.php', 'Predis\\Response\\ErrorInterface' => __DIR__ . '/..' . '/predis/predis/src/Response/ErrorInterface.php', 'Predis\\Response\\Iterator\\MultiBulk' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulk.php', 'Predis\\Response\\Iterator\\MultiBulkIterator' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulkIterator.php', 'Predis\\Response\\Iterator\\MultiBulkTuple' => __DIR__ . '/..' . '/predis/predis/src/Response/Iterator/MultiBulkTuple.php', 'Predis\\Response\\ResponseInterface' => __DIR__ . '/..' . '/predis/predis/src/Response/ResponseInterface.php', 'Predis\\Response\\ServerException' => __DIR__ . '/..' . '/predis/predis/src/Response/ServerException.php', 'Predis\\Response\\Status' => __DIR__ . '/..' . '/predis/predis/src/Response/Status.php', 'Predis\\Session\\Handler' => __DIR__ . '/..' . '/predis/predis/src/Session/Handler.php', 'Predis\\Transaction\\AbortedMultiExecException' => __DIR__ . '/..' . '/predis/predis/src/Transaction/AbortedMultiExecException.php', 'Predis\\Transaction\\MultiExec' => __DIR__ . '/..' . '/predis/predis/src/Transaction/MultiExec.php', 'Predis\\Transaction\\MultiExecState' => __DIR__ . '/..' . '/predis/predis/src/Transaction/MultiExecState.php', 'Prophecy\\Argument' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument.php', 'Prophecy\\Argument\\ArgumentsWildcard' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/ArgumentsWildcard.php', 'Prophecy\\Argument\\Token\\AnyValueToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValueToken.php', 'Prophecy\\Argument\\Token\\AnyValuesToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/AnyValuesToken.php', 'Prophecy\\Argument\\Token\\ApproximateValueToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/ApproximateValueToken.php', 'Prophecy\\Argument\\Token\\ArrayCountToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayCountToken.php', 'Prophecy\\Argument\\Token\\ArrayEntryToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEntryToken.php', 'Prophecy\\Argument\\Token\\ArrayEveryEntryToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/ArrayEveryEntryToken.php', 'Prophecy\\Argument\\Token\\CallbackToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/CallbackToken.php', 'Prophecy\\Argument\\Token\\ExactValueToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/ExactValueToken.php', 'Prophecy\\Argument\\Token\\IdenticalValueToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/IdenticalValueToken.php', 'Prophecy\\Argument\\Token\\LogicalAndToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalAndToken.php', 'Prophecy\\Argument\\Token\\LogicalNotToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/LogicalNotToken.php', 'Prophecy\\Argument\\Token\\ObjectStateToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/ObjectStateToken.php', 'Prophecy\\Argument\\Token\\StringContainsToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/StringContainsToken.php', 'Prophecy\\Argument\\Token\\TokenInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/TokenInterface.php', 'Prophecy\\Argument\\Token\\TypeToken' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Argument/Token/TypeToken.php', 'Prophecy\\Call\\Call' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Call/Call.php', 'Prophecy\\Call\\CallCenter' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Call/CallCenter.php', 'Prophecy\\Comparator\\ClosureComparator' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Comparator/ClosureComparator.php', 'Prophecy\\Comparator\\Factory' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Comparator/Factory.php', 'Prophecy\\Comparator\\ProphecyComparator' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Comparator/ProphecyComparator.php', 'Prophecy\\Doubler\\CachedDoubler' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/CachedDoubler.php', 'Prophecy\\Doubler\\ClassPatch\\ClassPatchInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php', 'Prophecy\\Doubler\\ClassPatch\\DisableConstructorPatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php', 'Prophecy\\Doubler\\ClassPatch\\HhvmExceptionPatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php', 'Prophecy\\Doubler\\ClassPatch\\KeywordPatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/KeywordPatch.php', 'Prophecy\\Doubler\\ClassPatch\\MagicCallPatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/MagicCallPatch.php', 'Prophecy\\Doubler\\ClassPatch\\ProphecySubjectPatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php', 'Prophecy\\Doubler\\ClassPatch\\ReflectionClassNewInstancePatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php', 'Prophecy\\Doubler\\ClassPatch\\SplFileInfoPatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php', 'Prophecy\\Doubler\\ClassPatch\\ThrowablePatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/ThrowablePatch.php', 'Prophecy\\Doubler\\ClassPatch\\TraversablePatch' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/ClassPatch/TraversablePatch.php', 'Prophecy\\Doubler\\DoubleInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/DoubleInterface.php', 'Prophecy\\Doubler\\Doubler' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Doubler.php', 'Prophecy\\Doubler\\Generator\\ClassCodeGenerator' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php', 'Prophecy\\Doubler\\Generator\\ClassCreator' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassCreator.php', 'Prophecy\\Doubler\\Generator\\ClassMirror' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ClassMirror.php', 'Prophecy\\Doubler\\Generator\\Node\\ArgumentNode' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ArgumentNode.php', 'Prophecy\\Doubler\\Generator\\Node\\ClassNode' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/ClassNode.php', 'Prophecy\\Doubler\\Generator\\Node\\MethodNode' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/Node/MethodNode.php', 'Prophecy\\Doubler\\Generator\\ReflectionInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/ReflectionInterface.php', 'Prophecy\\Doubler\\Generator\\TypeHintReference' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/Generator/TypeHintReference.php', 'Prophecy\\Doubler\\LazyDouble' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/LazyDouble.php', 'Prophecy\\Doubler\\NameGenerator' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Doubler/NameGenerator.php', 'Prophecy\\Exception\\Call\\UnexpectedCallException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Call/UnexpectedCallException.php', 'Prophecy\\Exception\\Doubler\\ClassCreatorException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassCreatorException.php', 'Prophecy\\Exception\\Doubler\\ClassMirrorException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassMirrorException.php', 'Prophecy\\Exception\\Doubler\\ClassNotFoundException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ClassNotFoundException.php', 'Prophecy\\Exception\\Doubler\\DoubleException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoubleException.php', 'Prophecy\\Exception\\Doubler\\DoublerException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/DoublerException.php', 'Prophecy\\Exception\\Doubler\\InterfaceNotFoundException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/InterfaceNotFoundException.php', 'Prophecy\\Exception\\Doubler\\MethodNotExtendableException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotExtendableException.php', 'Prophecy\\Exception\\Doubler\\MethodNotFoundException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/MethodNotFoundException.php', 'Prophecy\\Exception\\Doubler\\ReturnByReferenceException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Doubler/ReturnByReferenceException.php', 'Prophecy\\Exception\\Exception' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Exception.php', 'Prophecy\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/InvalidArgumentException.php', 'Prophecy\\Exception\\Prediction\\AggregateException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/AggregateException.php', 'Prophecy\\Exception\\Prediction\\FailedPredictionException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/FailedPredictionException.php', 'Prophecy\\Exception\\Prediction\\NoCallsException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/NoCallsException.php', 'Prophecy\\Exception\\Prediction\\PredictionException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/PredictionException.php', 'Prophecy\\Exception\\Prediction\\UnexpectedCallsCountException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php', 'Prophecy\\Exception\\Prediction\\UnexpectedCallsException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prediction/UnexpectedCallsException.php', 'Prophecy\\Exception\\Prophecy\\MethodProphecyException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prophecy/MethodProphecyException.php', 'Prophecy\\Exception\\Prophecy\\ObjectProphecyException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ObjectProphecyException.php', 'Prophecy\\Exception\\Prophecy\\ProphecyException' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Exception/Prophecy/ProphecyException.php', 'Prophecy\\PhpDocumentor\\ClassAndInterfaceTagRetriever' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php', 'Prophecy\\PhpDocumentor\\ClassTagRetriever' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/ClassTagRetriever.php', 'Prophecy\\PhpDocumentor\\LegacyClassTagRetriever' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php', 'Prophecy\\PhpDocumentor\\MethodTagRetrieverInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php', 'Prophecy\\Prediction\\CallPrediction' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prediction/CallPrediction.php', 'Prophecy\\Prediction\\CallTimesPrediction' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prediction/CallTimesPrediction.php', 'Prophecy\\Prediction\\CallbackPrediction' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prediction/CallbackPrediction.php', 'Prophecy\\Prediction\\NoCallsPrediction' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prediction/NoCallsPrediction.php', 'Prophecy\\Prediction\\PredictionInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prediction/PredictionInterface.php', 'Prophecy\\Promise\\CallbackPromise' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Promise/CallbackPromise.php', 'Prophecy\\Promise\\PromiseInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Promise/PromiseInterface.php', 'Prophecy\\Promise\\ReturnArgumentPromise' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Promise/ReturnArgumentPromise.php', 'Prophecy\\Promise\\ReturnPromise' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Promise/ReturnPromise.php', 'Prophecy\\Promise\\ThrowPromise' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Promise/ThrowPromise.php', 'Prophecy\\Prophecy\\MethodProphecy' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prophecy/MethodProphecy.php', 'Prophecy\\Prophecy\\ObjectProphecy' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prophecy/ObjectProphecy.php', 'Prophecy\\Prophecy\\ProphecyInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prophecy/ProphecyInterface.php', 'Prophecy\\Prophecy\\ProphecySubjectInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prophecy/ProphecySubjectInterface.php', 'Prophecy\\Prophecy\\Revealer' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prophecy/Revealer.php', 'Prophecy\\Prophecy\\RevealerInterface' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prophecy/RevealerInterface.php', 'Prophecy\\Prophet' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Prophet.php', 'Prophecy\\Util\\ExportUtil' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Util/ExportUtil.php', 'Prophecy\\Util\\StringUtil' => __DIR__ . '/..' . '/phpspec/prophecy/src/Prophecy/Util/StringUtil.php', 'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php', 'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php', 'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php', 'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php', 'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php', 'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php', 'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.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/DummyTest.php', 'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', 'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage.php', 'SebastianBergmann\\CodeCoverage\\CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/CoveredCodeNotExecutedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\HHVM' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/HHVM.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PHPDBG' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PHPDBG.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Xdebug.php', 'SebastianBergmann\\CodeCoverage\\Exception' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/Exception.php', 'SebastianBergmann\\CodeCoverage\\Filter' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Filter.php', 'SebastianBergmann\\CodeCoverage\\InvalidArgumentException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/InvalidArgumentException.php', 'SebastianBergmann\\CodeCoverage\\MissingCoversAnnotationException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/MissingCoversAnnotationException.php', 'SebastianBergmann\\CodeCoverage\\Node\\AbstractNode' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/AbstractNode.php', 'SebastianBergmann\\CodeCoverage\\Node\\Builder' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Builder.php', 'SebastianBergmann\\CodeCoverage\\Node\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Directory.php', 'SebastianBergmann\\CodeCoverage\\Node\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/File.php', 'SebastianBergmann\\CodeCoverage\\Node\\Iterator' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/Iterator.php', 'SebastianBergmann\\CodeCoverage\\Report\\Clover' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Clover.php', 'SebastianBergmann\\CodeCoverage\\Report\\Crap4j' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Crap4j.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Dashboard' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Dashboard.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/Directory.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Facade' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Facade.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php', 'SebastianBergmann\\CodeCoverage\\Report\\Html\\Renderer' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Html/Renderer.php', 'SebastianBergmann\\CodeCoverage\\Report\\PHP' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/PHP.php', 'SebastianBergmann\\CodeCoverage\\Report\\Text' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Text.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Coverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Coverage.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Directory' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Directory.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Facade' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Facade.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\File' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/File.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Method' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Method.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Node' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Node.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Project' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Project.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Report' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Report.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Tests' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Tests.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Totals' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Totals.php', 'SebastianBergmann\\CodeCoverage\\Report\\Xml\\Unit' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Unit.php', 'SebastianBergmann\\CodeCoverage\\RuntimeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/RuntimeException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', 'SebastianBergmann\\CodeCoverage\\Util' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Util.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => __DIR__ . '/..' . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', 'SebastianBergmann\\Comparator\\ArrayComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ArrayComparator.php', 'SebastianBergmann\\Comparator\\Comparator' => __DIR__ . '/..' . '/sebastian/comparator/src/Comparator.php', 'SebastianBergmann\\Comparator\\ComparisonFailure' => __DIR__ . '/..' . '/sebastian/comparator/src/ComparisonFailure.php', 'SebastianBergmann\\Comparator\\DOMNodeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DOMNodeComparator.php', 'SebastianBergmann\\Comparator\\DateTimeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DateTimeComparator.php', 'SebastianBergmann\\Comparator\\DoubleComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/DoubleComparator.php', 'SebastianBergmann\\Comparator\\ExceptionComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ExceptionComparator.php', 'SebastianBergmann\\Comparator\\Factory' => __DIR__ . '/..' . '/sebastian/comparator/src/Factory.php', 'SebastianBergmann\\Comparator\\MockObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/MockObjectComparator.php', 'SebastianBergmann\\Comparator\\NumericComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/NumericComparator.php', 'SebastianBergmann\\Comparator\\ObjectComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ObjectComparator.php', 'SebastianBergmann\\Comparator\\ResourceComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ResourceComparator.php', 'SebastianBergmann\\Comparator\\ScalarComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/ScalarComparator.php', 'SebastianBergmann\\Comparator\\SplObjectStorageComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/SplObjectStorageComparator.php', 'SebastianBergmann\\Comparator\\TypeComparator' => __DIR__ . '/..' . '/sebastian/comparator/src/TypeComparator.php', 'SebastianBergmann\\Diff\\Chunk' => __DIR__ . '/..' . '/sebastian/diff/src/Chunk.php', 'SebastianBergmann\\Diff\\Diff' => __DIR__ . '/..' . '/sebastian/diff/src/Diff.php', 'SebastianBergmann\\Diff\\Differ' => __DIR__ . '/..' . '/sebastian/diff/src/Differ.php', 'SebastianBergmann\\Diff\\LCS\\LongestCommonSubsequence' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/LongestCommonSubsequence.php', 'SebastianBergmann\\Diff\\LCS\\MemoryEfficientImplementation' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php', 'SebastianBergmann\\Diff\\LCS\\TimeEfficientImplementation' => __DIR__ . '/..' . '/sebastian/diff/src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php', 'SebastianBergmann\\Diff\\Line' => __DIR__ . '/..' . '/sebastian/diff/src/Line.php', 'SebastianBergmann\\Diff\\Parser' => __DIR__ . '/..' . '/sebastian/diff/src/Parser.php', 'SebastianBergmann\\Environment\\Console' => __DIR__ . '/..' . '/sebastian/environment/src/Console.php', 'SebastianBergmann\\Environment\\Runtime' => __DIR__ . '/..' . '/sebastian/environment/src/Runtime.php', 'SebastianBergmann\\Exporter\\Exporter' => __DIR__ . '/..' . '/sebastian/exporter/src/Exporter.php', 'SebastianBergmann\\GlobalState\\Blacklist' => __DIR__ . '/..' . '/sebastian/global-state/src/Blacklist.php', 'SebastianBergmann\\GlobalState\\CodeExporter' => __DIR__ . '/..' . '/sebastian/global-state/src/CodeExporter.php', 'SebastianBergmann\\GlobalState\\Exception' => __DIR__ . '/..' . '/sebastian/global-state/src/Exception.php', 'SebastianBergmann\\GlobalState\\Restorer' => __DIR__ . '/..' . '/sebastian/global-state/src/Restorer.php', 'SebastianBergmann\\GlobalState\\RuntimeException' => __DIR__ . '/..' . '/sebastian/global-state/src/RuntimeException.php', 'SebastianBergmann\\GlobalState\\Snapshot' => __DIR__ . '/..' . '/sebastian/global-state/src/Snapshot.php', 'SebastianBergmann\\ObjectEnumerator\\Enumerator' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/Enumerator.php', 'SebastianBergmann\\ObjectEnumerator\\Exception' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/Exception.php', 'SebastianBergmann\\ObjectEnumerator\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/object-enumerator/src/InvalidArgumentException.php', 'SebastianBergmann\\RecursionContext\\Context' => __DIR__ . '/..' . '/sebastian/recursion-context/src/Context.php', 'SebastianBergmann\\RecursionContext\\Exception' => __DIR__ . '/..' . '/sebastian/recursion-context/src/Exception.php', 'SebastianBergmann\\RecursionContext\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/recursion-context/src/InvalidArgumentException.php', 'SebastianBergmann\\ResourceOperations\\ResourceOperations' => __DIR__ . '/..' . '/sebastian/resource-operations/src/ResourceOperations.php', 'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php', 'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', 'Symfony\\Component\\BrowserKit\\Client' => __DIR__ . '/..' . '/symfony/browser-kit/Client.php', 'Symfony\\Component\\BrowserKit\\Cookie' => __DIR__ . '/..' . '/symfony/browser-kit/Cookie.php', 'Symfony\\Component\\BrowserKit\\CookieJar' => __DIR__ . '/..' . '/symfony/browser-kit/CookieJar.php', 'Symfony\\Component\\BrowserKit\\History' => __DIR__ . '/..' . '/symfony/browser-kit/History.php', 'Symfony\\Component\\BrowserKit\\Request' => __DIR__ . '/..' . '/symfony/browser-kit/Request.php', 'Symfony\\Component\\BrowserKit\\Response' => __DIR__ . '/..' . '/symfony/browser-kit/Response.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\\CssSelector\\CssSelectorConverter' => __DIR__ . '/..' . '/symfony/css-selector/CssSelectorConverter.php', 'Symfony\\Component\\CssSelector\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/css-selector/Exception/ExceptionInterface.php', 'Symfony\\Component\\CssSelector\\Exception\\ExpressionErrorException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/ExpressionErrorException.php', 'Symfony\\Component\\CssSelector\\Exception\\InternalErrorException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/InternalErrorException.php', 'Symfony\\Component\\CssSelector\\Exception\\ParseException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/ParseException.php', 'Symfony\\Component\\CssSelector\\Exception\\SyntaxErrorException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/SyntaxErrorException.php', 'Symfony\\Component\\CssSelector\\Node\\AbstractNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/AbstractNode.php', 'Symfony\\Component\\CssSelector\\Node\\AttributeNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/AttributeNode.php', 'Symfony\\Component\\CssSelector\\Node\\ClassNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/ClassNode.php', 'Symfony\\Component\\CssSelector\\Node\\CombinedSelectorNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/CombinedSelectorNode.php', 'Symfony\\Component\\CssSelector\\Node\\ElementNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/ElementNode.php', 'Symfony\\Component\\CssSelector\\Node\\FunctionNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/FunctionNode.php', 'Symfony\\Component\\CssSelector\\Node\\HashNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/HashNode.php', 'Symfony\\Component\\CssSelector\\Node\\NegationNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/NegationNode.php', 'Symfony\\Component\\CssSelector\\Node\\NodeInterface' => __DIR__ . '/..' . '/symfony/css-selector/Node/NodeInterface.php', 'Symfony\\Component\\CssSelector\\Node\\PseudoNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/PseudoNode.php', 'Symfony\\Component\\CssSelector\\Node\\SelectorNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/SelectorNode.php', 'Symfony\\Component\\CssSelector\\Node\\Specificity' => __DIR__ . '/..' . '/symfony/css-selector/Node/Specificity.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\CommentHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/CommentHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\HandlerInterface' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/HandlerInterface.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\HashHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/HashHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\IdentifierHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/IdentifierHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\NumberHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/NumberHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\StringHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/StringHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Handler\\WhitespaceHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/WhitespaceHandler.php', 'Symfony\\Component\\CssSelector\\Parser\\Parser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Parser.php', 'Symfony\\Component\\CssSelector\\Parser\\ParserInterface' => __DIR__ . '/..' . '/symfony/css-selector/Parser/ParserInterface.php', 'Symfony\\Component\\CssSelector\\Parser\\Reader' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Reader.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ClassParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/ClassParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ElementParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/ElementParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\EmptyStringParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\HashParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/HashParser.php', 'Symfony\\Component\\CssSelector\\Parser\\Token' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Token.php', 'Symfony\\Component\\CssSelector\\Parser\\TokenStream' => __DIR__ . '/..' . '/symfony/css-selector/Parser/TokenStream.php', 'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\Tokenizer' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Tokenizer/Tokenizer.php', 'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerEscaping' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php', 'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerPatterns' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\AbstractExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/AbstractExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\AttributeMatchingExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\CombinationExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/CombinationExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\ExtensionInterface' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/ExtensionInterface.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\FunctionExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/FunctionExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\HtmlExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/HtmlExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\NodeExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/NodeExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Extension\\PseudoClassExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/PseudoClassExtension.php', 'Symfony\\Component\\CssSelector\\XPath\\Translator' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Translator.php', 'Symfony\\Component\\CssSelector\\XPath\\TranslatorInterface' => __DIR__ . '/..' . '/symfony/css-selector/XPath/TranslatorInterface.php', 'Symfony\\Component\\CssSelector\\XPath\\XPathExpr' => __DIR__ . '/..' . '/symfony/css-selector/XPath/XPathExpr.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\\DomCrawler\\AbstractUriElement' => __DIR__ . '/..' . '/symfony/dom-crawler/AbstractUriElement.php', 'Symfony\\Component\\DomCrawler\\Crawler' => __DIR__ . '/..' . '/symfony/dom-crawler/Crawler.php', 'Symfony\\Component\\DomCrawler\\Field\\ChoiceFormField' => __DIR__ . '/..' . '/symfony/dom-crawler/Field/ChoiceFormField.php', 'Symfony\\Component\\DomCrawler\\Field\\FileFormField' => __DIR__ . '/..' . '/symfony/dom-crawler/Field/FileFormField.php', 'Symfony\\Component\\DomCrawler\\Field\\FormField' => __DIR__ . '/..' . '/symfony/dom-crawler/Field/FormField.php', 'Symfony\\Component\\DomCrawler\\Field\\InputFormField' => __DIR__ . '/..' . '/symfony/dom-crawler/Field/InputFormField.php', 'Symfony\\Component\\DomCrawler\\Field\\TextareaFormField' => __DIR__ . '/..' . '/symfony/dom-crawler/Field/TextareaFormField.php', 'Symfony\\Component\\DomCrawler\\Form' => __DIR__ . '/..' . '/symfony/dom-crawler/Form.php', 'Symfony\\Component\\DomCrawler\\FormFieldRegistry' => __DIR__ . '/..' . '/symfony/dom-crawler/FormFieldRegistry.php', 'Symfony\\Component\\DomCrawler\\Image' => __DIR__ . '/..' . '/symfony/dom-crawler/Image.php', 'Symfony\\Component\\DomCrawler\\Link' => __DIR__ . '/..' . '/symfony/dom-crawler/Link.php', 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/WrappedListener.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', 'Symfony\\Component\\EventDispatcher\\Event' => __DIR__ . '/..' . '/symfony/event-dispatcher/Event.php', 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcherInterface.php', 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php', 'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php', 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', 'Symfony\\Component\\Finder\\Comparator\\Comparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/Comparator.php', 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/DateComparator.php', 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/NumberComparator.php', 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/symfony/finder/Exception/AccessDeniedException.php', 'Symfony\\Component\\Finder\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/finder/Exception/ExceptionInterface.php', 'Symfony\\Component\\Finder\\Finder' => __DIR__ . '/..' . '/symfony/finder/Finder.php', 'Symfony\\Component\\Finder\\Glob' => __DIR__ . '/..' . '/symfony/finder/Glob.php', 'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/CustomFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/DateRangeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/DepthRangeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FileTypeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FileTypeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilecontentFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilenameFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/PathFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php', 'Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/SizeRangeFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\SortableIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/SortableIterator.php', 'Symfony\\Component\\Finder\\SplFileInfo' => __DIR__ . '/..' . '/symfony/finder/SplFileInfo.php', 'Symfony\\Component\\Process\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/process/Exception/ExceptionInterface.php', 'Symfony\\Component\\Process\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/process/Exception/InvalidArgumentException.php', 'Symfony\\Component\\Process\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/process/Exception/LogicException.php', 'Symfony\\Component\\Process\\Exception\\ProcessFailedException' => __DIR__ . '/..' . '/symfony/process/Exception/ProcessFailedException.php', 'Symfony\\Component\\Process\\Exception\\ProcessTimedOutException' => __DIR__ . '/..' . '/symfony/process/Exception/ProcessTimedOutException.php', 'Symfony\\Component\\Process\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/process/Exception/RuntimeException.php', 'Symfony\\Component\\Process\\ExecutableFinder' => __DIR__ . '/..' . '/symfony/process/ExecutableFinder.php', 'Symfony\\Component\\Process\\InputStream' => __DIR__ . '/..' . '/symfony/process/InputStream.php', 'Symfony\\Component\\Process\\PhpExecutableFinder' => __DIR__ . '/..' . '/symfony/process/PhpExecutableFinder.php', 'Symfony\\Component\\Process\\PhpProcess' => __DIR__ . '/..' . '/symfony/process/PhpProcess.php', 'Symfony\\Component\\Process\\Pipes\\AbstractPipes' => __DIR__ . '/..' . '/symfony/process/Pipes/AbstractPipes.php', 'Symfony\\Component\\Process\\Pipes\\PipesInterface' => __DIR__ . '/..' . '/symfony/process/Pipes/PipesInterface.php', 'Symfony\\Component\\Process\\Pipes\\UnixPipes' => __DIR__ . '/..' . '/symfony/process/Pipes/UnixPipes.php', 'Symfony\\Component\\Process\\Pipes\\WindowsPipes' => __DIR__ . '/..' . '/symfony/process/Pipes/WindowsPipes.php', 'Symfony\\Component\\Process\\Process' => __DIR__ . '/..' . '/symfony/process/Process.php', 'Symfony\\Component\\Process\\ProcessBuilder' => __DIR__ . '/..' . '/symfony/process/ProcessBuilder.php', 'Symfony\\Component\\Process\\ProcessUtils' => __DIR__ . '/..' . '/symfony/process/ProcessUtils.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\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php', 'Symfony\\Polyfill\\Intl\\Idn\\Idn' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/Idn.php', 'Symfony\\Polyfill\\Intl\\Idn\\Info' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/Info.php', 'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\DisallowedRanges' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php', 'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\Regex' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/Resources/unidata/Regex.php', 'Symfony\\Polyfill\\Intl\\Normalizer\\Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Normalizer.php', 'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', 'Symfony\\Polyfill\\Php70\\Php70' => __DIR__ . '/..' . '/symfony/polyfill-php70/Php70.php', 'Symfony\\Polyfill\\Php72\\Php72' => __DIR__ . '/..' . '/symfony/polyfill-php72/Php72.php', 'Text_Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php', 'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', 'Webmozart\\Assert\\Assert' => __DIR__ . '/..' . '/webmozart/assert/src/Assert.php', 'Webmozart\\Assert\\Mixin' => __DIR__ . '/..' . '/webmozart/assert/src/Mixin.php', 'phpDocumentor\\Reflection\\DocBlock' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock.php', 'phpDocumentor\\Reflection\\DocBlockFactory' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlockFactory.php', 'phpDocumentor\\Reflection\\DocBlockFactoryInterface' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlockFactoryInterface.php', 'phpDocumentor\\Reflection\\DocBlock\\Description' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Description.php', 'phpDocumentor\\Reflection\\DocBlock\\DescriptionFactory' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/DescriptionFactory.php', 'phpDocumentor\\Reflection\\DocBlock\\ExampleFinder' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/ExampleFinder.php', 'phpDocumentor\\Reflection\\DocBlock\\Serializer' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Serializer.php', 'phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/StandardTagFactory.php', 'phpDocumentor\\Reflection\\DocBlock\\Tag' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tag.php', 'phpDocumentor\\Reflection\\DocBlock\\TagFactory' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/TagFactory.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Author' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Author.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\BaseTag' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/BaseTag.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Covers' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Covers.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Deprecated' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Example' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Example.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\StaticMethod' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/StaticMethod.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Factory\\Strategy' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Factory/Strategy.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\AlignFormatter' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/AlignFormatter.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Formatter\\PassthroughFormatter' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Formatter/PassthroughFormatter.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Generic' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Generic.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Link' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Link.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Method' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Method.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Param' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Param.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Property' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Property.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyRead' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyRead.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\PropertyWrite' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/PropertyWrite.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Fqsen' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Fqsen.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Reference' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Reference.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Reference\\Url' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Reference/Url.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Return_' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Return_.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\See' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/See.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Since' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Since.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Source' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Source.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Throws' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Throws.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Uses' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Uses.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Var_' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Var_.php', 'phpDocumentor\\Reflection\\DocBlock\\Tags\\Version' => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Version.php', 'phpDocumentor\\Reflection\\Element' => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src/Element.php', 'phpDocumentor\\Reflection\\File' => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src/File.php', 'phpDocumentor\\Reflection\\Fqsen' => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src/Fqsen.php', 'phpDocumentor\\Reflection\\FqsenResolver' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/FqsenResolver.php', 'phpDocumentor\\Reflection\\Location' => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src/Location.php', 'phpDocumentor\\Reflection\\Project' => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src/Project.php', 'phpDocumentor\\Reflection\\ProjectFactory' => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src/ProjectFactory.php', 'phpDocumentor\\Reflection\\Type' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Type.php', 'phpDocumentor\\Reflection\\TypeResolver' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/TypeResolver.php', 'phpDocumentor\\Reflection\\Types\\Array_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Array_.php', 'phpDocumentor\\Reflection\\Types\\Boolean' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Boolean.php', 'phpDocumentor\\Reflection\\Types\\Callable_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Callable_.php', 'phpDocumentor\\Reflection\\Types\\Compound' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Compound.php', 'phpDocumentor\\Reflection\\Types\\Context' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Context.php', 'phpDocumentor\\Reflection\\Types\\ContextFactory' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/ContextFactory.php', 'phpDocumentor\\Reflection\\Types\\Float_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Float_.php', 'phpDocumentor\\Reflection\\Types\\Integer' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Integer.php', 'phpDocumentor\\Reflection\\Types\\Iterable_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Iterable_.php', 'phpDocumentor\\Reflection\\Types\\Mixed_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Mixed_.php', 'phpDocumentor\\Reflection\\Types\\Null_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Null_.php', 'phpDocumentor\\Reflection\\Types\\Nullable' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Nullable.php', 'phpDocumentor\\Reflection\\Types\\Object_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Object_.php', 'phpDocumentor\\Reflection\\Types\\Parent_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Parent_.php', 'phpDocumentor\\Reflection\\Types\\Resource_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Resource_.php', 'phpDocumentor\\Reflection\\Types\\Scalar' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Scalar.php', 'phpDocumentor\\Reflection\\Types\\Self_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Self_.php', 'phpDocumentor\\Reflection\\Types\\Static_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Static_.php', 'phpDocumentor\\Reflection\\Types\\String_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/String_.php', 'phpDocumentor\\Reflection\\Types\\This' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/This.php', 'phpDocumentor\\Reflection\\Types\\Void_' => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src/Types/Void_.php', 'phpseclib\\Crypt\\AES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php', 'phpseclib\\Crypt\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Base.php', 'phpseclib\\Crypt\\Blowfish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php', 'phpseclib\\Crypt\\DES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php', 'phpseclib\\Crypt\\Hash' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php', 'phpseclib\\Crypt\\RC2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php', 'phpseclib\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php', 'phpseclib\\Crypt\\RSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php', 'phpseclib\\Crypt\\Random' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php', 'phpseclib\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php', 'phpseclib\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php', 'phpseclib\\Crypt\\Twofish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php', 'phpseclib\\File\\ANSI' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ANSI.php', 'phpseclib\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1.php', 'phpseclib\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php', 'phpseclib\\File\\X509' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/X509.php', 'phpseclib\\Math\\BigInteger' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php', 'phpseclib\\Net\\SCP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SCP.php', 'phpseclib\\Net\\SFTP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php', 'phpseclib\\Net\\SFTP\\Stream' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php', 'phpseclib\\Net\\SSH1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH1.php', 'phpseclib\\Net\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php', 'phpseclib\\System\\SSH\\Agent' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php', 'phpseclib\\System\\SSH\\Agent\\Identity' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit27e074e2281a7b29fcc5bab5b46fd4ef::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit27e074e2281a7b29fcc5bab5b46fd4ef::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInit27e074e2281a7b29fcc5bab5b46fd4ef::$prefixesPsr0; $loader->classMap = ComposerStaticInit27e074e2281a7b29fcc5bab5b46fd4ef::$classMap; }, null, ClassLoader::class); } } = 50603)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.3". You are running ' . PHP_VERSION . '.'; } if ($issues) { if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } if (!ini_get('display_errors')) { if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); } elseif (!headers_sent()) { echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } trigger_error( 'Composer detected issues in your platform: ' . implode(' ', $issues), E_USER_ERROR ); } array($vendorDir . '/phpseclib/phpseclib/phpseclib'), 'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'), 'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'), 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'), 'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), 'Symfony\\Component\\DomCrawler\\' => array($vendorDir . '/symfony/dom-crawler'), 'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), 'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), 'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'), 'Predis\\' => array($vendorDir . '/predis/predis/src'), 'PhpAmqpLib\\' => array($vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib'), 'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src/JsonSchema'), 'Humbug\\SelfUpdate\\' => array($vendorDir . '/padraic/phar-updater/src'), 'Humbug\\' => array($vendorDir . '/padraic/humbug_get_contents/src'), 'Hoa\\Ustring\\' => array($vendorDir . '/hoa/ustring'), 'Hoa\\Stream\\' => array($vendorDir . '/hoa/stream'), 'Hoa\\Protocol\\' => array($vendorDir . '/hoa/protocol'), 'Hoa\\Iterator\\' => array($vendorDir . '/hoa/iterator'), 'Hoa\\File\\' => array($vendorDir . '/hoa/file'), 'Hoa\\Exception\\' => array($vendorDir . '/hoa/exception'), 'Hoa\\Event\\' => array($vendorDir . '/hoa/event'), 'Hoa\\Console\\' => array($vendorDir . '/hoa/console'), 'Hoa\\Consistency\\' => array($vendorDir . '/hoa/consistency'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), 'Flow\\JSONPath\\' => array($vendorDir . '/softcreatr/jsonpath/src'), 'Facebook\\WebDriver\\' => array($vendorDir . '/php-webdriver/webdriver/lib'), 'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), 'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'), 'Codeception\\PHPUnit\\' => array($vendorDir . '/codeception/phpunit-wrapper/src'), 'Codeception\\Extension\\' => array($vendorDir . '/codeception/codeception/ext'), 'Codeception\\' => array($vendorDir . '/codeception/stub/src', $vendorDir . '/codeception/codeception/src/Codeception'), ); $package) { if (isset($package['type']) && $package['type'] === $type) { $packagesByType[] = $name; } } } return $packagesByType; } public static function isInstalled($packageName, $includeDevRequirements = true) { foreach (self::getInstalled() as $installed) { if (isset($installed['versions'][$packageName])) { return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); } } return false; } public static function satisfies(VersionParser $parser, $packageName, $constraint) { $constraint = $parser->parseConstraints($constraint); $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); return $provided->matches($constraint); } public static function getVersionRanges($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } $ranges = array(); if (isset($installed['versions'][$packageName]['pretty_version'])) { $ranges[] = $installed['versions'][$packageName]['pretty_version']; } if (array_key_exists('aliases', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); } if (array_key_exists('replaced', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); } if (array_key_exists('provided', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); } return implode(' || ', $ranges); } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } public static function getVersion($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['version'])) { return null; } return $installed['versions'][$packageName]['version']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } public static function getPrettyVersion($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['pretty_version'])) { return null; } return $installed['versions'][$packageName]['pretty_version']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } public static function getReference($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['reference'])) { return null; } return $installed['versions'][$packageName]['reference']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } public static function getInstallPath($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } public static function getRootPackage() { $installed = self::getInstalled(); return $installed[0]['root']; } public static function getRawData() { @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); if (null === self::$installed) { if (substr(__DIR__, -8, 1) !== 'C') { self::$installed = include __DIR__ . '/installed.php'; } else { self::$installed = array(); } } return self::$installed; } public static function getAllRawData() { return self::getInstalled(); } public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); } private static function getInstalled() { if (null === self::$canGetVendors) { self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); } $installed = array(); if (self::$canGetVendors) { foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { self::$installed = $installed[count($installed) - 1]; } } } } if (null === self::$installed) { if (substr(__DIR__, -8, 1) !== 'C') { self::$installed = require __DIR__ . '/installed.php'; } else { self::$installed = array(); } } $installed[] = self::$installed; return $installed; } } array($vendorDir . '/behat/gherkin/src'), ); warning(sprintf( 'Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.', PHP_VERSION )); $warned = true; } $isValid = !empty($contents); } elseif (is_string($contents) && strlen($contents) > 0) { $contents = preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents); if (null === $contents) { $isValid = false; } else { $isValid = (bool) openssl_x509_parse($contents); } } else { $isValid = false; } if ($logger) { $logger->debug('Checked CA file '.realpath($filename).': '.($isValid ? 'valid' : 'invalid')); } return self::$caFileValidity[$filename] = $isValid; } public static function isOpensslParseSafe() { if (null !== self::$useOpensslParse) { return self::$useOpensslParse; } if (PHP_VERSION_ID >= 50600) { return self::$useOpensslParse = true; } if ( (PHP_VERSION_ID < 50400 && PHP_VERSION_ID >= 50328) || (PHP_VERSION_ID < 50500 && PHP_VERSION_ID >= 50423) || PHP_VERSION_ID >= 50507 ) { return self::$useOpensslParse = true; } if (defined('PHP_WINDOWS_VERSION_BUILD')) { return self::$useOpensslParse = false; } $compareDistroVersionPrefix = function ($prefix, $fixedVersion) { $regex = '{^'.preg_quote($prefix).'([0-9]+)$}'; if (preg_match($regex, PHP_VERSION, $m)) { return ((int) $m[1]) >= $fixedVersion; } return false; }; if ( $compareDistroVersionPrefix('5.3.3-7+squeeze', 18) || $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) || $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9) ) { return self::$useOpensslParse = true; } if (!class_exists('Symfony\Component\Process\PhpProcess')) { return self::$useOpensslParse = false; } $cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K'; $script = <<<'EOT' error_reporting(-1); $info = openssl_x509_parse(base64_decode('%s')); var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']); EOT; $script = '<'."?php\n".sprintf($script, $cert); try { $process = new PhpProcess($script); $process->mustRun(); } catch (\Exception $e) { return self::$useOpensslParse = false; } $output = preg_split('{\r?\n}', trim($process->getOutput())); $errorOutput = trim($process->getErrorOutput()); if ( is_array($output) && count($output) === 3 && $output[0] === sprintf('string(%d) "%s"', strlen(PHP_VERSION), PHP_VERSION) && $output[1] === 'string(27) "stefan.esser@sektioneins.de"' && $output[2] === 'int(-1)' && preg_match('{openssl_x509_parse\(\): illegal (?:ASN1 data type for|length in) timestamp in - on line \d+}', $errorOutput) ) { return self::$useOpensslParse = true; } return self::$useOpensslParse = false; } public static function reset() { self::$caFileValidity = array(); self::$caPath = null; self::$useOpensslParse = null; } private static function getEnvVariable($name) { if (isset($_SERVER[$name])) { return (string) $_SERVER[$name]; } if (PHP_SAPI === 'cli' && ($value = getenv($name)) !== false && $value !== null) { return (string) $value; } return false; } private static function caFileUsable($certFile, LoggerInterface $logger = null) { return $certFile && static::isFile($certFile, $logger) && static::isReadable($certFile, $logger) && static::validateCaFile($certFile, $logger); } private static function caDirUsable($certDir, LoggerInterface $logger = null) { return $certDir && static::isDir($certDir, $logger) && static::isReadable($certDir, $logger) && static::glob($certDir . '/*', $logger); } private static function isFile($certFile, LoggerInterface $logger = null) { $isFile = @is_file($certFile); if (!$isFile && $logger) { $logger->debug(sprintf('Checked CA file %s does not exist or it is not a file.', $certFile)); } return $isFile; } private static function isDir($certDir, LoggerInterface $logger = null) { $isDir = @is_dir($certDir); if (!$isDir && $logger) { $logger->debug(sprintf('Checked directory %s does not exist or it is not a directory.', $certDir)); } return $isDir; } private static function isReadable($certFileOrDir, LoggerInterface $logger = null) { $isReadable = @is_readable($certFileOrDir); if (!$isReadable && $logger) { $logger->debug(sprintf('Checked file or directory %s is not readable.', $certFileOrDir)); } return $isReadable; } private static function glob($pattern, LoggerInterface $logger = null) { $certs = glob($pattern); if ($certs === false) { if ($logger) { $logger->debug(sprintf("An error occurred while trying to find certificates for pattern: %s", $pattern)); } return false; } if (count($certs) === 0) { if ($logger) { $logger->debug(sprintf("No CA files found for pattern: %s", $pattern)); } return false; } return true; } } __get($key); } elseif (is_object($collection) && !$collection instanceof ArrayAccess) { $return = $collection->$key; } elseif (is_array($collection)) { if (is_int($key) && $key < 0) { $return = array_slice($collection, $key, 1, false)[0]; } else { $return = $collection[$key]; } } elseif (is_int($key)) { $return = self::getValueByIndex($collection, $key); } else { $return = $collection[$key]; } return $return; } private static function getValueByIndex($collection, $key) { $i = 0; foreach ($collection as $val) { if ($i === $key) { return $val; } ++$i; } if ($key < 0) { $total = $i; $i = 0; foreach ($collection as $val) { if ($i - $total === $key) { return $val; } ++$i; } } return null; } public static function setValue(&$collection, $key, $value) { if (is_object($collection) && !$collection instanceof ArrayAccess) { return $collection->$key = $value; } return $collection[$key] = $value; } public static function unsetValue(&$collection, $key) { if (is_object($collection) && !$collection instanceof ArrayAccess) { unset($collection->$key); } else { unset($collection[$key]); } } public static function arrayValues($collection) { if (is_array($collection)) { return array_values($collection); } if (is_object($collection)) { return array_values((array)$collection); } throw new JSONPathException("Invalid variable type for arrayValues"); } } token->value)) { $result = []; foreach ($this->token->value as $value) { if (AccessHelper::keyExists($collection, $value, $this->magicIsAllowed)) { $result[] = AccessHelper::getValue($collection, $value, $this->magicIsAllowed); } } return $result; } if (AccessHelper::keyExists($collection, $this->token->value, $this->magicIsAllowed)) { return [ AccessHelper::getValue($collection, $this->token->value, $this->magicIsAllowed), ]; } if ($this->token->value === '*') { return AccessHelper::arrayValues($collection); } return []; } } token->value['start']; $end = $this->token->value['end']; $step = $this->token->value['step'] ?: 1; if ($start === null) { $start = 0; } if ($start < 0) { $start = $length + $start; if ($start < 0) { $start = 0; } } if ($end === null) { $end = $length; } if ($end < 0) { $end = $length + $end; } $result = []; for ($i = $start; $i < $end; $i += $step) { $index = $i; if ($i < 0) { $index = $length + $i; } if (AccessHelper::keyExists($collection, $index, $this->magicIsAllowed)) { $result[] = $collection[$index]; } } return $result; } } [^\s<>!=]+)|\[["\']?(?.*?)["\']?\]) (\s*(?==|=~|=|<>|!==|!=|>=|<=|>|<|in|!in|nin)\s*(?.+))? '; public function filter($collection) { preg_match('/^' . static::MATCH_QUERY_OPERATORS . '$/x', $this->token->value, $matches); if (!isset($matches[1])) { throw new RuntimeException('Malformed filter query'); } $key = $matches['key'] ?: $matches['keySquare']; if ($key === '') { throw new RuntimeException('Malformed filter query: key was not set'); } $operator = isset($matches['operator']) ? $matches['operator'] : null; $comparisonValue = isset($matches['comparisonValue']) ? $matches['comparisonValue'] : null; if (is_string($comparisonValue)) { if (strpos($comparisonValue, "[") === 0 && substr($comparisonValue, -1) === "]") { $comparisonValue = substr($comparisonValue, 1, -1); $comparisonValue = preg_replace('/^[\'"]/', '', $comparisonValue); $comparisonValue = preg_replace('/[\'"]$/', '', $comparisonValue); $comparisonValue = preg_replace('/[\'"],[ ]*[\'"]/', ',', $comparisonValue); $comparisonValue = explode(",", $comparisonValue); } else { $comparisonValue = preg_replace('/^[\'"]/', '', $comparisonValue); $comparisonValue = preg_replace('/[\'"]$/', '', $comparisonValue); if (strtolower($comparisonValue) === 'false') { $comparisonValue = false; } elseif (strtolower($comparisonValue) === 'true') { $comparisonValue = true; } elseif (strtolower($comparisonValue) === 'null') { $comparisonValue = null; } } } $return = []; foreach ($collection as $value) { if (AccessHelper::keyExists($value, $key, $this->magicIsAllowed)) { $value1 = AccessHelper::getValue($value, $key, $this->magicIsAllowed); if ($operator === null && $value1) { $return[] = $value; } if (($operator === '=' || $operator === '==') && $value1 == $comparisonValue) { $return[] = $value; } if (($operator === '!=' || $operator === '!==' || $operator === '<>') && $value1 != $comparisonValue) { $return[] = $value; } if ($operator === '=~' && @preg_match($comparisonValue, $value1)) { $return[] = $value; } if ($operator === '>' && $value1 > $comparisonValue) { $return[] = $value; } if ($operator === '>=' && $value1 >= $comparisonValue) { $return[] = $value; } if ($operator === '<' && $value1 < $comparisonValue) { $return[] = $value; } if ($operator === '<=' && $value1 <= $comparisonValue) { $return[] = $value; } if ($operator === 'in' && is_array($comparisonValue) && in_array($value1, $comparisonValue, true)) { $return[] = $value; } if ( ($operator === 'nin' || $operator === '!in') && is_array($comparisonValue) && !in_array($value1, $comparisonValue, true) ) { $return[] = $value; } } } return $return; } } token = $token; $this->magicIsAllowed = (bool)($options & JSONPath::ALLOW_MAGIC); } abstract public function filter($collection); } \w+)\s*(?[-+*\/])\s*(?\d+)/', $this->token->value, $matches); $matchKey = $matches['key']; if (AccessHelper::keyExists($collection, $matchKey, $this->magicIsAllowed)) { $value = AccessHelper::getValue($collection, $matchKey, $this->magicIsAllowed); } elseif ($matches['key'] === 'length') { $value = count($collection); } else { return []; } switch ($matches['operator']) { case '+': $resultKey = $value + $matches['numeric']; break; case '*': $resultKey = $value * $matches['numeric']; break; case '-': $resultKey = $value - $matches['numeric']; break; case '/': $resultKey = $value / $matches['numeric']; break; default: throw new JSONPathException('Unsupported operator in expression'); } $result = []; if (AccessHelper::keyExists($collection, $resultKey, $this->magicIsAllowed)) { $result[] = AccessHelper::getValue($collection, $resultKey, $this->magicIsAllowed); } return $result; } } token->value as $index) { if (AccessHelper::keyExists($collection, $index, $this->magicIsAllowed)) { $return[] = AccessHelper::getValue($collection, $index, $this->magicIsAllowed); } } return $return; } } recurse($result, $collection); return $result; } private function recurse(array &$result, $data) { $result[] = $data; if (AccessHelper::isCollectionType($data)) { foreach (AccessHelper::arrayValues($data) as $key => $value) { $results[] = $value; if (AccessHelper::isCollectionType($value)) { $this->recurse($result, $value); } } } } } 1) { if ($expression[0] === '$') { $expression = substr($expression, 1); $len--; } if ($expression[0] !== '.' && $expression[0] !== '[') { $expression = '.' . $expression; $len++; } $this->expression = $expression; $this->expressionLength = $len; } } public function parseExpressionTokens() { $dotIndexDepth = 0; $squareBracketDepth = 0; $tokenValue = ''; $tokens = []; for ($i = 0; $i < $this->expressionLength; $i++) { $char = $this->expression[$i]; if (($squareBracketDepth === 0) && $char === '.') { if ($this->lookAhead($i) === '.') { $tokens[] = new JSONPathToken(JSONPathToken::T_RECURSIVE, null); } continue; } if ($char === '[') { ++$squareBracketDepth; if ($squareBracketDepth === 1) { continue; } } if ($char === ']') { --$squareBracketDepth; if ($squareBracketDepth === 0) { continue; } } if ($squareBracketDepth > 0) { $tokenValue .= $char; if ($squareBracketDepth === 1 && $this->lookAhead($i) === ']') { $tokens[] = $this->createToken($tokenValue); $tokenValue = ''; } } if ($squareBracketDepth === 0) { $tokenValue .= $char; if ($char === '.' && $dotIndexDepth > 1) { $tokens[] = $this->createToken($tokenValue); $tokenValue = ''; continue; } if ($this->atEnd($i) || in_array($this->lookAhead($i), ['.', '['])) { $tokens[] = $this->createToken($tokenValue); $tokenValue = ''; --$dotIndexDepth; } } } if ($tokenValue !== '') { $tokens[] = $this->createToken($tokenValue); } return $tokens; } protected function lookAhead($pos, $forward = 1) { return isset($this->expression[$pos + $forward]) ? $this->expression[$pos + $forward] : null; } protected function atEnd($pos) { return $pos === $this->expressionLength; } public function parseExpression() { return $this->parseExpressionTokens(); } protected function createToken($value) { $tokenValue = $value; $ret = null; if (preg_match('/^(' . static::MATCH_INDEX . ')$/xu', $tokenValue, $matches)) { if (preg_match('/^-?\d+$/', $tokenValue)) { $tokenValue = (int)$tokenValue; } $ret = new JSONPathToken(JSONPathToken::T_INDEX, $tokenValue); } elseif (preg_match('/^' . static::MATCH_INDEXES . '$/xu', $tokenValue, $matches)) { $tokenValue = explode(',', trim($tokenValue, ',')); foreach ($tokenValue as $i => $v) { $tokenValue[$i] = (int)trim($v); } $ret = new JSONPathToken(JSONPathToken::T_INDEXES, $tokenValue); } elseif (preg_match('/^' . static::MATCH_SLICE . '$/xu', $tokenValue, $matches)) { $parts = explode(':', $tokenValue); $tokenValue = [ 'start' => isset($parts[0]) && $parts[0] !== '' ? (int)$parts[0] : null, 'end' => isset($parts[1]) && $parts[1] !== '' ? (int)$parts[1] : null, 'step' => isset($parts[2]) && $parts[2] !== '' ? (int)$parts[2] : null, ]; $ret = new JSONPathToken(JSONPathToken::T_SLICE, $tokenValue); } elseif (preg_match('/^' . static::MATCH_QUERY_RESULT . '$/xu', $tokenValue)) { $tokenValue = substr($tokenValue, 1, -1); $ret = new JSONPathToken(JSONPathToken::T_QUERY_RESULT, $tokenValue); } elseif (preg_match('/^' . static::MATCH_QUERY_MATCH . '$/xu', $tokenValue)) { $tokenValue = substr($tokenValue, 2, -1); $ret = new JSONPathToken(JSONPathToken::T_QUERY_MATCH, $tokenValue); } elseif ( preg_match('/^' . static::MATCH_INDEX_IN_SINGLE_QUOTES . '$/xu', $tokenValue, $matches) || preg_match('/^' . static::MATCH_INDEX_IN_DOUBLE_QUOTES . '$/xu', $tokenValue, $matches) ) { if (isset($matches[1])) { $tokenValue = $matches[1]; $tokenValue = trim($tokenValue); $possibleArray = false; if ($matches[0][0] === '"') { $possibleArray = explode('","', $tokenValue); } elseif ($matches[0][0] === "'") { $possibleArray = explode("','", $tokenValue); } if ($possibleArray !== false && count($possibleArray) > 1) { $tokenValue = $possibleArray; } } else { $tokenValue = ''; } $ret = new JSONPathToken(JSONPathToken::T_INDEX, $tokenValue); } if ($ret !== null) { return $ret; } throw new JSONPathException("Unable to parse token {$tokenValue} in expression: $this->expression"); } } data = $data; $this->options = $options; } public function find($expression) { $tokens = $this->parseTokens($expression); $collectionData = [$this->data]; foreach ($tokens as $token) { $filter = $token->buildFilter($this->options); $filteredDataList = []; foreach ($collectionData as $value) { if (AccessHelper::isCollectionType($value)) { $filteredDataList[] = $filter->filter($value); } } if (!empty($filteredDataList)) { $collectionData = call_user_func_array('array_merge', $filteredDataList); } else { $collectionData = []; } } return new static($collectionData, $this->options); } public function first() { $keys = AccessHelper::collectionKeys($this->data); if (empty($keys)) { return null; } $value = isset($this->data[$keys[0]]) ? $this->data[$keys[0]] : null; return AccessHelper::isCollectionType($value) ? new static($value, $this->options) : $value; } public function last() { $keys = AccessHelper::collectionKeys($this->data); if (empty($keys)) { return null; } $value = $this->data[end($keys)] ?: null; return AccessHelper::isCollectionType($value) ? new static($value, $this->options) : $value; } public function firstKey() { $keys = AccessHelper::collectionKeys($this->data); if (empty($keys)) { return null; } return $keys[0]; } public function lastKey() { $keys = AccessHelper::collectionKeys($this->data); if (empty($keys) || end($keys) === false) { return null; } return end($keys); } public function parseTokens($expression) { $cacheKey = crc32($expression); if (isset(static::$tokenCache[$cacheKey])) { return static::$tokenCache[$cacheKey]; } $lexer = new JSONPathLexer($expression); $tokens = $lexer->parseExpression(); static::$tokenCache[$cacheKey] = $tokens; return $tokens; } public function getData() { return $this->data; } public function data() { trigger_error( 'Calling JSONPath::data() is deprecated, please use JSONPath::getData() instead.', E_USER_DEPRECATED ); return $this->getData(); } public function __get($key) { return $this->offsetExists($key) ? $this->offsetGet($key) : null; } public function offsetExists($offset) { return AccessHelper::keyExists($this->data, $offset); } public function offsetGet($offset) { $value = AccessHelper::getValue($this->data, $offset); return AccessHelper::isCollectionType($value) ? new static($value, $this->options) : $value; } public function offsetSet($offset, $value) { if ($offset === null) { $this->data[] = $value; } else { AccessHelper::setValue($this->data, $offset, $value); } } public function offsetUnset($offset) { AccessHelper::unsetValue($this->data, $offset); } public function jsonSerialize() { return $this->getData(); } public function current() { $value = current($this->data); return AccessHelper::isCollectionType($value) ? new static($value, $this->options) : $value; } public function next() { next($this->data); } public function key() { return key($this->data); } public function valid() { return key($this->data) !== null; } public function rewind() { reset($this->data); } public function count() { return count($this->data); } } validateType($type); $this->type = $type; $this->value = $value; } public function validateType($type) { if (!in_array($type, static::getTypes(), true)) { throw new JSONPathException('Invalid token: ' . $type); } } public static function getTypes() { return [ static::T_INDEX, static::T_RECURSIVE, static::T_QUERY_RESULT, static::T_QUERY_MATCH, static::T_SLICE, static::T_INDEXES, ]; } public function buildFilter($options) { $filterClass = 'Flow\\JSONPath\\Filters\\' . ucfirst($this->type) . 'Filter'; if (!class_exists($filterClass)) { throw new JSONPathException("No filter class exists for token [{$this->type}]"); } return new $filterClass($this, $options); } } assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); } 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}'), ); } 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() { $closed = fopen('php://memory', 'r'); fclose($closed); $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'), 'closed' => $closed, ); $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()); } } $level, 'message' => $message, 'context' => $context, ]; $this->recordsByLevel[$record['level']][] = $record; $this->records[] = $record; } public function hasRecords($level) { return isset($this->recordsByLevel[$level]); } public function hasRecord($record, $level) { if (is_string($record)) { $record = ['message' => $record]; } return $this->hasRecordThatPasses(function ($rec) use ($record) { if ($rec['message'] !== $record['message']) { return false; } if (isset($record['context']) && $rec['context'] !== $record['context']) { return false; } return true; }, $level); } public function hasRecordThatContains($message, $level) { return $this->hasRecordThatPasses(function ($rec) use ($message) { return strpos($rec['message'], $message) !== false; }, $level); } public function hasRecordThatMatches($regex, $level) { return $this->hasRecordThatPasses(function ($rec) use ($regex) { return preg_match($regex, $rec['message']) > 0; }, $level); } public function hasRecordThatPasses(callable $predicate, $level) { if (!isset($this->recordsByLevel[$level])) { return false; } foreach ($this->recordsByLevel[$level] as $i => $rec) { if (call_user_func($predicate, $rec, $i)) { return true; } } return false; } public function __call($method, $args) { if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; $level = strtolower($matches[2]); if (method_exists($this, $genericMethod)) { $args[] = $level; return call_user_func_array([$this, $genericMethod], $args); } } throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); } public function reset() { $this->records = []; $this->recordsByLevel = []; } } logger = $logger; } } log(LogLevel::EMERGENCY, $message, $context); } public function alert($message, array $context = array()) { $this->log(LogLevel::ALERT, $message, $context); } public function critical($message, array $context = array()) { $this->log(LogLevel::CRITICAL, $message, $context); } public function error($message, array $context = array()) { $this->log(LogLevel::ERROR, $message, $context); } public function warning($message, array $context = array()) { $this->log(LogLevel::WARNING, $message, $context); } public function notice($message, array $context = array()) { $this->log(LogLevel::NOTICE, $message, $context); } public function info($message, array $context = array()) { $this->log(LogLevel::INFO, $message, $context); } public function debug($message, array $context = array()) { $this->log(LogLevel::DEBUG, $message, $context); } } log(LogLevel::EMERGENCY, $message, $context); } public function alert($message, array $context = array()) { $this->log(LogLevel::ALERT, $message, $context); } public function critical($message, array $context = array()) { $this->log(LogLevel::CRITICAL, $message, $context); } public function error($message, array $context = array()) { $this->log(LogLevel::ERROR, $message, $context); } public function warning($message, array $context = array()) { $this->log(LogLevel::WARNING, $message, $context); } public function notice($message, array $context = array()) { $this->log(LogLevel::NOTICE, $message, $context); } public function info($message, array $context = array()) { $this->log(LogLevel::INFO, $message, $context); } public function debug($message, array $context = array()) { $this->log(LogLevel::DEBUG, $message, $context); } abstract public function log($level, $message, array $context = array()); } iterable = Create::iterFor($iterable); if (isset($config['concurrency'])) { $this->concurrency = $config['concurrency']; } if (isset($config['fulfilled'])) { $this->onFulfilled = $config['fulfilled']; } if (isset($config['rejected'])) { $this->onRejected = $config['rejected']; } } public function promise() { if ($this->aggregate) { return $this->aggregate; } try { $this->createPromise(); $this->iterable->rewind(); $this->refillPending(); } catch (\Throwable $e) { $this->aggregate->reject($e); } catch (\Exception $e) { $this->aggregate->reject($e); } return $this->aggregate; } private function createPromise() { $this->mutex = false; $this->aggregate = new Promise(function () { if ($this->checkIfFinished()) { return; } reset($this->pending); while ($promise = current($this->pending)) { next($this->pending); $promise->wait(); if (Is::settled($this->aggregate)) { return; } } }); $clearFn = function () { $this->iterable = $this->concurrency = $this->pending = null; $this->onFulfilled = $this->onRejected = null; $this->nextPendingIndex = 0; }; $this->aggregate->then($clearFn, $clearFn); } private function refillPending() { if (!$this->concurrency) { while ($this->addPending() && $this->advanceIterator()); return; } $concurrency = is_callable($this->concurrency) ? call_user_func($this->concurrency, count($this->pending)) : $this->concurrency; $concurrency = max($concurrency - count($this->pending), 0); if (!$concurrency) { return; } $this->addPending(); while (--$concurrency && $this->advanceIterator() && $this->addPending()); } private function addPending() { if (!$this->iterable || !$this->iterable->valid()) { return false; } $promise = Create::promiseFor($this->iterable->current()); $key = $this->iterable->key(); $idx = $this->nextPendingIndex++; $this->pending[$idx] = $promise->then( function ($value) use ($idx, $key) { if ($this->onFulfilled) { call_user_func( $this->onFulfilled, $value, $key, $this->aggregate ); } $this->step($idx); }, function ($reason) use ($idx, $key) { if ($this->onRejected) { call_user_func( $this->onRejected, $reason, $key, $this->aggregate ); } $this->step($idx); } ); return true; } private function advanceIterator() { if ($this->mutex) { return false; } $this->mutex = true; try { $this->iterable->next(); $this->mutex = false; return true; } catch (\Throwable $e) { $this->aggregate->reject($e); $this->mutex = false; return false; } catch (\Exception $e) { $this->aggregate->reject($e); $this->mutex = false; return false; } } private function step($idx) { if (Is::settled($this->aggregate)) { return; } unset($this->pending[$idx]); if ($this->advanceIterator() && !$this->checkIfFinished()) { $this->refillPending(); } } private function checkIfFinished() { if (!$this->pending && !$this->iterable->valid()) { $this->aggregate->resolve(null); return true; } return false; } } add(function () use ($task, $promise) { try { if (Is::pending($promise)) { $promise->resolve($task()); } } catch (\Throwable $e) { $promise->reject($e); } catch (\Exception $e) { $promise->reject($e); } }); return $promise; } public static function inspect(PromiseInterface $promise) { try { return [ 'state' => PromiseInterface::FULFILLED, 'value' => $promise->wait() ]; } catch (RejectionException $e) { return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()]; } catch (\Throwable $e) { return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; } catch (\Exception $e) { return ['state' => PromiseInterface::REJECTED, 'reason' => $e]; } } public static function inspectAll($promises) { $results = []; foreach ($promises as $key => $promise) { $results[$key] = inspect($promise); } return $results; } public static function unwrap($promises) { $results = []; foreach ($promises as $key => $promise) { $results[$key] = $promise->wait(); } return $results; } public static function all($promises, $recursive = false) { $results = []; $promise = Each::of( $promises, function ($value, $idx) use (&$results) { $results[$idx] = $value; }, function ($reason, $idx, Promise $aggregate) { $aggregate->reject($reason); } )->then(function () use (&$results) { ksort($results); return $results; }); if (true === $recursive) { $promise = $promise->then(function ($results) use ($recursive, &$promises) { foreach ($promises as $promise) { if (Is::pending($promise)) { return self::all($promises, $recursive); } } return $results; }); } return $promise; } public static function some($count, $promises) { $results = []; $rejections = []; return Each::of( $promises, function ($value, $idx, PromiseInterface $p) use (&$results, $count) { if (Is::settled($p)) { return; } $results[$idx] = $value; if (count($results) >= $count) { $p->resolve(null); } }, function ($reason) use (&$rejections) { $rejections[] = $reason; } )->then( function () use (&$results, &$rejections, $count) { if (count($results) !== $count) { throw new AggregateException( 'Not enough promises to fulfill count', $rejections ); } ksort($results); return array_values($results); } ); } public static function any($promises) { return self::some(1, $promises)->then(function ($values) { return $values[0]; }); } public static function settle($promises) { $results = []; return Each::of( $promises, function ($value, $idx) use (&$results) { $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value]; }, function ($reason, $idx) use (&$results) { $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason]; } )->then(function () use (&$results) { ksort($results); return $results; }); } } waitFn = $waitFn; $this->cancelFn = $cancelFn; } public function then( callable $onFulfilled = null, callable $onRejected = null ) { if ($this->state === self::PENDING) { $p = new Promise(null, [$this, 'cancel']); $this->handlers[] = [$p, $onFulfilled, $onRejected]; $p->waitList = $this->waitList; $p->waitList[] = $this; return $p; } if ($this->state === self::FULFILLED) { $promise = Create::promiseFor($this->result); return $onFulfilled ? $promise->then($onFulfilled) : $promise; } $rejection = Create::rejectionFor($this->result); return $onRejected ? $rejection->then(null, $onRejected) : $rejection; } public function otherwise(callable $onRejected) { return $this->then(null, $onRejected); } public function wait($unwrap = true) { $this->waitIfPending(); if ($this->result instanceof PromiseInterface) { return $this->result->wait($unwrap); } if ($unwrap) { if ($this->state === self::FULFILLED) { return $this->result; } throw Create::exceptionFor($this->result); } } public function getState() { return $this->state; } public function cancel() { if ($this->state !== self::PENDING) { return; } $this->waitFn = $this->waitList = null; if ($this->cancelFn) { $fn = $this->cancelFn; $this->cancelFn = null; try { $fn(); } catch (\Throwable $e) { $this->reject($e); } catch (\Exception $e) { $this->reject($e); } } if ($this->state === self::PENDING) { $this->reject(new CancellationException('Promise has been cancelled')); } } public function resolve($value) { $this->settle(self::FULFILLED, $value); } public function reject($reason) { $this->settle(self::REJECTED, $reason); } private function settle($state, $value) { if ($this->state !== self::PENDING) { if ($state === $this->state && $value === $this->result) { return; } throw $this->state === $state ? new \LogicException("The promise is already {$state}.") : new \LogicException("Cannot change a {$this->state} promise to {$state}"); } if ($value === $this) { throw new \LogicException('Cannot fulfill or reject a promise with itself'); } $this->state = $state; $this->result = $value; $handlers = $this->handlers; $this->handlers = null; $this->waitList = $this->waitFn = null; $this->cancelFn = null; if (!$handlers) { return; } if (!is_object($value) || !method_exists($value, 'then')) { $id = $state === self::FULFILLED ? 1 : 2; Utils::queue()->add(static function () use ($id, $value, $handlers) { foreach ($handlers as $handler) { self::callHandler($id, $value, $handler); } }); } elseif ($value instanceof Promise && Is::pending($value)) { $value->handlers = array_merge($value->handlers, $handlers); } else { $value->then( static function ($value) use ($handlers) { foreach ($handlers as $handler) { self::callHandler(1, $value, $handler); } }, static function ($reason) use ($handlers) { foreach ($handlers as $handler) { self::callHandler(2, $reason, $handler); } } ); } } private static function callHandler($index, $value, array $handler) { $promise = $handler[0]; if (Is::settled($promise)) { return; } try { if (isset($handler[$index])) { $f = $handler[$index]; unset($handler); $promise->resolve($f($value)); } elseif ($index === 1) { $promise->resolve($value); } else { $promise->reject($value); } } catch (\Throwable $reason) { $promise->reject($reason); } catch (\Exception $reason) { $promise->reject($reason); } } private function waitIfPending() { if ($this->state !== self::PENDING) { return; } elseif ($this->waitFn) { $this->invokeWaitFn(); } elseif ($this->waitList) { $this->invokeWaitList(); } else { $this->reject('Cannot wait on a promise that has ' . 'no internal wait function. You must provide a wait ' . 'function when constructing the promise to be able to ' . 'wait on a promise.'); } Utils::queue()->run(); if ($this->state === self::PENDING) { $this->reject('Invoking the wait callback did not resolve the promise'); } } private function invokeWaitFn() { try { $wfn = $this->waitFn; $this->waitFn = null; $wfn(true); } catch (\Exception $reason) { if ($this->state === self::PENDING) { $this->reject($reason); } else { throw $reason; } } } private function invokeWaitList() { $waitList = $this->waitList; $this->waitList = null; foreach ($waitList as $result) { do { $result->waitIfPending(); $result = $result->result; } while ($result instanceof Promise); if ($result instanceof PromiseInterface) { $result->wait(false); } } } } generator = $generatorFn(); $this->result = new Promise(function () { while (isset($this->currentPromise)) { $this->currentPromise->wait(); } }); try { $this->nextCoroutine($this->generator->current()); } catch (\Exception $exception) { $this->result->reject($exception); } catch (Throwable $throwable) { $this->result->reject($throwable); } } public static function of(callable $generatorFn) { return new self($generatorFn); } public function then( callable $onFulfilled = null, callable $onRejected = null ) { return $this->result->then($onFulfilled, $onRejected); } public function otherwise(callable $onRejected) { return $this->result->otherwise($onRejected); } public function wait($unwrap = true) { return $this->result->wait($unwrap); } public function getState() { return $this->result->getState(); } public function resolve($value) { $this->result->resolve($value); } public function reject($reason) { $this->result->reject($reason); } public function cancel() { $this->currentPromise->cancel(); $this->result->cancel(); } private function nextCoroutine($yielded) { $this->currentPromise = Create::promiseFor($yielded) ->then([$this, '_handleSuccess'], [$this, '_handleFailure']); } public function _handleSuccess($value) { unset($this->currentPromise); try { $next = $this->generator->send($value); if ($this->generator->valid()) { $this->nextCoroutine($next); } else { $this->result->resolve($value); } } catch (Exception $exception) { $this->result->reject($exception); } catch (Throwable $throwable) { $this->result->reject($throwable); } } public function _handleFailure($reason) { unset($this->currentPromise); try { $nextYield = $this->generator->throw(Create::exceptionFor($reason)); $this->nextCoroutine($nextYield); } catch (Exception $exception) { $this->result->reject($exception); } catch (Throwable $throwable) { $this->result->reject($throwable); } } } reason = $reason; $message = 'The promise was rejected'; if ($description) { $message .= ' with reason: ' . $description; } elseif (is_string($reason) || (is_object($reason) && method_exists($reason, '__toString')) ) { $message .= ' with reason: ' . $this->reason; } elseif ($reason instanceof \JsonSerializable) { $message .= ' with reason: ' . json_encode($this->reason, JSON_PRETTY_PRINT); } parent::__construct($message); } public function getReason() { return $this->reason; } } getState() === PromiseInterface::PENDING; } public static function settled(PromiseInterface $promise) { return $promise->getState() !== PromiseInterface::PENDING; } public static function fulfilled(PromiseInterface $promise) { return $promise->getState() === PromiseInterface::FULFILLED; } public static function rejected(PromiseInterface $promise) { return $promise->getState() === PromiseInterface::REJECTED; } } reason = $reason; } public function then( callable $onFulfilled = null, callable $onRejected = null ) { if (!$onRejected) { return $this; } $queue = Utils::queue(); $reason = $this->reason; $p = new Promise([$queue, 'run']); $queue->add(static function () use ($p, $reason, $onRejected) { if (Is::pending($p)) { try { $p->resolve($onRejected($reason)); } catch (\Throwable $e) { $p->reject($e); } catch (\Exception $e) { $p->reject($e); } } }); return $p; } public function otherwise(callable $onRejected) { return $this->then(null, $onRejected); } public function wait($unwrap = true, $defaultDelivery = null) { if ($unwrap) { throw Create::exceptionFor($this->reason); } return null; } public function getState() { return self::REJECTED; } public function resolve($value) { throw new \LogicException("Cannot resolve a rejected promise"); } public function reject($reason) { if ($reason !== $this->reason) { throw new \LogicException("Cannot reject a rejected promise"); } } public function cancel() { } } value = $value; } public function then( callable $onFulfilled = null, callable $onRejected = null ) { if (!$onFulfilled) { return $this; } $queue = Utils::queue(); $p = new Promise([$queue, 'run']); $value = $this->value; $queue->add(static function () use ($p, $value, $onFulfilled) { if (Is::pending($p)) { try { $p->resolve($onFulfilled($value)); } catch (\Throwable $e) { $p->reject($e); } catch (\Exception $e) { $p->reject($e); } } }); return $p; } public function otherwise(callable $onRejected) { return $this->then(null, $onRejected); } public function wait($unwrap = true, $defaultDelivery = null) { return $unwrap ? $this->value : null; } public function getState() { return self::FULFILLED; } public function resolve($value) { if ($value !== $this->value) { throw new \LogicException("Cannot resolve a fulfilled promise"); } } public function reject($reason) { throw new \LogicException("Cannot reject a fulfilled promise"); } public function cancel() { } } $onFulfilled, 'rejected' => $onRejected ]))->promise(); } public static function ofLimit( $iterable, $concurrency, callable $onFulfilled = null, callable $onRejected = null ) { return (new EachPromise($iterable, [ 'fulfilled' => $onFulfilled, 'rejected' => $onRejected, 'concurrency' => $concurrency ]))->promise(); } public static function ofLimitAll( $iterable, $concurrency, callable $onFulfilled = null ) { return each_limit( $iterable, $concurrency, $onFulfilled, function ($reason, $idx, PromiseInterface $aggregate) { $aggregate->reject($reason); } ); } } then([$promise, 'resolve'], [$promise, 'reject']); return $promise; } return new FulfilledPromise($value); } public static function rejectionFor($reason) { if ($reason instanceof PromiseInterface) { return $reason; } return new RejectedPromise($reason); } public static function exceptionFor($reason) { if ($reason instanceof \Exception || $reason instanceof \Throwable) { return $reason; } return new RejectionException($reason); } public static function iterFor($value) { if ($value instanceof \Iterator) { return $value; } if (is_array($value)) { return new \ArrayIterator($value); } return new \ArrayIterator([$value]); } } enableShutdown) { $err = error_get_last(); if (!$err || ($err['type'] ^ E_ERROR)) { $this->run(); } } }); } } public function isEmpty() { return !$this->queue; } public function add(callable $task) { $this->queue[] = $task; } public function run() { while ($task = array_shift($this->queue)) { $task(); } } public function disableShutdown() { $this->enableShutdown = false; } } protocol; } public function withProtocolVersion($version) { if ($this->protocol === $version) { return $this; } $new = clone $this; $new->protocol = $version; return $new; } public function getHeaders() { return $this->headers; } public function hasHeader($header) { return isset($this->headerNames[strtolower($header)]); } public function getHeader($header) { $header = strtolower($header); if (!isset($this->headerNames[$header])) { return []; } $header = $this->headerNames[$header]; return $this->headers[$header]; } public function getHeaderLine($header) { return implode(', ', $this->getHeader($header)); } public function withHeader($header, $value) { $this->assertHeader($header); $value = $this->normalizeHeaderValue($value); $normalized = strtolower($header); $new = clone $this; if (isset($new->headerNames[$normalized])) { unset($new->headers[$new->headerNames[$normalized]]); } $new->headerNames[$normalized] = $header; $new->headers[$header] = $value; return $new; } public function withAddedHeader($header, $value) { $this->assertHeader($header); $value = $this->normalizeHeaderValue($value); $normalized = strtolower($header); $new = clone $this; if (isset($new->headerNames[$normalized])) { $header = $this->headerNames[$normalized]; $new->headers[$header] = array_merge($this->headers[$header], $value); } else { $new->headerNames[$normalized] = $header; $new->headers[$header] = $value; } return $new; } public function withoutHeader($header) { $normalized = strtolower($header); if (!isset($this->headerNames[$normalized])) { return $this; } $header = $this->headerNames[$normalized]; $new = clone $this; unset($new->headers[$header], $new->headerNames[$normalized]); return $new; } public function getBody() { if (!$this->stream) { $this->stream = Utils::streamFor(''); } return $this->stream; } public function withBody(StreamInterface $body) { if ($body === $this->stream) { return $this; } $new = clone $this; $new->stream = $body; return $new; } private function setHeaders(array $headers) { $this->headerNames = $this->headers = []; foreach ($headers as $header => $value) { if (is_int($header)) { $header = (string) $header; } $this->assertHeader($header); $value = $this->normalizeHeaderValue($value); $normalized = strtolower($header); if (isset($this->headerNames[$normalized])) { $header = $this->headerNames[$normalized]; $this->headers[$header] = array_merge($this->headers[$header], $value); } else { $this->headerNames[$normalized] = $header; $this->headers[$header] = $value; } } } private function normalizeHeaderValue($value) { if (!is_array($value)) { return $this->trimAndValidateHeaderValues([$value]); } if (count($value) === 0) { throw new \InvalidArgumentException('Header value can not be an empty array.'); } return $this->trimAndValidateHeaderValues($value); } private function trimAndValidateHeaderValues(array $values) { return array_map(function ($value) { if (!is_scalar($value) && null !== $value) { throw new \InvalidArgumentException(sprintf( 'Header value must be scalar or null but %s provided.', is_object($value) ? get_class($value) : gettype($value) )); } $trimmed = trim((string) $value, " \t"); $this->assertValue($trimmed); return $trimmed; }, array_values($values)); } private function assertHeader($header) { if (!is_string($header)) { throw new \InvalidArgumentException(sprintf( 'Header name must be a string but %s provided.', is_object($header) ? get_class($header) : gettype($header) )); } if ($header === '') { throw new \InvalidArgumentException('Header name can not be empty.'); } if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/', $header)) { throw new \InvalidArgumentException( sprintf( '"%s" is not valid header name', $header ) ); } } private function assertValue($value) { if (! preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/', $value)) { throw new \InvalidArgumentException(sprintf('"%s" is not valid header value', $value)); } } } stream = $stream; $this->setLimit($limit); $this->setOffset($offset); } public function eof() { if ($this->stream->eof()) { return true; } if ($this->limit == -1) { return false; } return $this->stream->tell() >= $this->offset + $this->limit; } public function getSize() { if (null === ($length = $this->stream->getSize())) { return null; } elseif ($this->limit == -1) { return $length - $this->offset; } else { return min($this->limit, $length - $this->offset); } } public function seek($offset, $whence = SEEK_SET) { if ($whence !== SEEK_SET || $offset < 0) { throw new \RuntimeException(sprintf( 'Cannot seek to offset %s with whence %s', $offset, $whence )); } $offset += $this->offset; if ($this->limit !== -1) { if ($offset > $this->offset + $this->limit) { $offset = $this->offset + $this->limit; } } $this->stream->seek($offset); } public function tell() { return $this->stream->tell() - $this->offset; } public function setOffset($offset) { $current = $this->stream->tell(); if ($current !== $offset) { if ($this->stream->isSeekable()) { $this->stream->seek($offset); } elseif ($current > $offset) { throw new \RuntimeException("Could not seek to stream offset $offset"); } else { $this->stream->read($offset - $current); } } $this->offset = $offset; } public function setLimit($limit) { $this->limit = $limit; } public function read($length) { if ($this->limit == -1) { return $this->stream->read($length); } $remaining = ($this->offset + $this->limit) - $this->stream->tell(); if ($remaining > 0) { return $this->stream->read(min($remaining, $length)); } return ''; } } $v) { if (!in_array(strtolower($k), $keys)) { $result[$k] = $v; } } return $result; } public static function copyToStream(StreamInterface $source, StreamInterface $dest, $maxLen = -1) { $bufferSize = 8192; if ($maxLen === -1) { while (!$source->eof()) { if (!$dest->write($source->read($bufferSize))) { break; } } } else { $remaining = $maxLen; while ($remaining > 0 && !$source->eof()) { $buf = $source->read(min($bufferSize, $remaining)); $len = strlen($buf); if (!$len) { break; } $remaining -= $len; $dest->write($buf); } } } public static function copyToString(StreamInterface $stream, $maxLen = -1) { $buffer = ''; if ($maxLen === -1) { while (!$stream->eof()) { $buf = $stream->read(1048576); if ($buf == null) { break; } $buffer .= $buf; } return $buffer; } $len = 0; while (!$stream->eof() && $len < $maxLen) { $buf = $stream->read($maxLen - $len); if ($buf == null) { break; } $buffer .= $buf; $len = strlen($buffer); } return $buffer; } public static function hash(StreamInterface $stream, $algo, $rawOutput = false) { $pos = $stream->tell(); if ($pos > 0) { $stream->rewind(); } $ctx = hash_init($algo); while (!$stream->eof()) { hash_update($ctx, $stream->read(1048576)); } $out = hash_final($ctx, (bool) $rawOutput); $stream->seek($pos); return $out; } public static function modifyRequest(RequestInterface $request, array $changes) { if (!$changes) { return $request; } $headers = $request->getHeaders(); if (!isset($changes['uri'])) { $uri = $request->getUri(); } else { if ($host = $changes['uri']->getHost()) { $changes['set_headers']['Host'] = $host; if ($port = $changes['uri']->getPort()) { $standardPorts = ['http' => 80, 'https' => 443]; $scheme = $changes['uri']->getScheme(); if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { $changes['set_headers']['Host'] .= ':' . $port; } } } $uri = $changes['uri']; } if (!empty($changes['remove_headers'])) { $headers = self::caselessRemove($changes['remove_headers'], $headers); } if (!empty($changes['set_headers'])) { $headers = self::caselessRemove(array_keys($changes['set_headers']), $headers); $headers = $changes['set_headers'] + $headers; } if (isset($changes['query'])) { $uri = $uri->withQuery($changes['query']); } if ($request instanceof ServerRequestInterface) { $new = (new ServerRequest( isset($changes['method']) ? $changes['method'] : $request->getMethod(), $uri, $headers, isset($changes['body']) ? $changes['body'] : $request->getBody(), isset($changes['version']) ? $changes['version'] : $request->getProtocolVersion(), $request->getServerParams() )) ->withParsedBody($request->getParsedBody()) ->withQueryParams($request->getQueryParams()) ->withCookieParams($request->getCookieParams()) ->withUploadedFiles($request->getUploadedFiles()); foreach ($request->getAttributes() as $key => $value) { $new = $new->withAttribute($key, $value); } return $new; } return new Request( isset($changes['method']) ? $changes['method'] : $request->getMethod(), $uri, $headers, isset($changes['body']) ? $changes['body'] : $request->getBody(), isset($changes['version']) ? $changes['version'] : $request->getProtocolVersion() ); } public static function readLine(StreamInterface $stream, $maxLength = null) { $buffer = ''; $size = 0; while (!$stream->eof()) { if (null == ($byte = $stream->read(1))) { return $buffer; } $buffer .= $byte; if ($byte === "\n" || ++$size === $maxLength - 1) { break; } } return $buffer; } public static function streamFor($resource = '', array $options = []) { if (is_scalar($resource)) { $stream = self::tryFopen('php://temp', 'r+'); if ($resource !== '') { fwrite($stream, $resource); fseek($stream, 0); } return new Stream($stream, $options); } switch (gettype($resource)) { case 'resource': $metaData = \stream_get_meta_data($resource); if (isset($metaData['uri']) && $metaData['uri'] === 'php://input') { $stream = self::tryFopen('php://temp', 'w+'); fwrite($stream, stream_get_contents($resource)); fseek($stream, 0); $resource = $stream; } return new Stream($resource, $options); case 'object': if ($resource instanceof StreamInterface) { return $resource; } elseif ($resource instanceof \Iterator) { return new PumpStream(function () use ($resource) { if (!$resource->valid()) { return false; } $result = $resource->current(); $resource->next(); return $result; }, $options); } elseif (method_exists($resource, '__toString')) { return Utils::streamFor((string) $resource, $options); } break; case 'NULL': return new Stream(self::tryFopen('php://temp', 'r+'), $options); } if (is_callable($resource)) { return new PumpStream($resource, $options); } throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource)); } public static function tryFopen($filename, $mode) { $ex = null; set_error_handler(function () use ($filename, $mode, &$ex) { $ex = new \RuntimeException(sprintf( 'Unable to open "%s" using mode "%s": %s', $filename, $mode, func_get_args()[1] )); return true; }); try { $handle = fopen($filename, $mode); } catch (\Throwable $e) { $ex = new \RuntimeException(sprintf( 'Unable to open "%s" using mode "%s": %s', $filename, $mode, $e->getMessage() ), 0, $e); } restore_error_handler(); if ($ex) { throw $ex; } return $handle; } public static function uriFor($uri) { if ($uri instanceof UriInterface) { return $uri; } if (is_string($uri)) { return new Uri($uri); } throw new \InvalidArgumentException('URI must be a string or UriInterface'); } } addStream($stream); } } public function __toString() { try { $this->rewind(); return $this->getContents(); } catch (\Exception $e) { return ''; } } public function addStream(StreamInterface $stream) { if (!$stream->isReadable()) { throw new \InvalidArgumentException('Each stream must be readable'); } if (!$stream->isSeekable()) { $this->seekable = false; } $this->streams[] = $stream; } public function getContents() { return Utils::copyToString($this); } public function close() { $this->pos = $this->current = 0; $this->seekable = true; foreach ($this->streams as $stream) { $stream->close(); } $this->streams = []; } public function detach() { $this->pos = $this->current = 0; $this->seekable = true; foreach ($this->streams as $stream) { $stream->detach(); } $this->streams = []; return null; } public function tell() { return $this->pos; } public function getSize() { $size = 0; foreach ($this->streams as $stream) { $s = $stream->getSize(); if ($s === null) { return null; } $size += $s; } return $size; } public function eof() { return !$this->streams || ($this->current >= count($this->streams) - 1 && $this->streams[$this->current]->eof()); } public function rewind() { $this->seek(0); } public function seek($offset, $whence = SEEK_SET) { if (!$this->seekable) { throw new \RuntimeException('This AppendStream is not seekable'); } elseif ($whence !== SEEK_SET) { throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); } $this->pos = $this->current = 0; foreach ($this->streams as $i => $stream) { try { $stream->rewind(); } catch (\Exception $e) { throw new \RuntimeException('Unable to seek stream ' . $i . ' of the AppendStream', 0, $e); } } while ($this->pos < $offset && !$this->eof()) { $result = $this->read(min(8096, $offset - $this->pos)); if ($result === '') { break; } } } public function read($length) { $buffer = ''; $total = count($this->streams) - 1; $remaining = $length; $progressToNext = false; while ($remaining > 0) { if ($progressToNext || $this->streams[$this->current]->eof()) { $progressToNext = false; if ($this->current === $total) { break; } $this->current++; } $result = $this->streams[$this->current]->read($remaining); if ($result == null) { $progressToNext = true; continue; } $buffer .= $result; $remaining = $length - strlen($buffer); } $this->pos += strlen($buffer); return $buffer; } public function isReadable() { return true; } public function isWritable() { return false; } public function isSeekable() { return $this->seekable; } public function write($string) { throw new \RuntimeException('Cannot write to an AppendStream'); } public function getMetadata($key = null) { return $key ? null : []; } } getScheme() != '') { return $rel->withPath(self::removeDotSegments($rel->getPath())); } if ($rel->getAuthority() != '') { $targetAuthority = $rel->getAuthority(); $targetPath = self::removeDotSegments($rel->getPath()); $targetQuery = $rel->getQuery(); } else { $targetAuthority = $base->getAuthority(); if ($rel->getPath() === '') { $targetPath = $base->getPath(); $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); } else { if ($rel->getPath()[0] === '/') { $targetPath = $rel->getPath(); } else { if ($targetAuthority != '' && $base->getPath() === '') { $targetPath = '/' . $rel->getPath(); } else { $lastSlashPos = strrpos($base->getPath(), '/'); if ($lastSlashPos === false) { $targetPath = $rel->getPath(); } else { $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath(); } } } $targetPath = self::removeDotSegments($targetPath); $targetQuery = $rel->getQuery(); } } return new Uri(Uri::composeComponents( $base->getScheme(), $targetAuthority, $targetPath, $targetQuery, $rel->getFragment() )); } public static function relativize(UriInterface $base, UriInterface $target) { if ($target->getScheme() !== '' && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '') ) { return $target; } if (Uri::isRelativePathReference($target)) { return $target; } if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) { return $target->withScheme(''); } $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost(''); if ($base->getPath() !== $target->getPath()) { return $emptyPathUri->withPath(self::getRelativePath($base, $target)); } if ($base->getQuery() === $target->getQuery()) { return $emptyPathUri->withQuery(''); } if ($target->getQuery() === '') { $segments = explode('/', $target->getPath()); $lastSegment = end($segments); return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); } return $emptyPathUri; } private static function getRelativePath(UriInterface $base, UriInterface $target) { $sourceSegments = explode('/', $base->getPath()); $targetSegments = explode('/', $target->getPath()); array_pop($sourceSegments); $targetLastSegment = array_pop($targetSegments); foreach ($sourceSegments as $i => $segment) { if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) { unset($sourceSegments[$i], $targetSegments[$i]); } else { break; } } $targetSegments[] = $targetLastSegment; $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments); if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) { $relativePath = "./$relativePath"; } elseif ('/' === $relativePath[0]) { if ($base->getAuthority() != '' && $base->getPath() === '') { $relativePath = ".$relativePath"; } else { $relativePath = "./$relativePath"; } } return $relativePath; } private function __construct() { } } 80, 'https' => 443, 'ftp' => 21, 'gopher' => 70, 'nntp' => 119, 'news' => 119, 'telnet' => 23, 'tn3270' => 23, 'imap' => 143, 'pop' => 110, 'ldap' => 389, ]; private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; private static $charSubDelims = '!\$&\'\(\)\*\+,;='; private static $replaceQuery = ['=' => '%3D', '&' => '%26']; private $scheme = ''; private $userInfo = ''; private $host = ''; private $port; private $path = ''; private $query = ''; private $fragment = ''; public function __construct($uri = '') { if ($uri != '') { $parts = self::parse($uri); if ($parts === false) { throw new \InvalidArgumentException("Unable to parse URI: $uri"); } $this->applyParts($parts); } } private static function parse($url) { $prefix = ''; if (preg_match('%^(.*://\[[0-9:a-f]+\])(.*?)$%', $url, $matches)) { $prefix = $matches[1]; $url = $matches[2]; } $encodedUrl = preg_replace_callback( '%[^:/@?&=#]+%usD', static function ($matches) { return urlencode($matches[0]); }, $url ); $result = parse_url($prefix . $encodedUrl); if ($result === false) { return false; } return array_map('urldecode', $result); } public function __toString() { return self::composeComponents( $this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment ); } public static function composeComponents($scheme, $authority, $path, $query, $fragment) { $uri = ''; if ($scheme != '') { $uri .= $scheme . ':'; } if ($authority != ''|| $scheme === 'file') { $uri .= '//' . $authority; } $uri .= $path; if ($query != '') { $uri .= '?' . $query; } if ($fragment != '') { $uri .= '#' . $fragment; } return $uri; } public static function isDefaultPort(UriInterface $uri) { return $uri->getPort() === null || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]); } public static function isAbsolute(UriInterface $uri) { return $uri->getScheme() !== ''; } public static function isNetworkPathReference(UriInterface $uri) { return $uri->getScheme() === '' && $uri->getAuthority() !== ''; } public static function isAbsolutePathReference(UriInterface $uri) { return $uri->getScheme() === '' && $uri->getAuthority() === '' && isset($uri->getPath()[0]) && $uri->getPath()[0] === '/'; } public static function isRelativePathReference(UriInterface $uri) { return $uri->getScheme() === '' && $uri->getAuthority() === '' && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/'); } public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null) { if ($base !== null) { $uri = UriResolver::resolve($base, $uri); return ($uri->getScheme() === $base->getScheme()) && ($uri->getAuthority() === $base->getAuthority()) && ($uri->getPath() === $base->getPath()) && ($uri->getQuery() === $base->getQuery()); } return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === ''; } public static function removeDotSegments($path) { return UriResolver::removeDotSegments($path); } public static function resolve(UriInterface $base, $rel) { if (!($rel instanceof UriInterface)) { $rel = new self($rel); } return UriResolver::resolve($base, $rel); } public static function withoutQueryValue(UriInterface $uri, $key) { $result = self::getFilteredQueryString($uri, [$key]); return $uri->withQuery(implode('&', $result)); } public static function withQueryValue(UriInterface $uri, $key, $value) { $result = self::getFilteredQueryString($uri, [$key]); $result[] = self::generateQueryString($key, $value); return $uri->withQuery(implode('&', $result)); } public static function withQueryValues(UriInterface $uri, array $keyValueArray) { $result = self::getFilteredQueryString($uri, array_keys($keyValueArray)); foreach ($keyValueArray as $key => $value) { $result[] = self::generateQueryString($key, $value); } return $uri->withQuery(implode('&', $result)); } public static function fromParts(array $parts) { $uri = new self(); $uri->applyParts($parts); $uri->validateState(); return $uri; } public function getScheme() { return $this->scheme; } public function getAuthority() { $authority = $this->host; if ($this->userInfo !== '') { $authority = $this->userInfo . '@' . $authority; } if ($this->port !== null) { $authority .= ':' . $this->port; } return $authority; } public function getUserInfo() { return $this->userInfo; } public function getHost() { return $this->host; } public function getPort() { return $this->port; } public function getPath() { return $this->path; } public function getQuery() { return $this->query; } public function getFragment() { return $this->fragment; } public function withScheme($scheme) { $scheme = $this->filterScheme($scheme); if ($this->scheme === $scheme) { return $this; } $new = clone $this; $new->scheme = $scheme; $new->removeDefaultPort(); $new->validateState(); return $new; } public function withUserInfo($user, $password = null) { $info = $this->filterUserInfoComponent($user); if ($password !== null) { $info .= ':' . $this->filterUserInfoComponent($password); } if ($this->userInfo === $info) { return $this; } $new = clone $this; $new->userInfo = $info; $new->validateState(); return $new; } public function withHost($host) { $host = $this->filterHost($host); if ($this->host === $host) { return $this; } $new = clone $this; $new->host = $host; $new->validateState(); return $new; } public function withPort($port) { $port = $this->filterPort($port); if ($this->port === $port) { return $this; } $new = clone $this; $new->port = $port; $new->removeDefaultPort(); $new->validateState(); return $new; } public function withPath($path) { $path = $this->filterPath($path); if ($this->path === $path) { return $this; } $new = clone $this; $new->path = $path; $new->validateState(); return $new; } public function withQuery($query) { $query = $this->filterQueryAndFragment($query); if ($this->query === $query) { return $this; } $new = clone $this; $new->query = $query; return $new; } public function withFragment($fragment) { $fragment = $this->filterQueryAndFragment($fragment); if ($this->fragment === $fragment) { return $this; } $new = clone $this; $new->fragment = $fragment; return $new; } private function applyParts(array $parts) { $this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : ''; $this->userInfo = isset($parts['user']) ? $this->filterUserInfoComponent($parts['user']) : ''; $this->host = isset($parts['host']) ? $this->filterHost($parts['host']) : ''; $this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null; $this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : ''; $this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : ''; $this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : ''; if (isset($parts['pass'])) { $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']); } $this->removeDefaultPort(); } private function filterScheme($scheme) { if (!is_string($scheme)) { throw new \InvalidArgumentException('Scheme must be a string'); } return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } private function filterUserInfoComponent($component) { if (!is_string($component)) { throw new \InvalidArgumentException('User info must be a string'); } return preg_replace_callback( '/(?:[^%' . self::$charUnreserved . self::$charSubDelims . ']+|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $component ); } private function filterHost($host) { if (!is_string($host)) { throw new \InvalidArgumentException('Host must be a string'); } return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } private function filterPort($port) { if ($port === null) { return null; } $port = (int) $port; if (0 > $port || 0xffff < $port) { throw new \InvalidArgumentException( sprintf('Invalid port: %d. Must be between 0 and 65535', $port) ); } return $port; } private static function getFilteredQueryString(UriInterface $uri, array $keys) { $current = $uri->getQuery(); if ($current === '') { return []; } $decodedKeys = array_map('rawurldecode', $keys); return array_filter(explode('&', $current), function ($part) use ($decodedKeys) { return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true); }); } private static function generateQueryString($key, $value) { $queryString = strtr($key, self::$replaceQuery); if ($value !== null) { $queryString .= '=' . strtr($value, self::$replaceQuery); } return $queryString; } private function removeDefaultPort() { if ($this->port !== null && self::isDefaultPort($this)) { $this->port = null; } } private function filterPath($path) { if (!is_string($path)) { throw new \InvalidArgumentException('Path must be a string'); } return preg_replace_callback( '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $path ); } private function filterQueryAndFragment($str) { if (!is_string($str)) { throw new \InvalidArgumentException('Query and fragment must be a string'); } return preg_replace_callback( '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $str ); } private function rawurlencodeMatchZero(array $match) { return rawurlencode($match[0]); } private function validateState() { if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { $this->host = self::HTTP_DEFAULT_HOST; } if ($this->getAuthority() === '') { if (0 === strpos($this->path, '//')) { throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"'); } if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) { throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon'); } } elseif (isset($this->path[0]) && $this->path[0] !== '/') { @trigger_error( 'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' . 'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.', E_USER_DEPRECATED ); $this->path = '/' . $this->path; } } } assertMethod($method); if (!($uri instanceof UriInterface)) { $uri = new Uri($uri); } $this->method = strtoupper($method); $this->uri = $uri; $this->setHeaders($headers); $this->protocol = $version; if (!isset($this->headerNames['host'])) { $this->updateHostFromUri(); } if ($body !== '' && $body !== null) { $this->stream = Utils::streamFor($body); } } public function getRequestTarget() { if ($this->requestTarget !== null) { return $this->requestTarget; } $target = $this->uri->getPath(); if ($target == '') { $target = '/'; } if ($this->uri->getQuery() != '') { $target .= '?' . $this->uri->getQuery(); } return $target; } public function withRequestTarget($requestTarget) { if (preg_match('#\s#', $requestTarget)) { throw new InvalidArgumentException( 'Invalid request target provided; cannot contain whitespace' ); } $new = clone $this; $new->requestTarget = $requestTarget; return $new; } public function getMethod() { return $this->method; } public function withMethod($method) { $this->assertMethod($method); $new = clone $this; $new->method = strtoupper($method); return $new; } public function getUri() { return $this->uri; } public function withUri(UriInterface $uri, $preserveHost = false) { if ($uri === $this->uri) { return $this; } $new = clone $this; $new->uri = $uri; if (!$preserveHost || !isset($this->headerNames['host'])) { $new->updateHostFromUri(); } return $new; } private function updateHostFromUri() { $host = $this->uri->getHost(); if ($host == '') { return; } if (($port = $this->uri->getPort()) !== null) { $host .= ':' . $port; } if (isset($this->headerNames['host'])) { $header = $this->headerNames['host']; } else { $header = 'Host'; $this->headerNames['host'] = 'Host'; } $this->headers = [$header => [$host]] + $this->headers; } private function assertMethod($method) { if (!is_string($method) || $method === '') { throw new \InvalidArgumentException('Method must be a non-empty string.'); } } } stream = $stream; } public function __get($name) { if ($name == 'stream') { $this->stream = $this->createStream(); return $this->stream; } throw new \UnexpectedValueException("$name not found on class"); } public function __toString() { try { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); } catch (\Exception $e) { trigger_error('StreamDecorator::__toString exception: ' . (string) $e, E_USER_ERROR); return ''; } } public function getContents() { return Utils::copyToString($this); } public function __call($method, array $args) { $result = call_user_func_array([$this->stream, $method], $args); return $result === $this->stream ? $this : $result; } public function close() { $this->stream->close(); } public function getMetadata($key = null) { return $this->stream->getMetadata($key); } public function detach() { return $this->stream->detach(); } public function getSize() { return $this->stream->getSize(); } public function eof() { return $this->stream->eof(); } public function tell() { return $this->stream->tell(); } public function isReadable() { return $this->stream->isReadable(); } public function isWritable() { return $this->stream->isWritable(); } public function isSeekable() { return $this->stream->isSeekable(); } public function rewind() { $this->seek(0); } public function seek($offset, $whence = SEEK_SET) { $this->stream->seek($offset, $whence); } public function read($length) { return $this->stream->read($length); } public function write($string) { return $this->stream->write($string); } protected function createStream() { throw new \BadMethodCallException('Not implemented'); } } remoteStream = $stream; $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+')); } public function getSize() { $remoteSize = $this->remoteStream->getSize(); if (null === $remoteSize) { return null; } return max($this->stream->getSize(), $remoteSize); } public function rewind() { $this->seek(0); } public function seek($offset, $whence = SEEK_SET) { if ($whence == SEEK_SET) { $byte = $offset; } elseif ($whence == SEEK_CUR) { $byte = $offset + $this->tell(); } elseif ($whence == SEEK_END) { $size = $this->remoteStream->getSize(); if ($size === null) { $size = $this->cacheEntireStream(); } $byte = $size + $offset; } else { throw new \InvalidArgumentException('Invalid whence'); } $diff = $byte - $this->stream->getSize(); if ($diff > 0) { while ($diff > 0 && !$this->remoteStream->eof()) { $this->read($diff); $diff = $byte - $this->stream->getSize(); } } else { $this->stream->seek($byte); } } public function read($length) { $data = $this->stream->read($length); $remaining = $length - strlen($data); if ($remaining) { $remoteData = $this->remoteStream->read( $remaining + $this->skipReadBytes ); if ($this->skipReadBytes) { $len = strlen($remoteData); $remoteData = substr($remoteData, $this->skipReadBytes); $this->skipReadBytes = max(0, $this->skipReadBytes - $len); } $data .= $remoteData; $this->stream->write($remoteData); } return $data; } public function write($string) { $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); if ($overflow > 0) { $this->skipReadBytes += $overflow; } return $this->stream->write($string); } public function eof() { return $this->stream->eof() && $this->remoteStream->eof(); } public function close() { $this->remoteStream->close() && $this->stream->close(); } private function cacheEntireStream() { $target = new FnStream(['write' => 'strlen']); Utils::copyToStream($this, $target); return $this->tell(); } } read(10); $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header); $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength); $resource = StreamWrapper::getResource($stream); stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ); $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource)); } private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header) { $filename_header_length = 0; if (substr(bin2hex($header), 6, 2) === '08') { $filename_header_length = 1; while ($stream->read(1) !== chr(0)) { $filename_header_length++; } } return $filename_header_length; } } hwm = $hwm; } public function __toString() { return $this->getContents(); } public function getContents() { $buffer = $this->buffer; $this->buffer = ''; return $buffer; } public function close() { $this->buffer = ''; } public function detach() { $this->close(); return null; } public function getSize() { return strlen($this->buffer); } public function isReadable() { return true; } public function isWritable() { return true; } public function isSeekable() { return false; } public function rewind() { $this->seek(0); } public function seek($offset, $whence = SEEK_SET) { throw new \RuntimeException('Cannot seek a BufferStream'); } public function eof() { return strlen($this->buffer) === 0; } public function tell() { throw new \RuntimeException('Cannot determine the position of a BufferStream'); } public function read($length) { $currentLength = strlen($this->buffer); if ($length >= $currentLength) { $result = $this->buffer; $this->buffer = ''; } else { $result = substr($this->buffer, 0, $length); $this->buffer = substr($this->buffer, $length); } return $result; } public function write($string) { $this->buffer .= $string; if (strlen($this->buffer) >= $this->hwm) { return false; } return strlen($string); } public function getMetadata($key = null) { if ($key == 'hwm') { return $this->hwm; } return $key ? null : []; } } source = $source; $this->size = isset($options['size']) ? $options['size'] : null; $this->metadata = isset($options['metadata']) ? $options['metadata'] : []; $this->buffer = new BufferStream(); } public function __toString() { try { return Utils::copyToString($this); } catch (\Exception $e) { return ''; } } public function close() { $this->detach(); } public function detach() { $this->tellPos = false; $this->source = null; return null; } public function getSize() { return $this->size; } public function tell() { return $this->tellPos; } public function eof() { return !$this->source; } public function isSeekable() { return false; } public function rewind() { $this->seek(0); } public function seek($offset, $whence = SEEK_SET) { throw new \RuntimeException('Cannot seek a PumpStream'); } public function isWritable() { return false; } public function write($string) { throw new \RuntimeException('Cannot write to a PumpStream'); } public function isReadable() { return true; } public function read($length) { $data = $this->buffer->read($length); $readLen = strlen($data); $this->tellPos += $readLen; $remaining = $length - $readLen; if ($remaining) { $this->pump($remaining); $data .= $this->buffer->read($remaining); $this->tellPos += strlen($data) - $readLen; } return $data; } public function getContents() { $result = ''; while (!$this->eof()) { $result .= $this->read(1000000); } return $result; } public function getMetadata($key = null) { if (!$key) { return $this->metadata; } return isset($this->metadata[$key]) ? $this->metadata[$key] : null; } private function pump($length) { if ($this->source) { do { $data = call_user_func($this->source, $length); if ($data === false || $data === null) { $this->source = null; return; } $this->buffer->write($data); $length -= strlen($data); } while ($length > 0); } } } ]+>|[^=]+/', $kvp, $matches)) { $m = $matches[0]; if (isset($m[1])) { $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed); } else { $part[] = trim($m[0], $trimmed); } } } if ($part) { $params[] = $part; } } return $params; } public static function normalize($header) { if (!is_array($header)) { return array_map('trim', explode(',', $header)); } $result = []; foreach ($header as $value) { foreach ((array) $value as $v) { if (strpos($v, ',') === false) { $result[] = $v; continue; } foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) { $result[] = trim($vv); } } } return $result; } } boundary = $boundary ?: sha1(uniqid('', true)); $this->stream = $this->createStream($elements); } public function getBoundary() { return $this->boundary; } public function isWritable() { return false; } private function getHeaders(array $headers) { $str = ''; foreach ($headers as $key => $value) { $str .= "{$key}: {$value}\r\n"; } return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n"; } protected function createStream(array $elements) { $stream = new AppendStream(); foreach ($elements as $element) { $this->addElement($stream, $element); } $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n")); return $stream; } private function addElement(AppendStream $stream, array $element) { foreach (['contents', 'name'] as $key) { if (!array_key_exists($key, $element)) { throw new \InvalidArgumentException("A '{$key}' key is required"); } } $element['contents'] = Utils::streamFor($element['contents']); if (empty($element['filename'])) { $uri = $element['contents']->getMetadata('uri'); if (substr($uri, 0, 6) !== 'php://') { $element['filename'] = $uri; } } list($body, $headers) = $this->createElement( $element['name'], $element['contents'], isset($element['filename']) ? $element['filename'] : null, isset($element['headers']) ? $element['headers'] : [] ); $stream->addStream(Utils::streamFor($this->getHeaders($headers))); $stream->addStream($body); $stream->addStream(Utils::streamFor("\r\n")); } private function createElement($name, StreamInterface $stream, $filename, array $headers) { $disposition = $this->getHeader($headers, 'content-disposition'); if (!$disposition) { $headers['Content-Disposition'] = ($filename === '0' || $filename) ? sprintf( 'form-data; name="%s"; filename="%s"', $name, basename($filename) ) : "form-data; name=\"{$name}\""; } $length = $this->getHeader($headers, 'content-length'); if (!$length) { if ($length = $stream->getSize()) { $headers['Content-Length'] = (string) $length; } } $type = $this->getHeader($headers, 'content-type'); if (!$type && ($filename === '0' || $filename)) { if ($type = MimeType::fromFilename($filename)) { $headers['Content-Type'] = $type; } } return [$stream, $headers]; } private function getHeader(array $headers, $key) { $lowercaseHeader = strtolower($key); foreach ($headers as $k => $v) { if (strtolower($k) === $lowercaseHeader) { return $v; } } return null; } } $v) { $k = $encoder($k); if (!is_array($v)) { $qs .= $k; if ($v !== null) { $qs .= '=' . $encoder($v); } $qs .= '&'; } else { foreach ($v as $vv) { $qs .= $k; if ($vv !== null) { $qs .= '=' . $encoder($vv); } $qs .= '&'; } } } return $qs ? (string) substr($qs, 0, -1) : ''; } } serverParams = $serverParams; parent::__construct($method, $uri, $headers, $body, $version); } public static function normalizeFiles(array $files) { $normalized = []; foreach ($files as $key => $value) { if ($value instanceof UploadedFileInterface) { $normalized[$key] = $value; } elseif (is_array($value) && isset($value['tmp_name'])) { $normalized[$key] = self::createUploadedFileFromSpec($value); } elseif (is_array($value)) { $normalized[$key] = self::normalizeFiles($value); continue; } else { throw new InvalidArgumentException('Invalid value in files specification'); } } return $normalized; } private static function createUploadedFileFromSpec(array $value) { if (is_array($value['tmp_name'])) { return self::normalizeNestedFileSpec($value); } return new UploadedFile( $value['tmp_name'], (int) $value['size'], (int) $value['error'], $value['name'], $value['type'] ); } private static function normalizeNestedFileSpec(array $files = []) { $normalizedFiles = []; foreach (array_keys($files['tmp_name']) as $key) { $spec = [ 'tmp_name' => $files['tmp_name'][$key], 'size' => $files['size'][$key], 'error' => $files['error'][$key], 'name' => $files['name'][$key], 'type' => $files['type'][$key], ]; $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); } return $normalizedFiles; } public static function fromGlobals() { $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; $headers = getallheaders(); $uri = self::getUriFromGlobals(); $body = new CachingStream(new LazyOpenStream('php://input', 'r+')); $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); return $serverRequest ->withCookieParams($_COOKIE) ->withQueryParams($_GET) ->withParsedBody($_POST) ->withUploadedFiles(self::normalizeFiles($_FILES)); } private static function extractHostAndPortFromAuthority($authority) { $uri = 'http://' . $authority; $parts = parse_url($uri); if (false === $parts) { return [null, null]; } $host = isset($parts['host']) ? $parts['host'] : null; $port = isset($parts['port']) ? $parts['port'] : null; return [$host, $port]; } public static function getUriFromGlobals() { $uri = new Uri(''); $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); $hasPort = false; if (isset($_SERVER['HTTP_HOST'])) { list($host, $port) = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); if ($host !== null) { $uri = $uri->withHost($host); } if ($port !== null) { $hasPort = true; $uri = $uri->withPort($port); } } elseif (isset($_SERVER['SERVER_NAME'])) { $uri = $uri->withHost($_SERVER['SERVER_NAME']); } elseif (isset($_SERVER['SERVER_ADDR'])) { $uri = $uri->withHost($_SERVER['SERVER_ADDR']); } if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { $uri = $uri->withPort($_SERVER['SERVER_PORT']); } $hasQuery = false; if (isset($_SERVER['REQUEST_URI'])) { $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2); $uri = $uri->withPath($requestUriParts[0]); if (isset($requestUriParts[1])) { $hasQuery = true; $uri = $uri->withQuery($requestUriParts[1]); } } if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { $uri = $uri->withQuery($_SERVER['QUERY_STRING']); } return $uri; } public function getServerParams() { return $this->serverParams; } public function getUploadedFiles() { return $this->uploadedFiles; } public function withUploadedFiles(array $uploadedFiles) { $new = clone $this; $new->uploadedFiles = $uploadedFiles; return $new; } public function getCookieParams() { return $this->cookieParams; } public function withCookieParams(array $cookies) { $new = clone $this; $new->cookieParams = $cookies; return $new; } public function getQueryParams() { return $this->queryParams; } public function withQueryParams(array $query) { $new = clone $this; $new->queryParams = $query; return $new; } public function getParsedBody() { return $this->parsedBody; } public function withParsedBody($data) { $new = clone $this; $new->parsedBody = $data; return $new; } public function getAttributes() { return $this->attributes; } public function getAttribute($attribute, $default = null) { if (false === array_key_exists($attribute, $this->attributes)) { return $default; } return $this->attributes[$attribute]; } public function withAttribute($attribute, $value) { $new = clone $this; $new->attributes[$attribute] = $value; return $new; } public function withoutAttribute($attribute) { if (false === array_key_exists($attribute, $this->attributes)) { return $this; } $new = clone $this; unset($new->attributes[$attribute]); return $new; } } @,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m"; const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; } setError($errorStatus); $this->setSize($size); $this->setClientFilename($clientFilename); $this->setClientMediaType($clientMediaType); if ($this->isOk()) { $this->setStreamOrFile($streamOrFile); } } private function setStreamOrFile($streamOrFile) { if (is_string($streamOrFile)) { $this->file = $streamOrFile; } elseif (is_resource($streamOrFile)) { $this->stream = new Stream($streamOrFile); } elseif ($streamOrFile instanceof StreamInterface) { $this->stream = $streamOrFile; } else { throw new InvalidArgumentException( 'Invalid stream or file provided for UploadedFile' ); } } private function setError($error) { if (false === is_int($error)) { throw new InvalidArgumentException( 'Upload file error status must be an integer' ); } if (false === in_array($error, UploadedFile::$errors)) { throw new InvalidArgumentException( 'Invalid error status for UploadedFile' ); } $this->error = $error; } private function setSize($size) { if (false === is_int($size)) { throw new InvalidArgumentException( 'Upload file size must be an integer' ); } $this->size = $size; } private function isStringOrNull($param) { return in_array(gettype($param), ['string', 'NULL']); } private function isStringNotEmpty($param) { return is_string($param) && false === empty($param); } private function setClientFilename($clientFilename) { if (false === $this->isStringOrNull($clientFilename)) { throw new InvalidArgumentException( 'Upload file client filename must be a string or null' ); } $this->clientFilename = $clientFilename; } private function setClientMediaType($clientMediaType) { if (false === $this->isStringOrNull($clientMediaType)) { throw new InvalidArgumentException( 'Upload file client media type must be a string or null' ); } $this->clientMediaType = $clientMediaType; } private function isOk() { return $this->error === UPLOAD_ERR_OK; } public function isMoved() { return $this->moved; } private function validateActive() { if (false === $this->isOk()) { throw new RuntimeException('Cannot retrieve stream due to upload error'); } if ($this->isMoved()) { throw new RuntimeException('Cannot retrieve stream after it has already been moved'); } } public function getStream() { $this->validateActive(); if ($this->stream instanceof StreamInterface) { return $this->stream; } return new LazyOpenStream($this->file, 'r+'); } public function moveTo($targetPath) { $this->validateActive(); if (false === $this->isStringNotEmpty($targetPath)) { throw new InvalidArgumentException( 'Invalid path provided for move operation; must be a non-empty string' ); } if ($this->file) { $this->moved = php_sapi_name() == 'cli' ? rename($this->file, $targetPath) : move_uploaded_file($this->file, $targetPath); } else { Utils::copyToStream( $this->getStream(), new LazyOpenStream($targetPath, 'w') ); $this->moved = true; } if (false === $this->moved) { throw new RuntimeException( sprintf('Uploaded file could not be moved to %s', $targetPath) ); } } public function getSize() { return $this->size; } public function getError() { return $this->error; } public function getClientFilename() { return $this->clientFilename; } public function getClientMediaType() { return $this->clientMediaType; } } getHost(), $modified->getHost()) !== 0) { return true; } if ($original->getScheme() !== $modified->getScheme()) { return true; } if (self::computePort($original) !== self::computePort($modified)) { return true; } return false; } private static function computePort(UriInterface $uri) { $port = $uri->getPort(); if (null !== $port) { return $port; } return 'https' === $uri->getScheme() ? 443 : 80; } private function __construct() { } } getPath() === '' && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https') ) { $uri = $uri->withPath('/'); } if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') { $uri = $uri->withHost(''); } if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) { $uri = $uri->withPort(null); } if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) { $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath())); } if ($flags & self::REMOVE_DUPLICATE_SLASHES) { $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath())); } if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') { $queryKeyValues = explode('&', $uri->getQuery()); sort($queryKeyValues); $uri = $uri->withQuery(implode('&', $queryKeyValues)); } return $uri; } public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS) { return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations); } private static function capitalizePercentEncoding(UriInterface $uri) { $regex = '/(?:%[A-Fa-f0-9]{2})++/'; $callback = function (array $match) { return strtoupper($match[0]); }; return $uri->withPath( preg_replace_callback($regex, $callback, $uri->getPath()) )->withQuery( preg_replace_callback($regex, $callback, $uri->getQuery()) ); } private static function decodeUnreservedCharacters(UriInterface $uri) { $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; $callback = function (array $match) { return rawurldecode($match[0]); }; return $uri->withPath( preg_replace_callback($regex, $callback, $uri->getPath()) )->withQuery( preg_replace_callback($regex, $callback, $uri->getQuery()) ); } private function __construct() { } } getMethod() . ' ' . $message->getRequestTarget()) . ' HTTP/' . $message->getProtocolVersion(); if (!$message->hasHeader('host')) { $msg .= "\r\nHost: " . $message->getUri()->getHost(); } } elseif ($message instanceof ResponseInterface) { $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' . $message->getStatusCode() . ' ' . $message->getReasonPhrase(); } else { throw new \InvalidArgumentException('Unknown message type'); } foreach ($message->getHeaders() as $name => $values) { if (strtolower($name) === 'set-cookie') { foreach ($values as $value) { $msg .= "\r\n{$name}: " . $value; } } else { $msg .= "\r\n{$name}: " . implode(', ', $values); } } return "{$msg}\r\n\r\n" . $message->getBody(); } public static function bodySummary(MessageInterface $message, $truncateAt = 120) { $body = $message->getBody(); if (!$body->isSeekable() || !$body->isReadable()) { return null; } $size = $body->getSize(); if ($size === 0) { return null; } $summary = $body->read($truncateAt); $body->rewind(); if ($size > $truncateAt) { $summary .= ' (truncated...)'; } if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) { return null; } return $summary; } public static function rewindBody(MessageInterface $message) { $body = $message->getBody(); if ($body->tell()) { $body->rewind(); } } public static function parseMessage($message) { if (!$message) { throw new \InvalidArgumentException('Invalid message'); } $message = ltrim($message, "\r\n"); $messageParts = preg_split("/\r?\n\r?\n/", $message, 2); if ($messageParts === false || count($messageParts) !== 2) { throw new \InvalidArgumentException('Invalid message: Missing header delimiter'); } list($rawHeaders, $body) = $messageParts; $rawHeaders .= "\r\n"; $headerParts = preg_split("/\r?\n/", $rawHeaders, 2); if ($headerParts === false || count($headerParts) !== 2) { throw new \InvalidArgumentException('Invalid message: Missing status line'); } list($startLine, $rawHeaders) = $headerParts; if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') { $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders); } $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER); if ($count !== substr_count($rawHeaders, "\n")) { if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) { throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding'); } throw new \InvalidArgumentException('Invalid header syntax'); } $headers = []; foreach ($headerLines as $headerLine) { $headers[$headerLine[1]][] = $headerLine[2]; } return [ 'start-line' => $startLine, 'headers' => $headers, 'body' => $body, ]; } public static function parseRequestUri($path, array $headers) { $hostKey = array_filter(array_keys($headers), function ($k) { return strtolower($k) === 'host'; }); if (!$hostKey) { return $path; } $host = $headers[reset($hostKey)][0]; $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; return $scheme . '://' . $host . '/' . ltrim($path, '/'); } public static function parseRequest($message) { $data = self::parseMessage($message); $matches = []; if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { throw new \InvalidArgumentException('Invalid request string'); } $parts = explode(' ', $data['start-line'], 3); $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1'; $request = new Request( $parts[0], $matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1], $data['headers'], $data['body'], $version ); return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]); } public static function parseResponse($message) { $data = self::parseMessage($message); if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']); } $parts = explode(' ', $data['start-line'], 3); return new Response( (int) $parts[1], $data['headers'], $data['body'], explode('/', $parts[0])[1], isset($parts[2]) ? $parts[2] : null ); } } isReadable()) { $mode = $stream->isWritable() ? 'r+' : 'r'; } elseif ($stream->isWritable()) { $mode = 'w'; } else { throw new \InvalidArgumentException('The stream must be readable, ' . 'writable, or both.'); } return fopen('guzzle://stream', $mode, null, self::createStreamContext($stream)); } public static function createStreamContext(StreamInterface $stream) { return stream_context_create([ 'guzzle' => ['stream' => $stream] ]); } public static function register() { if (!in_array('guzzle', stream_get_wrappers())) { stream_wrapper_register('guzzle', __CLASS__); } } public function stream_open($path, $mode, $options, &$opened_path) { $options = stream_context_get_options($this->context); if (!isset($options['guzzle']['stream'])) { return false; } $this->mode = $mode; $this->stream = $options['guzzle']['stream']; return true; } public function stream_read($count) { return $this->stream->read($count); } public function stream_write($data) { return (int) $this->stream->write($data); } public function stream_tell() { return $this->stream->tell(); } public function stream_eof() { return $this->stream->eof(); } public function stream_seek($offset, $whence) { $this->stream->seek($offset, $whence); return true; } public function stream_cast($cast_as) { $stream = clone($this->stream); return $stream->detach(); } public function stream_stat() { static $modeMap = [ 'r' => 33060, 'rb' => 33060, 'r+' => 33206, 'w' => 33188, 'wb' => 33188 ]; return [ 'dev' => 0, 'ino' => 0, 'mode' => $modeMap[$this->mode], 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => $this->stream->getSize() ?: 0, 'atime' => 0, 'mtime' => 0, 'ctime' => 0, 'blksize' => 0, 'blocks' => 0 ]; } public function url_stat($path, $flags) { return [ 'dev' => 0, 'ino' => 0, 'mode' => 0, 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => 0, 'atime' => 0, 'mtime' => 0, 'ctime' => 0, 'blksize' => 0, 'blocks' => 0 ]; } } 'video/3gpp', '7z' => 'application/x-7z-compressed', 'aac' => 'audio/x-aac', 'ai' => 'application/postscript', 'aif' => 'audio/x-aiff', 'asc' => 'text/plain', 'asf' => 'video/x-ms-asf', 'atom' => 'application/atom+xml', 'avi' => 'video/x-msvideo', 'bmp' => 'image/bmp', 'bz2' => 'application/x-bzip2', 'cer' => 'application/pkix-cert', 'crl' => 'application/pkix-crl', 'crt' => 'application/x-x509-ca-cert', 'css' => 'text/css', 'csv' => 'text/csv', 'cu' => 'application/cu-seeme', 'deb' => 'application/x-debian-package', 'doc' => 'application/msword', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dvi' => 'application/x-dvi', 'eot' => 'application/vnd.ms-fontobject', 'eps' => 'application/postscript', 'epub' => 'application/epub+zip', 'etx' => 'text/x-setext', 'flac' => 'audio/flac', 'flv' => 'video/x-flv', 'gif' => 'image/gif', 'gz' => 'application/gzip', 'htm' => 'text/html', 'html' => 'text/html', 'ico' => 'image/x-icon', 'ics' => 'text/calendar', 'ini' => 'text/plain', 'iso' => 'application/x-iso9660-image', 'jar' => 'application/java-archive', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'js' => 'text/javascript', 'json' => 'application/json', 'latex' => 'application/x-latex', 'log' => 'text/plain', 'm4a' => 'audio/mp4', 'm4v' => 'video/mp4', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mov' => 'video/quicktime', 'mkv' => 'video/x-matroska', 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 'mp4a' => 'audio/mp4', 'mp4v' => 'video/mp4', 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', 'mpg4' => 'video/mp4', 'oga' => 'audio/ogg', 'ogg' => 'audio/ogg', 'ogv' => 'video/ogg', 'ogx' => 'application/ogg', 'pbm' => 'image/x-portable-bitmap', 'pdf' => 'application/pdf', 'pgm' => 'image/x-portable-graymap', 'png' => 'image/png', 'pnm' => 'image/x-portable-anymap', 'ppm' => 'image/x-portable-pixmap', 'ppt' => 'application/vnd.ms-powerpoint', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'ps' => 'application/postscript', 'qt' => 'video/quicktime', 'rar' => 'application/x-rar-compressed', 'ras' => 'image/x-cmu-raster', 'rss' => 'application/rss+xml', 'rtf' => 'application/rtf', 'sgm' => 'text/sgml', 'sgml' => 'text/sgml', 'svg' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash', 'tar' => 'application/x-tar', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'torrent' => 'application/x-bittorrent', 'ttf' => 'application/x-font-ttf', 'txt' => 'text/plain', 'wav' => 'audio/x-wav', 'webm' => 'video/webm', 'webp' => 'image/webp', 'wma' => 'audio/x-ms-wma', 'wmv' => 'video/x-ms-wmv', 'woff' => 'application/x-font-woff', 'wsdl' => 'application/wsdl+xml', 'xbm' => 'image/x-xbitmap', 'xls' => 'application/vnd.ms-excel', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xml' => 'application/xml', 'xpm' => 'image/x-xpixmap', 'xwd' => 'image/x-xwindowdump', 'yaml' => 'text/yaml', 'yml' => 'text/yaml', 'zip' => 'application/zip', ]; $extension = strtolower($extension); return isset($mimetypes[$extension]) ? $mimetypes[$extension] : null; } } size = $options['size']; } $this->customMetadata = isset($options['metadata']) ? $options['metadata'] : []; $this->stream = $stream; $meta = stream_get_meta_data($this->stream); $this->seekable = $meta['seekable']; $this->readable = (bool)preg_match(self::READABLE_MODES, $meta['mode']); $this->writable = (bool)preg_match(self::WRITABLE_MODES, $meta['mode']); $this->uri = $this->getMetadata('uri'); } public function __destruct() { $this->close(); } public function __toString() { try { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); } catch (\Exception $e) { return ''; } } public function getContents() { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } $contents = stream_get_contents($this->stream); if ($contents === false) { throw new \RuntimeException('Unable to read stream contents'); } return $contents; } public function close() { if (isset($this->stream)) { if (is_resource($this->stream)) { fclose($this->stream); } $this->detach(); } } public function detach() { if (!isset($this->stream)) { return null; } $result = $this->stream; unset($this->stream); $this->size = $this->uri = null; $this->readable = $this->writable = $this->seekable = false; return $result; } public function getSize() { if ($this->size !== null) { return $this->size; } if (!isset($this->stream)) { return null; } if ($this->uri) { clearstatcache(true, $this->uri); } $stats = fstat($this->stream); if (isset($stats['size'])) { $this->size = $stats['size']; return $this->size; } return null; } public function isReadable() { return $this->readable; } public function isWritable() { return $this->writable; } public function isSeekable() { return $this->seekable; } public function eof() { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } return feof($this->stream); } public function tell() { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } $result = ftell($this->stream); if ($result === false) { throw new \RuntimeException('Unable to determine stream position'); } return $result; } public function rewind() { $this->seek(0); } public function seek($offset, $whence = SEEK_SET) { $whence = (int) $whence; if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->seekable) { throw new \RuntimeException('Stream is not seekable'); } if (fseek($this->stream, $offset, $whence) === -1) { throw new \RuntimeException('Unable to seek to stream position ' . $offset . ' with whence ' . var_export($whence, true)); } } public function read($length) { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->readable) { throw new \RuntimeException('Cannot read from non-readable stream'); } if ($length < 0) { throw new \RuntimeException('Length parameter cannot be negative'); } if (0 === $length) { return ''; } $string = fread($this->stream, $length); if (false === $string) { throw new \RuntimeException('Unable to read from stream'); } return $string; } public function write($string) { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->writable) { throw new \RuntimeException('Cannot write to a non-writable stream'); } $this->size = null; $result = fwrite($this->stream, $string); if ($result === false) { throw new \RuntimeException('Unable to write to stream'); } return $result; } public function getMetadata($key = null) { if (!isset($this->stream)) { return $key ? null : []; } elseif (!$key) { return $this->customMetadata + stream_get_meta_data($this->stream); } elseif (isset($this->customMetadata[$key])) { return $this->customMetadata[$key]; } $meta = stream_get_meta_data($this->stream); return isset($meta[$key]) ? $meta[$key] : null; } } stream = $stream; $this->maxLength = $maxLength; } public function write($string) { $diff = $this->maxLength - $this->stream->getSize(); if ($diff <= 0) { return 0; } if (strlen($string) < $diff) { return $this->stream->write($string); } return $this->stream->write(substr($string, 0, $diff)); } } methods = $methods; foreach ($methods as $name => $fn) { $this->{'_fn_' . $name} = $fn; } } public function __get($name) { throw new \BadMethodCallException(str_replace('_fn_', '', $name) . '() is not implemented in the FnStream'); } public function __destruct() { if (isset($this->_fn_close)) { call_user_func($this->_fn_close); } } public function __wakeup() { throw new \LogicException('FnStream should never be unserialized'); } public static function decorate(StreamInterface $stream, array $methods) { foreach (array_diff(self::$slots, array_keys($methods)) as $diff) { $methods[$diff] = [$stream, $diff]; } return new self($methods); } public function __toString() { return call_user_func($this->_fn___toString); } public function close() { return call_user_func($this->_fn_close); } public function detach() { return call_user_func($this->_fn_detach); } public function getSize() { return call_user_func($this->_fn_getSize); } public function tell() { return call_user_func($this->_fn_tell); } public function eof() { return call_user_func($this->_fn_eof); } public function isSeekable() { return call_user_func($this->_fn_isSeekable); } public function rewind() { call_user_func($this->_fn_rewind); } public function seek($offset, $whence = SEEK_SET) { call_user_func($this->_fn_seek, $offset, $whence); } public function isWritable() { return call_user_func($this->_fn_isWritable); } public function write($string) { return call_user_func($this->_fn_write, $string); } public function isReadable() { return call_user_func($this->_fn_isReadable); } public function read($length) { return call_user_func($this->_fn_read, $length); } public function getContents() { return call_user_func($this->_fn_getContents); } public function getMetadata($key = null) { return call_user_func($this->_fn_getMetadata, $key); } } 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 451 => 'Unavailable For Legal Reasons', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 511 => 'Network Authentication Required', ]; private $reasonPhrase = ''; private $statusCode = 200; public function __construct( $status = 200, array $headers = [], $body = null, $version = '1.1', $reason = null ) { $this->assertStatusCodeIsInteger($status); $status = (int) $status; $this->assertStatusCodeRange($status); $this->statusCode = $status; if ($body !== '' && $body !== null) { $this->stream = Utils::streamFor($body); } $this->setHeaders($headers); if ($reason == '' && isset(self::$phrases[$this->statusCode])) { $this->reasonPhrase = self::$phrases[$this->statusCode]; } else { $this->reasonPhrase = (string) $reason; } $this->protocol = $version; } public function getStatusCode() { return $this->statusCode; } public function getReasonPhrase() { return $this->reasonPhrase; } public function withStatus($code, $reasonPhrase = '') { $this->assertStatusCodeIsInteger($code); $code = (int) $code; $this->assertStatusCodeRange($code); $new = clone $this; $new->statusCode = $code; if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) { $reasonPhrase = self::$phrases[$new->statusCode]; } $new->reasonPhrase = (string) $reasonPhrase; return $new; } private function assertStatusCodeIsInteger($statusCode) { if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) { throw new \InvalidArgumentException('Status code must be an integer value.'); } } private function assertStatusCodeRange($statusCode) { if ($statusCode < 100 || $statusCode >= 600) { throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.'); } } } filename = $filename; $this->mode = $mode; } protected function createStream() { return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode)); } } configureDefaults($config); } public function __call($method, $args) { if (count($args) < 1) { throw new \InvalidArgumentException('Magic request methods require a URI and optional options array'); } $uri = $args[0]; $opts = isset($args[1]) ? $args[1] : []; return substr($method, -5) === 'Async' ? $this->requestAsync(substr($method, 0, -5), $uri, $opts) : $this->request($method, $uri, $opts); } public function sendAsync(RequestInterface $request, array $options = []) { $options = $this->prepareDefaults($options); return $this->transfer( $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')), $options ); } public function send(RequestInterface $request, array $options = []) { $options[RequestOptions::SYNCHRONOUS] = true; return $this->sendAsync($request, $options)->wait(); } public function requestAsync($method, $uri = '', array $options = []) { $options = $this->prepareDefaults($options); $headers = isset($options['headers']) ? $options['headers'] : []; $body = isset($options['body']) ? $options['body'] : null; $version = isset($options['version']) ? $options['version'] : '1.1'; $uri = $this->buildUri($uri, $options); if (is_array($body)) { $this->invalidBody(); } $request = new Psr7\Request($method, $uri, $headers, $body, $version); unset($options['headers'], $options['body'], $options['version']); return $this->transfer($request, $options); } public function request($method, $uri = '', array $options = []) { $options[RequestOptions::SYNCHRONOUS] = true; return $this->requestAsync($method, $uri, $options)->wait(); } public function getConfig($option = null) { return $option === null ? $this->config : (isset($this->config[$option]) ? $this->config[$option] : null); } private function buildUri($uri, array $config) { $uri = Psr7\uri_for($uri === null ? '' : $uri); if (isset($config['base_uri'])) { $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri); } if (isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) { $idnOptions = ($config['idn_conversion'] === true) ? IDNA_DEFAULT : $config['idn_conversion']; $uri = Utils::idnUriConvert($uri, $idnOptions); } return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; } private function configureDefaults(array $config) { $defaults = [ 'allow_redirects' => RedirectMiddleware::$defaultSettings, 'http_errors' => true, 'decode_content' => true, 'verify' => true, 'cookies' => false, 'idn_conversion' => true, ]; if (php_sapi_name() === 'cli' && getenv('HTTP_PROXY')) { $defaults['proxy']['http'] = getenv('HTTP_PROXY'); } if ($proxy = getenv('HTTPS_PROXY')) { $defaults['proxy']['https'] = $proxy; } if ($noProxy = getenv('NO_PROXY')) { $cleanedNoProxy = str_replace(' ', '', $noProxy); $defaults['proxy']['no'] = explode(',', $cleanedNoProxy); } $this->config = $config + $defaults; if (!empty($config['cookies']) && $config['cookies'] === true) { $this->config['cookies'] = new CookieJar(); } if (!isset($this->config['headers'])) { $this->config['headers'] = ['User-Agent' => default_user_agent()]; } else { foreach (array_keys($this->config['headers']) as $name) { if (strtolower($name) === 'user-agent') { return; } } $this->config['headers']['User-Agent'] = default_user_agent(); } } private function prepareDefaults(array $options) { $defaults = $this->config; if (!empty($defaults['headers'])) { $defaults['_conditional'] = $defaults['headers']; unset($defaults['headers']); } if (array_key_exists('headers', $options)) { if ($options['headers'] === null) { $defaults['_conditional'] = []; unset($options['headers']); } elseif (!is_array($options['headers'])) { throw new \InvalidArgumentException('headers must be an array'); } } $result = $options + $defaults; foreach ($result as $k => $v) { if ($v === null) { unset($result[$k]); } } return $result; } private function transfer(RequestInterface $request, array $options) { if (isset($options['save_to'])) { $options['sink'] = $options['save_to']; unset($options['save_to']); } if (isset($options['exceptions'])) { $options['http_errors'] = $options['exceptions']; unset($options['exceptions']); } $request = $this->applyOptions($request, $options); $handler = $options['handler']; try { return Promise\promise_for($handler($request, $options)); } catch (\Exception $e) { return Promise\rejection_for($e); } } private function applyOptions(RequestInterface $request, array &$options) { $modify = [ 'set_headers' => [], ]; if (isset($options['headers'])) { $modify['set_headers'] = $options['headers']; unset($options['headers']); } if (isset($options['form_params'])) { if (isset($options['multipart'])) { throw new \InvalidArgumentException('You cannot use ' . 'form_params and multipart at the same time. Use the ' . 'form_params option if you want to send application/' . 'x-www-form-urlencoded requests, and the multipart ' . 'option to send multipart/form-data requests.'); } $options['body'] = http_build_query($options['form_params'], '', '&'); unset($options['form_params']); $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded'; } if (isset($options['multipart'])) { $options['body'] = new Psr7\MultipartStream($options['multipart']); unset($options['multipart']); } if (isset($options['json'])) { $options['body'] = \GuzzleHttp\json_encode($options['json']); unset($options['json']); $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/json'; } if (!empty($options['decode_content']) && $options['decode_content'] !== true ) { $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']); $modify['set_headers']['Accept-Encoding'] = $options['decode_content']; } if (isset($options['body'])) { if (is_array($options['body'])) { $this->invalidBody(); } $modify['body'] = Psr7\stream_for($options['body']); unset($options['body']); } if (!empty($options['auth']) && is_array($options['auth'])) { $value = $options['auth']; $type = isset($value[2]) ? strtolower($value[2]) : 'basic'; switch ($type) { case 'basic': $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']); $modify['set_headers']['Authorization'] = 'Basic ' . base64_encode("$value[0]:$value[1]"); break; case 'digest': $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST; $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; break; case 'ntlm': $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM; $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]"; break; } } if (isset($options['query'])) { $value = $options['query']; if (is_array($value)) { $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986); } if (!is_string($value)) { throw new \InvalidArgumentException('query must be a string or array'); } $modify['query'] = $value; unset($options['query']); } if (isset($options['sink'])) { if (is_bool($options['sink'])) { throw new \InvalidArgumentException('sink must not be a boolean'); } } $request = Psr7\modify_request($request, $modify); if ($request->getBody() instanceof Psr7\MultipartStream) { $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' . $request->getBody()->getBoundary(); } if (isset($options['_conditional'])) { $modify = []; foreach ($options['_conditional'] as $k => $v) { if (!$request->hasHeader($k)) { $modify['set_headers'][$k] = $v; } } $request = Psr7\modify_request($request, $modify); unset($options['_conditional']); } return $request; } private function invalidBody() { throw new \InvalidArgumentException('Passing in the "body" request ' . 'option as an array to send a POST request has been deprecated. ' . 'Please use the "form_params" request option to send a ' . 'application/x-www-form-urlencoded request, or the "multipart" ' . 'request option to send a multipart/form-data request.'); } } >>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; private $template; public function __construct($template = self::CLF) { $this->template = $template ?: self::CLF; } public function format( RequestInterface $request, ResponseInterface $response = null, \Exception $error = null ) { $cache = []; return preg_replace_callback( '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', function (array $matches) use ($request, $response, $error, &$cache) { if (isset($cache[$matches[1]])) { return $cache[$matches[1]]; } $result = ''; switch ($matches[1]) { case 'request': $result = Psr7\str($request); break; case 'response': $result = $response ? Psr7\str($response) : ''; break; case 'req_headers': $result = trim($request->getMethod() . ' ' . $request->getRequestTarget()) . ' HTTP/' . $request->getProtocolVersion() . "\r\n" . $this->headers($request); break; case 'res_headers': $result = $response ? sprintf( 'HTTP/%s %d %s', $response->getProtocolVersion(), $response->getStatusCode(), $response->getReasonPhrase() ) . "\r\n" . $this->headers($response) : 'NULL'; break; case 'req_body': $result = $request->getBody(); break; case 'res_body': $result = $response ? $response->getBody() : 'NULL'; break; case 'ts': case 'date_iso_8601': $result = gmdate('c'); break; case 'date_common_log': $result = date('d/M/Y:H:i:s O'); break; case 'method': $result = $request->getMethod(); break; case 'version': $result = $request->getProtocolVersion(); break; case 'uri': case 'url': $result = $request->getUri(); break; case 'target': $result = $request->getRequestTarget(); break; case 'req_version': $result = $request->getProtocolVersion(); break; case 'res_version': $result = $response ? $response->getProtocolVersion() : 'NULL'; break; case 'host': $result = $request->getHeaderLine('Host'); break; case 'hostname': $result = gethostname(); break; case 'code': $result = $response ? $response->getStatusCode() : 'NULL'; break; case 'phrase': $result = $response ? $response->getReasonPhrase() : 'NULL'; break; case 'error': $result = $error ? $error->getMessage() : 'NULL'; break; default: if (strpos($matches[1], 'req_header_') === 0) { $result = $request->getHeaderLine(substr($matches[1], 11)); } elseif (strpos($matches[1], 'res_header_') === 0) { $result = $response ? $response->getHeaderLine(substr($matches[1], 11)) : 'NULL'; } } $cache[$matches[1]] = $result; return $result; }, $this->template ); } private function headers(MessageInterface $message) { $result = ''; foreach ($message->getHeaders() as $name => $values) { $result .= $name . ': ' . implode(', ', $values) . "\r\n"; } return trim($result); } } ['prefix' => '', 'joiner' => ',', 'query' => false], '+' => ['prefix' => '', 'joiner' => ',', 'query' => false], '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false], '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false], '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false], ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true], '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true], '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true] ]; private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=']; private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', '%3B', '%3D']; public function expand($template, array $variables) { if (false === strpos($template, '{')) { return $template; } $this->template = $template; $this->variables = $variables; return preg_replace_callback( '/\{([^\}]+)\}/', [$this, 'expandMatch'], $this->template ); } private function parseExpression($expression) { $result = []; if (isset(self::$operatorHash[$expression[0]])) { $result['operator'] = $expression[0]; $expression = substr($expression, 1); } else { $result['operator'] = ''; } foreach (explode(',', $expression) as $value) { $value = trim($value); $varspec = []; if ($colonPos = strpos($value, ':')) { $varspec['value'] = substr($value, 0, $colonPos); $varspec['modifier'] = ':'; $varspec['position'] = (int) substr($value, $colonPos + 1); } elseif (substr($value, -1) === '*') { $varspec['modifier'] = '*'; $varspec['value'] = substr($value, 0, -1); } else { $varspec['value'] = (string) $value; $varspec['modifier'] = ''; } $result['values'][] = $varspec; } return $result; } private function expandMatch(array $matches) { static $rfc1738to3986 = ['+' => '%20', '%7e' => '~']; $replacements = []; $parsed = self::parseExpression($matches[1]); $prefix = self::$operatorHash[$parsed['operator']]['prefix']; $joiner = self::$operatorHash[$parsed['operator']]['joiner']; $useQuery = self::$operatorHash[$parsed['operator']]['query']; foreach ($parsed['values'] as $value) { if (!isset($this->variables[$value['value']])) { continue; } $variable = $this->variables[$value['value']]; $actuallyUseQuery = $useQuery; $expanded = ''; if (is_array($variable)) { $isAssoc = $this->isAssoc($variable); $kvp = []; foreach ($variable as $key => $var) { if ($isAssoc) { $key = rawurlencode($key); $isNestedArray = is_array($var); } else { $isNestedArray = false; } if (!$isNestedArray) { $var = rawurlencode($var); if ($parsed['operator'] === '+' || $parsed['operator'] === '#' ) { $var = $this->decodeReserved($var); } } if ($value['modifier'] === '*') { if ($isAssoc) { if ($isNestedArray) { $var = strtr( http_build_query([$key => $var]), $rfc1738to3986 ); } else { $var = $key . '=' . $var; } } elseif ($key > 0 && $actuallyUseQuery) { $var = $value['value'] . '=' . $var; } } $kvp[$key] = $var; } if (empty($variable)) { $actuallyUseQuery = false; } elseif ($value['modifier'] === '*') { $expanded = implode($joiner, $kvp); if ($isAssoc) { $actuallyUseQuery = false; } } else { if ($isAssoc) { foreach ($kvp as $k => &$v) { $v = $k . ',' . $v; } } $expanded = implode(',', $kvp); } } else { if ($value['modifier'] === ':') { $variable = substr($variable, 0, $value['position']); } $expanded = rawurlencode($variable); if ($parsed['operator'] === '+' || $parsed['operator'] === '#') { $expanded = $this->decodeReserved($expanded); } } if ($actuallyUseQuery) { if (!$expanded && $joiner !== '&') { $expanded = $value['value']; } else { $expanded = $value['value'] . '=' . $expanded; } } $replacements[] = $expanded; } $ret = implode($joiner, $replacements); if ($ret && $prefix) { return $prefix . $ret; } return $ret; } private function isAssoc(array $array) { return $array && array_keys($array)[0] !== 0; } private function decodeReserved($string) { return str_replace(self::$delimsPct, self::$delims, $string); } } getHost()) { $asciiHost = self::idnToAsci($uri->getHost(), $options, $info); if ($asciiHost === false) { $errorBitSet = isset($info['errors']) ? $info['errors'] : 0; $errorConstants = array_filter(array_keys(get_defined_constants()), function ($name) { return substr($name, 0, 11) === 'IDNA_ERROR_'; }); $errors = []; foreach ($errorConstants as $errorConstant) { if ($errorBitSet & constant($errorConstant)) { $errors[] = $errorConstant; } } $errorMessage = 'IDN conversion failed'; if ($errors) { $errorMessage .= ' (errors: ' . implode(', ', $errors) . ')'; } throw new InvalidArgumentException($errorMessage); } else { if ($uri->getHost() !== $asciiHost) { $uri = $uri->withHost($asciiHost); } } } return $uri; } private static function idnToAsci($domain, $options, &$info = []) { if (\preg_match('%^[ -~]+$%', $domain) === 1) { return $domain; } if (\extension_loaded('intl') && defined('INTL_IDNA_VARIANT_UTS46')) { return \idn_to_ascii($domain, $options, INTL_IDNA_VARIANT_UTS46, $info); } if (method_exists(Idn::class, 'idn_to_ascii')) { return Idn::idn_to_ascii($domain, $options, Idn::INTL_IDNA_VARIANT_UTS46, $info); } throw new \RuntimeException('ext-intl or symfony/polyfill-intl-idn not loaded or too old'); } } push(Middleware::httpErrors(), 'http_errors'); $stack->push(Middleware::redirect(), 'allow_redirects'); $stack->push(Middleware::cookies(), 'cookies'); $stack->push(Middleware::prepareBody(), 'prepare_body'); return $stack; } public function __construct(callable $handler = null) { $this->handler = $handler; } public function __invoke(RequestInterface $request, array $options) { $handler = $this->resolve(); return $handler($request, $options); } public function __toString() { $depth = 0; $stack = []; if ($this->handler) { $stack[] = "0) Handler: " . $this->debugCallable($this->handler); } $result = ''; foreach (array_reverse($this->stack) as $tuple) { $depth++; $str = "{$depth}) Name: '{$tuple[1]}', "; $str .= "Function: " . $this->debugCallable($tuple[0]); $result = "> {$str}\n{$result}"; $stack[] = $str; } foreach (array_keys($stack) as $k) { $result .= "< {$stack[$k]}\n"; } return $result; } public function setHandler(callable $handler) { $this->handler = $handler; $this->cached = null; } public function hasHandler() { return (bool) $this->handler; } public function unshift(callable $middleware, $name = null) { array_unshift($this->stack, [$middleware, $name]); $this->cached = null; } public function push(callable $middleware, $name = '') { $this->stack[] = [$middleware, $name]; $this->cached = null; } public function before($findName, callable $middleware, $withName = '') { $this->splice($findName, $withName, $middleware, true); } public function after($findName, callable $middleware, $withName = '') { $this->splice($findName, $withName, $middleware, false); } public function remove($remove) { $this->cached = null; $idx = is_callable($remove) ? 0 : 1; $this->stack = array_values(array_filter( $this->stack, function ($tuple) use ($idx, $remove) { return $tuple[$idx] !== $remove; } )); } public function resolve() { if (!$this->cached) { if (!($prev = $this->handler)) { throw new \LogicException('No handler has been specified'); } foreach (array_reverse($this->stack) as $fn) { $prev = $fn[0]($prev); } $this->cached = $prev; } return $this->cached; } private function findByName($name) { foreach ($this->stack as $k => $v) { if ($v[1] === $name) { return $k; } } throw new \InvalidArgumentException("Middleware not found: $name"); } private function splice($findName, $withName, callable $middleware, $before) { $this->cached = null; $idx = $this->findByName($findName); $tuple = [$middleware, $withName]; if ($before) { if ($idx === 0) { array_unshift($this->stack, $tuple); } else { $replacement = [$tuple, $this->stack[$idx]]; array_splice($this->stack, $idx, 1, $replacement); } } elseif ($idx === count($this->stack) - 1) { $this->stack[] = $tuple; } else { $replacement = [$this->stack[$idx], $tuple]; array_splice($this->stack, $idx, 1, $replacement); } } private function debugCallable($fn) { if (is_string($fn)) { return "callable({$fn})"; } if (is_array($fn)) { return is_string($fn[0]) ? "callable({$fn[0]}::{$fn[1]})" : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])"; } return 'callable(' . spl_object_hash($fn) . ')'; } } null, 'Value' => null, 'Domain' => null, 'Path' => '/', 'Max-Age' => null, 'Expires' => null, 'Secure' => false, 'Discard' => false, 'HttpOnly' => false ]; private $data; public static function fromString($cookie) { $data = self::$defaults; $pieces = array_filter(array_map('trim', explode(';', $cookie))); if (empty($pieces[0]) || !strpos($pieces[0], '=')) { return new self($data); } foreach ($pieces as $part) { $cookieParts = explode('=', $part, 2); $key = trim($cookieParts[0]); $value = isset($cookieParts[1]) ? trim($cookieParts[1], " \n\r\t\0\x0B") : true; if (empty($data['Name'])) { $data['Name'] = $key; $data['Value'] = $value; } else { foreach (array_keys(self::$defaults) as $search) { if (!strcasecmp($search, $key)) { $data[$search] = $value; continue 2; } } $data[$key] = $value; } } return new self($data); } public function __construct(array $data = []) { $this->data = array_replace(self::$defaults, $data); if (!$this->getExpires() && $this->getMaxAge()) { $this->setExpires(time() + $this->getMaxAge()); } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { $this->setExpires($this->getExpires()); } } public function __toString() { $str = $this->data['Name'] . '=' . $this->data['Value'] . '; '; foreach ($this->data as $k => $v) { if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) { if ($k === 'Expires') { $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; '; } else { $str .= ($v === true ? $k : "{$k}={$v}") . '; '; } } } return rtrim($str, '; '); } public function toArray() { return $this->data; } public function getName() { return $this->data['Name']; } public function setName($name) { $this->data['Name'] = $name; } public function getValue() { return $this->data['Value']; } public function setValue($value) { $this->data['Value'] = $value; } public function getDomain() { return $this->data['Domain']; } public function setDomain($domain) { $this->data['Domain'] = $domain; } public function getPath() { return $this->data['Path']; } public function setPath($path) { $this->data['Path'] = $path; } public function getMaxAge() { return $this->data['Max-Age']; } public function setMaxAge($maxAge) { $this->data['Max-Age'] = $maxAge; } public function getExpires() { return $this->data['Expires']; } public function setExpires($timestamp) { $this->data['Expires'] = is_numeric($timestamp) ? (int) $timestamp : strtotime($timestamp); } public function getSecure() { return $this->data['Secure']; } public function setSecure($secure) { $this->data['Secure'] = $secure; } public function getDiscard() { return $this->data['Discard']; } public function setDiscard($discard) { $this->data['Discard'] = $discard; } public function getHttpOnly() { return $this->data['HttpOnly']; } public function setHttpOnly($httpOnly) { $this->data['HttpOnly'] = $httpOnly; } public function matchesPath($requestPath) { $cookiePath = $this->getPath(); if ($cookiePath === '/' || $cookiePath == $requestPath) { return true; } if (0 !== strpos($requestPath, $cookiePath)) { return false; } if (substr($cookiePath, -1, 1) === '/') { return true; } return substr($requestPath, strlen($cookiePath), 1) === '/'; } public function matchesDomain($domain) { $cookieDomain = $this->getDomain(); if (null === $cookieDomain) { return true; } $cookieDomain = ltrim(strtolower($cookieDomain), '.'); $domain = strtolower($domain); if ('' === $cookieDomain || $domain === $cookieDomain) { return true; } if (filter_var($domain, FILTER_VALIDATE_IP)) { return false; } return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain); } public function isExpired() { return $this->getExpires() !== null && time() > $this->getExpires(); } public function validate() { $name = $this->getName(); if (empty($name) && !is_numeric($name)) { return 'The cookie name must not be empty'; } if (preg_match( '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', $name )) { return 'Cookie name must not contain invalid characters: ASCII ' . 'Control characters (0-31;127), space, tab and the ' . 'following characters: ()<>@,;:\"/?={}'; } $value = $this->getValue(); if (empty($value) && !is_numeric($value)) { return 'The cookie value must not be empty'; } $domain = $this->getDomain(); if (empty($domain) && !is_numeric($domain)) { return 'The cookie domain must not be empty'; } return true; } } strictMode = $strictMode; foreach ($cookieArray as $cookie) { if (!($cookie instanceof SetCookie)) { $cookie = new SetCookie($cookie); } $this->setCookie($cookie); } } public static function fromArray(array $cookies, $domain) { $cookieJar = new self(); foreach ($cookies as $name => $value) { $cookieJar->setCookie(new SetCookie([ 'Domain' => $domain, 'Name' => $name, 'Value' => $value, 'Discard' => true ])); } return $cookieJar; } public static function getCookieValue($value) { return $value; } public static function shouldPersist( SetCookie $cookie, $allowSessionCookies = false ) { if ($cookie->getExpires() || $allowSessionCookies) { if (!$cookie->getDiscard()) { return true; } } return false; } public function getCookieByName($name) { if ($name === null || !is_scalar($name)) { return null; } foreach ($this->cookies as $cookie) { if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) { return $cookie; } } return null; } public function toArray() { return array_map(function (SetCookie $cookie) { return $cookie->toArray(); }, $this->getIterator()->getArrayCopy()); } public function clear($domain = null, $path = null, $name = null) { if (!$domain) { $this->cookies = []; return; } elseif (!$path) { $this->cookies = array_filter( $this->cookies, function (SetCookie $cookie) use ($domain) { return !$cookie->matchesDomain($domain); } ); } elseif (!$name) { $this->cookies = array_filter( $this->cookies, function (SetCookie $cookie) use ($path, $domain) { return !($cookie->matchesPath($path) && $cookie->matchesDomain($domain)); } ); } else { $this->cookies = array_filter( $this->cookies, function (SetCookie $cookie) use ($path, $domain, $name) { return !($cookie->getName() == $name && $cookie->matchesPath($path) && $cookie->matchesDomain($domain)); } ); } } public function clearSessionCookies() { $this->cookies = array_filter( $this->cookies, function (SetCookie $cookie) { return !$cookie->getDiscard() && $cookie->getExpires(); } ); } public function setCookie(SetCookie $cookie) { $name = $cookie->getName(); if (!$name && $name !== '0') { return false; } $result = $cookie->validate(); if ($result !== true) { if ($this->strictMode) { throw new \RuntimeException('Invalid cookie: ' . $result); } else { $this->removeCookieIfEmpty($cookie); return false; } } foreach ($this->cookies as $i => $c) { if ($c->getPath() != $cookie->getPath() || $c->getDomain() != $cookie->getDomain() || $c->getName() != $cookie->getName() ) { continue; } if (!$cookie->getDiscard() && $c->getDiscard()) { unset($this->cookies[$i]); continue; } if ($cookie->getExpires() > $c->getExpires()) { unset($this->cookies[$i]); continue; } if ($cookie->getValue() !== $c->getValue()) { unset($this->cookies[$i]); continue; } return false; } $this->cookies[] = $cookie; return true; } public function count() { return count($this->cookies); } public function getIterator() { return new \ArrayIterator(array_values($this->cookies)); } public function extractCookies( RequestInterface $request, ResponseInterface $response ) { if ($cookieHeader = $response->getHeader('Set-Cookie')) { foreach ($cookieHeader as $cookie) { $sc = SetCookie::fromString($cookie); if (!$sc->getDomain()) { $sc->setDomain($request->getUri()->getHost()); } if (0 !== strpos($sc->getPath(), '/')) { $sc->setPath($this->getCookiePathFromRequest($request)); } if (!$sc->matchesDomain($request->getUri()->getHost())) { continue; } $this->setCookie($sc); } } } private function getCookiePathFromRequest(RequestInterface $request) { $uriPath = $request->getUri()->getPath(); if ('' === $uriPath) { return '/'; } if (0 !== strpos($uriPath, '/')) { return '/'; } if ('/' === $uriPath) { return '/'; } if (0 === $lastSlashPos = strrpos($uriPath, '/')) { return '/'; } return substr($uriPath, 0, $lastSlashPos); } public function withCookieHeader(RequestInterface $request) { $values = []; $uri = $request->getUri(); $scheme = $uri->getScheme(); $host = $uri->getHost(); $path = $uri->getPath() ?: '/'; foreach ($this->cookies as $cookie) { if ($cookie->matchesPath($path) && $cookie->matchesDomain($host) && !$cookie->isExpired() && (!$cookie->getSecure() || $scheme === 'https') ) { $values[] = $cookie->getName() . '=' . $cookie->getValue(); } } return $values ? $request->withHeader('Cookie', implode('; ', $values)) : $request; } private function removeCookieIfEmpty(SetCookie $cookie) { $cookieValue = $cookie->getValue(); if ($cookieValue === null || $cookieValue === '') { $this->clear( $cookie->getDomain(), $cookie->getPath(), $cookie->getName() ); } } } sessionKey = $sessionKey; $this->storeSessionCookies = $storeSessionCookies; $this->load(); } public function __destruct() { $this->save(); } public function save() { $json = []; foreach ($this as $cookie) { if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } $_SESSION[$this->sessionKey] = json_encode($json); } protected function load() { if (!isset($_SESSION[$this->sessionKey])) { return; } $data = json_decode($_SESSION[$this->sessionKey], true); if (is_array($data)) { foreach ($data as $cookie) { $this->setCookie(new SetCookie($cookie)); } } elseif (strlen($data)) { throw new \RuntimeException("Invalid cookie data"); } } } filename = $cookieFile; $this->storeSessionCookies = $storeSessionCookies; if (file_exists($cookieFile)) { $this->load($cookieFile); } } public function __destruct() { $this->save($this->filename); } public function save($filename) { $json = []; foreach ($this as $cookie) { if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } $jsonStr = \GuzzleHttp\json_encode($json); if (false === file_put_contents($filename, $jsonStr, LOCK_EX)) { throw new \RuntimeException("Unable to save file {$filename}"); } } public function load($filename) { $json = file_get_contents($filename); if (false === $json) { throw new \RuntimeException("Unable to load file {$filename}"); } elseif ($json === '') { return; } $data = \GuzzleHttp\json_decode($json, true); if (is_array($data)) { foreach (json_decode($json, true) as $cookie) { $this->setCookie(new SetCookie($cookie)); } } elseif (strlen($data)) { throw new \RuntimeException("Invalid cookie file: {$filename}"); } } } withCookieHeader($request); return $handler($request, $options) ->then( function ($response) use ($cookieJar, $request) { $cookieJar->extractCookies($request, $response); return $response; } ); }; }; } public static function httpErrors() { return function (callable $handler) { return function ($request, array $options) use ($handler) { if (empty($options['http_errors'])) { return $handler($request, $options); } return $handler($request, $options)->then( function (ResponseInterface $response) use ($request) { $code = $response->getStatusCode(); if ($code < 400) { return $response; } throw RequestException::create($request, $response); } ); }; }; } public static function history(&$container) { if (!is_array($container) && !$container instanceof \ArrayAccess) { throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess'); } return function (callable $handler) use (&$container) { return function ($request, array $options) use ($handler, &$container) { return $handler($request, $options)->then( function ($value) use ($request, &$container, $options) { $container[] = [ 'request' => $request, 'response' => $value, 'error' => null, 'options' => $options ]; return $value; }, function ($reason) use ($request, &$container, $options) { $container[] = [ 'request' => $request, 'response' => null, 'error' => $reason, 'options' => $options ]; return \GuzzleHttp\Promise\rejection_for($reason); } ); }; }; } public static function tap(callable $before = null, callable $after = null) { return function (callable $handler) use ($before, $after) { return function ($request, array $options) use ($handler, $before, $after) { if ($before) { $before($request, $options); } $response = $handler($request, $options); if ($after) { $after($request, $options, $response); } return $response; }; }; } public static function redirect() { return function (callable $handler) { return new RedirectMiddleware($handler); }; } public static function retry(callable $decider, callable $delay = null) { return function (callable $handler) use ($decider, $delay) { return new RetryMiddleware($decider, $handler, $delay); }; } public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = 'info' ) { return function (callable $handler) use ($logger, $formatter, $logLevel) { return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) { return $handler($request, $options)->then( function ($response) use ($logger, $request, $formatter, $logLevel) { $message = $formatter->format($request, $response); $logger->log($logLevel, $message); return $response; }, function ($reason) use ($logger, $request, $formatter) { $response = $reason instanceof RequestException ? $reason->getResponse() : null; $message = $formatter->format($request, $response, $reason); $logger->notice($message); return \GuzzleHttp\Promise\rejection_for($reason); } ); }; }; } public static function prepareBody() { return function (callable $handler) { return new PrepareBodyMiddleware($handler); }; } public static function mapRequest(callable $fn) { return function (callable $handler) use ($fn) { return function ($request, array $options) use ($handler, $fn) { return $handler($fn($request), $options); }; }; } public static function mapResponse(callable $fn) { return function (callable $handler) use ($fn) { return function ($request, array $options) use ($handler, $fn) { return $handler($request, $options)->then($fn); }; }; } } $rfn) { if ($rfn instanceof RequestInterface) { yield $key => $client->sendAsync($rfn, $opts); } elseif (is_callable($rfn)) { yield $key => $rfn($opts); } else { throw new \InvalidArgumentException('Each value yielded by ' . 'the iterator must be a Psr7\Http\Message\RequestInterface ' . 'or a callable that returns a promise that fulfills ' . 'with a Psr7\Message\Http\ResponseInterface object.'); } } }; $this->each = new EachPromise($requests(), $config); } public function promise() { return $this->each->promise(); } public static function batch( ClientInterface $client, $requests, array $options = [] ) { $res = []; self::cmpCallback($options, 'fulfilled', $res); self::cmpCallback($options, 'rejected', $res); $pool = new static($client, $requests, $options); $pool->promise()->wait(); ksort($res); return $res; } private static function cmpCallback(array &$options, $name, array &$results) { if (!isset($options[$name])) { $options[$name] = function ($v, $k) use (&$results) { $results[$k] = $v; }; } else { $currentFn = $options[$name]; $options[$name] = function ($v, $k) use (&$results, $currentFn) { $currentFn($v, $k); $results[$k] = $v; }; } } } decider = $decider; $this->nextHandler = $nextHandler; $this->delay = $delay ?: __CLASS__ . '::exponentialDelay'; } public static function exponentialDelay($retries) { return (int) pow(2, $retries - 1) * 1000; } public function __invoke(RequestInterface $request, array $options) { if (!isset($options['retries'])) { $options['retries'] = 0; } $fn = $this->nextHandler; return $fn($request, $options) ->then( $this->onFulfilled($request, $options), $this->onRejected($request, $options) ); } private function onFulfilled(RequestInterface $req, array $options) { return function ($value) use ($req, $options) { if (!call_user_func( $this->decider, $options['retries'], $req, $value, null )) { return $value; } return $this->doRetry($req, $options, $value); }; } private function onRejected(RequestInterface $req, array $options) { return function ($reason) use ($req, $options) { if (!call_user_func( $this->decider, $options['retries'], $req, null, $reason )) { return \GuzzleHttp\Promise\rejection_for($reason); } return $this->doRetry($req, $options); }; } private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) { $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response); return $this($request, $options); } } request = $request; $this->response = $response; $this->transferTime = $transferTime; $this->handlerErrorData = $handlerErrorData; $this->handlerStats = $handlerStats; } public function getRequest() { return $this->request; } public function getResponse() { return $this->response; } public function hasResponse() { return $this->response !== null; } public function getHandlerErrorData() { return $this->handlerErrorData; } public function getEffectiveUri() { return $this->request->getUri(); } public function getTransferTime() { return $this->transferTime; } public function getHandlerStats() { return $this->handlerStats; } public function getHandlerStat($stat) { return isset($this->handlerStats[$stat]) ? $this->handlerStats[$stat] : null; } } headers)) { throw new \RuntimeException('No headers have been received'); } $startLine = explode(' ', array_shift($this->headers), 3); $headers = \GuzzleHttp\headers_from_lines($this->headers); $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); if (!empty($this->options['decode_content']) && isset($normalizedKeys['content-encoding']) ) { $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; unset($headers[$normalizedKeys['content-encoding']]); if (isset($normalizedKeys['content-length'])) { $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']]; $bodyLength = (int) $this->sink->getSize(); if ($bodyLength) { $headers[$normalizedKeys['content-length']] = $bodyLength; } else { unset($headers[$normalizedKeys['content-length']]); } } } $this->response = new Response( $startLine[1], $headers, $this->sink, substr($startLine[0], 5), isset($startLine[2]) ? (string) $startLine[2] : null ); } public function __get($name) { $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: ' . $name; throw new \BadMethodCallException($msg); } } factory = isset($options['handle_factory']) ? $options['handle_factory'] : new CurlFactory(3); } public function __invoke(RequestInterface $request, array $options) { if (isset($options['delay'])) { usleep($options['delay'] * 1000); } $easy = $this->factory->create($request, $options); curl_exec($easy->handle); $easy->errno = curl_errno($easy->handle); return CurlFactory::finish($this, $easy, $this->factory); } } maxHandles = $maxHandles; } public function create(RequestInterface $request, array $options) { if (isset($options['curl']['body_as_string'])) { $options['_body_as_string'] = $options['curl']['body_as_string']; unset($options['curl']['body_as_string']); } $easy = new EasyHandle; $easy->request = $request; $easy->options = $options; $conf = $this->getDefaultConf($easy); $this->applyMethod($easy, $conf); $this->applyHandlerOptions($easy, $conf); $this->applyHeaders($easy, $conf); unset($conf['_headers']); if (isset($options['curl'])) { $conf = array_replace($conf, $options['curl']); } $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); $easy->handle = $this->handles ? array_pop($this->handles) : curl_init(); curl_setopt_array($easy->handle, $conf); return $easy; } public function release(EasyHandle $easy) { $resource = $easy->handle; unset($easy->handle); if (count($this->handles) >= $this->maxHandles) { curl_close($resource); } else { curl_setopt($resource, CURLOPT_HEADERFUNCTION, null); curl_setopt($resource, CURLOPT_READFUNCTION, null); curl_setopt($resource, CURLOPT_WRITEFUNCTION, null); curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null); curl_reset($resource); $this->handles[] = $resource; } } public static function finish( callable $handler, EasyHandle $easy, CurlFactoryInterface $factory ) { if (isset($easy->options['on_stats'])) { self::invokeStats($easy); } if (!$easy->response || $easy->errno) { return self::finishError($handler, $easy, $factory); } $factory->release($easy); $body = $easy->response->getBody(); if ($body->isSeekable()) { $body->rewind(); } return new FulfilledPromise($easy->response); } private static function invokeStats(EasyHandle $easy) { $curlStats = curl_getinfo($easy->handle); $curlStats['appconnect_time'] = curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME); $stats = new TransferStats( $easy->request, $easy->response, $curlStats['total_time'], $easy->errno, $curlStats ); call_user_func($easy->options['on_stats'], $stats); } private static function finishError( callable $handler, EasyHandle $easy, CurlFactoryInterface $factory ) { $ctx = [ 'errno' => $easy->errno, 'error' => curl_error($easy->handle), 'appconnect_time' => curl_getinfo($easy->handle, CURLINFO_APPCONNECT_TIME), ] + curl_getinfo($easy->handle); $ctx[self::CURL_VERSION_STR] = curl_version()['version']; $factory->release($easy); if (empty($easy->options['_err_message']) && (!$easy->errno || $easy->errno == 65) ) { return self::retryFailedRewind($handler, $easy, $ctx); } return self::createRejection($easy, $ctx); } private static function createRejection(EasyHandle $easy, array $ctx) { static $connectionErrors = [ CURLE_OPERATION_TIMEOUTED => true, CURLE_COULDNT_RESOLVE_HOST => true, CURLE_COULDNT_CONNECT => true, CURLE_SSL_CONNECT_ERROR => true, CURLE_GOT_NOTHING => true, ]; if ($easy->onHeadersException) { return \GuzzleHttp\Promise\rejection_for( new RequestException( 'An error was encountered during the on_headers event', $easy->request, $easy->response, $easy->onHeadersException, $ctx ) ); } if (version_compare($ctx[self::CURL_VERSION_STR], self::LOW_CURL_VERSION_NUMBER)) { $message = sprintf( 'cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html' ); } else { $message = sprintf( 'cURL error %s: %s (%s) for %s', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html', $easy->request->getUri() ); } $error = isset($connectionErrors[$easy->errno]) ? new ConnectException($message, $easy->request, null, $ctx) : new RequestException($message, $easy->request, $easy->response, null, $ctx); return \GuzzleHttp\Promise\rejection_for($error); } private function getDefaultConf(EasyHandle $easy) { $conf = [ '_headers' => $easy->request->getHeaders(), CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), CURLOPT_RETURNTRANSFER => false, CURLOPT_HEADER => false, CURLOPT_CONNECTTIMEOUT => 150, ]; if (defined('CURLOPT_PROTOCOLS')) { $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; } $version = $easy->request->getProtocolVersion(); if ($version == 1.1) { $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; } elseif ($version == 2.0) { $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; } else { $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; } return $conf; } private function applyMethod(EasyHandle $easy, array &$conf) { $body = $easy->request->getBody(); $size = $body->getSize(); if ($size === null || $size > 0) { $this->applyBody($easy->request, $easy->options, $conf); return; } $method = $easy->request->getMethod(); if ($method === 'PUT' || $method === 'POST') { if (!$easy->request->hasHeader('Content-Length')) { $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; } } elseif ($method === 'HEAD') { $conf[CURLOPT_NOBODY] = true; unset( $conf[CURLOPT_WRITEFUNCTION], $conf[CURLOPT_READFUNCTION], $conf[CURLOPT_FILE], $conf[CURLOPT_INFILE] ); } } private function applyBody(RequestInterface $request, array $options, array &$conf) { $size = $request->hasHeader('Content-Length') ? (int) $request->getHeaderLine('Content-Length') : null; if (($size !== null && $size < 1000000) || !empty($options['_body_as_string']) ) { $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody(); $this->removeHeader('Content-Length', $conf); $this->removeHeader('Transfer-Encoding', $conf); } else { $conf[CURLOPT_UPLOAD] = true; if ($size !== null) { $conf[CURLOPT_INFILESIZE] = $size; $this->removeHeader('Content-Length', $conf); } $body = $request->getBody(); if ($body->isSeekable()) { $body->rewind(); } $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) { return $body->read($length); }; } if (!$request->hasHeader('Expect')) { $conf[CURLOPT_HTTPHEADER][] = 'Expect:'; } if (!$request->hasHeader('Content-Type')) { $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:'; } } private function applyHeaders(EasyHandle $easy, array &$conf) { foreach ($conf['_headers'] as $name => $values) { foreach ($values as $value) { $value = (string) $value; if ($value === '') { $conf[CURLOPT_HTTPHEADER][] = "$name;"; } else { $conf[CURLOPT_HTTPHEADER][] = "$name: $value"; } } } if (!$easy->request->hasHeader('Accept')) { $conf[CURLOPT_HTTPHEADER][] = 'Accept:'; } } private function removeHeader($name, array &$options) { foreach (array_keys($options['_headers']) as $key) { if (!strcasecmp($key, $name)) { unset($options['_headers'][$key]); return; } } } private function applyHandlerOptions(EasyHandle $easy, array &$conf) { $options = $easy->options; if (isset($options['verify'])) { if ($options['verify'] === false) { unset($conf[CURLOPT_CAINFO]); $conf[CURLOPT_SSL_VERIFYHOST] = 0; $conf[CURLOPT_SSL_VERIFYPEER] = false; } else { $conf[CURLOPT_SSL_VERIFYHOST] = 2; $conf[CURLOPT_SSL_VERIFYPEER] = true; if (is_string($options['verify'])) { if (!file_exists($options['verify'])) { throw new \InvalidArgumentException( "SSL CA bundle not found: {$options['verify']}" ); } if (is_dir($options['verify']) || (is_link($options['verify']) && is_dir(readlink($options['verify'])))) { $conf[CURLOPT_CAPATH] = $options['verify']; } else { $conf[CURLOPT_CAINFO] = $options['verify']; } } } } if (!empty($options['decode_content'])) { $accept = $easy->request->getHeaderLine('Accept-Encoding'); if ($accept) { $conf[CURLOPT_ENCODING] = $accept; } else { $conf[CURLOPT_ENCODING] = ''; $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; } } if (isset($options['sink'])) { $sink = $options['sink']; if (!is_string($sink)) { $sink = \GuzzleHttp\Psr7\stream_for($sink); } elseif (!is_dir(dirname($sink))) { throw new \RuntimeException(sprintf( 'Directory %s does not exist for sink value of %s', dirname($sink), $sink )); } else { $sink = new LazyOpenStream($sink, 'w+'); } $easy->sink = $sink; $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) { return $sink->write($write); }; } else { $conf[CURLOPT_FILE] = fopen('php://temp', 'w+'); $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]); } $timeoutRequiresNoSignal = false; if (isset($options['timeout'])) { $timeoutRequiresNoSignal |= $options['timeout'] < 1; $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; } if (isset($options['force_ip_resolve'])) { if ('v4' === $options['force_ip_resolve']) { $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4; } elseif ('v6' === $options['force_ip_resolve']) { $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6; } } if (isset($options['connect_timeout'])) { $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; } if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') { $conf[CURLOPT_NOSIGNAL] = true; } if (isset($options['proxy'])) { if (!is_array($options['proxy'])) { $conf[CURLOPT_PROXY] = $options['proxy']; } else { $scheme = $easy->request->getUri()->getScheme(); if (isset($options['proxy'][$scheme])) { $host = $easy->request->getUri()->getHost(); if (!isset($options['proxy']['no']) || !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no']) ) { $conf[CURLOPT_PROXY] = $options['proxy'][$scheme]; } } } } if (isset($options['cert'])) { $cert = $options['cert']; if (is_array($cert)) { $conf[CURLOPT_SSLCERTPASSWD] = $cert[1]; $cert = $cert[0]; } if (!file_exists($cert)) { throw new \InvalidArgumentException( "SSL certificate not found: {$cert}" ); } $conf[CURLOPT_SSLCERT] = $cert; } if (isset($options['ssl_key'])) { if (is_array($options['ssl_key'])) { if (count($options['ssl_key']) === 2) { list($sslKey, $conf[CURLOPT_SSLKEYPASSWD]) = $options['ssl_key']; } else { list($sslKey) = $options['ssl_key']; } } $sslKey = isset($sslKey) ? $sslKey: $options['ssl_key']; if (!file_exists($sslKey)) { throw new \InvalidArgumentException( "SSL private key not found: {$sslKey}" ); } $conf[CURLOPT_SSLKEY] = $sslKey; } if (isset($options['progress'])) { $progress = $options['progress']; if (!is_callable($progress)) { throw new \InvalidArgumentException( 'progress client option must be callable' ); } $conf[CURLOPT_NOPROGRESS] = false; $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) { $args = func_get_args(); if (is_resource($args[0])) { array_shift($args); } call_user_func_array($progress, $args); }; } if (!empty($options['debug'])) { $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']); $conf[CURLOPT_VERBOSE] = true; } } private static function retryFailedRewind( callable $handler, EasyHandle $easy, array $ctx ) { try { $body = $easy->request->getBody(); if ($body->tell() > 0) { $body->rewind(); } } catch (\RuntimeException $e) { $ctx['error'] = 'The connection unexpectedly failed without ' . 'providing an error. The request would have been retried, ' . 'but attempting to rewind the request body failed. ' . 'Exception: ' . $e; return self::createRejection($easy, $ctx); } if (!isset($easy->options['_curl_retries'])) { $easy->options['_curl_retries'] = 1; } elseif ($easy->options['_curl_retries'] == 2) { $ctx['error'] = 'The cURL request was retried 3 times ' . 'and did not succeed. The most likely reason for the failure ' . 'is that cURL was unable to rewind the body of the request ' . 'and subsequent retries resulted in the same error. Turn on ' . 'the debug option to see what went wrong. See ' . 'https://bugs.php.net/bug.php?id=47204 for more information.'; return self::createRejection($easy, $ctx); } else { $easy->options['_curl_retries']++; } return $handler($easy->request, $easy->options); } private function createHeaderFn(EasyHandle $easy) { if (isset($easy->options['on_headers'])) { $onHeaders = $easy->options['on_headers']; if (!is_callable($onHeaders)) { throw new \InvalidArgumentException('on_headers must be callable'); } } else { $onHeaders = null; } return function ($ch, $h) use ( $onHeaders, $easy, &$startingResponse ) { $value = trim($h); if ($value === '') { $startingResponse = true; $easy->createResponse(); if ($onHeaders !== null) { try { $onHeaders($easy->response); } catch (\Exception $e) { $easy->onHeadersException = $e; return -1; } } } elseif ($startingResponse) { $startingResponse = false; $easy->headers = [$value]; } else { $easy->headers[] = $value; } return strlen($h); }; } } withoutHeader('Expect'); if (0 === $request->getBody()->getSize()) { $request = $request->withHeader('Content-Length', '0'); } return $this->createResponse( $request, $options, $this->createStream($request, $options), $startTime ); } catch (\InvalidArgumentException $e) { throw $e; } catch (\Exception $e) { $message = $e->getMessage(); if (strpos($message, 'getaddrinfo') || strpos($message, 'Connection refused') || strpos($message, "couldn't connect to host") || strpos($message, "connection attempt failed") ) { $e = new ConnectException($e->getMessage(), $request, $e); } $e = RequestException::wrapException($request, $e); $this->invokeStats($options, $request, $startTime, null, $e); return \GuzzleHttp\Promise\rejection_for($e); } } private function invokeStats( array $options, RequestInterface $request, $startTime, ResponseInterface $response = null, $error = null ) { if (isset($options['on_stats'])) { $stats = new TransferStats( $request, $response, Utils::currentTime() - $startTime, $error, [] ); call_user_func($options['on_stats'], $stats); } } private function createResponse( RequestInterface $request, array $options, $stream, $startTime ) { $hdrs = $this->lastHeaders; $this->lastHeaders = []; $parts = explode(' ', array_shift($hdrs), 3); $ver = explode('/', $parts[0])[1]; $status = $parts[1]; $reason = isset($parts[2]) ? $parts[2] : null; $headers = \GuzzleHttp\headers_from_lines($hdrs); list($stream, $headers) = $this->checkDecode($options, $headers, $stream); $stream = Psr7\stream_for($stream); $sink = $stream; if (strcasecmp('HEAD', $request->getMethod())) { $sink = $this->createSink($stream, $options); } $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); if (isset($options['on_headers'])) { try { $options['on_headers']($response); } catch (\Exception $e) { $msg = 'An error was encountered during the on_headers event'; $ex = new RequestException($msg, $request, $response, $e); return \GuzzleHttp\Promise\rejection_for($ex); } } if ($sink !== $stream) { $this->drain( $stream, $sink, $response->getHeaderLine('Content-Length') ); } $this->invokeStats($options, $request, $startTime, $response, null); return new FulfilledPromise($response); } private function createSink(StreamInterface $stream, array $options) { if (!empty($options['stream'])) { return $stream; } $sink = isset($options['sink']) ? $options['sink'] : fopen('php://temp', 'r+'); return is_string($sink) ? new Psr7\LazyOpenStream($sink, 'w+') : Psr7\stream_for($sink); } private function checkDecode(array $options, array $headers, $stream) { if (!empty($options['decode_content'])) { $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers); if (isset($normalizedKeys['content-encoding'])) { $encoding = $headers[$normalizedKeys['content-encoding']]; if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') { $stream = new Psr7\InflateStream( Psr7\stream_for($stream) ); $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; unset($headers[$normalizedKeys['content-encoding']]); if (isset($normalizedKeys['content-length'])) { $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']]; $length = (int) $stream->getSize(); if ($length === 0) { unset($headers[$normalizedKeys['content-length']]); } else { $headers[$normalizedKeys['content-length']] = [$length]; } } } } } return [$stream, $headers]; } private function drain( StreamInterface $source, StreamInterface $sink, $contentLength ) { Psr7\copy_to_stream( $source, $sink, (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1 ); $sink->seek(0); $source->close(); return $sink; } private function createResource(callable $callback) { $errors = null; set_error_handler(function ($_, $msg, $file, $line) use (&$errors) { $errors[] = [ 'message' => $msg, 'file' => $file, 'line' => $line ]; return true; }); $resource = $callback(); restore_error_handler(); if (!$resource) { $message = 'Error creating resource: '; foreach ($errors as $err) { foreach ($err as $key => $value) { $message .= "[$key] $value" . PHP_EOL; } } throw new \RuntimeException(trim($message)); } return $resource; } private function createStream(RequestInterface $request, array $options) { static $methods; if (!$methods) { $methods = array_flip(get_class_methods(__CLASS__)); } if ($request->getProtocolVersion() == '1.1' && !$request->hasHeader('Connection') ) { $request = $request->withHeader('Connection', 'close'); } if (!isset($options['verify'])) { $options['verify'] = true; } $params = []; $context = $this->getDefaultContext($request); if (isset($options['on_headers']) && !is_callable($options['on_headers'])) { throw new \InvalidArgumentException('on_headers must be callable'); } if (!empty($options)) { foreach ($options as $key => $value) { $method = "add_{$key}"; if (isset($methods[$method])) { $this->{$method}($request, $context, $value, $params); } } } if (isset($options['stream_context'])) { if (!is_array($options['stream_context'])) { throw new \InvalidArgumentException('stream_context must be an array'); } $context = array_replace_recursive( $context, $options['stream_context'] ); } if (isset($options['auth']) && is_array($options['auth']) && isset($options['auth'][2]) && 'ntlm' == $options['auth'][2] ) { throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler'); } $uri = $this->resolveHost($request, $options); $context = $this->createResource( function () use ($context, $params) { return stream_context_create($context, $params); } ); return $this->createResource( function () use ($uri, &$http_response_header, $context, $options) { $resource = fopen((string) $uri, 'r', null, $context); $this->lastHeaders = $http_response_header; if (isset($options['read_timeout'])) { $readTimeout = $options['read_timeout']; $sec = (int) $readTimeout; $usec = ($readTimeout - $sec) * 100000; stream_set_timeout($resource, $sec, $usec); } return $resource; } ); } private function resolveHost(RequestInterface $request, array $options) { $uri = $request->getUri(); if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) { if ('v4' === $options['force_ip_resolve']) { $records = dns_get_record($uri->getHost(), DNS_A); if (!isset($records[0]['ip'])) { throw new ConnectException( sprintf( "Could not resolve IPv4 address for host '%s'", $uri->getHost() ), $request ); } $uri = $uri->withHost($records[0]['ip']); } elseif ('v6' === $options['force_ip_resolve']) { $records = dns_get_record($uri->getHost(), DNS_AAAA); if (!isset($records[0]['ipv6'])) { throw new ConnectException( sprintf( "Could not resolve IPv6 address for host '%s'", $uri->getHost() ), $request ); } $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']'); } } return $uri; } private function getDefaultContext(RequestInterface $request) { $headers = ''; foreach ($request->getHeaders() as $name => $value) { foreach ($value as $val) { $headers .= "$name: $val\r\n"; } } $context = [ 'http' => [ 'method' => $request->getMethod(), 'header' => $headers, 'protocol_version' => $request->getProtocolVersion(), 'ignore_errors' => true, 'follow_location' => 0, ], ]; $body = (string) $request->getBody(); if (!empty($body)) { $context['http']['content'] = $body; if (!$request->hasHeader('Content-Type')) { $context['http']['header'] .= "Content-Type:\r\n"; } } $context['http']['header'] = rtrim($context['http']['header']); return $context; } private function add_proxy(RequestInterface $request, &$options, $value, &$params) { if (!is_array($value)) { $options['http']['proxy'] = $value; } else { $scheme = $request->getUri()->getScheme(); if (isset($value[$scheme])) { if (!isset($value['no']) || !\GuzzleHttp\is_host_in_noproxy( $request->getUri()->getHost(), $value['no'] ) ) { $options['http']['proxy'] = $value[$scheme]; } } } } private function add_timeout(RequestInterface $request, &$options, $value, &$params) { if ($value > 0) { $options['http']['timeout'] = $value; } } private function add_verify(RequestInterface $request, &$options, $value, &$params) { if ($value === true) { if (PHP_VERSION_ID < 50600) { $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle(); } } elseif (is_string($value)) { $options['ssl']['cafile'] = $value; if (!file_exists($value)) { throw new \RuntimeException("SSL CA bundle not found: $value"); } } elseif ($value === false) { $options['ssl']['verify_peer'] = false; $options['ssl']['verify_peer_name'] = false; return; } else { throw new \InvalidArgumentException('Invalid verify request option'); } $options['ssl']['verify_peer'] = true; $options['ssl']['verify_peer_name'] = true; $options['ssl']['allow_self_signed'] = false; } private function add_cert(RequestInterface $request, &$options, $value, &$params) { if (is_array($value)) { $options['ssl']['passphrase'] = $value[1]; $value = $value[0]; } if (!file_exists($value)) { throw new \RuntimeException("SSL certificate not found: {$value}"); } $options['ssl']['local_cert'] = $value; } private function add_progress(RequestInterface $request, &$options, $value, &$params) { $this->addNotification( $params, function ($code, $a, $b, $c, $transferred, $total) use ($value) { if ($code == STREAM_NOTIFY_PROGRESS) { $value($total, $transferred, null, null); } } ); } private function add_debug(RequestInterface $request, &$options, $value, &$params) { if ($value === false) { return; } static $map = [ STREAM_NOTIFY_CONNECT => 'CONNECT', STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', STREAM_NOTIFY_PROGRESS => 'PROGRESS', STREAM_NOTIFY_FAILURE => 'FAILURE', STREAM_NOTIFY_COMPLETED => 'COMPLETED', STREAM_NOTIFY_RESOLVE => 'RESOLVE', ]; static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max']; $value = \GuzzleHttp\debug_resource($value); $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment(''); $this->addNotification( $params, function () use ($ident, $value, $map, $args) { $passed = func_get_args(); $code = array_shift($passed); fprintf($value, '<%s> [%s] ', $ident, $map[$code]); foreach (array_filter($passed) as $i => $v) { fwrite($value, $args[$i] . ': "' . $v . '" '); } fwrite($value, "\n"); } ); } private function addNotification(array &$params, callable $notify) { if (!isset($params['notification'])) { $params['notification'] = $notify; } else { $params['notification'] = $this->callArray([ $params['notification'], $notify ]); } } private function callArray(array $functions) { return function () use ($functions) { $args = func_get_args(); foreach ($functions as $fn) { call_user_func_array($fn, $args); } }; } } onFulfilled = $onFulfilled; $this->onRejected = $onRejected; if ($queue) { call_user_func_array([$this, 'append'], $queue); } } public function __invoke(RequestInterface $request, array $options) { if (!$this->queue) { throw new \OutOfBoundsException('Mock queue is empty'); } if (isset($options['delay']) && is_numeric($options['delay'])) { usleep($options['delay'] * 1000); } $this->lastRequest = $request; $this->lastOptions = $options; $response = array_shift($this->queue); if (isset($options['on_headers'])) { if (!is_callable($options['on_headers'])) { throw new \InvalidArgumentException('on_headers must be callable'); } try { $options['on_headers']($response); } catch (\Exception $e) { $msg = 'An error was encountered during the on_headers event'; $response = new RequestException($msg, $request, $response, $e); } } if (is_callable($response)) { $response = call_user_func($response, $request, $options); } $response = $response instanceof \Exception ? \GuzzleHttp\Promise\rejection_for($response) : \GuzzleHttp\Promise\promise_for($response); return $response->then( function ($value) use ($request, $options) { $this->invokeStats($request, $options, $value); if ($this->onFulfilled) { call_user_func($this->onFulfilled, $value); } if (isset($options['sink'])) { $contents = (string) $value->getBody(); $sink = $options['sink']; if (is_resource($sink)) { fwrite($sink, $contents); } elseif (is_string($sink)) { file_put_contents($sink, $contents); } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) { $sink->write($contents); } } return $value; }, function ($reason) use ($request, $options) { $this->invokeStats($request, $options, null, $reason); if ($this->onRejected) { call_user_func($this->onRejected, $reason); } return \GuzzleHttp\Promise\rejection_for($reason); } ); } public function append() { foreach (func_get_args() as $value) { if ($value instanceof ResponseInterface || $value instanceof \Exception || $value instanceof PromiseInterface || is_callable($value) ) { $this->queue[] = $value; } else { throw new \InvalidArgumentException('Expected a response or ' . 'exception. Found ' . \GuzzleHttp\describe_type($value)); } } } public function getLastRequest() { return $this->lastRequest; } public function getLastOptions() { return $this->lastOptions; } public function count() { return count($this->queue); } public function reset() { $this->queue = []; } private function invokeStats( RequestInterface $request, array $options, ResponseInterface $response = null, $reason = null ) { if (isset($options['on_stats'])) { $transferTime = isset($options['transfer_time']) ? $options['transfer_time'] : 0; $stats = new TransferStats($request, $response, $transferTime, $reason); call_user_func($options['on_stats'], $stats); } } } factory = isset($options['handle_factory']) ? $options['handle_factory'] : new CurlFactory(50); if (isset($options['select_timeout'])) { $this->selectTimeout = $options['select_timeout']; } elseif ($selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT')) { $this->selectTimeout = $selectTimeout; } else { $this->selectTimeout = 1; } $this->options = isset($options['options']) ? $options['options'] : []; } public function __get($name) { if ($name === '_mh') { $this->_mh = curl_multi_init(); foreach ($this->options as $option => $value) { curl_multi_setopt($this->_mh, $option, $value); } return $this->_mh; } throw new \BadMethodCallException(); } public function __destruct() { if (isset($this->_mh)) { curl_multi_close($this->_mh); unset($this->_mh); } } public function __invoke(RequestInterface $request, array $options) { $easy = $this->factory->create($request, $options); $id = (int) $easy->handle; $promise = new Promise( [$this, 'execute'], function () use ($id) { return $this->cancel($id); } ); $this->addRequest(['easy' => $easy, 'deferred' => $promise]); return $promise; } public function tick() { if ($this->delays) { $currentTime = Utils::currentTime(); foreach ($this->delays as $id => $delay) { if ($currentTime >= $delay) { unset($this->delays[$id]); curl_multi_add_handle( $this->_mh, $this->handles[$id]['easy']->handle ); } } } P\queue()->run(); if ($this->active && curl_multi_select($this->_mh, $this->selectTimeout) === -1 ) { usleep(250); } while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM); $this->processMessages(); } public function execute() { $queue = P\queue(); while ($this->handles || !$queue->isEmpty()) { if (!$this->active && $this->delays) { usleep($this->timeToNext()); } $this->tick(); } } private function addRequest(array $entry) { $easy = $entry['easy']; $id = (int) $easy->handle; $this->handles[$id] = $entry; if (empty($easy->options['delay'])) { curl_multi_add_handle($this->_mh, $easy->handle); } else { $this->delays[$id] = Utils::currentTime() + ($easy->options['delay'] / 1000); } } private function cancel($id) { if (!isset($this->handles[$id])) { return false; } $handle = $this->handles[$id]['easy']->handle; unset($this->delays[$id], $this->handles[$id]); curl_multi_remove_handle($this->_mh, $handle); curl_close($handle); return true; } private function processMessages() { while ($done = curl_multi_info_read($this->_mh)) { $id = (int) $done['handle']; curl_multi_remove_handle($this->_mh, $done['handle']); if (!isset($this->handles[$id])) { continue; } $entry = $this->handles[$id]; unset($this->handles[$id], $this->delays[$id]); $entry['easy']->errno = $done['result']; $entry['deferred']->resolve( CurlFactory::finish( $this, $entry['easy'], $this->factory ) ); } } private function timeToNext() { $currentTime = Utils::currentTime(); $nextTime = PHP_INT_MAX; foreach ($this->delays as $time) { if ($time < $nextTime) { $nextTime = $time; } } return max(0, $nextTime - $currentTime) * 1000000; } } nextHandler = $nextHandler; } public function __invoke(RequestInterface $request, array $options) { $fn = $this->nextHandler; if ($request->getBody()->getSize() === 0) { return $fn($request, $options); } $modify = []; if (!$request->hasHeader('Content-Type')) { if ($uri = $request->getBody()->getMetadata('uri')) { if ($type = Psr7\mimetype_from_filename($uri)) { $modify['set_headers']['Content-Type'] = $type; } } } if (!$request->hasHeader('Content-Length') && !$request->hasHeader('Transfer-Encoding') ) { $size = $request->getBody()->getSize(); if ($size !== null) { $modify['set_headers']['Content-Length'] = $size; } else { $modify['set_headers']['Transfer-Encoding'] = 'chunked'; } } $this->addExpectHeader($request, $options, $modify); return $fn(Psr7\modify_request($request, $modify), $options); } private function addExpectHeader( RequestInterface $request, array $options, array &$modify ) { if ($request->hasHeader('Expect')) { return; } $expect = isset($options['expect']) ? $options['expect'] : null; if ($expect === false || $request->getProtocolVersion() < 1.1) { return; } if ($expect === true) { $modify['set_headers']['Expect'] = '100-Continue'; return; } if ($expect === null) { $expect = 1048576; } $body = $request->getBody(); $size = $body->getSize(); if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { $modify['set_headers']['Expect'] = '100-Continue'; } } } 5, 'protocols' => ['http', 'https'], 'strict' => false, 'referer' => false, 'track_redirects' => false, ]; private $nextHandler; public function __construct(callable $nextHandler) { $this->nextHandler = $nextHandler; } public function __invoke(RequestInterface $request, array $options) { $fn = $this->nextHandler; if (empty($options['allow_redirects'])) { return $fn($request, $options); } if ($options['allow_redirects'] === true) { $options['allow_redirects'] = self::$defaultSettings; } elseif (!is_array($options['allow_redirects'])) { throw new \InvalidArgumentException('allow_redirects must be true, false, or array'); } else { $options['allow_redirects'] += self::$defaultSettings; } if (empty($options['allow_redirects']['max'])) { return $fn($request, $options); } return $fn($request, $options) ->then(function (ResponseInterface $response) use ($request, $options) { return $this->checkRedirect($request, $options, $response); }); } public function checkRedirect( RequestInterface $request, array $options, ResponseInterface $response ) { if (substr($response->getStatusCode(), 0, 1) != '3' || !$response->hasHeader('Location') ) { return $response; } $this->guardMax($request, $options); $nextRequest = $this->modifyRequest($request, $options, $response); if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $nextRequest->getUri()) && defined('\CURLOPT_HTTPAUTH')) { unset( $options['curl'][\CURLOPT_HTTPAUTH], $options['curl'][\CURLOPT_USERPWD] ); } if (isset($options['allow_redirects']['on_redirect'])) { call_user_func( $options['allow_redirects']['on_redirect'], $request, $response, $nextRequest->getUri() ); } $promise = $this($nextRequest, $options); if (!empty($options['allow_redirects']['track_redirects'])) { return $this->withTracking( $promise, (string) $nextRequest->getUri(), $response->getStatusCode() ); } return $promise; } private function withTracking(PromiseInterface $promise, $uri, $statusCode) { return $promise->then( function (ResponseInterface $response) use ($uri, $statusCode) { $historyHeader = $response->getHeader(self::HISTORY_HEADER); $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER); array_unshift($historyHeader, $uri); array_unshift($statusHeader, $statusCode); return $response->withHeader(self::HISTORY_HEADER, $historyHeader) ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader); } ); } private function guardMax(RequestInterface $request, array &$options) { $current = isset($options['__redirect_count']) ? $options['__redirect_count'] : 0; $options['__redirect_count'] = $current + 1; $max = $options['allow_redirects']['max']; if ($options['__redirect_count'] > $max) { throw new TooManyRedirectsException( "Will not follow more than {$max} redirects", $request ); } } public function modifyRequest( RequestInterface $request, array $options, ResponseInterface $response ) { $modify = []; $protocols = $options['allow_redirects']['protocols']; $statusCode = $response->getStatusCode(); if ($statusCode == 303 || ($statusCode <= 302 && !$options['allow_redirects']['strict']) ) { $modify['method'] = 'GET'; $modify['body'] = ''; } $uri = self::redirectUri($request, $response, $protocols); if (isset($options['idn_conversion']) && ($options['idn_conversion'] !== false)) { $idnOptions = ($options['idn_conversion'] === true) ? IDNA_DEFAULT : $options['idn_conversion']; $uri = Utils::idnUriConvert($uri, $idnOptions); } $modify['uri'] = $uri; Psr7\rewind_body($request); if ($options['allow_redirects']['referer'] && $modify['uri']->getScheme() === $request->getUri()->getScheme() ) { $uri = $request->getUri()->withUserInfo(''); $modify['set_headers']['Referer'] = (string) $uri; } else { $modify['remove_headers'][] = 'Referer'; } if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $modify['uri'])) { $modify['remove_headers'][] = 'Authorization'; $modify['remove_headers'][] = 'Cookie'; } return Psr7\modify_request($request, $modify); } private static function redirectUri( RequestInterface $request, ResponseInterface $response, array $protocols ) { $location = Psr7\UriResolver::resolve( $request->getUri(), new Psr7\Uri($response->getHeaderLine('Location')) ); if (!in_array($location->getScheme(), $protocols)) { throw new BadResponseException( sprintf( 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s', $location, implode(', ', $protocols) ), $request, $response ); } return $location; } } getStatusCode() : 0; parent::__construct($message, $code, $previous); $this->request = $request; $this->response = $response; $this->handlerContext = $handlerContext; } public static function wrapException(RequestInterface $request, \Exception $e) { return $e instanceof RequestException ? $e : new RequestException($e->getMessage(), $request, null, $e); } public static function create( RequestInterface $request, ResponseInterface $response = null, \Exception $previous = null, array $ctx = [] ) { if (!$response) { return new self( 'Error completing request', $request, null, $previous, $ctx ); } $level = (int) floor($response->getStatusCode() / 100); if ($level === 4) { $label = 'Client error'; $className = ClientException::class; } elseif ($level === 5) { $label = 'Server error'; $className = ServerException::class; } else { $label = 'Unsuccessful request'; $className = __CLASS__; } $uri = $request->getUri(); $uri = static::obfuscateUri($uri); $message = sprintf( '%s: `%s %s` resulted in a `%s %s` response', $label, $request->getMethod(), $uri, $response->getStatusCode(), $response->getReasonPhrase() ); $summary = static::getResponseBodySummary($response); if ($summary !== null) { $message .= ":\n{$summary}\n"; } return new $className($message, $request, $response, $previous, $ctx); } public static function getResponseBodySummary(ResponseInterface $response) { return \GuzzleHttp\Psr7\get_message_body_summary($response); } private static function obfuscateUri(UriInterface $uri) { $userInfo = $uri->getUserInfo(); if (false !== ($pos = strpos($userInfo, ':'))) { return $uri->withUserInfo(substr($userInfo, 0, $pos), '***'); } return $uri; } public function getRequest() { return $this->request; } public function getResponse() { return $this->response; } public function hasResponse() { return $this->response !== null; } public function getHandlerContext() { return $this->handlerContext; } } stream = $stream; $msg = $msg ?: 'Could not seek the stream to position ' . $pos; parent::__construct($msg); } public function getStream() { return $this->stream; } } expand($template, $variables); } function describe_type($input) { switch (gettype($input)) { case 'object': return 'object(' . get_class($input) . ')'; case 'array': return 'array(' . count($input) . ')'; default: ob_start(); var_dump($input); return str_replace('double(', 'float(', rtrim(ob_get_clean())); } } function headers_from_lines($lines) { $headers = []; foreach ($lines as $line) { $parts = explode(':', $line, 2); $headers[trim($parts[0])][] = isset($parts[1]) ? trim($parts[1]) : null; } return $headers; } function debug_resource($value = null) { if (is_resource($value)) { return $value; } elseif (defined('STDOUT')) { return STDOUT; } return fopen('php://output', 'w'); } function choose_handler() { $handler = null; if (function_exists('curl_multi_exec') && function_exists('curl_exec')) { $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler()); } elseif (function_exists('curl_exec')) { $handler = new CurlHandler(); } elseif (function_exists('curl_multi_exec')) { $handler = new CurlMultiHandler(); } if (ini_get('allow_url_fopen')) { $handler = $handler ? Proxy::wrapStreaming($handler, new StreamHandler()) : new StreamHandler(); } elseif (!$handler) { throw new \RuntimeException('GuzzleHttp requires cURL, the ' . 'allow_url_fopen ini setting, or a custom HTTP handler.'); } return $handler; } function default_user_agent() { static $defaultAgent = ''; if (!$defaultAgent) { $defaultAgent = 'GuzzleHttp/' . Client::VERSION; if (extension_loaded('curl') && function_exists('curl_version')) { $defaultAgent .= ' curl/' . \curl_version()['version']; } $defaultAgent .= ' PHP/' . PHP_VERSION; } return $defaultAgent; } function default_ca_bundle() { static $cached = null; static $cafiles = [ '/etc/pki/tls/certs/ca-bundle.crt', '/etc/ssl/certs/ca-certificates.crt', '/usr/local/share/certs/ca-root-nss.crt', '/var/lib/ca-certificates/ca-bundle.pem', '/usr/local/etc/openssl/cert.pem', '/etc/ca-certificates.crt', 'C:\\windows\\system32\\curl-ca-bundle.crt', 'C:\\windows\\curl-ca-bundle.crt', ]; if ($cached) { return $cached; } if ($ca = ini_get('openssl.cafile')) { return $cached = $ca; } if ($ca = ini_get('curl.cainfo')) { return $cached = $ca; } foreach ($cafiles as $filename) { if (file_exists($filename)) { return $cached = $filename; } } throw new \RuntimeException( <<< EOT No system CA bundle could be found in any of the the common system locations. PHP versions earlier than 5.6 are not properly configured to use the system's CA bundle by default. In order to verify peer certificates, you will need to supply the path on disk to a certificate bundle to the 'verify' request option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not need a specific certificate bundle, then Mozilla provides a commonly used CA bundle which can be downloaded here (provided by the maintainer of cURL): https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to the file, allowing you to omit the 'verify' request option. See http://curl.haxx.se/docs/sslcerts.html for more information. EOT ); } function normalize_header_keys(array $headers) { $result = []; foreach (array_keys($headers) as $key) { $result[strtolower($key)] = $key; } return $result; } function is_host_in_noproxy($host, array $noProxyArray) { if (strlen($host) === 0) { throw new \InvalidArgumentException('Empty host provided'); } if (strpos($host, ':')) { $host = explode($host, ':', 2)[0]; } foreach ($noProxyArray as $area) { if ($area === '*') { return true; } elseif (empty($area)) { continue; } elseif ($area === $host) { return true; } else { $area = '.' . ltrim($area, '.'); if (substr($host, -(strlen($area))) === $area) { return true; } } } return false; } function json_decode($json, $assoc = false, $depth = 512, $options = 0) { $data = \json_decode($json, $assoc, $depth, $options); if (JSON_ERROR_NONE !== json_last_error()) { throw new Exception\InvalidArgumentException( 'json_decode error: ' . json_last_error_msg() ); } return $data; } function json_encode($value, $options = 0, $depth = 512) { $json = \json_encode($value, $options, $depth); if (JSON_ERROR_NONE !== json_last_error()) { throw new Exception\InvalidArgumentException( 'json_encode error: ' . json_last_error_msg() ); } return $json; } prefixes = $prefixes; $this->suffixes = $suffixes; $this->exclude = $exclude; $this->basepath = $basepath; parent::__construct($iterator); } public function accept() { $current = $this->getInnerIterator()->current(); $filename = $current->getFilename(); $realpath = $current->getRealPath(); if ($this->basepath !== NULL) { $realpath = str_replace($this->basepath, '', $realpath); } if (preg_match('=/\.[^/]*/=', $realpath)) { return FALSE; } return $this->acceptPath($realpath) && $this->acceptPrefix($filename) && $this->acceptSuffix($filename); } protected function acceptPath($path) { foreach ($this->exclude as $exclude) { if (strpos($path, $exclude) === 0) { return FALSE; } } return TRUE; } protected function acceptPrefix($filename) { return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); } protected function acceptSuffix($filename) { return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); } protected function acceptSubString($filename, array $subStrings, $type) { if (empty($subStrings)) { return TRUE; } $matched = FALSE; foreach ($subStrings as $string) { if (($type == self::PREFIX && strpos($filename, $string) === 0) || ($type == self::SUFFIX && substr($filename, -1 * strlen($string)) == $string)) { $matched = TRUE; break; } } return $matched; } } getPathsAfterResolvingWildcards($paths); $exclude = $this->getPathsAfterResolvingWildcards($exclude); if (is_string($prefixes)) { if ($prefixes != '') { $prefixes = array($prefixes); } else { $prefixes = array(); } } if (is_string($suffixes)) { if ($suffixes != '') { $suffixes = array($suffixes); } else { $suffixes = array(); } } $iterator = new AppendIterator; foreach ($paths as $path) { if (is_dir($path)) { $iterator->append( new File_Iterator( new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::FOLLOW_SYMLINKS) ), $suffixes, $prefixes, $exclude, $path ) ); } } return $iterator; } protected function getPathsAfterResolvingWildcards(array $paths) { $_paths = array(); foreach ($paths as $path) { if ($locals = glob($path, GLOB_ONLYDIR)) { $_paths = array_merge($_paths, array_map('realpath', $locals)); } else { $_paths[] = realpath($path); } } return $_paths; } } getFileIterator( $paths, $suffixes, $prefixes, $exclude ); $files = array(); foreach ($iterator as $file) { $file = $file->getRealPath(); if ($file) { $files[] = $file; } } foreach ($paths as $path) { if (is_file($path)) { $files[] = realpath($path); } } $files = array_unique($files); sort($files); if ($commonPath) { return array( 'commonPath' => $this->getCommonPath($files), 'files' => $files ); } else { return $files; } } protected function getCommonPath(array $files) { $count = count($files); if ($count == 0) { return ''; } if ($count == 1) { return dirname($files[0]) . DIRECTORY_SEPARATOR; } $_files = array(); foreach ($files as $file) { $_files[] = $_fileParts = explode(DIRECTORY_SEPARATOR, $file); if (empty($_fileParts[0])) { $_fileParts[0] = DIRECTORY_SEPARATOR; } } $common = ''; $done = FALSE; $j = 0; $count--; while (!$done) { for ($i = 0; $i < $count; $i++) { if ($_files[$i][$j] != $_files[$i+1][$j]) { $done = TRUE; break; } } if (!$done) { $common .= $_files[0][$j]; if ($j > 0) { $common .= DIRECTORY_SEPARATOR; } } $j++; } return DIRECTORY_SEPARATOR . $common; } } setFile($file); $this->openDelimiter = $openDelimiter; $this->closeDelimiter = $closeDelimiter; } public function setFile($file) { $distFile = $file . '.dist'; if (file_exists($file)) { $this->template = file_get_contents($file); } else if (file_exists($distFile)) { $this->template = file_get_contents($distFile); } else { throw new InvalidArgumentException( 'Template file could not be loaded.' ); } } public function setVar(array $values, $merge = TRUE) { if (!$merge || empty($this->values)) { $this->values = $values; } else { $this->values = array_merge($this->values, $values); } } public function render() { $keys = array(); foreach ($this->values as $key => $value) { $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; } return str_replace($keys, $this->values, $this->template); } public function renderTo($target) { $fp = @fopen($target, 'wt'); if ($fp) { fwrite($fp, $this->render()); fclose($fp); } else { $error = error_get_last(); throw new RuntimeException( sprintf( 'Could not write to %s: %s', $target, substr( $error['message'], strpos($error['message'], ':') + 2 ) ) ); } } } 3600000, 'minute' => 60000, 'second' => 1000 ); private static $startTimes = array(); public static $requestTime; public static function start() { array_push(self::$startTimes, microtime(true)); } public static function stop() { return microtime(true) - array_pop(self::$startTimes); } public static function secondsToTimeString($time) { $ms = round($time * 1000); foreach (self::$times as $unit => $value) { if ($ms >= $value) { $time = floor($ms / $value * 100.0) / 100.0; return $time . ' ' . ($time == 1 ? $unit : $unit . 's'); } } return $ms . ' ms'; } public static function timeSinceStartOfRequest() { return self::secondsToTimeString(microtime(true) - self::$requestTime); } public static function resourceUsage() { return sprintf( 'Time: %s, Memory: %4.2fMB', self::timeSinceStartOfRequest(), memory_get_peak_usage(true) / 1048576 ); } } if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME_FLOAT']; } elseif (isset($_SERVER['REQUEST_TIME'])) { PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME']; } else { PHP_Timer::$requestTime = microtime(true); } getFilesAsArray( $suiteClassName, $suffixes ); $suite = new PHPUnit_Framework_TestSuite($suiteClassName); $suite->addTestFiles($files); return $suite; } try { $testClass = $this->loadSuiteClass( $suiteClassName, $suiteClassFile ); } catch (PHPUnit_Framework_Exception $e) { $this->runFailed($e->getMessage()); return; } try { $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME); if (!$suiteMethod->isStatic()) { $this->runFailed( 'suite() method must be static.' ); return; } try { $test = $suiteMethod->invoke(null, $testClass->getName()); } catch (ReflectionException $e) { $this->runFailed( sprintf( "Failed to invoke suite() method.\n%s", $e->getMessage() ) ); return; } } catch (ReflectionException $e) { try { $test = new PHPUnit_Framework_TestSuite($testClass); } catch (PHPUnit_Framework_Exception $e) { $test = new PHPUnit_Framework_TestSuite; $test->setName($suiteClassName); } } $this->clearStatus(); return $test; } protected function loadSuiteClass($suiteClassName, $suiteClassFile = '') { $loader = $this->getLoader(); return $loader->load($suiteClassName, $suiteClassFile); } protected function clearStatus() { } abstract protected function runFailed($message); } getVersion(); } return self::$version; } public static function series() { if (strpos(self::id(), '-')) { $version = explode('-', self::id())[0]; } else { $version = self::id(); } return implode('.', array_slice(explode('.', $version), 0, 2)); } public static function getVersionString() { return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; } public static function getReleaseChannel() { if (strpos(self::$pharVersion, '-') !== false) { return '-nightly'; } return ''; } } getFileName() == $filename) { $suiteClassName = $loadedClass; break; } } } if (!class_exists($suiteClassName, false) && !empty($loadedClasses)) { $testCaseClass = 'PHPUnit_Framework_TestCase'; foreach ($loadedClasses as $loadedClass) { $class = new ReflectionClass($loadedClass); $classFile = $class->getFileName(); if ($class->isSubclassOf($testCaseClass) && !$class->isAbstract()) { $suiteClassName = $loadedClass; $testCaseClass = $loadedClass; if ($classFile == realpath($suiteClassFile)) { break; } } if ($class->hasMethod('suite')) { $method = $class->getMethod('suite'); if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) { $suiteClassName = $loadedClass; if ($classFile == realpath($suiteClassFile)) { break; } } } } } if (class_exists($suiteClassName, false)) { $class = new ReflectionClass($suiteClassName); if ($class->getFileName() == realpath($suiteClassFile)) { return $class; } } throw new PHPUnit_Framework_Exception( sprintf( "Class '%s' could not be found in '%s'.", $suiteClassName, $suiteClassFile ) ); } public function reload(ReflectionClass $aClass) { return $aClass; } } isSubclassOf('RecursiveFilterIterator')) { throw new InvalidArgumentException( sprintf( 'Class "%s" does not extend RecursiveFilterIterator', $filter->name ) ); } $this->filters[] = [$filter, $args]; } public function factory(Iterator $iterator, PHPUnit_Framework_TestSuite $suite) { foreach ($this->filters as $filter) { list($class, $args) = $filter; $iterator = $class->newInstance($iterator, $args, $suite); } return $iterator; } } setFilter($filter); } protected function setFilter($filter) { if (PHPUnit_Util_Regex::pregMatchSafe($filter, '') === false) { if (preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) { if (isset($matches[3]) && $matches[2] < $matches[3]) { $filter = sprintf( '%s.*with data set #(\d+)$', $matches[1] ); $this->filterMin = $matches[2]; $this->filterMax = $matches[3]; } else { $filter = sprintf( '%s.*with data set #%s$', $matches[1], $matches[2] ); } } elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) { $filter = sprintf( '%s.*with data set "%s"$', $matches[1], $matches[2] ); } $filter = sprintf('/%s/', str_replace( '/', '\\/', $filter )); } $this->filter = $filter; } public function accept() { $test = $this->getInnerIterator()->current(); if ($test instanceof PHPUnit_Framework_TestSuite) { return true; } if ($test instanceof PHPUnit_Framework_WarningTestCase) { $name = $test->getMessage(); } else { $tmp = PHPUnit_Util_Test::describe($test, false); if ($tmp[0] != '') { $name = implode('::', $tmp); } else { $name = $tmp[1]; } } $accepted = @preg_match($this->filter, $name, $matches); if ($accepted && isset($this->filterMax)) { $set = end($matches); $accepted = $set >= $this->filterMin && $set <= $this->filterMax; } return $accepted; } } groupTests); } } groupTests); } } getGroupDetails() as $group => $tests) { if (in_array($group, $groups)) { $testHashes = array_map( function ($test) { return spl_object_hash($test); }, $tests ); $this->groupTests = array_merge($this->groupTests, $testHashes); } } } public function accept() { $test = $this->getInnerIterator()->current(); if ($test instanceof PHPUnit_Framework_TestSuite) { return true; } return $this->doAccept(spl_object_hash($test)); } abstract protected function doAccept($hash); } listeners[] = $listener; } public function removeListener(PHPUnit_Framework_TestListener $listener) { foreach ($this->listeners as $key => $_listener) { if ($listener === $_listener) { unset($this->listeners[$key]); } } } public function flushListeners() { foreach ($this->listeners as $listener) { if ($listener instanceof PHPUnit_Util_Printer) { $listener->flush(); } } } public function addError(PHPUnit_Framework_Test $test, $t, $time) { if ($t instanceof PHPUnit_Framework_RiskyTest) { $this->risky[] = new PHPUnit_Framework_TestFailure($test, $t); $notifyMethod = 'addRiskyTest'; if ($test instanceof PHPUnit_Framework_TestCase) { $test->markAsRisky(); } if ($this->stopOnRisky) { $this->stop(); } } elseif ($t instanceof PHPUnit_Framework_IncompleteTest) { $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $t); $notifyMethod = 'addIncompleteTest'; if ($this->stopOnIncomplete) { $this->stop(); } } elseif ($t instanceof PHPUnit_Framework_SkippedTest) { $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $t); $notifyMethod = 'addSkippedTest'; if ($this->stopOnSkipped) { $this->stop(); } } else { $this->errors[] = new PHPUnit_Framework_TestFailure($test, $t); $notifyMethod = 'addError'; if ($this->stopOnError || $this->stopOnFailure) { $this->stop(); } } if ($t instanceof Error) { $t = new PHPUnit_Framework_ExceptionWrapper($t); } foreach ($this->listeners as $listener) { $listener->$notifyMethod($test, $t, $time); } $this->lastTestFailed = true; $this->time += $time; } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { if ($this->stopOnWarning) { $this->stop(); } $this->warnings[] = new PHPUnit_Framework_TestFailure($test, $e); foreach ($this->listeners as $listener) { if (method_exists($listener, 'addWarning')) { $listener->addWarning($test, $e, $time); } } $this->time += $time; } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { if ($e instanceof PHPUnit_Framework_RiskyTest || $e instanceof PHPUnit_Framework_OutputError) { $this->risky[] = new PHPUnit_Framework_TestFailure($test, $e); $notifyMethod = 'addRiskyTest'; if ($test instanceof PHPUnit_Framework_TestCase) { $test->markAsRisky(); } if ($this->stopOnRisky) { $this->stop(); } } elseif ($e instanceof PHPUnit_Framework_IncompleteTest) { $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e); $notifyMethod = 'addIncompleteTest'; if ($this->stopOnIncomplete) { $this->stop(); } } elseif ($e instanceof PHPUnit_Framework_SkippedTest) { $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e); $notifyMethod = 'addSkippedTest'; if ($this->stopOnSkipped) { $this->stop(); } } else { $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e); $notifyMethod = 'addFailure'; if ($this->stopOnFailure) { $this->stop(); } } foreach ($this->listeners as $listener) { $listener->$notifyMethod($test, $e, $time); } $this->lastTestFailed = true; $this->time += $time; } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { if ($this->topTestSuite === null) { $this->topTestSuite = $suite; } foreach ($this->listeners as $listener) { $listener->startTestSuite($suite); } } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { foreach ($this->listeners as $listener) { $listener->endTestSuite($suite); } } public function startTest(PHPUnit_Framework_Test $test) { $this->lastTestFailed = false; $this->runTests += count($test); foreach ($this->listeners as $listener) { $listener->startTest($test); } } public function endTest(PHPUnit_Framework_Test $test, $time) { foreach ($this->listeners as $listener) { $listener->endTest($test, $time); } if (!$this->lastTestFailed && $test instanceof PHPUnit_Framework_TestCase) { $class = get_class($test); $key = $class . '::' . $test->getName(); $this->passed[$key] = [ 'result' => $test->getResult(), 'size' => PHPUnit_Util_Test::getSize( $class, $test->getName(false) ) ]; $this->time += $time; } } public function allHarmless() { return $this->riskyCount() == 0; } public function riskyCount() { return count($this->risky); } public function allCompletelyImplemented() { return $this->notImplementedCount() == 0; } public function notImplementedCount() { return count($this->notImplemented); } public function risky() { return $this->risky; } public function notImplemented() { return $this->notImplemented; } public function noneSkipped() { return $this->skippedCount() == 0; } public function skippedCount() { return count($this->skipped); } public function skipped() { return $this->skipped; } public function errorCount() { return count($this->errors); } public function errors() { return $this->errors; } public function failureCount() { return count($this->failures); } public function failures() { return $this->failures; } public function warningCount() { return count($this->warnings); } public function warnings() { return $this->warnings; } public function passed() { return $this->passed; } public function topTestSuite() { return $this->topTestSuite; } public function getCollectCodeCoverageInformation() { return $this->codeCoverage !== null; } public function run(PHPUnit_Framework_Test $test) { PHPUnit_Framework_Assert::resetCount(); $coversNothing = false; if ($test instanceof PHPUnit_Framework_TestCase) { $test->setRegisterMockObjectsFromTestArgumentsRecursively( $this->registerMockObjectsFromTestArgumentsRecursively ); $annotations = $test->getAnnotations(); if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) { $coversNothing = true; } } $error = false; $failure = false; $warning = false; $incomplete = false; $risky = false; $skipped = false; $this->startTest($test); $errorHandlerSet = false; if ($this->convertErrorsToExceptions) { $oldErrorHandler = set_error_handler( ['PHPUnit_Util_ErrorHandler', 'handleError'], E_ALL | E_STRICT ); if ($oldErrorHandler === null) { $errorHandlerSet = true; } else { restore_error_handler(); } } $collectCodeCoverage = $this->codeCoverage !== null && !$test instanceof PHPUnit_Framework_WarningTestCase && !$coversNothing; if ($collectCodeCoverage) { $this->codeCoverage->start($test); } $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && !$test instanceof PHPUnit_Framework_WarningTestCase && $test->getSize() == PHPUnit_Util_Test::SMALL && function_exists('xdebug_start_function_monitor'); if ($monitorFunctions) { xdebug_start_function_monitor(ResourceOperations::getFunctions()); } PHP_Timer::start(); try { if (!$test instanceof PHPUnit_Framework_WarningTestCase && $test->getSize() != PHPUnit_Util_Test::UNKNOWN && $this->enforceTimeLimit && extension_loaded('pcntl') && class_exists('PHP_Invoker')) { switch ($test->getSize()) { case PHPUnit_Util_Test::SMALL: $_timeout = $this->timeoutForSmallTests; break; case PHPUnit_Util_Test::MEDIUM: $_timeout = $this->timeoutForMediumTests; break; case PHPUnit_Util_Test::LARGE: $_timeout = $this->timeoutForLargeTests; break; } $invoker = new PHP_Invoker; $invoker->invoke([$test, 'runBare'], [], $_timeout); } else { $test->runBare(); } } catch (PHP_Invoker_TimeoutException $e) { $this->addFailure( $test, new PHPUnit_Framework_RiskyTestError( $e->getMessage() ), $_timeout ); $risky = true; } catch (PHPUnit_Framework_MockObject_Exception $e) { $e = new PHPUnit_Framework_Warning( $e->getMessage() ); $warning = true; } catch (PHPUnit_Framework_AssertionFailedError $e) { $failure = true; if ($e instanceof PHPUnit_Framework_RiskyTestError) { $risky = true; } elseif ($e instanceof PHPUnit_Framework_IncompleteTestError) { $incomplete = true; } elseif ($e instanceof PHPUnit_Framework_SkippedTestError) { $skipped = true; } } catch (PHPUnit_Framework_Warning $e) { $warning = true; } catch (PHPUnit_Framework_Exception $e) { $error = true; } catch (Throwable $e) { if (PHP_MAJOR_VERSION === 7 && $e instanceof \AssertionError) { $test->addToAssertionCount(1); $failure = true; $frame = $e->getTrace()[0]; $e = new PHPUnit_Framework_AssertionFailedError( sprintf( '%s in %s:%s', $e->getMessage(), $frame['file'], $frame['line'] ) ); } else { $e = new PHPUnit_Framework_ExceptionWrapper($e); $error = true; } } catch (Exception $e) { $e = new PHPUnit_Framework_ExceptionWrapper($e); $error = true; } $time = PHP_Timer::stop(); $test->addToAssertionCount(PHPUnit_Framework_Assert::getCount()); if ($monitorFunctions) { $blacklist = new PHPUnit_Util_Blacklist; $functions = xdebug_get_monitored_functions(); xdebug_stop_function_monitor(); foreach ($functions as $function) { if (!$blacklist->isBlacklisted($function['filename'])) { $this->addFailure( $test, new PHPUnit_Framework_RiskyTestError( sprintf( '%s() used in %s:%s', $function['function'], $function['filename'], $function['lineno'] ) ), $time ); } } } if ($this->beStrictAboutTestsThatDoNotTestAnything && $test->getNumAssertions() == 0) { $risky = true; } if ($collectCodeCoverage) { $append = !$risky && !$incomplete && !$skipped; $linesToBeCovered = []; $linesToBeUsed = []; if ($append && $test instanceof PHPUnit_Framework_TestCase) { try { $linesToBeCovered = PHPUnit_Util_Test::getLinesToBeCovered( get_class($test), $test->getName(false) ); $linesToBeUsed = PHPUnit_Util_Test::getLinesToBeUsed( get_class($test), $test->getName(false) ); } catch (PHPUnit_Framework_InvalidCoversTargetException $cce) { $this->addWarning( $test, new PHPUnit_Framework_Warning( $cce->getMessage() ), $time ); } } try { $this->codeCoverage->stop( $append, $linesToBeCovered, $linesToBeUsed ); } catch (UnintentionallyCoveredCodeException $cce) { $this->addFailure( $test, new PHPUnit_Framework_UnintentionallyCoveredCodeError( 'This test executed code that is not listed as code to be covered or used:' . PHP_EOL . $cce->getMessage() ), $time ); } catch (CoveredCodeNotExecutedException $cce) { $this->addFailure( $test, new PHPUnit_Framework_CoveredCodeNotExecutedException( 'This test did not execute all the code that is listed as code to be covered:' . PHP_EOL . $cce->getMessage() ), $time ); } catch (MissingCoversAnnotationException $cce) { if ($linesToBeCovered !== false) { $this->addFailure( $test, new PHPUnit_Framework_MissingCoversAnnotationException( 'This test does not have a @covers annotation but is expected to have one' ), $time ); } } catch (CodeCoverageException $cce) { $error = true; if (!isset($e)) { $e = $cce; } } } if ($errorHandlerSet === true) { restore_error_handler(); } if ($error === true) { $this->addError($test, $e, $time); } elseif ($failure === true) { $this->addFailure($test, $e, $time); } elseif ($warning === true) { $this->addWarning($test, $e, $time); } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && !$test->doesNotPerformAssertions() && $test->getNumAssertions() == 0) { $this->addFailure( $test, new PHPUnit_Framework_RiskyTestError( 'This test did not perform any assertions' ), $time ); } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { $this->addFailure( $test, new PHPUnit_Framework_OutputError( sprintf( 'This test printed output: %s', $test->getActualOutput() ) ), $time ); } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof PHPUnit_Framework_TestCase) { $annotations = $test->getAnnotations(); if (isset($annotations['method']['todo'])) { $this->addFailure( $test, new PHPUnit_Framework_RiskyTestError( 'Test method is annotated with @todo' ), $time ); } } $this->endTest($test, $time); } public function count() { return $this->runTests; } public function shouldStop() { return $this->stop; } public function stop() { $this->stop = true; } public function getCodeCoverage() { return $this->codeCoverage; } public function setCodeCoverage(CodeCoverage $codeCoverage) { $this->codeCoverage = $codeCoverage; } public function convertErrorsToExceptions($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->convertErrorsToExceptions = $flag; } public function getConvertErrorsToExceptions() { return $this->convertErrorsToExceptions; } public function stopOnError($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->stopOnError = $flag; } public function stopOnFailure($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->stopOnFailure = $flag; } public function stopOnWarning($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->stopOnWarning = $flag; } public function beStrictAboutTestsThatDoNotTestAnything($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->beStrictAboutTestsThatDoNotTestAnything = $flag; } public function isStrictAboutTestsThatDoNotTestAnything() { return $this->beStrictAboutTestsThatDoNotTestAnything; } public function beStrictAboutOutputDuringTests($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->beStrictAboutOutputDuringTests = $flag; } public function isStrictAboutOutputDuringTests() { return $this->beStrictAboutOutputDuringTests; } public function beStrictAboutResourceUsageDuringSmallTests($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->beStrictAboutResourceUsageDuringSmallTests = $flag; } public function isStrictAboutResourceUsageDuringSmallTests() { return $this->beStrictAboutResourceUsageDuringSmallTests; } public function enforceTimeLimit($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->enforceTimeLimit = $flag; } public function enforcesTimeLimit() { return $this->enforceTimeLimit; } public function beStrictAboutTodoAnnotatedTests($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->beStrictAboutTodoAnnotatedTests = $flag; } public function isStrictAboutTodoAnnotatedTests() { return $this->beStrictAboutTodoAnnotatedTests; } public function stopOnRisky($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->stopOnRisky = $flag; } public function stopOnIncomplete($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->stopOnIncomplete = $flag; } public function stopOnSkipped($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->stopOnSkipped = $flag; } public function time() { return $this->time; } public function wasSuccessful($includeWarnings = true) { if ($includeWarnings) { return empty($this->errors) && empty($this->failures) && empty($this->warnings); } else { return empty($this->errors) && empty($this->failures); } } public function setTimeoutForSmallTests($timeout) { if (!is_int($timeout)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); } $this->timeoutForSmallTests = $timeout; } public function setTimeoutForMediumTests($timeout) { if (!is_int($timeout)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); } $this->timeoutForMediumTests = $timeout; } public function setTimeoutForLargeTests($timeout) { if (!is_int($timeout)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); } $this->timeoutForLargeTests = $timeout; } public function getTimeoutForLargeTests() { return $this->timeoutForLargeTests; } public function setRegisterMockObjectsFromTestArgumentsRecursively($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->registerMockObjectsFromTestArgumentsRecursively = $flag; } protected function getHierarchy($className, $asReflectionObjects = false) { if ($asReflectionObjects) { $classes = [new ReflectionClass($className)]; } else { $classes = [$className]; } $done = false; while (!$done) { if ($asReflectionObjects) { $class = new ReflectionClass( $classes[count($classes) - 1]->getName() ); } else { $class = new ReflectionClass($classes[count($classes) - 1]); } $parent = $class->getParentClass(); if ($parent !== false) { if ($asReflectionObjects) { $classes[] = $parent; } else { $classes[] = $parent->getName(); } } else { $done = true; } } return $classes; } } message = $message; parent::__construct($className . '::' . $methodName); } protected function runTest() { $this->markTestIncomplete($this->message); } public function getMessage() { return $this->message; } public function toString() { return $this->getName(); } } getMessage(), (int) $e->getCode()); $this->className = get_class($e); $this->file = $e->getFile(); $this->line = $e->getLine(); $this->serializableTrace = $e->getTrace(); foreach ($this->serializableTrace as $i => $call) { unset($this->serializableTrace[$i]['args']); } if ($e->getPrevious()) { $this->previous = new self($e->getPrevious()); } } public function getClassName() { return $this->className; } public function getPreviousWrapped() { return $this->previous; } public function __toString() { $string = PHPUnit_Framework_TestFailure::exceptionToString($this); if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) { $string .= "\n" . $trace; } if ($this->previous) { $string .= "\nCaused by\n" . $this->previous; } return $string; } } attributeName = $attributeName; } public function evaluate($other, $description = '', $returnResult = false) { return parent::evaluate( PHPUnit_Framework_Assert::readAttribute( $other, $this->attributeName ), $description, $returnResult ); } public function toString() { return 'attribute "' . $this->attributeName . '" ' . $this->innerConstraint->toString(); } protected function failureDescription($other) { return $this->toString(); } } string = $string; $this->ignoreCase = $ignoreCase; } protected function matches($other) { if ($this->ignoreCase) { return mb_stripos($other, $this->string) !== false; } else { return mb_strpos($other, $this->string) !== false; } } public function toString() { if ($this->ignoreCase) { $string = mb_strtolower($this->string); } else { $string = $this->string; } return sprintf( 'contains "%s"', $string ); } } expectedMessage = $expected; } protected function matches($other) { if ($this->expectedMessage === '') { return $other->getMessage() === ''; } return strpos($other->getMessage(), $this->expectedMessage) !== false; } protected function failureDescription($other) { if ($this->expectedMessage === '') { return sprintf( "exception message is empty but is '%s'", $other->getMessage() ); } return sprintf( "exception message '%s' contains '%s'", $other->getMessage(), $this->expectedMessage ); } public function toString() { if ($this->expectedMessage === '') { return 'exception message is empty'; } return 'exception message contains '; } } constraint = new PHPUnit_Framework_Constraint_IsType($type); } else { $this->constraint = new PHPUnit_Framework_Constraint_IsInstanceOf( $type ); } $this->type = $type; } public function evaluate($other, $description = '', $returnResult = false) { $success = true; foreach ($other as $item) { if (!$this->constraint->evaluate($item, '', true)) { $success = false; break; } } if ($returnResult) { return $success; } if (!$success) { $this->fail($other, $description); } } public function toString() { return 'contains only values of type "' . $this->type . '"'; } } callback = $callback; } protected function matches($other) { return call_user_func($this->callback, $other); } public function toString() { return 'is accepted by specified callback'; } } pattern = $this->createPatternFromFormat( preg_replace('/\r\n/', "\n", $string) ); $this->string = $string; } protected function failureDescription($other) { return 'format description matches text'; } protected function additionalFailureDescription($other) { $from = preg_split('(\r\n|\r|\n)', $this->string); $to = preg_split('(\r\n|\r|\n)', $other); foreach ($from as $index => $line) { if (isset($to[$index]) && $line !== $to[$index]) { $line = $this->createPatternFromFormat($line); if (preg_match($line, $to[$index]) > 0) { $from[$index] = $to[$index]; } } } $this->string = implode("\n", $from); $other = implode("\n", $to); $differ = new Differ("--- Expected\n+++ Actual\n"); return $differ->diff($this->string, $other); } protected function createPatternFromFormat($string) { $string = str_replace( [ '%e', '%s', '%S', '%a', '%A', '%w', '%i', '%d', '%x', '%f', '%c' ], [ '\\' . DIRECTORY_SEPARATOR, '[^\r\n]+', '[^\r\n]*', '.+', '.*', '\s*', '[+-]?\d+', '\d+', '[0-9a-fA-F]+', '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', '.' ], preg_quote($string, '/') ); return '/^' . $string . '$/s'; } } className = $className; } protected function matches($other) { return $other instanceof $this->className; } protected function failureDescription($other) { if ($other !== null) { $message = ''; if ($other instanceof Exception || $other instanceof Throwable) { $message = '. Message was: "' . $other->getMessage() . '" at' . "\n" . PHPUnit_Util_Filter::getFilteredStacktrace($other); } return sprintf( 'exception of type "%s" matches expected exception "%s"%s', get_class($other), $this->className, $message ); } return sprintf( 'exception of type "%s" is thrown', $this->className ); } public function toString() { return sprintf( 'exception of type "%s"', $this->className ); } } key = $key; } protected function matches($other) { if (is_array($other)) { return array_key_exists($this->key, $other); } if ($other instanceof ArrayAccess) { return $other->offsetExists($this->key); } return false; } public function toString() { return 'has the key ' . $this->exporter->export($this->key); } protected function failureDescription($other) { return 'an array ' . $this->toString(); } } suffix = $suffix; } protected function matches($other) { return substr($other, 0 - strlen($this->suffix)) == $this->suffix; } public function toString() { return 'ends with "' . $this->suffix . '"'; } } toString() ); } } value = $value; } protected function matches($other) { return $this->value < $other; } public function toString() { return 'is greater than ' . $this->exporter->export($this->value); } } expectedCount = $expected; } protected function matches($other) { return $this->expectedCount === $this->getCountOf($other); } protected function getCountOf($other) { if ($other instanceof Countable || is_array($other)) { return count($other); } elseif ($other instanceof Traversable) { if ($other instanceof IteratorAggregate) { $iterator = $other->getIterator(); } else { $iterator = $other; } if ($iterator instanceof Generator) { return $this->getCountOfGenerator($iterator); } $key = $iterator->key(); $count = iterator_count($iterator); if ($key !== null) { $iterator->rewind(); while ($iterator->valid() && $key !== $iterator->key()) { $iterator->next(); } } return $count; } } protected function getCountOfGenerator(Generator $generator) { for ($count = 0; $generator->valid(); $generator->next()) { $count += 1; } return $count; } protected function failureDescription($other) { return sprintf( 'actual size %d matches expected size %d', $this->getCountOf($other), $this->expectedCount ); } public function toString() { return sprintf( 'count matches %d', $this->expectedCount ); } } value = $value; } public function evaluate($other, $description = '', $returnResult = false) { if (is_float($this->value) && is_float($other) && !is_infinite($this->value) && !is_infinite($other) && !is_nan($this->value) && !is_nan($other)) { $success = abs($this->value - $other) < self::EPSILON; } else { $success = $this->value === $other; } if ($returnResult) { return $success; } if (!$success) { $f = null; if (is_string($this->value) && is_string($other)) { $f = new SebastianBergmann\Comparator\ComparisonFailure( $this->value, $other, $this->value, $other ); } $this->fail($other, $description, $f); } } protected function failureDescription($other) { if (is_object($this->value) && is_object($other)) { return 'two variables reference the same object'; } if (is_string($this->value) && is_string($other)) { return 'two strings are identical'; } return parent::failureDescription($other); } public function toString() { if (is_object($this->value)) { return 'is identical to an object of class "' . get_class($this->value) . '"'; } else { return 'is identical to ' . $this->exporter->export($this->value); } } } constraints = []; foreach ($constraints as $constraint) { if (!($constraint instanceof PHPUnit_Framework_Constraint)) { $constraint = new PHPUnit_Framework_Constraint_IsEqual( $constraint ); } $this->constraints[] = $constraint; } } public function evaluate($other, $description = '', $returnResult = false) { $success = false; $constraint = null; foreach ($this->constraints as $constraint) { if ($constraint->evaluate($other, $description, true)) { $success = true; break; } } if ($returnResult) { return $success; } if (!$success) { $this->fail($other, $description); } } public function toString() { $text = ''; foreach ($this->constraints as $key => $constraint) { if ($key > 0) { $text .= ' or '; } $text .= $constraint->toString(); } return $text; } public function count() { $count = 0; foreach ($this->constraints as $constraint) { $count += count($constraint); } return $count; } } constraints = []; foreach ($constraints as $constraint) { if (!($constraint instanceof PHPUnit_Framework_Constraint)) { $constraint = new PHPUnit_Framework_Constraint_IsEqual( $constraint ); } $this->constraints[] = $constraint; } } public function evaluate($other, $description = '', $returnResult = false) { $success = true; $lastResult = null; $constraint = null; foreach ($this->constraints as $constraint) { $result = $constraint->evaluate($other, $description, true); if ($result === $lastResult) { $success = false; break; } $lastResult = $result; } if ($returnResult) { return $success; } if (!$success) { $this->fail($other, $description); } } public function toString() { $text = ''; foreach ($this->constraints as $key => $constraint) { if ($key > 0) { $text .= ' xor '; } $text .= $constraint->toString(); } return $text; } public function count() { $count = 0; foreach ($this->constraints as $constraint) { $count += count($constraint); } return $count; } } exporter->shortenedExport($other), $error ); } public function toString() { return 'is valid JSON'; } } constraints = []; foreach ($constraints as $constraint) { if (!($constraint instanceof PHPUnit_Framework_Constraint)) { throw new PHPUnit_Framework_Exception( 'All parameters to ' . __CLASS__ . ' must be a constraint object.' ); } $this->constraints[] = $constraint; } } public function evaluate($other, $description = '', $returnResult = false) { $success = true; $constraint = null; foreach ($this->constraints as $constraint) { if (!$constraint->evaluate($other, $description, true)) { $success = false; break; } } if ($returnResult) { return $success; } if (!$success) { $this->fail($other, $description); } } public function toString() { $text = ''; foreach ($this->constraints as $key => $constraint) { if ($key > 0) { $text .= ' and '; } $text .= $constraint->toString(); } return $text; } public function count() { $count = 0; foreach ($this->constraints as $constraint) { $count += count($constraint); } return $count; } } pattern = $pattern; } protected function matches($other) { return preg_match($this->pattern, $other) > 0; } public function toString() { return sprintf( 'matches PCRE pattern "%s"', $this->pattern ); } } expectedCode = $expected; } protected function matches($other) { return (string) $other->getCode() == (string) $this->expectedCode; } protected function failureDescription($other) { return sprintf( '%s is equal to expected exception code %s', $this->exporter->export($other->getCode()), $this->exporter->export($this->expectedCode) ); } public function toString() { return 'exception code is '; } } className = $className; } protected function matches($other) { return ($other instanceof $this->className); } protected function failureDescription($other) { return sprintf( '%s is an instance of %s "%s"', $this->exporter->shortenedExport($other), $this->getType(), $this->className ); } public function toString() { return sprintf( 'is instance of %s "%s"', $this->getType(), $this->className ); } private function getType() { try { $reflection = new ReflectionClass($this->className); if ($reflection->isInterface()) { return 'interface'; } } catch (ReflectionException $e) { } return 'class'; } } hasProperty($this->attributeName)) { $attribute = $class->getProperty($this->attributeName); return $attribute->isStatic(); } else { return false; } } public function toString() { return sprintf( 'has static attribute "%s"', $this->attributeName ); } } true, 'boolean' => true, 'bool' => true, 'double' => true, 'float' => true, 'integer' => true, 'int' => true, 'null' => true, 'numeric' => true, 'object' => true, 'real' => true, 'resource' => true, 'string' => true, 'scalar' => true, 'callable' => true ]; protected $type; public function __construct($type) { parent::__construct(); if (!isset($this->types[$type])) { throw new PHPUnit_Framework_Exception( sprintf( 'Type specified for PHPUnit_Framework_Constraint_IsType <%s> ' . 'is not a valid type.', $type ) ); } $this->type = $type; } protected function matches($other) { switch ($this->type) { case 'numeric': return is_numeric($other); case 'integer': case 'int': return is_int($other); case 'double': case 'float': case 'real': return is_float($other); case 'string': return is_string($other); case 'boolean': case 'bool': return is_bool($other); case 'null': return is_null($other); case 'array': return is_array($other); case 'object': return is_object($other); case 'resource': return is_resource($other) || is_string(@get_resource_type($other)); case 'scalar': return is_scalar($other); case 'callable': return is_callable($other); } } public function toString() { return sprintf( 'is of type "%s"', $this->type ); } } getCountOf($expected)); } } constraint = $constraint; } public static function negate($string) { return str_replace( [ 'contains ', 'exists', 'has ', 'is ', 'are ', 'matches ', 'starts with ', 'ends with ', 'reference ', 'not not ' ], [ 'does not contain ', 'does not exist', 'does not have ', 'is not ', 'are not ', 'does not match ', 'starts not with ', 'ends not with ', 'don\'t reference ', 'not ' ], $string ); } public function evaluate($other, $description = '', $returnResult = false) { $success = !$this->constraint->evaluate($other, $description, true); if ($returnResult) { return $success; } if (!$success) { $this->fail($other, $description); } } protected function failureDescription($other) { switch (get_class($this->constraint)) { case 'PHPUnit_Framework_Constraint_And': case 'PHPUnit_Framework_Constraint_Not': case 'PHPUnit_Framework_Constraint_Or': return 'not( ' . $this->constraint->failureDescription($other) . ' )'; default: return self::negate( $this->constraint->failureDescription($other) ); } } public function toString() { switch (get_class($this->constraint)) { case 'PHPUnit_Framework_Constraint_And': case 'PHPUnit_Framework_Constraint_Not': case 'PHPUnit_Framework_Constraint_Or': return 'not( ' . $this->constraint->toString() . ' )'; default: return self::negate( $this->constraint->toString() ); } } public function count() { return count($this->constraint); } } expectedMessageRegExp = $expected; } protected function matches($other) { $match = PHPUnit_Util_Regex::pregMatchSafe($this->expectedMessageRegExp, $other->getMessage()); if (false === $match) { throw new PHPUnit_Framework_Exception( "Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'" ); } return 1 === $match; } protected function failureDescription($other) { return sprintf( "exception message '%s' matches '%s'", $other->getMessage(), $this->expectedMessageRegExp ); } public function toString() { return 'exception message matches '; } } hasProperty($this->attributeName); } } value = $value; } protected function matches($other) { $decodedOther = json_decode($other); if (json_last_error()) { return false; } $decodedValue = json_decode($this->value); if (json_last_error()) { return false; } return $decodedOther == $decodedValue; } public function toString() { return sprintf( 'matches JSON string "%s"', $this->value ); } } attributeName = $attributeName; } protected function matches($other) { $class = new ReflectionClass($other); return $class->hasProperty($this->attributeName); } public function toString() { return sprintf( 'has attribute "%s"', $this->attributeName ); } protected function failureDescription($other) { return sprintf( '%sclass "%s" %s', is_object($other) ? 'object of ' : '', is_object($other) ? get_class($other) : $other, $this->toString() ); } } checkForObjectIdentity = $checkForObjectIdentity; $this->checkForNonObjectIdentity = $checkForNonObjectIdentity; $this->value = $value; } protected function matches($other) { if ($other instanceof SplObjectStorage) { return $other->contains($this->value); } if (is_object($this->value)) { foreach ($other as $element) { if ($this->checkForObjectIdentity && $element === $this->value) { return true; } elseif (!$this->checkForObjectIdentity && $element == $this->value) { return true; } } } else { foreach ($other as $element) { if ($this->checkForNonObjectIdentity && $element === $this->value) { return true; } elseif (!$this->checkForNonObjectIdentity && $element == $this->value) { return true; } } } return false; } public function toString() { if (is_string($this->value) && strpos($this->value, "\n") !== false) { return 'contains "' . $this->value . '"'; } else { return 'contains ' . $this->exporter->export($this->value); } } protected function failureDescription($other) { return sprintf( '%s %s', is_array($other) ? 'an array' : 'a traversable', $this->toString() ); } } innerConstraint = $innerConstraint; } public function evaluate($other, $description = '', $returnResult = false) { try { return $this->innerConstraint->evaluate( $other, $description, $returnResult ); } catch (PHPUnit_Framework_ExpectationFailedException $e) { $this->fail($other, $description); } } public function count() { return count($this->innerConstraint); } } value = $value; } protected function matches($other) { return $this->value > $other; } public function toString() { return 'is less than ' . $this->exporter->export($this->value); } } prefix = $prefix; } protected function matches($other) { return strpos($other, $this->prefix) === 0; } public function toString() { return 'starts with "' . $this->prefix . '"'; } } value = $value; $this->delta = $delta; $this->maxDepth = $maxDepth; $this->canonicalize = $canonicalize; $this->ignoreCase = $ignoreCase; } public function evaluate($other, $description = '', $returnResult = false) { if ($this->value === $other) { return true; } $comparatorFactory = SebastianBergmann\Comparator\Factory::getInstance(); try { $comparator = $comparatorFactory->getComparatorFor( $this->value, $other ); $comparator->assertEquals( $this->value, $other, $this->delta, $this->canonicalize, $this->ignoreCase ); } catch (SebastianBergmann\Comparator\ComparisonFailure $f) { if ($returnResult) { return false; } throw new PHPUnit_Framework_ExpectationFailedException( trim($description . "\n" . $f->getMessage()), $f ); } return true; } public function toString() { $delta = ''; if (is_string($this->value)) { if (strpos($this->value, "\n") !== false) { return 'is equal to '; } else { return sprintf( 'is equal to ', $this->value ); } } else { if ($this->delta != 0) { $delta = sprintf( ' with delta <%F>', $this->delta ); } return sprintf( 'is equal to %s%s', $this->exporter->export($this->value), $delta ); } } } strict = $strict; $this->subset = $subset; } protected function matches($other) { $other = $this->toArray($other); $this->subset = $this->toArray($this->subset); $patched = array_replace_recursive($other, $this->subset); if ($this->strict) { return $other === $patched; } else { return $other == $patched; } } public function toString() { return 'has the subset ' . $this->exporter->export($this->subset); } protected function failureDescription($other) { return 'an array ' . $this->toString(); } private function toArray($other) { if (is_array($other)) { return $other; } elseif ($other instanceof ArrayObject) { return $other->getArrayCopy(); } elseif ($other instanceof Traversable) { return iterator_to_array($other); } return (array) $other; } } serializableTrace = $this->getTrace(); foreach ($this->serializableTrace as $i => $call) { unset($this->serializableTrace[$i]['args']); } } public function getSerializableTrace() { return $this->serializableTrace; } public function __toString() { $string = PHPUnit_Framework_TestFailure::exceptionToString($this); if ($trace = PHPUnit_Util_Filter::getFilteredStacktrace($this)) { $string .= "\n" . $trace; } return $string; } public function __sleep() { return array_keys(get_object_vars($this)); } } getMessage(); } } tests as $test) { $test->setDependencies($dependencies); } } } setName($name); } $this->data = $data; $this->dataName = $dataName; } public function toString() { $class = new ReflectionClass($this); $buffer = sprintf( '%s::%s', $class->name, $this->getName(false) ); return $buffer . $this->getDataSetAsString(); } public function count() { return 1; } public function getGroups() { return $this->groups; } public function setGroups(array $groups) { $this->groups = $groups; } public function getAnnotations() { return PHPUnit_Util_Test::parseTestMethodAnnotations( get_class($this), $this->name ); } public function getName($withDataSet = true) { if ($withDataSet) { return $this->name . $this->getDataSetAsString(false); } else { return $this->name; } } public function getSize() { return PHPUnit_Util_Test::getSize( get_class($this), $this->getName(false) ); } public function hasSize() { return $this->getSize() !== PHPUnit_Util_Test::UNKNOWN; } public function isSmall() { return $this->getSize() === PHPUnit_Util_Test::SMALL; } public function isMedium() { return $this->getSize() === PHPUnit_Util_Test::MEDIUM; } public function isLarge() { return $this->getSize() === PHPUnit_Util_Test::LARGE; } public function getActualOutput() { if (!$this->outputBufferingActive) { return $this->output; } else { return ob_get_contents(); } } public function hasOutput() { if (strlen($this->output) === 0) { return false; } if ($this->hasExpectationOnOutput()) { return false; } return true; } public function doesNotPerformAssertions() { return $this->doesNotPerformAssertions; } public function expectOutputRegex($expectedRegex) { if ($this->outputExpectedString !== null) { throw new PHPUnit_Framework_Exception; } if (is_string($expectedRegex) || is_null($expectedRegex)) { $this->outputExpectedRegex = $expectedRegex; } } public function expectOutputString($expectedString) { if ($this->outputExpectedRegex !== null) { throw new PHPUnit_Framework_Exception; } if (is_string($expectedString) || is_null($expectedString)) { $this->outputExpectedString = $expectedString; } } public function hasPerformedExpectationsOnOutput() { return $this->hasExpectationOnOutput(); } public function hasExpectationOnOutput() { return is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex); } public function getExpectedException() { return $this->expectedException; } public function setExpectedException($exception, $message = '', $code = null) { if (null !== $message && !is_string($message)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); } if (func_num_args() < 2) { $message = null; } $this->expectedException = $exception; if ($message !== null) { $this->expectExceptionMessage($message); } if ($code !== null) { $this->expectExceptionCode($code); } } public function setExpectedExceptionRegExp($exception, $messageRegExp = '', $code = null) { if (!is_string($messageRegExp)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); } $this->expectedException = $exception; $this->expectedExceptionMessageRegExp = $messageRegExp; if ($code !== null) { $this->expectExceptionCode($code); } } public function expectException($exception) { if (!is_string($exception)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } $this->expectedException = $exception; } public function expectExceptionCode($code) { if (!$this->expectedException) { $this->expectedException = \Exception::class; } if (!is_int($code) && !is_string($code)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer or string'); } $this->expectedExceptionCode = $code; } public function expectExceptionMessage($message) { if (!$this->expectedException) { $this->expectedException = \Exception::class; } if (!is_string($message)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } $this->expectedExceptionMessage = $message; } public function expectExceptionMessageRegExp($messageRegExp) { if (!is_string($messageRegExp)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } $this->expectedExceptionMessageRegExp = $messageRegExp; } public function setRegisterMockObjectsFromTestArgumentsRecursively($flag) { if (!is_bool($flag)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->registerMockObjectsFromTestArgumentsRecursively = $flag; } protected function setExpectedExceptionFromAnnotation() { try { $expectedException = PHPUnit_Util_Test::getExpectedException( get_class($this), $this->name ); if ($expectedException !== false) { $this->expectException($expectedException['class']); if ($expectedException['code'] !== null) { $this->expectExceptionCode($expectedException['code']); } if ($expectedException['message'] !== '') { $this->expectExceptionMessage($expectedException['message']); } elseif ($expectedException['message_regex'] !== '') { $this->expectExceptionMessageRegExp($expectedException['message_regex']); } } } catch (ReflectionException $e) { } } public function setUseErrorHandler($useErrorHandler) { $this->useErrorHandler = $useErrorHandler; } protected function setUseErrorHandlerFromAnnotation() { try { $useErrorHandler = PHPUnit_Util_Test::getErrorHandlerSettings( get_class($this), $this->name ); if ($useErrorHandler !== null) { $this->setUseErrorHandler($useErrorHandler); } } catch (ReflectionException $e) { } } protected function checkRequirements() { if (!$this->name || !method_exists($this, $this->name)) { return; } $missingRequirements = PHPUnit_Util_Test::getMissingRequirements( get_class($this), $this->name ); if (!empty($missingRequirements)) { $this->markTestSkipped(implode(PHP_EOL, $missingRequirements)); } } public function getStatus() { return $this->status; } public function markAsRisky() { $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY; } public function getStatusMessage() { return $this->statusMessage; } public function hasFailed() { $status = $this->getStatus(); return $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE || $status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; } public function run(PHPUnit_Framework_TestResult $result = null) { if ($result === null) { $result = $this->createResult(); } if (!$this instanceof PHPUnit_Framework_WarningTestCase) { $this->setTestResultObject($result); $this->setUseErrorHandlerFromAnnotation(); } if ($this->useErrorHandler !== null) { $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions(); $result->convertErrorsToExceptions($this->useErrorHandler); } if (!$this instanceof PHPUnit_Framework_WarningTestCase && !$this instanceof PHPUnit_Framework_SkippedTestCase && !$this->handleDependencies()) { return; } if ($this->runTestInSeparateProcess === true && $this->inIsolation !== true && !$this instanceof PHPUnit_Extensions_PhptTestCase) { $class = new ReflectionClass($this); $template = new Text_Template( __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl' ); if ($this->preserveGlobalState) { $constants = PHPUnit_Util_GlobalState::getConstantsAsString(); $globals = PHPUnit_Util_GlobalState::getGlobalsAsString(); $includedFiles = PHPUnit_Util_GlobalState::getIncludedFilesAsString(); $iniSettings = PHPUnit_Util_GlobalState::getIniSettingsAsString(); } else { $constants = ''; if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n"; } else { $globals = ''; } $includedFiles = ''; $iniSettings = ''; } $coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false'; $isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false'; $isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false'; $enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false'; $isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false'; $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false'; if (defined('PHPUNIT_COMPOSER_INSTALL')) { $composerAutoload = var_export(PHPUNIT_COMPOSER_INSTALL, true); } else { $composerAutoload = '\'\''; } if (defined('__PHPUNIT_PHAR__')) { $phar = var_export(__PHPUNIT_PHAR__, true); } else { $phar = '\'\''; } if ($result->getCodeCoverage()) { $codeCoverageFilter = $result->getCodeCoverage()->filter(); } else { $codeCoverageFilter = null; } $data = var_export(serialize($this->data), true); $dataName = var_export($this->dataName, true); $dependencyInput = var_export(serialize($this->dependencyInput), true); $includePath = var_export(get_include_path(), true); $codeCoverageFilter = var_export(serialize($codeCoverageFilter), true); $data = "'." . $data . ".'"; $dataName = "'.(" . $dataName . ").'"; $dependencyInput = "'." . $dependencyInput . ".'"; $includePath = "'." . $includePath . ".'"; $codeCoverageFilter = "'." . $codeCoverageFilter . ".'"; $configurationFilePath = (isset($GLOBALS['__PHPUNIT_CONFIGURATION_FILE']) ? $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] : ''); $template->setVar( [ 'composerAutoload' => $composerAutoload, 'phar' => $phar, 'filename' => $class->getFileName(), 'className' => $class->getName(), 'methodName' => $this->name, 'collectCodeCoverageInformation' => $coverage, 'data' => $data, 'dataName' => $dataName, 'dependencyInput' => $dependencyInput, 'constants' => $constants, 'globals' => $globals, 'include_path' => $includePath, 'included_files' => $includedFiles, 'iniSettings' => $iniSettings, 'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything, 'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests, 'enforcesTimeLimit' => $enforcesTimeLimit, 'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests, 'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests, 'codeCoverageFilter' => $codeCoverageFilter, 'configurationFilePath' => $configurationFilePath ] ); $this->prepareTemplate($template); $php = PHPUnit_Util_PHP::factory(); $php->runTestJob($template->render(), $this, $result); } else { $result->run($this); } if (isset($oldErrorHandlerSetting)) { $result->convertErrorsToExceptions($oldErrorHandlerSetting); } $this->result = null; return $result; } public function runBare() { $this->numAssertions = 0; $this->snapshotGlobalState(); $this->startOutputBuffering(); clearstatcache(); $currentWorkingDirectory = getcwd(); $hookMethods = PHPUnit_Util_Test::getHookMethods(get_class($this)); try { $hasMetRequirements = false; $this->checkRequirements(); $hasMetRequirements = true; if ($this->inIsolation) { foreach ($hookMethods['beforeClass'] as $method) { $this->$method(); } } $this->setExpectedExceptionFromAnnotation(); $this->setDoesNotPerformAssertionsFromAnnotation(); foreach ($hookMethods['before'] as $method) { $this->$method(); } $this->assertPreConditions(); $this->testResult = $this->runTest(); $this->verifyMockObjects(); $this->assertPostConditions(); if (!empty($this->warnings)) { throw new PHPUnit_Framework_Warning( implode( "\n", array_unique($this->warnings) ) ); } $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED; } catch (PHPUnit_Framework_IncompleteTest $e) { $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE; $this->statusMessage = $e->getMessage(); } catch (PHPUnit_Framework_SkippedTest $e) { $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED; $this->statusMessage = $e->getMessage(); } catch (PHPUnit_Framework_Warning $e) { $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_WARNING; $this->statusMessage = $e->getMessage(); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; $this->statusMessage = $e->getMessage(); } catch (PredictionException $e) { $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; $this->statusMessage = $e->getMessage(); } catch (Throwable $_e) { $e = $_e; } catch (Exception $_e) { $e = $_e; } $this->mockObjects = []; $this->prophet = null; try { if ($hasMetRequirements) { foreach ($hookMethods['after'] as $method) { $this->$method(); } if ($this->inIsolation) { foreach ($hookMethods['afterClass'] as $method) { $this->$method(); } } } } catch (Throwable $_e) { if (!isset($e)) { $e = $_e; } } catch (Exception $_e) { if (!isset($e)) { $e = $_e; } } try { $this->stopOutputBuffering(); } catch (PHPUnit_Framework_RiskyTestError $_e) { if (!isset($e)) { $e = $_e; } } if (isset($_e)) { $this->status = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; $this->statusMessage = $_e->getMessage(); } clearstatcache(); if ($currentWorkingDirectory != getcwd()) { chdir($currentWorkingDirectory); } $this->restoreGlobalState(); foreach ($this->iniSettings as $varName => $oldValue) { ini_set($varName, $oldValue); } $this->iniSettings = []; foreach ($this->locale as $category => $locale) { setlocale($category, $locale); } if (!isset($e)) { try { if ($this->outputExpectedRegex !== null) { $this->assertRegExp($this->outputExpectedRegex, $this->output); } elseif ($this->outputExpectedString !== null) { $this->assertEquals($this->outputExpectedString, $this->output); } } catch (Throwable $_e) { $e = $_e; } catch (Exception $_e) { $e = $_e; } } if (isset($e)) { if ($e instanceof PredictionException) { $e = new PHPUnit_Framework_AssertionFailedError($e->getMessage()); } $this->onNotSuccessfulTest($e); } } protected function runTest() { if ($this->name === null) { throw new PHPUnit_Framework_Exception( 'PHPUnit_Framework_TestCase::$name must not be null.' ); } try { $class = new ReflectionClass($this); $method = $class->getMethod($this->name); } catch (ReflectionException $e) { $this->fail($e->getMessage()); } $testArguments = array_merge($this->data, $this->dependencyInput); $this->registerMockObjectsFromTestArguments($testArguments); try { $testResult = $method->invokeArgs($this, $testArguments); } catch (Throwable $_e) { $e = $_e; } catch (Exception $_e) { $e = $_e; } if (isset($e)) { $checkException = false; if (!($e instanceof PHPUnit_Framework_SkippedTestError) && is_string($this->expectedException)) { $checkException = true; if ($e instanceof PHPUnit_Framework_Exception) { $checkException = false; } $reflector = new ReflectionClass($this->expectedException); if ($this->expectedException === 'PHPUnit_Framework_Exception' || $this->expectedException === '\PHPUnit_Framework_Exception' || $reflector->isSubclassOf('PHPUnit_Framework_Exception')) { $checkException = true; } } if ($checkException) { $this->assertThat( $e, new PHPUnit_Framework_Constraint_Exception( $this->expectedException ) ); if ($this->expectedExceptionMessage !== null) { $this->assertThat( $e, new PHPUnit_Framework_Constraint_ExceptionMessage( $this->expectedExceptionMessage ) ); } if ($this->expectedExceptionMessageRegExp !== null) { $this->assertThat( $e, new PHPUnit_Framework_Constraint_ExceptionMessageRegExp( $this->expectedExceptionMessageRegExp ) ); } if ($this->expectedExceptionCode !== null) { $this->assertThat( $e, new PHPUnit_Framework_Constraint_ExceptionCode( $this->expectedExceptionCode ) ); } return; } else { throw $e; } } if ($this->expectedException !== null) { $this->assertThat( null, new PHPUnit_Framework_Constraint_Exception( $this->expectedException ) ); } return $testResult; } protected function verifyMockObjects() { foreach ($this->mockObjects as $mockObject) { if ($mockObject->__phpunit_hasMatchers()) { $this->numAssertions++; } $mockObject->__phpunit_verify( $this->shouldInvocationMockerBeReset($mockObject) ); } if ($this->prophet !== null) { try { $this->prophet->checkPredictions(); } catch (Throwable $t) { } catch (Exception $t) { } foreach ($this->prophet->getProphecies() as $objectProphecy) { foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { foreach ($methodProphecies as $methodProphecy) { $this->numAssertions += count($methodProphecy->getCheckedPredictions()); } } } if (isset($t)) { throw $t; } } } public function setName($name) { $this->name = $name; } public function setDependencies(array $dependencies) { $this->dependencies = $dependencies; } public function hasDependencies() { return count($this->dependencies) > 0; } public function setDependencyInput(array $dependencyInput) { $this->dependencyInput = $dependencyInput; } public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState) { $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; } public function setBackupGlobals($backupGlobals) { if (is_null($this->backupGlobals) && is_bool($backupGlobals)) { $this->backupGlobals = $backupGlobals; } } public function setBackupStaticAttributes($backupStaticAttributes) { if (is_null($this->backupStaticAttributes) && is_bool($backupStaticAttributes)) { $this->backupStaticAttributes = $backupStaticAttributes; } } public function setRunTestInSeparateProcess($runTestInSeparateProcess) { if (is_bool($runTestInSeparateProcess)) { if ($this->runTestInSeparateProcess === null) { $this->runTestInSeparateProcess = $runTestInSeparateProcess; } } else { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } } public function setPreserveGlobalState($preserveGlobalState) { if (is_bool($preserveGlobalState)) { $this->preserveGlobalState = $preserveGlobalState; } else { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } } public function setInIsolation($inIsolation) { if (is_bool($inIsolation)) { $this->inIsolation = $inIsolation; } else { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } } public function isInIsolation() { return $this->inIsolation; } public function getResult() { return $this->testResult; } public function setResult($result) { $this->testResult = $result; } public function setOutputCallback($callback) { if (!is_callable($callback)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'callback'); } $this->outputCallback = $callback; } public function getTestResultObject() { return $this->result; } public function setTestResultObject(PHPUnit_Framework_TestResult $result) { $this->result = $result; } public function registerMockObject(PHPUnit_Framework_MockObject_MockObject $mockObject) { $this->mockObjects[] = $mockObject; } protected function iniSet($varName, $newValue) { if (!is_string($varName)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } $currentValue = ini_set($varName, $newValue); if ($currentValue !== false) { $this->iniSettings[$varName] = $currentValue; } else { throw new PHPUnit_Framework_Exception( sprintf( 'INI setting "%s" could not be set to "%s".', $varName, $newValue ) ); } } protected function setLocale() { $args = func_get_args(); if (count($args) < 2) { throw new PHPUnit_Framework_Exception; } $category = $args[0]; $locale = $args[1]; $categories = [ LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME ]; if (defined('LC_MESSAGES')) { $categories[] = LC_MESSAGES; } if (!in_array($category, $categories)) { throw new PHPUnit_Framework_Exception; } if (!is_array($locale) && !is_string($locale)) { throw new PHPUnit_Framework_Exception; } $this->locale[$category] = setlocale($category, 0); $result = call_user_func_array('setlocale', $args); if ($result === false) { throw new PHPUnit_Framework_Exception( 'The locale functionality is not implemented on your platform, ' . 'the specified locale does not exist or the category name is ' . 'invalid.' ); } } public function getMockBuilder($className) { return new PHPUnit_Framework_MockObject_MockBuilder($this, $className); } protected function createMock($originalClassName) { return $this->getMockBuilder($originalClassName) ->disableOriginalConstructor() ->disableOriginalClone() ->disableArgumentCloning() ->disallowMockingUnknownTypes() ->getMock(); } protected function createConfiguredMock($originalClassName, array $configuration) { $o = $this->createMock($originalClassName); foreach ($configuration as $method => $return) { $o->method($method)->willReturn($return); } return $o; } protected function createPartialMock($originalClassName, array $methods) { return $this->getMockBuilder($originalClassName) ->disableOriginalConstructor() ->disableOriginalClone() ->disableArgumentCloning() ->disallowMockingUnknownTypes() ->setMethods(empty($methods) ? null : $methods) ->getMock(); } protected function getMock($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null) { $this->warnings[] = 'PHPUnit_Framework_TestCase::getMock() is deprecated, use PHPUnit_Framework_TestCase::createMock() or PHPUnit_Framework_TestCase::getMockBuilder() instead'; $mockObject = $this->getMockObjectGenerator()->getMock( $originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods, $proxyTarget ); $this->registerMockObject($mockObject); return $mockObject; } protected function getMockWithoutInvokingTheOriginalConstructor($originalClassName) { $this->warnings[] = 'PHPUnit_Framework_TestCase::getMockWithoutInvokingTheOriginalConstructor() is deprecated, use PHPUnit_Framework_TestCase::createMock() instead'; return $this->getMockBuilder($originalClassName) ->disableOriginalConstructor() ->getMock(); } protected function getMockClass($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false) { $mock = $this->getMockObjectGenerator()->getMock( $originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments ); return get_class($mock); } protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false) { $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass( $originalClassName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments ); $this->registerMockObject($mockObject); return $mockObject; } protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = []) { if ($originalClassName === '') { $originalClassName = pathinfo(basename(parse_url($wsdlFile)['path']), PATHINFO_FILENAME); } if (!class_exists($originalClassName)) { eval( $this->getMockObjectGenerator()->generateClassFromWsdl( $wsdlFile, $originalClassName, $methods, $options ) ); } $mockObject = $this->getMockObjectGenerator()->getMock( $originalClassName, $methods, ['', $options], $mockClassName, $callOriginalConstructor, false, false ); $this->registerMockObject($mockObject); return $mockObject; } protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false) { $mockObject = $this->getMockObjectGenerator()->getMockForTrait( $traitName, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments ); $this->registerMockObject($mockObject); return $mockObject; } protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false) { return $this->getMockObjectGenerator()->getObjectForTrait( $traitName, $arguments, $traitClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments ); } protected function prophesize($classOrInterface = null) { return $this->getProphet()->prophesize($classOrInterface); } public function addToAssertionCount($count) { $this->numAssertions += $count; } public function getNumAssertions() { return $this->numAssertions; } public static function any() { return new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount; } public static function never() { return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(0); } public static function atLeast($requiredInvocations) { return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastCount( $requiredInvocations ); } public static function atLeastOnce() { return new PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce; } public static function once() { return new PHPUnit_Framework_MockObject_Matcher_InvokedCount(1); } public static function exactly($count) { return new PHPUnit_Framework_MockObject_Matcher_InvokedCount($count); } public static function atMost($allowedInvocations) { return new PHPUnit_Framework_MockObject_Matcher_InvokedAtMostCount( $allowedInvocations ); } public static function at($index) { return new PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex($index); } public static function returnValue($value) { return new PHPUnit_Framework_MockObject_Stub_Return($value); } public static function returnValueMap(array $valueMap) { return new PHPUnit_Framework_MockObject_Stub_ReturnValueMap($valueMap); } public static function returnArgument($argumentIndex) { return new PHPUnit_Framework_MockObject_Stub_ReturnArgument( $argumentIndex ); } public static function returnCallback($callback) { return new PHPUnit_Framework_MockObject_Stub_ReturnCallback($callback); } public static function returnSelf() { return new PHPUnit_Framework_MockObject_Stub_ReturnSelf(); } public static function throwException($exception) { return new PHPUnit_Framework_MockObject_Stub_Exception($exception); } public static function onConsecutiveCalls() { $args = func_get_args(); return new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls($args); } public function usesDataProvider() { return !empty($this->data); } public function dataDescription() { return is_string($this->dataName) ? $this->dataName : ''; } protected function getDataSetAsString($includeData = true) { $buffer = ''; if (!empty($this->data)) { if (is_int($this->dataName)) { $buffer .= sprintf(' with data set #%d', $this->dataName); } else { $buffer .= sprintf(' with data set "%s"', $this->dataName); } $exporter = new Exporter; if ($includeData) { $buffer .= sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); } } return $buffer; } protected function getProvidedData() { return $this->data; } protected function createResult() { return new PHPUnit_Framework_TestResult; } protected function handleDependencies() { if (!empty($this->dependencies) && !$this->inIsolation) { $className = get_class($this); $passed = $this->result->passed(); $passedKeys = array_keys($passed); $numKeys = count($passedKeys); for ($i = 0; $i < $numKeys; $i++) { $pos = strpos($passedKeys[$i], ' with data set'); if ($pos !== false) { $passedKeys[$i] = substr($passedKeys[$i], 0, $pos); } } $passedKeys = array_flip(array_unique($passedKeys)); foreach ($this->dependencies as $dependency) { $clone = false; if (strpos($dependency, 'clone ') === 0) { $clone = true; $dependency = substr($dependency, strlen('clone ')); } elseif (strpos($dependency, '!clone ') === 0) { $clone = false; $dependency = substr($dependency, strlen('!clone ')); } if (strpos($dependency, '::') === false) { $dependency = $className . '::' . $dependency; } if (!isset($passedKeys[$dependency])) { $this->result->startTest($this); $this->result->addError( $this, new PHPUnit_Framework_SkippedTestError( sprintf( 'This test depends on "%s" to pass.', $dependency ) ), 0 ); $this->result->endTest($this, 0); return false; } if (isset($passed[$dependency])) { if ($passed[$dependency]['size'] != PHPUnit_Util_Test::UNKNOWN && $this->getSize() != PHPUnit_Util_Test::UNKNOWN && $passed[$dependency]['size'] > $this->getSize()) { $this->result->addError( $this, new PHPUnit_Framework_SkippedTestError( 'This test depends on a test that is larger than itself.' ), 0 ); return false; } if ($clone) { $deepCopy = new DeepCopy; $deepCopy->skipUncloneable(false); $this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']); } else { $this->dependencyInput[$dependency] = $passed[$dependency]['result']; } } else { $this->dependencyInput[$dependency] = null; } } } return true; } public static function setUpBeforeClass() { } protected function setUp() { } protected function assertPreConditions() { } protected function assertPostConditions() { } protected function tearDown() { } public static function tearDownAfterClass() { } protected function onNotSuccessfulTest($e) { $expected = PHP_MAJOR_VERSION >= 7 ? 'Throwable' : 'Exception'; if ($e instanceof $expected) { throw $e; } throw PHPUnit_Util_InvalidArgumentHelper::factory( 1, 'Throwable or Exception' ); } protected function prepareTemplate(Text_Template $template) { } protected function getMockObjectGenerator() { if (null === $this->mockObjectGenerator) { $this->mockObjectGenerator = new PHPUnit_Framework_MockObject_Generator; } return $this->mockObjectGenerator; } private function startOutputBuffering() { ob_start(); $this->outputBufferingActive = true; $this->outputBufferingLevel = ob_get_level(); } private function stopOutputBuffering() { if (ob_get_level() != $this->outputBufferingLevel) { while (ob_get_level() >= $this->outputBufferingLevel) { ob_end_clean(); } throw new PHPUnit_Framework_RiskyTestError( 'Test code or tested code did not (only) close its own output buffers' ); } $output = ob_get_contents(); if ($this->outputCallback === false) { $this->output = $output; } else { $this->output = call_user_func_array( $this->outputCallback, [$output] ); } ob_end_clean(); $this->outputBufferingActive = false; $this->outputBufferingLevel = ob_get_level(); } private function snapshotGlobalState() { $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; if ($this->runTestInSeparateProcess || $this->inIsolation || (!$backupGlobals && !$this->backupStaticAttributes)) { return; } $this->snapshot = $this->createGlobalStateSnapshot($backupGlobals); } private function restoreGlobalState() { if (!$this->snapshot instanceof Snapshot) { return; } $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; if ($this->beStrictAboutChangesToGlobalState) { try { $this->compareGlobalStateSnapshots( $this->snapshot, $this->createGlobalStateSnapshot($backupGlobals) ); } catch (PHPUnit_Framework_RiskyTestError $rte) { } } $restorer = new Restorer; if ($backupGlobals) { $restorer->restoreGlobalVariables($this->snapshot); } if ($this->backupStaticAttributes) { $restorer->restoreStaticAttributes($this->snapshot); } $this->snapshot = null; if (isset($rte)) { throw $rte; } } private function createGlobalStateSnapshot($backupGlobals) { $blacklist = new Blacklist; foreach ($this->backupGlobalsBlacklist as $globalVariable) { $blacklist->addGlobalVariable($globalVariable); } if (!defined('PHPUNIT_TESTSUITE')) { $blacklist->addClassNamePrefix('PHPUnit'); $blacklist->addClassNamePrefix('File_Iterator'); $blacklist->addClassNamePrefix('SebastianBergmann\CodeCoverage'); $blacklist->addClassNamePrefix('PHP_Invoker'); $blacklist->addClassNamePrefix('PHP_Timer'); $blacklist->addClassNamePrefix('PHP_Token'); $blacklist->addClassNamePrefix('Symfony'); $blacklist->addClassNamePrefix('Text_Template'); $blacklist->addClassNamePrefix('Doctrine\Instantiator'); $blacklist->addClassNamePrefix('Prophecy'); foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { foreach ($attributes as $attribute) { $blacklist->addStaticAttribute($class, $attribute); } } } return new Snapshot( $blacklist, $backupGlobals, (bool) $this->backupStaticAttributes, false, false, false, false, false, false, false ); } private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after) { $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; if ($backupGlobals) { $this->compareGlobalStateSnapshotPart( $before->globalVariables(), $after->globalVariables(), "--- Global variables before the test\n+++ Global variables after the test\n" ); $this->compareGlobalStateSnapshotPart( $before->superGlobalVariables(), $after->superGlobalVariables(), "--- Super-global variables before the test\n+++ Super-global variables after the test\n" ); } if ($this->backupStaticAttributes) { $this->compareGlobalStateSnapshotPart( $before->staticAttributes(), $after->staticAttributes(), "--- Static attributes before the test\n+++ Static attributes after the test\n" ); } } private function compareGlobalStateSnapshotPart(array $before, array $after, $header) { if ($before != $after) { $differ = new Differ($header); $exporter = new Exporter; $diff = $differ->diff( $exporter->export($before), $exporter->export($after) ); throw new PHPUnit_Framework_RiskyTestError( $diff ); } } private function getProphet() { if ($this->prophet === null) { $this->prophet = new Prophet; } return $this->prophet; } private function shouldInvocationMockerBeReset(PHPUnit_Framework_MockObject_MockObject $mock) { $enumerator = new Enumerator; foreach ($enumerator->enumerate($this->dependencyInput) as $object) { if ($mock === $object) { return false; } } if (!is_array($this->testResult) && !is_object($this->testResult)) { return true; } foreach ($enumerator->enumerate($this->testResult) as $object) { if ($mock === $object) { return false; } } return true; } private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []) { if ($this->registerMockObjectsFromTestArgumentsRecursively) { $enumerator = new Enumerator; foreach ($enumerator->enumerate($testArguments) as $object) { if ($object instanceof PHPUnit_Framework_MockObject_MockObject) { $this->registerMockObject($object); } } } else { foreach ($testArguments as $testArgument) { if ($testArgument instanceof PHPUnit_Framework_MockObject_MockObject) { if ($this->isCloneable($testArgument)) { $testArgument = clone $testArgument; } $this->registerMockObject($testArgument); } elseif (is_array($testArgument) && !in_array($testArgument, $visited, true)) { $visited[] = $testArgument; $this->registerMockObjectsFromTestArguments( $testArgument, $visited ); } } } } private function setDoesNotPerformAssertionsFromAnnotation() { $annotations = $this->getAnnotations(); if (isset($annotations['method']['doesNotPerformAssertions'])) { $this->doesNotPerformAssertions = true; } } private function isCloneable(PHPUnit_Framework_MockObject_MockObject $testArgument) { $reflector = new ReflectionObject($testArgument); if (!$reflector->isCloneable()) { return false; } if ($reflector->hasMethod('__clone') && $reflector->getMethod('__clone')->isPublic()) { return true; } return false; } } declaredClasses = get_declared_classes(); $argumentsValid = false; if (is_object($theClass) && $theClass instanceof ReflectionClass) { $argumentsValid = true; } elseif (is_string($theClass) && $theClass !== '' && class_exists($theClass, false)) { $argumentsValid = true; if ($name == '') { $name = $theClass; } $theClass = new ReflectionClass($theClass); } elseif (is_string($theClass)) { $this->setName($theClass); return; } if (!$argumentsValid) { throw new PHPUnit_Framework_Exception; } if (!$theClass->isSubclassOf('PHPUnit_Framework_TestCase')) { throw new PHPUnit_Framework_Exception( 'Class "' . $theClass->name . '" does not extend PHPUnit_Framework_TestCase.' ); } if ($name != '') { $this->setName($name); } else { $this->setName($theClass->getName()); } $constructor = $theClass->getConstructor(); if ($constructor !== null && !$constructor->isPublic()) { $this->addTest( self::warning( sprintf( 'Class "%s" has no public constructor.', $theClass->getName() ) ) ); return; } foreach ($theClass->getMethods() as $method) { $this->addTestMethod($theClass, $method); } if (empty($this->tests)) { $this->addTest( self::warning( sprintf( 'No tests found in class "%s".', $theClass->getName() ) ) ); } $this->testCase = true; } public function toString() { return $this->getName(); } public function addTest(PHPUnit_Framework_Test $test, $groups = []) { $class = new ReflectionClass($test); if (!$class->isAbstract()) { $this->tests[] = $test; $this->numTests = -1; if ($test instanceof self && empty($groups)) { $groups = $test->getGroups(); } if (empty($groups)) { $groups = ['default']; } foreach ($groups as $group) { if (!isset($this->groups[$group])) { $this->groups[$group] = [$test]; } else { $this->groups[$group][] = $test; } } if ($test instanceof PHPUnit_Framework_TestCase) { $test->setGroups($groups); } } } public function addTestSuite($testClass) { if (is_string($testClass) && class_exists($testClass)) { $testClass = new ReflectionClass($testClass); } if (!is_object($testClass)) { throw PHPUnit_Util_InvalidArgumentHelper::factory( 1, 'class name or object' ); } if ($testClass instanceof self) { $this->addTest($testClass); } elseif ($testClass instanceof ReflectionClass) { $suiteMethod = false; if (!$testClass->isAbstract()) { if ($testClass->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) { $method = $testClass->getMethod( PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME ); if ($method->isStatic()) { $this->addTest( $method->invoke(null, $testClass->getName()) ); $suiteMethod = true; } } } if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(PHPUnit_Framework_TestCase::class)) { $this->addTest(new self($testClass)); } } else { throw new PHPUnit_Framework_Exception; } } public function addTestFile($filename) { if (!is_string($filename)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } if (file_exists($filename) && substr($filename, -5) == '.phpt') { $this->addTest( new PHPUnit_Extensions_PhptTestCase($filename) ); return; } $filename = PHPUnit_Util_Fileloader::checkAndLoad($filename); $newClasses = array_diff(get_declared_classes(), $this->declaredClasses); if (!empty($newClasses)) { $this->foundClasses = array_merge($newClasses, $this->foundClasses); $this->declaredClasses = get_declared_classes(); } $shortname = basename($filename, '.php'); $shortnameRegEx = '/(?:^|_|\\\\)' . preg_quote($shortname, '/') . '$/'; foreach ($this->foundClasses as $i => $className) { if (preg_match($shortnameRegEx, $className)) { $class = new ReflectionClass($className); if ($class->getFileName() == $filename) { $newClasses = [$className]; unset($this->foundClasses[$i]); break; } } } foreach ($newClasses as $className) { if (strpos($className, 'PHPUnit_Framework') === 0) { continue; } $class = new ReflectionClass($className); if (!$class->isAbstract()) { if ($class->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) { $method = $class->getMethod( PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME ); if ($method->isStatic()) { $this->addTest($method->invoke(null, $className)); } } elseif ($class->implementsInterface('PHPUnit_Framework_Test')) { $this->addTestSuite($class); } } } $this->numTests = -1; } public function addTestFiles($filenames) { if (!(is_array($filenames) || (is_object($filenames) && $filenames instanceof Iterator))) { throw PHPUnit_Util_InvalidArgumentHelper::factory( 1, 'array or iterator' ); } foreach ($filenames as $filename) { $this->addTestFile((string) $filename); } } public function count($preferCache = false) { if ($preferCache && $this->cachedNumTests !== null) { $numTests = $this->cachedNumTests; } else { $numTests = 0; foreach ($this as $test) { $numTests += count($test); } $this->cachedNumTests = $numTests; } return $numTests; } public static function createTest(ReflectionClass $theClass, $name) { $className = $theClass->getName(); if (!$theClass->isInstantiable()) { return self::warning( sprintf('Cannot instantiate class "%s".', $className) ); } $backupSettings = PHPUnit_Util_Test::getBackupSettings( $className, $name ); $preserveGlobalState = PHPUnit_Util_Test::getPreserveGlobalStateSettings( $className, $name ); $runTestInSeparateProcess = PHPUnit_Util_Test::getProcessIsolationSettings( $className, $name ); $constructor = $theClass->getConstructor(); if ($constructor !== null) { $parameters = $constructor->getParameters(); if (count($parameters) < 2) { $test = new $className; } else { try { $data = PHPUnit_Util_Test::getProvidedData( $className, $name ); } catch (PHPUnit_Framework_IncompleteTestError $e) { $message = sprintf( 'Test for %s::%s marked incomplete by data provider', $className, $name ); $_message = $e->getMessage(); if (!empty($_message)) { $message .= "\n" . $_message; } $data = self::incompleteTest($className, $name, $message); } catch (PHPUnit_Framework_SkippedTestError $e) { $message = sprintf( 'Test for %s::%s skipped by data provider', $className, $name ); $_message = $e->getMessage(); if (!empty($_message)) { $message .= "\n" . $_message; } $data = self::skipTest($className, $name, $message); } catch (Throwable $_t) { $t = $_t; } catch (Exception $_t) { $t = $_t; } if (isset($t)) { $message = sprintf( 'The data provider specified for %s::%s is invalid.', $className, $name ); $_message = $t->getMessage(); if (!empty($_message)) { $message .= "\n" . $_message; } $data = self::warning($message); } if (isset($data)) { $test = new PHPUnit_Framework_TestSuite_DataProvider( $className . '::' . $name ); if (empty($data)) { $data = self::warning( sprintf( 'No tests found in suite "%s".', $test->getName() ) ); } $groups = PHPUnit_Util_Test::getGroups($className, $name); if ($data instanceof PHPUnit_Framework_WarningTestCase || $data instanceof PHPUnit_Framework_SkippedTestCase || $data instanceof PHPUnit_Framework_IncompleteTestCase) { $test->addTest($data, $groups); } else { foreach ($data as $_dataName => $_data) { $_test = new $className($name, $_data, $_dataName); if ($runTestInSeparateProcess) { $_test->setRunTestInSeparateProcess(true); if ($preserveGlobalState !== null) { $_test->setPreserveGlobalState($preserveGlobalState); } } if ($backupSettings['backupGlobals'] !== null) { $_test->setBackupGlobals( $backupSettings['backupGlobals'] ); } if ($backupSettings['backupStaticAttributes'] !== null) { $_test->setBackupStaticAttributes( $backupSettings['backupStaticAttributes'] ); } $test->addTest($_test, $groups); } } } else { $test = new $className; } } } if (!isset($test)) { throw new PHPUnit_Framework_Exception('No valid test provided.'); } if ($test instanceof PHPUnit_Framework_TestCase) { $test->setName($name); if ($runTestInSeparateProcess) { $test->setRunTestInSeparateProcess(true); if ($preserveGlobalState !== null) { $test->setPreserveGlobalState($preserveGlobalState); } } if ($backupSettings['backupGlobals'] !== null) { $test->setBackupGlobals($backupSettings['backupGlobals']); } if ($backupSettings['backupStaticAttributes'] !== null) { $test->setBackupStaticAttributes( $backupSettings['backupStaticAttributes'] ); } } return $test; } protected function createResult() { return new PHPUnit_Framework_TestResult; } public function getName() { return $this->name; } public function getGroups() { return array_keys($this->groups); } public function getGroupDetails() { return $this->groups; } public function setGroupDetails(array $groups) { $this->groups = $groups; } public function run(PHPUnit_Framework_TestResult $result = null) { if ($result === null) { $result = $this->createResult(); } if (count($this) == 0) { return $result; } $hookMethods = PHPUnit_Util_Test::getHookMethods($this->name); $result->startTestSuite($this); try { $this->setUp(); foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { if ($this->testCase === true && class_exists($this->name, false) && method_exists($this->name, $beforeClassMethod)) { if ($missingRequirements = PHPUnit_Util_Test::getMissingRequirements($this->name, $beforeClassMethod)) { $this->markTestSuiteSkipped(implode(PHP_EOL, $missingRequirements)); } call_user_func([$this->name, $beforeClassMethod]); } } } catch (PHPUnit_Framework_SkippedTestSuiteError $e) { $numTests = count($this); for ($i = 0; $i < $numTests; $i++) { $result->startTest($this); $result->addFailure($this, $e, 0); $result->endTest($this, 0); } $this->tearDown(); $result->endTestSuite($this); return $result; } catch (Throwable $_t) { $t = $_t; } catch (Exception $_t) { $t = $_t; } if (isset($t)) { $numTests = count($this); for ($i = 0; $i < $numTests; $i++) { if ($result->shouldStop()) { break; } $result->startTest($this); $result->addError($this, $t, 0); $result->endTest($this, 0); } $this->tearDown(); $result->endTestSuite($this); return $result; } foreach ($this as $test) { if ($result->shouldStop()) { break; } if ($test instanceof PHPUnit_Framework_TestCase || $test instanceof self) { $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); $test->setBackupGlobals($this->backupGlobals); $test->setBackupStaticAttributes($this->backupStaticAttributes); $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); } $test->run($result); } foreach ($hookMethods['afterClass'] as $afterClassMethod) { if ($this->testCase === true && class_exists($this->name, false) && method_exists($this->name, $afterClassMethod)) { call_user_func([$this->name, $afterClassMethod]); } } $this->tearDown(); $result->endTestSuite($this); return $result; } public function setRunTestInSeparateProcess($runTestInSeparateProcess) { if (is_bool($runTestInSeparateProcess)) { $this->runTestInSeparateProcess = $runTestInSeparateProcess; } else { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } } public function runTest(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result) { $test->run($result); } public function setName($name) { $this->name = $name; } public function testAt($index) { if (isset($this->tests[$index])) { return $this->tests[$index]; } else { return false; } } public function tests() { return $this->tests; } public function setTests(array $tests) { $this->tests = $tests; } public function markTestSuiteSkipped($message = '') { throw new PHPUnit_Framework_SkippedTestSuiteError($message); } protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method) { if (!$this->isTestMethod($method)) { return; } $name = $method->getName(); if (!$method->isPublic()) { $this->addTest( self::warning( sprintf( 'Test method "%s" in test class "%s" is not public.', $name, $class->getName() ) ) ); return; } $test = self::createTest($class, $name); if ($test instanceof PHPUnit_Framework_TestCase || $test instanceof PHPUnit_Framework_TestSuite_DataProvider) { $test->setDependencies( PHPUnit_Util_Test::getDependencies($class->getName(), $name) ); } $this->addTest( $test, PHPUnit_Util_Test::getGroups($class->getName(), $name) ); } public static function isTestMethod(ReflectionMethod $method) { if (strpos($method->name, 'test') === 0) { return true; } $docComment = $method->getDocComment(); return strpos($docComment, '@test') !== false || strpos($docComment, '@scenario') !== false; } protected static function warning($message) { return new PHPUnit_Framework_WarningTestCase($message); } protected static function skipTest($class, $methodName, $message) { return new PHPUnit_Framework_SkippedTestCase($class, $methodName, $message); } protected static function incompleteTest($class, $methodName, $message) { return new PHPUnit_Framework_IncompleteTestCase($class, $methodName, $message); } public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState) { if (is_null($this->beStrictAboutChangesToGlobalState) && is_bool($beStrictAboutChangesToGlobalState)) { $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; } } public function setBackupGlobals($backupGlobals) { if (is_null($this->backupGlobals) && is_bool($backupGlobals)) { $this->backupGlobals = $backupGlobals; } } public function setBackupStaticAttributes($backupStaticAttributes) { if (is_null($this->backupStaticAttributes) && is_bool($backupStaticAttributes)) { $this->backupStaticAttributes = $backupStaticAttributes; } } public function getIterator() { $iterator = new PHPUnit_Util_TestSuiteIterator($this); if ($this->iteratorFilter !== null) { $iterator = $this->iteratorFilter->factory($iterator, $this); } return $iterator; } public function injectFilter(PHPUnit_Runner_Filter_Factory $filter) { $this->iteratorFilter = $filter; foreach ($this as $test) { if ($test instanceof self) { $test->injectFilter($filter); } } } protected function setUp() { } protected function tearDown() { } } message = $message; parent::__construct('Warning'); } protected function runTest() { throw new PHPUnit_Framework_Warning($this->message); } public function getMessage() { return $this->message; } public function toString() { return 'Warning'; } } getMessage(); } } testName = $failedTest->toString(); } else { $this->testName = get_class($failedTest); } if (!$failedTest instanceof PHPUnit_Framework_TestCase || !$failedTest->isInIsolation()) { $this->failedTest = $failedTest; } $this->thrownException = $t; } public function toString() { return sprintf( '%s: %s', $this->testName, $this->thrownException->getMessage() ); } public function getExceptionAsString() { return self::exceptionToString($this->thrownException); } public static function exceptionToString(Exception $e) { if ($e instanceof PHPUnit_Framework_SelfDescribing) { $buffer = $e->toString(); if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) { $buffer = $buffer . $e->getComparisonFailure()->getDiff(); } if (!empty($buffer)) { $buffer = trim($buffer) . "\n"; } } elseif ($e instanceof PHPUnit_Framework_Error) { $buffer = $e->getMessage() . "\n"; } elseif ($e instanceof PHPUnit_Framework_ExceptionWrapper) { $buffer = $e->getClassName() . ': ' . $e->getMessage() . "\n"; } else { $buffer = get_class($e) . ': ' . $e->getMessage() . "\n"; } return $buffer; } public function getTestName() { return $this->testName; } public function failedTest() { return $this->failedTest; } public function thrownException() { return $this->thrownException; } public function exceptionMessage() { return $this->thrownException()->getMessage(); } public function isFailure() { return ($this->thrownException() instanceof PHPUnit_Framework_AssertionFailedError); } } message = $message; parent::__construct($className . '::' . $methodName); } protected function runTest() { $this->markTestSkipped($this->message); } public function getMessage() { return $this->message; } public function toString() { return $this->getName(); } } importNode($expectedElement, true); $tmp = new DOMDocument; $actualElement = $tmp->importNode($actualElement, true); unset($tmp); static::assertEquals( $expectedElement->tagName, $actualElement->tagName, $message ); if ($checkAttributes) { static::assertEquals( $expectedElement->attributes->length, $actualElement->attributes->length, sprintf( '%s%sNumber of attributes on node "%s" does not match', $message, !empty($message) ? "\n" : '', $expectedElement->tagName ) ); for ($i = 0; $i < $expectedElement->attributes->length; $i++) { $expectedAttribute = $expectedElement->attributes->item($i); $actualAttribute = $actualElement->attributes->getNamedItem( $expectedAttribute->name ); if (!$actualAttribute) { static::fail( sprintf( '%s%sCould not find attribute "%s" on node "%s"', $message, !empty($message) ? "\n" : '', $expectedAttribute->name, $expectedElement->tagName ) ); } } } PHPUnit_Util_XML::removeCharacterDataNodes($expectedElement); PHPUnit_Util_XML::removeCharacterDataNodes($actualElement); static::assertEquals( $expectedElement->childNodes->length, $actualElement->childNodes->length, sprintf( '%s%sNumber of child nodes of "%s" differs', $message, !empty($message) ? "\n" : '', $expectedElement->tagName ) ); for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { static::assertEqualXMLStructure( $expectedElement->childNodes->item($i), $actualElement->childNodes->item($i), $checkAttributes, $message ); } } public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '') { self::$count += count($constraint); $constraint->evaluate($value, $message); } public static function assertJson($actualJson, $message = '') { if (!is_string($actualJson)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } static::assertThat($actualJson, static::isJson(), $message); } public static function assertJsonStringEqualsJsonString($expectedJson, $actualJson, $message = '') { static::assertJson($expectedJson, $message); static::assertJson($actualJson, $message); $expected = json_decode($expectedJson); $actual = json_decode($actualJson); static::assertEquals($expected, $actual, $message); } public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, $message = '') { static::assertJson($expectedJson, $message); static::assertJson($actualJson, $message); $expected = json_decode($expectedJson); $actual = json_decode($actualJson); static::assertNotEquals($expected, $actual, $message); } public static function assertJsonStringEqualsJsonFile($expectedFile, $actualJson, $message = '') { static::assertFileExists($expectedFile, $message); $expectedJson = file_get_contents($expectedFile); static::assertJson($expectedJson, $message); static::assertJson($actualJson, $message); $constraint = new PHPUnit_Framework_Constraint_JsonMatches( $expectedJson ); static::assertThat($actualJson, $constraint, $message); } public static function assertJsonStringNotEqualsJsonFile($expectedFile, $actualJson, $message = '') { static::assertFileExists($expectedFile, $message); $expectedJson = file_get_contents($expectedFile); static::assertJson($expectedJson, $message); static::assertJson($actualJson, $message); $constraint = new PHPUnit_Framework_Constraint_JsonMatches( $expectedJson ); static::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraint), $message); } public static function assertJsonFileEqualsJsonFile($expectedFile, $actualFile, $message = '') { static::assertFileExists($expectedFile, $message); static::assertFileExists($actualFile, $message); $actualJson = file_get_contents($actualFile); $expectedJson = file_get_contents($expectedFile); static::assertJson($expectedJson, $message); static::assertJson($actualJson, $message); $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches( $expectedJson ); $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson); static::assertThat($expectedJson, $constraintActual, $message); static::assertThat($actualJson, $constraintExpected, $message); } public static function assertJsonFileNotEqualsJsonFile($expectedFile, $actualFile, $message = '') { static::assertFileExists($expectedFile, $message); static::assertFileExists($actualFile, $message); $actualJson = file_get_contents($actualFile); $expectedJson = file_get_contents($expectedFile); static::assertJson($expectedJson, $message); static::assertJson($actualJson, $message); $constraintExpected = new PHPUnit_Framework_Constraint_JsonMatches( $expectedJson ); $constraintActual = new PHPUnit_Framework_Constraint_JsonMatches($actualJson); static::assertThat($expectedJson, new PHPUnit_Framework_Constraint_Not($constraintActual), $message); static::assertThat($actualJson, new PHPUnit_Framework_Constraint_Not($constraintExpected), $message); } public static function logicalAnd() { $constraints = func_get_args(); $constraint = new PHPUnit_Framework_Constraint_And; $constraint->setConstraints($constraints); return $constraint; } public static function logicalOr() { $constraints = func_get_args(); $constraint = new PHPUnit_Framework_Constraint_Or; $constraint->setConstraints($constraints); return $constraint; } public static function logicalNot(PHPUnit_Framework_Constraint $constraint) { return new PHPUnit_Framework_Constraint_Not($constraint); } public static function logicalXor() { $constraints = func_get_args(); $constraint = new PHPUnit_Framework_Constraint_Xor; $constraint->setConstraints($constraints); return $constraint; } public static function anything() { return new PHPUnit_Framework_Constraint_IsAnything; } public static function isTrue() { return new PHPUnit_Framework_Constraint_IsTrue; } public static function callback($callback) { return new PHPUnit_Framework_Constraint_Callback($callback); } public static function isFalse() { return new PHPUnit_Framework_Constraint_IsFalse; } public static function isJson() { return new PHPUnit_Framework_Constraint_IsJson; } public static function isNull() { return new PHPUnit_Framework_Constraint_IsNull; } public static function isFinite() { return new PHPUnit_Framework_Constraint_IsFinite; } public static function isInfinite() { return new PHPUnit_Framework_Constraint_IsInfinite; } public static function isNan() { return new PHPUnit_Framework_Constraint_IsNan; } public static function attribute(PHPUnit_Framework_Constraint $constraint, $attributeName) { return new PHPUnit_Framework_Constraint_Attribute( $constraint, $attributeName ); } public static function contains($value, $checkForObjectIdentity = true, $checkForNonObjectIdentity = false) { return new PHPUnit_Framework_Constraint_TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity); } public static function containsOnly($type) { return new PHPUnit_Framework_Constraint_TraversableContainsOnly($type); } public static function containsOnlyInstancesOf($classname) { return new PHPUnit_Framework_Constraint_TraversableContainsOnly($classname, false); } public static function arrayHasKey($key) { return new PHPUnit_Framework_Constraint_ArrayHasKey($key); } public static function equalTo($value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) { return new PHPUnit_Framework_Constraint_IsEqual( $value, $delta, $maxDepth, $canonicalize, $ignoreCase ); } public static function attributeEqualTo($attributeName, $value, $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false) { return static::attribute( static::equalTo( $value, $delta, $maxDepth, $canonicalize, $ignoreCase ), $attributeName ); } public static function isEmpty() { return new PHPUnit_Framework_Constraint_IsEmpty; } public static function isWritable() { return new PHPUnit_Framework_Constraint_IsWritable; } public static function isReadable() { return new PHPUnit_Framework_Constraint_IsReadable; } public static function directoryExists() { return new PHPUnit_Framework_Constraint_DirectoryExists; } public static function fileExists() { return new PHPUnit_Framework_Constraint_FileExists; } public static function greaterThan($value) { return new PHPUnit_Framework_Constraint_GreaterThan($value); } public static function greaterThanOrEqual($value) { return static::logicalOr( new PHPUnit_Framework_Constraint_IsEqual($value), new PHPUnit_Framework_Constraint_GreaterThan($value) ); } public static function classHasAttribute($attributeName) { return new PHPUnit_Framework_Constraint_ClassHasAttribute( $attributeName ); } public static function classHasStaticAttribute($attributeName) { return new PHPUnit_Framework_Constraint_ClassHasStaticAttribute( $attributeName ); } public static function objectHasAttribute($attributeName) { return new PHPUnit_Framework_Constraint_ObjectHasAttribute( $attributeName ); } public static function identicalTo($value) { return new PHPUnit_Framework_Constraint_IsIdentical($value); } public static function isInstanceOf($className) { return new PHPUnit_Framework_Constraint_IsInstanceOf($className); } public static function isType($type) { return new PHPUnit_Framework_Constraint_IsType($type); } public static function lessThan($value) { return new PHPUnit_Framework_Constraint_LessThan($value); } public static function lessThanOrEqual($value) { return static::logicalOr( new PHPUnit_Framework_Constraint_IsEqual($value), new PHPUnit_Framework_Constraint_LessThan($value) ); } public static function matchesRegularExpression($pattern) { return new PHPUnit_Framework_Constraint_PCREMatch($pattern); } public static function matches($string) { return new PHPUnit_Framework_Constraint_StringMatches($string); } public static function stringStartsWith($prefix) { return new PHPUnit_Framework_Constraint_StringStartsWith($prefix); } public static function stringContains($string, $case = true) { return new PHPUnit_Framework_Constraint_StringContains($string, $case); } public static function stringEndsWith($suffix) { return new PHPUnit_Framework_Constraint_StringEndsWith($suffix); } public static function countOf($count) { return new PHPUnit_Framework_Constraint_Count($count); } public static function fail($message = '') { throw new PHPUnit_Framework_AssertionFailedError($message); } public static function readAttribute($classOrObject, $attributeName) { if (!is_string($attributeName)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); } if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name'); } if (is_string($classOrObject)) { if (!class_exists($classOrObject)) { throw PHPUnit_Util_InvalidArgumentHelper::factory( 1, 'class name' ); } return static::getStaticAttribute( $classOrObject, $attributeName ); } elseif (is_object($classOrObject)) { return static::getObjectAttribute( $classOrObject, $attributeName ); } else { throw PHPUnit_Util_InvalidArgumentHelper::factory( 1, 'class name or object' ); } } public static function getStaticAttribute($className, $attributeName) { if (!is_string($className)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); } if (!class_exists($className)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'class name'); } if (!is_string($attributeName)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); } if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name'); } $class = new ReflectionClass($className); while ($class) { $attributes = $class->getStaticProperties(); if (array_key_exists($attributeName, $attributes)) { return $attributes[$attributeName]; } $class = $class->getParentClass(); } throw new PHPUnit_Framework_Exception( sprintf( 'Attribute "%s" not found in class.', $attributeName ) ); } public static function getObjectAttribute($object, $attributeName) { if (!is_object($object)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'object'); } if (!is_string($attributeName)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'string'); } if (!preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'valid attribute name'); } try { $attribute = new ReflectionProperty($object, $attributeName); } catch (ReflectionException $e) { $reflector = new ReflectionObject($object); while ($reflector = $reflector->getParentClass()) { try { $attribute = $reflector->getProperty($attributeName); break; } catch (ReflectionException $e) { } } } if (isset($attribute)) { if (!$attribute || $attribute->isPublic()) { return $object->$attributeName; } $attribute->setAccessible(true); $value = $attribute->getValue($object); $attribute->setAccessible(false); return $value; } throw new PHPUnit_Framework_Exception( sprintf( 'Attribute "%s" not found in object.', $attributeName ) ); } public static function markTestIncomplete($message = '') { throw new PHPUnit_Framework_IncompleteTestError($message); } public static function markTestSkipped($message = '') { throw new PHPUnit_Framework_SkippedTestError($message); } public static function getCount() { return self::$count; } public static function resetCount() { self::$count = 0; } } exporter = new Exporter; } public function evaluate($other, $description = '', $returnResult = false) { $success = false; if ($this->matches($other)) { $success = true; } if ($returnResult) { return $success; } if (!$success) { $this->fail($other, $description); } } protected function matches($other) { return false; } public function count() { return 1; } protected function fail($other, $description, SebastianBergmann\Comparator\ComparisonFailure $comparisonFailure = null) { $failureDescription = sprintf( 'Failed asserting that %s.', $this->failureDescription($other) ); $additionalFailureDescription = $this->additionalFailureDescription($other); if ($additionalFailureDescription) { $failureDescription .= "\n" . $additionalFailureDescription; } if (!empty($description)) { $failureDescription = $description . "\n" . $failureDescription; } throw new PHPUnit_Framework_ExpectationFailedException( $failureDescription, $comparisonFailure ); } protected function additionalFailureDescription($other) { return ''; } protected function failureDescription($other) { return $this->exporter->export($other) . ' ' . $this->toString(); } } syntheticFile = $file; $this->syntheticLine = $line; $this->syntheticTrace = $trace; } public function getSyntheticFile() { return $this->syntheticFile; } public function getSyntheticLine() { return $this->syntheticLine; } public function getSyntheticTrace() { return $this->syntheticTrace; } } file = $file; $this->line = $line; } } comparisonFailure = $comparisonFailure; parent::__construct($message, 0, $previous); } public function getComparisonFailure() { return $this->comparisonFailure; } } setName($directory); $facade = new File_Iterator_Facade; $files = $facade->getFilesAsArray($directory, '.phpt'); foreach ($files as $file) { $this->addTestFile($file); } } else { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'directory name'); } } } getName(); foreach ($groups as $group) { $groupSuites[$group] = new PHPUnit_Framework_TestSuite($name . ' - ' . $group); $this->addTest($groupSuites[$group]); } $tests = new RecursiveIteratorIterator( new PHPUnit_Util_TestSuiteIterator($suite), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($tests as $test) { if ($test instanceof PHPUnit_Framework_TestCase) { $testGroups = PHPUnit_Util_Test::getGroups( get_class($test), $test->getName(false) ); foreach ($groups as $group) { foreach ($testGroups as $testGroup) { if ($group == $testGroup) { $groupSuites[$group]->addTest($test); } } } } } } } ran) { return; } $name = $test->getName(false); $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name); foreach ($tickets as $ticket) { $this->ticketCounts[$ticket][$name] = 1; } $this->ran = true; } } public function endTest(PHPUnit_Framework_Test $test, $time) { if (!$test instanceof PHPUnit_Framework_WarningTestCase) { if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { $ifStatus = ['assigned', 'new', 'reopened']; $newStatus = 'closed'; $message = 'Automatically closed by PHPUnit (test passed).'; $resolution = 'fixed'; $cumulative = true; } elseif ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) { $ifStatus = ['closed']; $newStatus = 'reopened'; $message = 'Automatically reopened by PHPUnit (test failed).'; $resolution = ''; $cumulative = false; } else { return; } $name = $test->getName(false); $tickets = PHPUnit_Util_Test::getTickets(get_class($test), $name); foreach ($tickets as $ticket) { if ($test->getStatus() == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { unset($this->ticketCounts[$ticket][$name]); } if ($cumulative) { if (count($this->ticketCounts[$ticket]) > 0) { $adjustTicket = false; } else { $adjustTicket = true; } } else { $adjustTicket = true; } $ticketInfo = $this->getTicketInfo($ticket); if ($adjustTicket && in_array($ticketInfo['status'], $ifStatus)) { $this->updateTicket($ticket, $newStatus, $message, $resolution); } } } } abstract protected function getTicketInfo($ticketId = null); abstract protected function updateTicket($ticketId, $newStatus, $message, $resolution); } filename = $filename; $this->phpUtil = $phpUtil ?: PHPUnit_Util_PHP::factory(); } public function count() { return 1; } private function assertPhptExpectation(array $sections, $output) { $assertions = [ 'EXPECT' => 'assertEquals', 'EXPECTF' => 'assertStringMatchesFormat', 'EXPECTREGEX' => 'assertRegExp', ]; $actual = preg_replace('/\r\n/', "\n", trim($output)); foreach ($assertions as $sectionName => $sectionAssertion) { if (isset($sections[$sectionName])) { $sectionContent = preg_replace('/\r\n/', "\n", trim($sections[$sectionName])); $assertion = $sectionAssertion; $expected = $sectionName == 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent; break; } } PHPUnit_Framework_Assert::$assertion($expected, $actual); } public function run(PHPUnit_Framework_TestResult $result = null) { $sections = $this->parse(); $code = $this->render($sections['FILE']); if ($result === null) { $result = new PHPUnit_Framework_TestResult; } $skip = false; $xfail = false; $time = 0; $settings = $this->settings; $result->startTest($this); if (isset($sections['INI'])) { $settings = array_merge($settings, $this->parseIniSection($sections['INI'])); } if (isset($sections['ENV'])) { $env = $this->parseEnvSection($sections['ENV']); $this->phpUtil->setEnv($env); } $this->phpUtil->setUseStderrRedirection(true); if ($result->enforcesTimeLimit()) { $this->phpUtil->setTimeout($result->getTimeoutForLargeTests()); } if (isset($sections['SKIPIF'])) { $jobResult = $this->phpUtil->runJob($sections['SKIPIF'], $settings); if (!strncasecmp('skip', ltrim($jobResult['stdout']), 4)) { if (preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $message)) { $message = substr($message[1], 2); } else { $message = ''; } $result->addFailure($this, new PHPUnit_Framework_SkippedTestError($message), 0); $skip = true; } } if (isset($sections['XFAIL'])) { $xfail = trim($sections['XFAIL']); } if (!$skip) { if (isset($sections['STDIN'])) { $this->phpUtil->setStdin($sections['STDIN']); } if (isset($sections['ARGS'])) { $this->phpUtil->setArgs($sections['ARGS']); } PHP_Timer::start(); $jobResult = $this->phpUtil->runJob($code, $settings); $time = PHP_Timer::stop(); try { $this->assertPhptExpectation($sections, $jobResult['stdout']); } catch (PHPUnit_Framework_AssertionFailedError $e) { if ($xfail !== false) { $result->addFailure( $this, new PHPUnit_Framework_IncompleteTestError( $xfail, 0, $e ), $time ); } else { $result->addFailure($this, $e, $time); } } catch (Throwable $t) { $result->addError($this, $t, $time); } catch (Exception $e) { $result->addError($this, $e, $time); } if ($result->allCompletelyImplemented() && $xfail !== false) { $result->addFailure( $this, new PHPUnit_Framework_IncompleteTestError( 'XFAIL section but test passes' ), $time ); } $this->phpUtil->setStdin(''); $this->phpUtil->setArgs(''); if (isset($sections['CLEAN'])) { $cleanCode = $this->render($sections['CLEAN']); $this->phpUtil->runJob($cleanCode, $this->settings); } } $result->endTest($this, $time); return $result; } public function getName() { return $this->toString(); } public function toString() { return $this->filename; } private function parse() { $sections = []; $section = ''; $allowExternalSections = [ 'FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX' ]; $requiredSections = [ 'FILE', [ 'EXPECT', 'EXPECTF', 'EXPECTREGEX' ] ]; $unsupportedSections = [ 'REDIRECTTEST', 'REQUEST', 'POST', 'PUT', 'POST_RAW', 'GZIP_POST', 'DEFLATE_POST', 'GET', 'COOKIE', 'HEADERS', 'CGI', 'EXPECTHEADERS', 'EXTENSIONS', 'PHPDBG' ]; foreach (file($this->filename) as $line) { if (preg_match('/^--([_A-Z]+)--/', $line, $result)) { $section = $result[1]; $sections[$section] = ''; continue; } elseif (empty($section)) { throw new PHPUnit_Framework_Exception('Invalid PHPT file'); } $sections[$section] .= $line; } if (isset($sections['FILEEOF'])) { $sections['FILE'] = rtrim($sections['FILEEOF'], "\r\n"); unset($sections['FILEEOF']); } $testDirectory = dirname($this->filename) . DIRECTORY_SEPARATOR; foreach ($allowExternalSections as $section) { if (isset($sections[$section . '_EXTERNAL'])) { $externalFilename = str_replace('..', '', trim($sections[$section . '_EXTERNAL'])); if (!is_file($testDirectory . $externalFilename) || !is_readable($testDirectory . $externalFilename)) { throw new PHPUnit_Framework_Exception( sprintf( 'Could not load --%s-- %s for PHPT file', $section . '_EXTERNAL', $testDirectory . $externalFilename ) ); } $sections[$section] = file_get_contents($testDirectory . $externalFilename); unset($sections[$section . '_EXTERNAL']); } } $isValid = true; foreach ($requiredSections as $section) { if (is_array($section)) { $foundSection = false; foreach ($section as $anySection) { if (isset($sections[$anySection])) { $foundSection = true; break; } } if (!$foundSection) { $isValid = false; break; } } else { if (!isset($sections[$section])) { $isValid = false; break; } } } if (!$isValid) { throw new PHPUnit_Framework_Exception('Invalid PHPT file'); } foreach ($unsupportedSections as $section) { if (isset($sections[$section])) { throw new PHPUnit_Framework_Exception( 'PHPUnit does not support this PHPT file' ); } } return $sections; } private function render($code) { return str_replace( [ '__DIR__', '__FILE__' ], [ "'" . dirname($this->filename) . "'", "'" . $this->filename . "'" ], $code ); } protected function parseIniSection($content) { return preg_split('/\n|\r/', $content, -1, PREG_SPLIT_NO_EMPTY); } protected function parseEnvSection($content) { $env = []; foreach (explode("\n", trim($content)) as $e) { $e = explode('=', trim($e), 2); if (!empty($e[0]) && isset($e[1])) { $env[$e[0]] = $e[1]; } } return $env; } } test = $test; } public function toString() { return $this->test->toString(); } public function basicRun(PHPUnit_Framework_TestResult $result) { $this->test->run($result); } public function count() { return count($this->test); } protected function createResult() { return new PHPUnit_Framework_TestResult; } public function getTest() { return $this->test; } public function run(PHPUnit_Framework_TestResult $result = null) { if ($result === null) { $result = $this->createResult(); } $this->basicRun($result); return $result; } } = 0) { $this->timesRepeat = $timesRepeat; } else { throw PHPUnit_Util_InvalidArgumentHelper::factory( 2, 'positive integer' ); } $this->processIsolation = $processIsolation; } public function count() { return $this->timesRepeat * count($this->test); } public function run(PHPUnit_Framework_TestResult $result = null) { if ($result === null) { $result = $this->createResult(); } for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) { if ($this->test instanceof PHPUnit_Framework_TestSuite) { $this->test->setRunTestInSeparateProcess($this->processIsolation); } $this->test->run($result); } return $result; } } {tests_directory} {src_directory} EOT; public function generateDefaultConfiguration($phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory) { return str_replace( [ '{phpunit_version}', '{bootstrap_script}', '{tests_directory}', '{src_directory}' ], [ $phpunitVersion, $bootstrapScript, $testsDirectory, $srcDirectory ], $this->defaultTemplate ); } } 1, 'PHP_Invoker' => 1, 'PHP_Timer' => 1, 'PHP_Token' => 1, 'PHPUnit_Framework_TestCase' => 2, 'PHPUnit_Extensions_Database_TestCase' => 2, 'PHPUnit_Framework_MockObject_Generator' => 2, 'Text_Template' => 1, 'Symfony\Component\Yaml\Yaml' => 1, 'SebastianBergmann\CodeCoverage\CodeCoverage' => 1, 'SebastianBergmann\Diff\Diff' => 1, 'SebastianBergmann\Environment\Runtime' => 1, 'SebastianBergmann\Comparator\Comparator' => 1, 'SebastianBergmann\Exporter\Exporter' => 1, 'SebastianBergmann\GlobalState\Snapshot' => 1, 'SebastianBergmann\RecursionContext\Context' => 1, 'SebastianBergmann\Version' => 1, 'Composer\Autoload\ClassLoader' => 1, 'Doctrine\Instantiator\Instantiator' => 1, 'phpDocumentor\Reflection\DocBlock' => 1, 'Prophecy\Prophet' => 1, 'DeepCopy\DeepCopy' => 1 ]; private static $directories; public function getBlacklistedDirectories() { $this->initialize(); return self::$directories; } public function isBlacklisted($file) { if (defined('PHPUNIT_TESTSUITE')) { return false; } $this->initialize(); foreach (self::$directories as $directory) { if (strpos($file, $directory) === 0) { return true; } } return false; } private function initialize() { if (self::$directories === null) { self::$directories = []; foreach (self::$blacklistedClassNames as $className => $parent) { if (!class_exists($className)) { continue; } $reflector = new ReflectionClass($className); $directory = $reflector->getFileName(); for ($i = 0; $i < $parent; $i++) { $directory = dirname($directory); } self::$directories[] = $directory; } if (DIRECTORY_SEPARATOR === '\\') { self::$directories[] = sys_get_temp_dir() . '\\PHP'; } } } } runtime = new Runtime(); } public function setUseStderrRedirection($stderrRedirection) { if (!is_bool($stderrRedirection)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } $this->stderrRedirection = $stderrRedirection; } public function useStderrRedirection() { return $this->stderrRedirection; } public function setStdin($stdin) { $this->stdin = (string) $stdin; } public function getStdin() { return $this->stdin; } public function setArgs($args) { $this->args = (string) $args; } public function getArgs() { return $this->args; } public function setEnv(array $env) { $this->env = $env; } public function getEnv() { return $this->env; } public function setTimeout($timeout) { $this->timeout = (int) $timeout; } public function getTimeout() { return $this->timeout; } public static function factory() { if (DIRECTORY_SEPARATOR == '\\') { return new PHPUnit_Util_PHP_Windows; } return new PHPUnit_Util_PHP_Default; } public function runTestJob($job, PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result) { $result->startTest($test); $_result = $this->runJob($job); $this->processChildResult( $test, $result, $_result['stdout'], $_result['stderr'] ); } public function getCommand(array $settings, $file = null) { $command = $this->runtime->getBinary(); $command .= $this->settingsToParameters($settings); if ('phpdbg' === PHP_SAPI) { $command .= ' -qrr '; if ($file) { $command .= '-e ' . escapeshellarg($file); } else { $command .= escapeshellarg(__DIR__ . '/PHP/eval-stdin.php'); } } elseif ($file) { $command .= ' -f ' . escapeshellarg($file); } if ($this->args) { $command .= ' -- ' . $this->args; } if (true === $this->stderrRedirection) { $command .= ' 2>&1'; } return $command; } abstract public function runJob($job, array $settings = []); protected function settingsToParameters(array $settings) { $buffer = ''; foreach ($settings as $setting) { $buffer .= ' -d ' . $setting; } return $buffer; } private function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framework_TestResult $result, $stdout, $stderr) { $time = 0; if (!empty($stderr)) { $result->addError( $test, new PHPUnit_Framework_Exception(trim($stderr)), $time ); } else { set_error_handler(function ($errno, $errstr, $errfile, $errline) { throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); }); try { if (strpos($stdout, "#!/usr/bin/env php\n") === 0) { $stdout = substr($stdout, 19); } $childResult = unserialize(str_replace("#!/usr/bin/env php\n", '', $stdout)); restore_error_handler(); } catch (ErrorException $e) { restore_error_handler(); $childResult = false; $result->addError( $test, new PHPUnit_Framework_Exception(trim($stdout), 0, $e), $time ); } if ($childResult !== false) { if (!empty($childResult['output'])) { $output = $childResult['output']; } $test->setResult($childResult['testResult']); $test->addToAssertionCount($childResult['numAssertions']); $childResult = $childResult['result']; if ($result->getCollectCodeCoverageInformation()) { $result->getCodeCoverage()->merge( $childResult->getCodeCoverage() ); } $time = $childResult->time(); $notImplemented = $childResult->notImplemented(); $risky = $childResult->risky(); $skipped = $childResult->skipped(); $errors = $childResult->errors(); $warnings = $childResult->warnings(); $failures = $childResult->failures(); if (!empty($notImplemented)) { $result->addError( $test, $this->getException($notImplemented[0]), $time ); } elseif (!empty($risky)) { $result->addError( $test, $this->getException($risky[0]), $time ); } elseif (!empty($skipped)) { $result->addError( $test, $this->getException($skipped[0]), $time ); } elseif (!empty($errors)) { $result->addError( $test, $this->getException($errors[0]), $time ); } elseif (!empty($warnings)) { $result->addWarning( $test, $this->getException($warnings[0]), $time ); } elseif (!empty($failures)) { $result->addFailure( $test, $this->getException($failures[0]), $time ); } } } $result->endTest($test, $time); if (!empty($output)) { print $output; } } private function getException(PHPUnit_Framework_TestFailure $error) { $exception = $error->thrownException(); if ($exception instanceof __PHP_Incomplete_Class) { $exceptionArray = []; foreach ((array) $exception as $key => $value) { $key = substr($key, strrpos($key, "\0") + 1); $exceptionArray[$key] = $value; } $exception = new PHPUnit_Framework_SyntheticError( sprintf( '%s: %s', $exceptionArray['_PHP_Incomplete_Class_Name'], $exceptionArray['message'] ), $exceptionArray['code'], $exceptionArray['file'], $exceptionArray['line'], $exceptionArray['trace'] ); } return $exception; } } ' . file_get_contents('php://stdin')); setCodeCoverage( new CodeCoverage( null, unserialize('{codeCoverageFilter}') ) ); } $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); $result->enforceTimeLimit({enforcesTimeLimit}); $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); $test->setDependencyInput(unserialize('{dependencyInput}')); $test->setInIsolation(TRUE); ob_end_clean(); $test->run($result); $output = ''; if (!$test->hasExpectationOnOutput()) { $output = $test->getActualOutput(); } @rewind(STDOUT); if ($stdout = stream_get_contents(STDOUT)) { $output = $stdout . $output; } print serialize( array( 'testResult' => $test->getResult(), 'numAssertions' => $test->getNumAssertions(), 'result' => $result, 'output' => $output ) ); } $configurationFilePath = '{configurationFilePath}'; if ('' !== $configurationFilePath) { $configuration = PHPUnit_Util_Configuration::getInstance($configurationFilePath); $configuration->handlePHPConfiguration(); unset($configuration); } function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext) { return true; } set_error_handler("__phpunit_error_handler"); {constants} {included_files} {globals} restore_error_handler(); if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); } __phpunit_run_isolated_test(); $stdout_handle ]; } public function getCommand(array $settings, $file = null) { return '"' . parent::getCommand($settings, $file) . '"'; } } useTempFile || $this->stdin) { if (!($this->tempFile = tempnam(sys_get_temp_dir(), 'PHPUnit')) || file_put_contents($this->tempFile, $job) === false) { throw new PHPUnit_Framework_Exception( 'Unable to write temporary file' ); } $job = $this->stdin; } return $this->runProcess($job, $settings); } protected function getHandles() { return []; } protected function runProcess($job, $settings) { $handles = $this->getHandles(); $env = null; if ($this->env) { $env = isset($_SERVER) ? $_SERVER : []; unset($env['argv'], $env['argc']); $env = array_merge($env, $this->env); foreach ($env as $envKey => $envVar) { if (is_array($envVar)) { unset($env[$envKey]); } } } $pipeSpec = [ 0 => isset($handles[0]) ? $handles[0] : ['pipe', 'r'], 1 => isset($handles[1]) ? $handles[1] : ['pipe', 'w'], 2 => isset($handles[2]) ? $handles[2] : ['pipe', 'w'], ]; $process = proc_open( $this->getCommand($settings, $this->tempFile), $pipeSpec, $pipes, null, $env ); if (!is_resource($process)) { throw new PHPUnit_Framework_Exception( 'Unable to spawn worker process' ); } if ($job) { $this->process($pipes[0], $job); } fclose($pipes[0]); if ($this->timeout) { $stderr = $stdout = ''; unset($pipes[0]); while (true) { $r = $pipes; $w = null; $e = null; $n = @stream_select($r, $w, $e, $this->timeout); if ($n === false) { break; } elseif ($n === 0) { proc_terminate($process, 9); throw new PHPUnit_Framework_Exception(sprintf('Job execution aborted after %d seconds', $this->timeout)); } elseif ($n > 0) { foreach ($r as $pipe) { $pipeOffset = 0; foreach ($pipes as $i => $origPipe) { if ($pipe == $origPipe) { $pipeOffset = $i; break; } } if (!$pipeOffset) { break; } $line = fread($pipe, 8192); if (strlen($line) == 0) { fclose($pipes[$pipeOffset]); unset($pipes[$pipeOffset]); } else { if ($pipeOffset == 1) { $stdout .= $line; } else { $stderr .= $line; } } } if (empty($pipes)) { break; } } } } else { if (isset($pipes[1])) { $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); } if (isset($pipes[2])) { $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); } } if (isset($handles[1])) { rewind($handles[1]); $stdout = stream_get_contents($handles[1]); fclose($handles[1]); } if (isset($handles[2])) { rewind($handles[2]); $stderr = stream_get_contents($handles[2]); fclose($handles[2]); } proc_close($process); $this->cleanup(); return ['stdout' => $stdout, 'stderr' => $stderr]; } protected function process($pipe, $job) { fwrite($pipe, $job); } protected function cleanup() { if ($this->tempFile) { unlink($this->tempFile); } } } filename = $filename; $this->document = PHPUnit_Util_XML::loadFile($filename, false, true, true); $this->xpath = new DOMXPath($this->document); } final private function __clone() { } public static function getInstance($filename) { $realpath = realpath($filename); if ($realpath === false) { throw new PHPUnit_Framework_Exception( sprintf( 'Could not read "%s".', $filename ) ); } if (!isset(self::$instances[$realpath])) { self::$instances[$realpath] = new self($realpath); } return self::$instances[$realpath]; } public function getFilename() { return $this->filename; } public function getFilterConfiguration() { $addUncoveredFilesFromWhitelist = true; $processUncoveredFilesFromWhitelist = false; $tmp = $this->xpath->query('filter/whitelist'); if ($tmp->length == 1) { if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { $addUncoveredFilesFromWhitelist = $this->getBoolean( (string) $tmp->item(0)->getAttribute( 'addUncoveredFilesFromWhitelist' ), true ); } if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) { $processUncoveredFilesFromWhitelist = $this->getBoolean( (string) $tmp->item(0)->getAttribute( 'processUncoveredFilesFromWhitelist' ), false ); } } return [ 'whitelist' => [ 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, 'include' => [ 'directory' => $this->readFilterDirectories( 'filter/whitelist/directory' ), 'file' => $this->readFilterFiles( 'filter/whitelist/file' ) ], 'exclude' => [ 'directory' => $this->readFilterDirectories( 'filter/whitelist/exclude/directory' ), 'file' => $this->readFilterFiles( 'filter/whitelist/exclude/file' ) ] ] ]; } public function getGroupConfiguration() { return $this->parseGroupConfiguration('groups'); } public function getTestdoxGroupConfiguration() { return $this->parseGroupConfiguration('testdoxGroups'); } private function parseGroupConfiguration($root) { $groups = [ 'include' => [], 'exclude' => [] ]; foreach ($this->xpath->query($root . '/include/group') as $group) { $groups['include'][] = (string) $group->textContent; } foreach ($this->xpath->query($root . '/exclude/group') as $group) { $groups['exclude'][] = (string) $group->textContent; } return $groups; } public function getListenerConfiguration() { $result = []; foreach ($this->xpath->query('listeners/listener') as $listener) { $class = (string) $listener->getAttribute('class'); $file = ''; $arguments = []; if ($listener->getAttribute('file')) { $file = $this->toAbsolutePath( (string) $listener->getAttribute('file'), true ); } foreach ($listener->childNodes as $node) { if ($node instanceof DOMElement && $node->tagName == 'arguments') { foreach ($node->childNodes as $argument) { if ($argument instanceof DOMElement) { if ($argument->tagName == 'file' || $argument->tagName == 'directory') { $arguments[] = $this->toAbsolutePath((string) $argument->textContent); } else { $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument); } } } } } $result[] = [ 'class' => $class, 'file' => $file, 'arguments' => $arguments ]; } return $result; } public function getLoggingConfiguration() { $result = []; foreach ($this->xpath->query('logging/log') as $log) { $type = (string) $log->getAttribute('type'); $target = (string) $log->getAttribute('target'); if (!$target) { continue; } $target = $this->toAbsolutePath($target); if ($type == 'coverage-html') { if ($log->hasAttribute('lowUpperBound')) { $result['lowUpperBound'] = $this->getInteger( (string) $log->getAttribute('lowUpperBound'), 50 ); } if ($log->hasAttribute('highLowerBound')) { $result['highLowerBound'] = $this->getInteger( (string) $log->getAttribute('highLowerBound'), 90 ); } } elseif ($type == 'coverage-crap4j') { if ($log->hasAttribute('threshold')) { $result['crap4jThreshold'] = $this->getInteger( (string) $log->getAttribute('threshold'), 30 ); } } elseif ($type == 'junit') { if ($log->hasAttribute('logIncompleteSkipped')) { $result['logIncompleteSkipped'] = $this->getBoolean( (string) $log->getAttribute('logIncompleteSkipped'), false ); } } elseif ($type == 'coverage-text') { if ($log->hasAttribute('showUncoveredFiles')) { $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( (string) $log->getAttribute('showUncoveredFiles'), false ); } if ($log->hasAttribute('showOnlySummary')) { $result['coverageTextShowOnlySummary'] = $this->getBoolean( (string) $log->getAttribute('showOnlySummary'), false ); } } $result[$type] = $target; } return $result; } public function getPHPConfiguration() { $result = [ 'include_path' => [], 'ini' => [], 'const' => [], 'var' => [], 'env' => [], 'post' => [], 'get' => [], 'cookie' => [], 'server' => [], 'files' => [], 'request' => [] ]; foreach ($this->xpath->query('php/includePath') as $includePath) { $path = (string) $includePath->textContent; if ($path) { $result['include_path'][] = $this->toAbsolutePath($path); } } foreach ($this->xpath->query('php/ini') as $ini) { $name = (string) $ini->getAttribute('name'); $value = (string) $ini->getAttribute('value'); $result['ini'][$name] = $value; } foreach ($this->xpath->query('php/const') as $const) { $name = (string) $const->getAttribute('name'); $value = (string) $const->getAttribute('value'); $result['const'][$name] = $this->getBoolean($value, $value); } foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { foreach ($this->xpath->query('php/' . $array) as $var) { $name = (string) $var->getAttribute('name'); $value = (string) $var->getAttribute('value'); $result[$array][$name] = $this->getBoolean($value, $value); } } return $result; } public function handlePHPConfiguration() { $configuration = $this->getPHPConfiguration(); if (! empty($configuration['include_path'])) { ini_set( 'include_path', implode(PATH_SEPARATOR, $configuration['include_path']) . PATH_SEPARATOR . ini_get('include_path') ); } foreach ($configuration['ini'] as $name => $value) { if (defined($value)) { $value = constant($value); } ini_set($name, $value); } foreach ($configuration['const'] as $name => $value) { if (!defined($name)) { define($name, $value); } } foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { switch ($array) { case 'var': $target = &$GLOBALS; break; case 'server': $target = &$_SERVER; break; default: $target = &$GLOBALS['_' . strtoupper($array)]; break; } foreach ($configuration[$array] as $name => $value) { $target[$name] = $value; } } foreach ($configuration['env'] as $name => $value) { if (false === getenv($name)) { putenv("{$name}={$value}"); } if (!isset($_ENV[$name])) { $_ENV[$name] = $value; } } } public function getPHPUnitConfiguration() { $result = []; $root = $this->document->documentElement; if ($root->hasAttribute('cacheTokens')) { $result['cacheTokens'] = $this->getBoolean( (string) $root->getAttribute('cacheTokens'), false ); } if ($root->hasAttribute('columns')) { $columns = (string) $root->getAttribute('columns'); if ($columns == 'max') { $result['columns'] = 'max'; } else { $result['columns'] = $this->getInteger($columns, 80); } } if ($root->hasAttribute('colors')) { if ($this->getBoolean($root->getAttribute('colors'), false)) { $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_AUTO; } else { $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_NEVER; } } if ($root->hasAttribute('stderr')) { $result['stderr'] = $this->getBoolean( (string) $root->getAttribute('stderr'), false ); } if ($root->hasAttribute('backupGlobals')) { $result['backupGlobals'] = $this->getBoolean( (string) $root->getAttribute('backupGlobals'), true ); } if ($root->hasAttribute('backupStaticAttributes')) { $result['backupStaticAttributes'] = $this->getBoolean( (string) $root->getAttribute('backupStaticAttributes'), false ); } if ($root->getAttribute('bootstrap')) { $result['bootstrap'] = $this->toAbsolutePath( (string) $root->getAttribute('bootstrap') ); } if ($root->hasAttribute('convertErrorsToExceptions')) { $result['convertErrorsToExceptions'] = $this->getBoolean( (string) $root->getAttribute('convertErrorsToExceptions'), true ); } if ($root->hasAttribute('convertNoticesToExceptions')) { $result['convertNoticesToExceptions'] = $this->getBoolean( (string) $root->getAttribute('convertNoticesToExceptions'), true ); } if ($root->hasAttribute('convertWarningsToExceptions')) { $result['convertWarningsToExceptions'] = $this->getBoolean( (string) $root->getAttribute('convertWarningsToExceptions'), true ); } if ($root->hasAttribute('forceCoversAnnotation')) { $result['forceCoversAnnotation'] = $this->getBoolean( (string) $root->getAttribute('forceCoversAnnotation'), false ); } if ($root->hasAttribute('disableCodeCoverageIgnore')) { $result['disableCodeCoverageIgnore'] = $this->getBoolean( (string) $root->getAttribute('disableCodeCoverageIgnore'), false ); } if ($root->hasAttribute('processIsolation')) { $result['processIsolation'] = $this->getBoolean( (string) $root->getAttribute('processIsolation'), false ); } if ($root->hasAttribute('stopOnError')) { $result['stopOnError'] = $this->getBoolean( (string) $root->getAttribute('stopOnError'), false ); } if ($root->hasAttribute('stopOnFailure')) { $result['stopOnFailure'] = $this->getBoolean( (string) $root->getAttribute('stopOnFailure'), false ); } if ($root->hasAttribute('stopOnWarning')) { $result['stopOnWarning'] = $this->getBoolean( (string) $root->getAttribute('stopOnWarning'), false ); } if ($root->hasAttribute('stopOnIncomplete')) { $result['stopOnIncomplete'] = $this->getBoolean( (string) $root->getAttribute('stopOnIncomplete'), false ); } if ($root->hasAttribute('stopOnRisky')) { $result['stopOnRisky'] = $this->getBoolean( (string) $root->getAttribute('stopOnRisky'), false ); } if ($root->hasAttribute('stopOnSkipped')) { $result['stopOnSkipped'] = $this->getBoolean( (string) $root->getAttribute('stopOnSkipped'), false ); } if ($root->hasAttribute('failOnWarning')) { $result['failOnWarning'] = $this->getBoolean( (string) $root->getAttribute('failOnWarning'), false ); } if ($root->hasAttribute('failOnRisky')) { $result['failOnRisky'] = $this->getBoolean( (string) $root->getAttribute('failOnRisky'), false ); } if ($root->hasAttribute('testSuiteLoaderClass')) { $result['testSuiteLoaderClass'] = (string) $root->getAttribute( 'testSuiteLoaderClass' ); } if ($root->getAttribute('testSuiteLoaderFile')) { $result['testSuiteLoaderFile'] = $this->toAbsolutePath( (string) $root->getAttribute('testSuiteLoaderFile') ); } if ($root->hasAttribute('printerClass')) { $result['printerClass'] = (string) $root->getAttribute( 'printerClass' ); } if ($root->getAttribute('printerFile')) { $result['printerFile'] = $this->toAbsolutePath( (string) $root->getAttribute('printerFile') ); } if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) { $result['beStrictAboutChangesToGlobalState'] = $this->getBoolean( (string) $root->getAttribute('beStrictAboutChangesToGlobalState'), false ); } if ($root->hasAttribute('beStrictAboutOutputDuringTests')) { $result['disallowTestOutput'] = $this->getBoolean( (string) $root->getAttribute('beStrictAboutOutputDuringTests'), false ); } if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) { $result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean( (string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'), false ); } if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) { $result['reportUselessTests'] = $this->getBoolean( (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'), false ); } if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) { $result['disallowTodoAnnotatedTests'] = $this->getBoolean( (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'), false ); } if ($root->hasAttribute('beStrictAboutCoversAnnotation')) { $result['strictCoverage'] = $this->getBoolean( (string) $root->getAttribute('beStrictAboutCoversAnnotation'), false ); } elseif ($root->hasAttribute('checkForUnintentionallyCoveredCode')) { $result['strictCoverage'] = $this->getBoolean( (string) $root->getAttribute('checkForUnintentionallyCoveredCode'), false ); $result['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'] = true; } if ($root->hasAttribute('enforceTimeLimit')) { $result['enforceTimeLimit'] = $this->getBoolean( (string) $root->getAttribute('enforceTimeLimit'), false ); } if ($root->hasAttribute('timeoutForSmallTests')) { $result['timeoutForSmallTests'] = $this->getInteger( (string) $root->getAttribute('timeoutForSmallTests'), 1 ); } if ($root->hasAttribute('timeoutForMediumTests')) { $result['timeoutForMediumTests'] = $this->getInteger( (string) $root->getAttribute('timeoutForMediumTests'), 10 ); } if ($root->hasAttribute('timeoutForLargeTests')) { $result['timeoutForLargeTests'] = $this->getInteger( (string) $root->getAttribute('timeoutForLargeTests'), 60 ); } if ($root->hasAttribute('reverseDefectList')) { $result['reverseDefectList'] = $this->getBoolean( (string) $root->getAttribute('reverseDefectList'), false ); } if ($root->hasAttribute('verbose')) { $result['verbose'] = $this->getBoolean( (string) $root->getAttribute('verbose'), false ); } if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) { $result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean( (string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'), false ); } if ($root->hasAttribute('extensionsDirectory')) { $result['extensionsDirectory'] = $this->toAbsolutePath( (string) $root->getAttribute( 'extensionsDirectory' ) ); } return $result; } public function getTestSuiteConfiguration($testSuiteFilter = null) { $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); if ($testSuiteNodes->length == 0) { $testSuiteNodes = $this->xpath->query('testsuite'); } if ($testSuiteNodes->length == 1) { return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); } if ($testSuiteNodes->length > 1) { $suite = new PHPUnit_Framework_TestSuite; foreach ($testSuiteNodes as $testSuiteNode) { $suite->addTestSuite( $this->getTestSuite($testSuiteNode, $testSuiteFilter) ); } return $suite; } } public function getTestSuiteNames() { $names = []; $nodes = $this->xpath->query('*/testsuite'); foreach ($nodes as $node) { $names[] = $node->getAttribute('name'); } return $names; } protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter = null) { if ($testSuiteNode->hasAttribute('name')) { $suite = new PHPUnit_Framework_TestSuite( (string) $testSuiteNode->getAttribute('name') ); } else { $suite = new PHPUnit_Framework_TestSuite; } $exclude = []; foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) { $excludeFile = (string) $excludeNode->textContent; if ($excludeFile) { $exclude[] = $this->toAbsolutePath($excludeFile); } } $fileIteratorFacade = new File_Iterator_Facade; foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) { if ($testSuiteFilter && $directoryNode->parentNode->getAttribute('name') != $testSuiteFilter) { continue; } $directory = (string) $directoryNode->textContent; if (empty($directory)) { continue; } if ($directoryNode->hasAttribute('phpVersion')) { $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); } else { $phpVersion = PHP_VERSION; } if ($directoryNode->hasAttribute('phpVersionOperator')) { $phpVersionOperator = (string) $directoryNode->getAttribute('phpVersionOperator'); } else { $phpVersionOperator = '>='; } if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { continue; } if ($directoryNode->hasAttribute('prefix')) { $prefix = (string) $directoryNode->getAttribute('prefix'); } else { $prefix = ''; } if ($directoryNode->hasAttribute('suffix')) { $suffix = (string) $directoryNode->getAttribute('suffix'); } else { $suffix = 'Test.php'; } $files = $fileIteratorFacade->getFilesAsArray( $this->toAbsolutePath($directory), $suffix, $prefix, $exclude ); $suite->addTestFiles($files); } foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) { if ($testSuiteFilter && $fileNode->parentNode->getAttribute('name') != $testSuiteFilter) { continue; } $file = (string) $fileNode->textContent; if (empty($file)) { continue; } $file = $fileIteratorFacade->getFilesAsArray( $this->toAbsolutePath($file) ); if (!isset($file[0])) { continue; } $file = $file[0]; if ($fileNode->hasAttribute('phpVersion')) { $phpVersion = (string) $fileNode->getAttribute('phpVersion'); } else { $phpVersion = PHP_VERSION; } if ($fileNode->hasAttribute('phpVersionOperator')) { $phpVersionOperator = (string) $fileNode->getAttribute('phpVersionOperator'); } else { $phpVersionOperator = '>='; } if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { continue; } $suite->addTestFile($file); } return $suite; } protected function getBoolean($value, $default) { if (strtolower($value) == 'false') { return false; } elseif (strtolower($value) == 'true') { return true; } return $default; } protected function getInteger($value, $default) { if (is_numeric($value)) { return (int) $value; } return $default; } protected function readFilterDirectories($query) { $directories = []; foreach ($this->xpath->query($query) as $directory) { $directoryPath = (string) $directory->textContent; if (!$directoryPath) { continue; } if ($directory->hasAttribute('prefix')) { $prefix = (string) $directory->getAttribute('prefix'); } else { $prefix = ''; } if ($directory->hasAttribute('suffix')) { $suffix = (string) $directory->getAttribute('suffix'); } else { $suffix = '.php'; } if ($directory->hasAttribute('group')) { $group = (string) $directory->getAttribute('group'); } else { $group = 'DEFAULT'; } $directories[] = [ 'path' => $this->toAbsolutePath($directoryPath), 'prefix' => $prefix, 'suffix' => $suffix, 'group' => $group ]; } return $directories; } protected function readFilterFiles($query) { $files = []; foreach ($this->xpath->query($query) as $file) { $filePath = (string) $file->textContent; if ($filePath) { $files[] = $this->toAbsolutePath($filePath); } } return $files; } protected function toAbsolutePath($path, $useIncludePath = false) { $path = trim($path); if ($path[0] === '/') { return $path; } if (defined('PHP_WINDOWS_VERSION_BUILD') && ($path[0] === '\\' || (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))))) { return $path; } if (strpos($path, '://') !== false) { return $path; } $file = dirname($this->filename) . DIRECTORY_SEPARATOR . $path; if ($useIncludePath && !file_exists($file)) { $includePathFile = stream_resolve_include_path($path); if ($includePathFile) { $file = $includePathFile; } } return $file; } } 1 && $arg[1] == '-' && !$long_options)) { $non_opts[] = $args[$i]; continue; } elseif (strlen($arg) > 1 && $arg[1] == '-') { self::parseLongOption( substr($arg, 2), $long_options, $opts, $args ); } else { self::parseShortOption( substr($arg, 1), $short_options, $opts, $args ); } } return [$opts, $non_opts]; } protected static function parseShortOption($arg, $short_options, &$opts, &$args) { $argLen = strlen($arg); for ($i = 0; $i < $argLen; $i++) { $opt = $arg[$i]; $opt_arg = null; if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') { throw new PHPUnit_Framework_Exception( "unrecognized option -- $opt" ); } if (strlen($spec) > 1 && $spec[1] == ':') { if ($i + 1 < $argLen) { $opts[] = [$opt, substr($arg, $i + 1)]; break; } if (!(strlen($spec) > 2 && $spec[2] == ':')) { if (false === $opt_arg = current($args)) { throw new PHPUnit_Framework_Exception( "option requires an argument -- $opt" ); } next($args); } } $opts[] = [$opt, $opt_arg]; } } protected static function parseLongOption($arg, $long_options, &$opts, &$args) { $count = count($long_options); $list = explode('=', $arg); $opt = $list[0]; $opt_arg = null; if (count($list) > 1) { $opt_arg = $list[1]; } $opt_len = strlen($opt); for ($i = 0; $i < $count; $i++) { $long_opt = $long_options[$i]; $opt_start = substr($long_opt, 0, $opt_len); if ($opt_start != $opt) { continue; } $opt_rest = substr($long_opt, $opt_len); if ($opt_rest != '' && $opt[0] != '=' && $i + 1 < $count && $opt == substr($long_options[$i + 1], 0, $opt_len)) { throw new PHPUnit_Framework_Exception( "option --$opt is ambiguous" ); } if (substr($long_opt, -1) == '=') { if (substr($long_opt, -2) != '==') { if (!strlen($opt_arg)) { if (false === $opt_arg = current($args)) { throw new PHPUnit_Framework_Exception( "option --$opt requires an argument" ); } next($args); } } } elseif ($opt_arg) { throw new PHPUnit_Framework_Exception( "option --$opt doesn't allow an argument" ); } $full_option = '--' . preg_replace('/={1,2}$/', '', $long_opt); $opts[] = [$full_option, $opt_arg]; return; } throw new PHPUnit_Framework_Exception("unrecognized option --$opt"); } } preserveWhiteSpace = false; $internal = libxml_use_internal_errors(true); $message = ''; $reporting = error_reporting(0); if ('' !== $filename) { $document->documentURI = $filename; } if ($isHtml) { $loaded = $document->loadHTML($actual); } else { $loaded = $document->loadXML($actual); } if (!$isHtml && $xinclude) { $document->xinclude(); } foreach (libxml_get_errors() as $error) { $message .= "\n" . $error->message; } libxml_use_internal_errors($internal); error_reporting($reporting); if ($xinclude) { @chdir($cwd); } if ($loaded === false || ($strict && $message !== '')) { if ($filename !== '') { throw new PHPUnit_Framework_Exception( sprintf( 'Could not load "%s".%s', $filename, $message != '' ? "\n" . $message : '' ) ); } else { if ($message === '') { $message = 'Could not load XML for unknown reason'; } throw new PHPUnit_Framework_Exception($message); } } return $document; } public static function loadFile($filename, $isHtml = false, $xinclude = false, $strict = false) { $reporting = error_reporting(0); $contents = file_get_contents($filename); error_reporting($reporting); if ($contents === false) { throw new PHPUnit_Framework_Exception( sprintf( 'Could not read "%s".', $filename ) ); } return self::load($contents, $isHtml, $filename, $xinclude, $strict); } public static function removeCharacterDataNodes(DOMNode $node) { if ($node->hasChildNodes()) { for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { $node->removeChild($child); } } } } public static function prepareString($string) { return preg_replace( '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', '', htmlspecialchars( PHPUnit_Util_String::convertToUtf8($string), ENT_QUOTES, 'UTF-8' ) ); } public static function xmlToVariable(DOMElement $element) { $variable = null; switch ($element->tagName) { case 'array': $variable = []; foreach ($element->childNodes as $entry) { if (!$entry instanceof DOMElement || $entry->tagName !== 'element') { continue; } $item = $entry->childNodes->item(0); if ($item instanceof DOMText) { $item = $entry->childNodes->item(1); } $value = self::xmlToVariable($item); if ($entry->hasAttribute('key')) { $variable[(string) $entry->getAttribute('key')] = $value; } else { $variable[] = $value; } } break; case 'object': $className = $element->getAttribute('class'); if ($element->hasChildNodes()) { $arguments = $element->childNodes->item(1)->childNodes; $constructorArgs = []; foreach ($arguments as $argument) { if ($argument instanceof DOMElement) { $constructorArgs[] = self::xmlToVariable($argument); } } $class = new ReflectionClass($className); $variable = $class->newInstanceArgs($constructorArgs); } else { $variable = new $className; } break; case 'boolean': $variable = $element->textContent == 'true' ? true : false; break; case 'integer': case 'double': case 'string': $variable = $element->textContent; settype($variable, $element->tagName); break; } return $variable; } } out = fsockopen($out[0], $out[1]); } else { if (strpos($out, 'php://') === false && !is_dir(dirname($out))) { mkdir(dirname($out), 0777, true); } $this->out = fopen($out, 'wt'); } $this->outTarget = $out; } else { $this->out = $out; } } } public function flush() { if ($this->out && strncmp($this->outTarget, 'php://', 6) !== 0) { fclose($this->out); } } public function incrementalFlush() { if ($this->out) { fflush($this->out); } else { flush(); } } public function write($buffer) { if ($this->out) { fwrite($this->out, $buffer); if ($this->autoFlush) { $this->incrementalFlush(); } } else { if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') { $buffer = htmlspecialchars($buffer, ENT_SUBSTITUTE); } print $buffer; if ($this->autoFlush) { $this->incrementalFlush(); } } } public function getAutoFlush() { return $this->autoFlush; } public function setAutoFlush($autoFlush) { if (is_bool($autoFlush)) { $this->autoFlush = $autoFlush; } else { throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); } } } writeCase( 'error', $time, PHPUnit_Util_Filter::getFilteredStacktrace($e, false), PHPUnit_Framework_TestFailure::exceptionToString($e), $test ); $this->currentTestPass = false; } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { $this->writeCase( 'warning', $time, PHPUnit_Util_Filter::getFilteredStacktrace($e, false), PHPUnit_Framework_TestFailure::exceptionToString($e), $test ); $this->currentTestPass = false; } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { $this->writeCase( 'fail', $time, PHPUnit_Util_Filter::getFilteredStacktrace($e, false), PHPUnit_Framework_TestFailure::exceptionToString($e), $test ); $this->currentTestPass = false; } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeCase( 'error', $time, PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 'Incomplete Test: ' . $e->getMessage(), $test ); $this->currentTestPass = false; } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeCase( 'error', $time, PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 'Risky Test: ' . $e->getMessage(), $test ); $this->currentTestPass = false; } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeCase( 'error', $time, PHPUnit_Util_Filter::getFilteredStacktrace($e, false), 'Skipped Test: ' . $e->getMessage(), $test ); $this->currentTestPass = false; } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { $this->currentTestSuiteName = $suite->getName(); $this->currentTestName = ''; $this->write( [ 'event' => 'suiteStart', 'suite' => $this->currentTestSuiteName, 'tests' => count($suite) ] ); } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { $this->currentTestSuiteName = ''; $this->currentTestName = ''; } public function startTest(PHPUnit_Framework_Test $test) { $this->currentTestName = PHPUnit_Util_Test::describe($test); $this->currentTestPass = true; $this->write( [ 'event' => 'testStart', 'suite' => $this->currentTestSuiteName, 'test' => $this->currentTestName ] ); } public function endTest(PHPUnit_Framework_Test $test, $time) { if ($this->currentTestPass) { $this->writeCase('pass', $time, [], '', $test); } } protected function writeCase($status, $time, array $trace = [], $message = '', $test = null) { $output = ''; if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) { $output = $test->getActualOutput(); } $this->write( [ 'event' => 'test', 'suite' => $this->currentTestSuiteName, 'test' => $this->currentTestName, 'status' => $status, 'time' => $time, 'trace' => $trace, 'message' => PHPUnit_Util_String::convertToUtf8($message), 'output' => $output, ] ); } public function write($buffer) { array_walk_recursive($buffer, function (&$input) { if (is_string($input)) { $input = PHPUnit_Util_String::convertToUtf8($input); } }); parent::write(json_encode($buffer, JSON_PRETTY_PRINT)); } } printHeader(); $this->printFooter($result); } public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->printEvent( 'testFailed', [ 'name' => $test->getName(), 'message' => self::getMessage($e), 'details' => self::getDetails($e), ] ); } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { $this->printEvent( 'testFailed', [ 'name' => $test->getName(), 'message' => self::getMessage($e), 'details' => self::getDetails($e) ] ); } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { $parameters = [ 'name' => $test->getName(), 'message' => self::getMessage($e), 'details' => self::getDetails($e), ]; if ($e instanceof PHPUnit_Framework_ExpectationFailedException) { $comparisonFailure = $e->getComparisonFailure(); if ($comparisonFailure instanceof ComparisonFailure) { $expectedString = $comparisonFailure->getExpectedAsString(); if (is_null($expectedString) || empty($expectedString)) { $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected()); } $actualString = $comparisonFailure->getActualAsString(); if (is_null($actualString) || empty($actualString)) { $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual()); } if (!is_null($actualString) && !is_null($expectedString)) { $parameters['type'] = 'comparisonFailure'; $parameters['actual'] = $actualString; $parameters['expected'] = $expectedString; } } } $this->printEvent('testFailed', $parameters); } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->printIgnoredTest($test->getName(), $e); } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->addError($test, $e, $time); } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $testName = $test->getName(); if ($this->startedTestName != $testName) { $this->startTest($test); $this->printIgnoredTest($testName, $e); $this->endTest($test, $time); } else { $this->printIgnoredTest($testName, $e); } } public function printIgnoredTest($testName, Exception $e) { $this->printEvent( 'testIgnored', [ 'name' => $testName, 'message' => self::getMessage($e), 'details' => self::getDetails($e), ] ); } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { if (stripos(ini_get('disable_functions'), 'getmypid') === false) { $this->flowId = getmypid(); } else { $this->flowId = false; } if (!$this->isSummaryTestCountPrinted) { $this->isSummaryTestCountPrinted = true; $this->printEvent( 'testCount', ['count' => count($suite)] ); } $suiteName = $suite->getName(); if (empty($suiteName)) { return; } $parameters = ['name' => $suiteName]; if (class_exists($suiteName, false)) { $fileName = self::getFileName($suiteName); $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName"; } else { $split = preg_split('/::/', $suiteName); if (count($split) == 2 && method_exists($split[0], $split[1])) { $fileName = self::getFileName($split[0]); $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName"; $parameters['name'] = $split[1]; } } $this->printEvent('testSuiteStarted', $parameters); } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { $suiteName = $suite->getName(); if (empty($suiteName)) { return; } $parameters = ['name' => $suiteName]; if (!class_exists($suiteName, false)) { $split = preg_split('/::/', $suiteName); if (count($split) == 2 && method_exists($split[0], $split[1])) { $parameters['name'] = $split[1]; } } $this->printEvent('testSuiteFinished', $parameters); } public function startTest(PHPUnit_Framework_Test $test) { $testName = $test->getName(); $this->startedTestName = $testName; $params = ['name' => $testName]; if ($test instanceof PHPUnit_Framework_TestCase) { $className = get_class($test); $fileName = self::getFileName($className); $params['locationHint'] = "php_qn://$fileName::\\$className::$testName"; } $this->printEvent('testStarted', $params); } public function endTest(PHPUnit_Framework_Test $test, $time) { parent::endTest($test, $time); $this->printEvent( 'testFinished', [ 'name' => $test->getName(), 'duration' => (int) (round($time, 2) * 1000) ] ); } private function printEvent($eventName, $params = []) { $this->write("\n##teamcity[$eventName"); if ($this->flowId) { $params['flowId'] = $this->flowId; } foreach ($params as $key => $value) { $escapedValue = self::escapeValue($value); $this->write(" $key='$escapedValue'"); } $this->write("]\n"); } private static function getMessage(Exception $e) { $message = ''; if ($e instanceof PHPUnit_Framework_ExceptionWrapper) { if (strlen($e->getClassName()) != 0) { $message = $message . $e->getClassName(); } if (strlen($message) != 0 && strlen($e->getMessage()) != 0) { $message = $message . ' : '; } } return $message . $e->getMessage(); } private static function getDetails(Exception $e) { $stackTrace = PHPUnit_Util_Filter::getFilteredStacktrace($e); $previous = $e->getPrevious(); while ($previous) { $stackTrace .= "\nCaused by\n" . PHPUnit_Framework_TestFailure::exceptionToString($previous) . "\n" . PHPUnit_Util_Filter::getFilteredStacktrace($previous); $previous = $previous->getPrevious(); } return ' ' . str_replace("\n", "\n ", $stackTrace); } private static function getPrimitiveValueAsString($value) { if (is_null($value)) { return 'null'; } elseif (is_bool($value)) { return $value == true ? 'true' : 'false'; } elseif (is_scalar($value)) { return print_r($value, true); } return; } private static function escapeValue($text) { $text = str_replace('|', '||', $text); $text = str_replace("'", "|'", $text); $text = str_replace("\n", '|n', $text); $text = str_replace("\r", '|r', $text); $text = str_replace(']', '|]', $text); $text = str_replace('[', '|[', $text); return $text; } private static function getFileName($className) { $reflectionClass = new ReflectionClass($className); $fileName = $reflectionClass->getFileName(); return $fileName; } } document = new DOMDocument('1.0', 'UTF-8'); $this->document->formatOutput = true; $this->root = $this->document->createElement('testsuites'); $this->document->appendChild($this->root); parent::__construct($out); $this->logIncompleteSkipped = $logIncompleteSkipped; } public function flush() { if ($this->writeDocument === true) { $this->write($this->getXML()); } parent::flush(); } public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->doAddFault($test, $e, $time, 'error'); $this->testSuiteErrors[$this->testSuiteLevel]++; } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { if (!$this->logIncompleteSkipped) { return; } $this->doAddFault($test, $e, $time, 'warning'); $this->testSuiteFailures[$this->testSuiteLevel]++; } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { $this->doAddFault($test, $e, $time, 'failure'); $this->testSuiteFailures[$this->testSuiteLevel]++; } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { if ($this->logIncompleteSkipped && $this->currentTestCase !== null) { $error = $this->document->createElement( 'error', PHPUnit_Util_XML::prepareString( "Incomplete Test\n" . PHPUnit_Util_Filter::getFilteredStacktrace($e) ) ); $error->setAttribute('type', get_class($e)); $this->currentTestCase->appendChild($error); $this->testSuiteErrors[$this->testSuiteLevel]++; } else { $this->attachCurrentTestCase = false; } } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { if ($this->logIncompleteSkipped && $this->currentTestCase !== null) { $error = $this->document->createElement( 'error', PHPUnit_Util_XML::prepareString( "Risky Test\n" . PHPUnit_Util_Filter::getFilteredStacktrace($e) ) ); $error->setAttribute('type', get_class($e)); $this->currentTestCase->appendChild($error); $this->testSuiteErrors[$this->testSuiteLevel]++; } else { $this->attachCurrentTestCase = false; } } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { if ($this->logIncompleteSkipped && $this->currentTestCase !== null) { $error = $this->document->createElement( 'error', PHPUnit_Util_XML::prepareString( "Skipped Test\n" . PHPUnit_Util_Filter::getFilteredStacktrace($e) ) ); $error->setAttribute('type', get_class($e)); $this->currentTestCase->appendChild($error); $this->testSuiteErrors[$this->testSuiteLevel]++; } else { $this->attachCurrentTestCase = false; } } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { $testSuite = $this->document->createElement('testsuite'); $testSuite->setAttribute('name', $suite->getName()); if (class_exists($suite->getName(), false)) { try { $class = new ReflectionClass($suite->getName()); $testSuite->setAttribute('file', $class->getFileName()); } catch (ReflectionException $e) { } } if ($this->testSuiteLevel > 0) { $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); } else { $this->root->appendChild($testSuite); } $this->testSuiteLevel++; $this->testSuites[$this->testSuiteLevel] = $testSuite; $this->testSuiteTests[$this->testSuiteLevel] = 0; $this->testSuiteAssertions[$this->testSuiteLevel] = 0; $this->testSuiteErrors[$this->testSuiteLevel] = 0; $this->testSuiteFailures[$this->testSuiteLevel] = 0; $this->testSuiteTimes[$this->testSuiteLevel] = 0; } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { $this->testSuites[$this->testSuiteLevel]->setAttribute( 'tests', $this->testSuiteTests[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'assertions', $this->testSuiteAssertions[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'failures', $this->testSuiteFailures[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'errors', $this->testSuiteErrors[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'time', sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]) ); if ($this->testSuiteLevel > 1) { $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel]; $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel]; $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel]; $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel]; $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; } $this->testSuiteLevel--; } public function startTest(PHPUnit_Framework_Test $test) { $testCase = $this->document->createElement('testcase'); $testCase->setAttribute('name', $test->getName()); if ($test instanceof PHPUnit_Framework_TestCase) { $class = new ReflectionClass($test); $methodName = $test->getName(); if ($class->hasMethod($methodName)) { $method = $class->getMethod($test->getName()); $testCase->setAttribute('class', $class->getName()); $testCase->setAttribute('file', $class->getFileName()); $testCase->setAttribute('line', $method->getStartLine()); } } $this->currentTestCase = $testCase; } public function endTest(PHPUnit_Framework_Test $test, $time) { if ($this->attachCurrentTestCase) { if ($test instanceof PHPUnit_Framework_TestCase) { $numAssertions = $test->getNumAssertions(); $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; $this->currentTestCase->setAttribute( 'assertions', $numAssertions ); } $this->currentTestCase->setAttribute( 'time', sprintf('%F', $time) ); $this->testSuites[$this->testSuiteLevel]->appendChild( $this->currentTestCase ); $this->testSuiteTests[$this->testSuiteLevel]++; $this->testSuiteTimes[$this->testSuiteLevel] += $time; if (method_exists($test, 'hasOutput') && $test->hasOutput()) { $systemOut = $this->document->createElement('system-out'); $systemOut->appendChild( $this->document->createTextNode($test->getActualOutput()) ); $this->currentTestCase->appendChild($systemOut); } } $this->attachCurrentTestCase = true; $this->currentTestCase = null; } public function getXML() { return $this->document->saveXML(); } public function setWriteDocument($flag) { if (is_bool($flag)) { $this->writeDocument = $flag; } } private function doAddFault(PHPUnit_Framework_Test $test, Exception $e, $time, $type) { if ($this->currentTestCase === null) { return; } if ($test instanceof PHPUnit_Framework_SelfDescribing) { $buffer = $test->toString() . PHP_EOL; } else { $buffer = ''; } $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . PHP_EOL . PHPUnit_Util_Filter::getFilteredStacktrace($e); $fault = $this->document->createElement( $type, PHPUnit_Util_XML::prepareString($buffer) ); if ($e instanceof PHPUnit_Framework_ExceptionWrapper) { $fault->setAttribute('type', $e->getClassName()); } else { $fault->setAttribute('type', get_class($e)); } $this->currentTestCase->appendChild($fault); } } write("TAP version 13\n"); } public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeNotOk($test, 'Error'); } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { $this->writeNotOk($test, 'Warning'); } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { $this->writeNotOk($test, 'Failure'); $message = explode( "\n", PHPUnit_Framework_TestFailure::exceptionToString($e) ); $diagnostic = [ 'message' => $message[0], 'severity' => 'fail' ]; if ($e instanceof PHPUnit_Framework_ExpectationFailedException) { $cf = $e->getComparisonFailure(); if ($cf !== null) { $diagnostic['data'] = [ 'got' => $cf->getActual(), 'expected' => $cf->getExpected() ]; } } $yaml = new Symfony\Component\Yaml\Dumper; $this->write( sprintf( " ---\n%s ...\n", $yaml->dump($diagnostic, 2, 2) ) ); } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeNotOk($test, '', 'TODO Incomplete Test'); } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->write( sprintf( "ok %d - # RISKY%s\n", $this->testNumber, $e->getMessage() != '' ? ' ' . $e->getMessage() : '' ) ); $this->testSuccessful = false; } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->write( sprintf( "ok %d - # SKIP%s\n", $this->testNumber, $e->getMessage() != '' ? ' ' . $e->getMessage() : '' ) ); $this->testSuccessful = false; } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { $this->testSuiteLevel++; } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { $this->testSuiteLevel--; if ($this->testSuiteLevel == 0) { $this->write(sprintf("1..%d\n", $this->testNumber)); } } public function startTest(PHPUnit_Framework_Test $test) { $this->testNumber++; $this->testSuccessful = true; } public function endTest(PHPUnit_Framework_Test $test, $time) { if ($this->testSuccessful === true) { $this->write( sprintf( "ok %d - %s\n", $this->testNumber, PHPUnit_Util_Test::describe($test) ) ); } $this->writeDiagnostics($test); } protected function writeNotOk(PHPUnit_Framework_Test $test, $prefix = '', $directive = '') { $this->write( sprintf( "not ok %d - %s%s%s\n", $this->testNumber, $prefix != '' ? $prefix . ': ' : '', PHPUnit_Util_Test::describe($test), $directive != '' ? ' # ' . $directive : '' ) ); $this->testSuccessful = false; } private function writeDiagnostics(PHPUnit_Framework_Test $test) { if (!$test instanceof PHPUnit_Framework_TestCase) { return; } if (!$test->hasOutput()) { return; } foreach (explode("\n", trim($test->getActualOutput())) as $line) { $this->write( sprintf( "# %s\n", $line ) ); } } } getSyntheticTrace(); $eFile = $e->getSyntheticFile(); $eLine = $e->getSyntheticLine(); } elseif ($e instanceof PHPUnit_Framework_Exception) { $eTrace = $e->getSerializableTrace(); $eFile = $e->getFile(); $eLine = $e->getLine(); } else { if ($e->getPrevious()) { $e = $e->getPrevious(); } $eTrace = $e->getTrace(); $eFile = $e->getFile(); $eLine = $e->getLine(); } if (!self::frameExists($eTrace, $eFile, $eLine)) { array_unshift( $eTrace, ['file' => $eFile, 'line' => $eLine] ); } $blacklist = new PHPUnit_Util_Blacklist; foreach ($eTrace as $frame) { if (isset($frame['file']) && is_file($frame['file']) && !$blacklist->isBlacklisted($frame['file']) && ($prefix === false || strpos($frame['file'], $prefix) !== 0) && $frame['file'] !== $script) { if ($asString === true) { $filteredStacktrace .= sprintf( "%s:%s\n", $frame['file'], isset($frame['line']) ? $frame['line'] : '?' ); } else { $filteredStacktrace[] = $frame; } } } return $filteredStacktrace; } private static function frameExists(array $trace, $file, $line) { foreach ($trace as $frame) { if (isset($frame['file']) && $frame['file'] == $file && isset($frame['line']) && $frame['line'] == $line) { return true; } } return false; } } PHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; const REGEX_REQUIRES_OS = '/@requires\s+OS\s+(?P.+?)[ \t]*\r?$/m'; const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^ ]+?))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m'; const UNKNOWN = -1; const SMALL = 0; const MEDIUM = 1; const LARGE = 2; private static $annotationCache = []; private static $hookMethods = []; public static function describe(PHPUnit_Framework_Test $test, $asString = true) { if ($asString) { if ($test instanceof PHPUnit_Framework_SelfDescribing) { return $test->toString(); } else { return get_class($test); } } else { if ($test instanceof PHPUnit_Framework_TestCase) { return [ get_class($test), $test->getName() ]; } elseif ($test instanceof PHPUnit_Framework_SelfDescribing) { return ['', $test->toString()]; } else { return ['', get_class($test)]; } } } public static function getLinesToBeCovered($className, $methodName) { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) { return false; } return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers'); } public static function getLinesToBeUsed($className, $methodName) { return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses'); } private static function getLinesToBeCoveredOrUsed($className, $methodName, $mode) { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); $classShortcut = null; if (!empty($annotations['class'][$mode . 'DefaultClass'])) { if (count($annotations['class'][$mode . 'DefaultClass']) > 1) { throw new PHPUnit_Framework_CodeCoverageException( sprintf( 'More than one @%sClass annotation in class or interface "%s".', $mode, $className ) ); } $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0]; } $list = []; if (isset($annotations['class'][$mode])) { $list = $annotations['class'][$mode]; } if (isset($annotations['method'][$mode])) { $list = array_merge($list, $annotations['method'][$mode]); } $codeList = []; foreach (array_unique($list) as $element) { if ($classShortcut && strncmp($element, '::', 2) === 0) { $element = $classShortcut . $element; } $element = preg_replace('/[\s()]+$/', '', $element); $element = explode(' ', $element); $element = $element[0]; $codeList = array_merge( $codeList, self::resolveElementToReflectionObjects($element) ); } return self::resolveReflectionObjectsToLines($codeList); } public static function getRequirements($className, $methodName) { $reflector = new ReflectionClass($className); $docComment = $reflector->getDocComment(); $reflector = new ReflectionMethod($className, $methodName); $docComment .= "\n" . $reflector->getDocComment(); $requires = []; if ($count = preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) { $requires['OS'] = sprintf( '/%s/i', addcslashes($matches['value'][$count - 1], '/') ); } if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) { for ($i = 0; $i < $count; $i++) { $requires[$matches['name'][$i]] = [ 'version' => $matches['version'][$i], 'operator' => $matches['operator'][$i] ]; } } $matches = []; if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) { for ($i = 0; $i < $count; $i++) { $name = $matches['name'][$i] . 's'; if (!isset($requires[$name])) { $requires[$name] = []; } $requires[$name][] = $matches['value'][$i]; if (empty($matches['version'][$i]) || $name != 'extensions') { continue; } $requires['extension_versions'][$matches['value'][$i]] = [ 'version' => $matches['version'][$i], 'operator' => $matches['operator'][$i] ]; } } return $requires; } public static function getMissingRequirements($className, $methodName) { $required = static::getRequirements($className, $methodName); $missing = []; $operator = empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']; if (!empty($required['PHP']) && !version_compare(PHP_VERSION, $required['PHP']['version'], $operator)) { $missing[] = sprintf('PHP %s %s is required.', $operator, $required['PHP']['version']); } if (!empty($required['PHPUnit'])) { $phpunitVersion = PHPUnit_Runner_Version::id(); $operator = empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']; if (!version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) { $missing[] = sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']); } } if (!empty($required['OS']) && !preg_match($required['OS'], PHP_OS)) { $missing[] = sprintf('Operating system matching %s is required.', $required['OS']); } if (!empty($required['functions'])) { foreach ($required['functions'] as $function) { $pieces = explode('::', $function); if (2 === count($pieces) && method_exists($pieces[0], $pieces[1])) { continue; } if (function_exists($function)) { continue; } $missing[] = sprintf('Function %s is required.', $function); } } if (!empty($required['extensions'])) { foreach ($required['extensions'] as $extension) { if (isset($required['extension_versions'][$extension])) { continue; } if (!extension_loaded($extension)) { $missing[] = sprintf('Extension %s is required.', $extension); } } } if (!empty($required['extension_versions'])) { foreach ($required['extension_versions'] as $extension => $required) { $actualVersion = phpversion($extension); $operator = empty($required['operator']) ? '>=' : $required['operator']; if (false === $actualVersion || !version_compare($actualVersion, $required['version'], $operator)) { $missing[] = sprintf('Extension %s %s %s is required.', $extension, $operator, $required['version']); } } } return $missing; } public static function getExpectedException($className, $methodName) { $reflector = new ReflectionMethod($className, $methodName); $docComment = $reflector->getDocComment(); $docComment = substr($docComment, 3, -2); if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); $class = $matches[1]; $code = null; $message = ''; $messageRegExp = ''; if (isset($matches[2])) { $message = trim($matches[2]); } elseif (isset($annotations['method']['expectedExceptionMessage'])) { $message = self::parseAnnotationContent( $annotations['method']['expectedExceptionMessage'][0] ); } if (isset($annotations['method']['expectedExceptionMessageRegExp'])) { $messageRegExp = self::parseAnnotationContent( $annotations['method']['expectedExceptionMessageRegExp'][0] ); } if (isset($matches[3])) { $code = $matches[3]; } elseif (isset($annotations['method']['expectedExceptionCode'])) { $code = self::parseAnnotationContent( $annotations['method']['expectedExceptionCode'][0] ); } if (is_numeric($code)) { $code = (int) $code; } elseif (is_string($code) && defined($code)) { $code = (int) constant($code); } return [ 'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp ]; } return false; } private static function parseAnnotationContent($message) { if (strpos($message, '::') !== false && count(explode('::', $message)) == 2) { if (defined($message)) { $message = constant($message); } } return $message; } public static function getProvidedData($className, $methodName) { $reflector = new ReflectionMethod($className, $methodName); $docComment = $reflector->getDocComment(); $data = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName); if ($data === null) { $data = self::getDataFromTestWithAnnotation($docComment); } if (is_array($data) && empty($data)) { throw new PHPUnit_Framework_SkippedTestError; } if ($data !== null) { foreach ($data as $key => $value) { if (!is_array($value)) { throw new PHPUnit_Framework_Exception( sprintf( 'Data set %s is invalid.', is_int($key) ? '#' . $key : '"' . $key . '"' ) ); } } } return $data; } private static function getDataFromDataProviderAnnotation($docComment, $className, $methodName) { if (preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) { $result = []; foreach ($matches[1] as $match) { $dataProviderMethodNameNamespace = explode('\\', $match); $leaf = explode('::', array_pop($dataProviderMethodNameNamespace)); $dataProviderMethodName = array_pop($leaf); if (!empty($dataProviderMethodNameNamespace)) { $dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\'; } else { $dataProviderMethodNameNamespace = ''; } if (!empty($leaf)) { $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf); } else { $dataProviderClassName = $className; } $dataProviderClass = new ReflectionClass($dataProviderClassName); $dataProviderMethod = $dataProviderClass->getMethod( $dataProviderMethodName ); if ($dataProviderMethod->isStatic()) { $object = null; } else { $object = $dataProviderClass->newInstance(); } if ($dataProviderMethod->getNumberOfParameters() == 0) { $data = $dataProviderMethod->invoke($object); } else { $data = $dataProviderMethod->invoke($object, $methodName); } if ($data instanceof Iterator) { $data = iterator_to_array($data); } if (is_array($data)) { $result = array_merge($result, $data); } } return $result; } } public static function getDataFromTestWithAnnotation($docComment) { $docComment = self::cleanUpMultiLineAnnotation($docComment); if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) { $offset = strlen($matches[0][0]) + $matches[0][1]; $annotationContent = substr($docComment, $offset); $data = []; foreach (explode("\n", $annotationContent) as $candidateRow) { $candidateRow = trim($candidateRow); if ($candidateRow[0] !== '[') { break; } $dataSet = json_decode($candidateRow, true); if (json_last_error() != JSON_ERROR_NONE) { throw new PHPUnit_Framework_Exception( 'The dataset for the @testWith annotation cannot be parsed: ' . json_last_error_msg() ); } $data[] = $dataSet; } if (!$data) { throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.'); } return $data; } } private static function cleanUpMultiLineAnnotation($docComment) { $docComment = str_replace("\r\n", "\n", $docComment); $docComment = preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment); $docComment = substr($docComment, 0, -1); $docComment = rtrim($docComment, "\n"); return $docComment; } public static function parseTestMethodAnnotations($className, $methodName = '') { if (!isset(self::$annotationCache[$className])) { $class = new ReflectionClass($className); self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment()); } if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) { try { $method = new ReflectionMethod($className, $methodName); $annotations = self::parseAnnotations($method->getDocComment()); } catch (ReflectionException $e) { $annotations = []; } self::$annotationCache[$className . '::' . $methodName] = $annotations; } return [ 'class' => self::$annotationCache[$className], 'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : [] ]; } public static function getInlineAnnotations($className, $methodName) { $method = new ReflectionMethod($className, $methodName); $code = file($method->getFileName()); $lineNumber = $method->getStartLine(); $startLine = $method->getStartLine() - 1; $endLine = $method->getEndLine() - 1; $methodLines = array_slice($code, $startLine, $endLine - $startLine + 1); $annotations = []; foreach ($methodLines as $line) { if (preg_match('#/\*\*?\s*@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?\*/$#m', $line, $matches)) { $annotations[strtolower($matches['name'])] = [ 'line' => $lineNumber, 'value' => $matches['value'] ]; } $lineNumber++; } return $annotations; } private static function parseAnnotations($docblock) { $annotations = []; $docblock = substr($docblock, 3, -2); if (preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docblock, $matches)) { $numMatches = count($matches[0]); for ($i = 0; $i < $numMatches; ++$i) { $annotations[$matches['name'][$i]][] = (string) $matches['value'][$i]; } } return $annotations; } public static function getBackupSettings($className, $methodName) { return [ 'backupGlobals' => self::getBooleanAnnotationSetting( $className, $methodName, 'backupGlobals' ), 'backupStaticAttributes' => self::getBooleanAnnotationSetting( $className, $methodName, 'backupStaticAttributes' ) ]; } public static function getDependencies($className, $methodName) { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); $dependencies = []; if (isset($annotations['class']['depends'])) { $dependencies = $annotations['class']['depends']; } if (isset($annotations['method']['depends'])) { $dependencies = array_merge( $dependencies, $annotations['method']['depends'] ); } return array_unique($dependencies); } public static function getErrorHandlerSettings($className, $methodName) { return self::getBooleanAnnotationSetting( $className, $methodName, 'errorHandler' ); } public static function getGroups($className, $methodName = '') { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); $groups = []; if (isset($annotations['method']['author'])) { $groups = $annotations['method']['author']; } elseif (isset($annotations['class']['author'])) { $groups = $annotations['class']['author']; } if (isset($annotations['class']['group'])) { $groups = array_merge($groups, $annotations['class']['group']); } if (isset($annotations['method']['group'])) { $groups = array_merge($groups, $annotations['method']['group']); } if (isset($annotations['class']['ticket'])) { $groups = array_merge($groups, $annotations['class']['ticket']); } if (isset($annotations['method']['ticket'])) { $groups = array_merge($groups, $annotations['method']['ticket']); } foreach (['method', 'class'] as $element) { foreach (['small', 'medium', 'large'] as $size) { if (isset($annotations[$element][$size])) { $groups[] = $size; break 2; } } } return array_unique($groups); } public static function getSize($className, $methodName) { $groups = array_flip(self::getGroups($className, $methodName)); $size = self::UNKNOWN; $class = new ReflectionClass($className); if (isset($groups['large']) || (class_exists('PHPUnit_Extensions_Database_TestCase', false) && $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase'))) { $size = self::LARGE; } elseif (isset($groups['medium'])) { $size = self::MEDIUM; } elseif (isset($groups['small'])) { $size = self::SMALL; } return $size; } public static function getTickets($className, $methodName) { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); $tickets = []; if (isset($annotations['class']['ticket'])) { $tickets = $annotations['class']['ticket']; } if (isset($annotations['method']['ticket'])) { $tickets = array_merge($tickets, $annotations['method']['ticket']); } return array_unique($tickets); } public static function getProcessIsolationSettings($className, $methodName) { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); if (isset($annotations['class']['runTestsInSeparateProcesses']) || isset($annotations['method']['runInSeparateProcess'])) { return true; } else { return false; } } public static function getPreserveGlobalStateSettings($className, $methodName) { return self::getBooleanAnnotationSetting( $className, $methodName, 'preserveGlobalState' ); } public static function getHookMethods($className) { if (!class_exists($className, false)) { return self::emptyHookMethodsArray(); } if (!isset(self::$hookMethods[$className])) { self::$hookMethods[$className] = self::emptyHookMethodsArray(); try { $class = new ReflectionClass($className); foreach ($class->getMethods() as $method) { if (self::isBeforeClassMethod($method)) { self::$hookMethods[$className]['beforeClass'][] = $method->getName(); } if (self::isBeforeMethod($method)) { self::$hookMethods[$className]['before'][] = $method->getName(); } if (self::isAfterMethod($method)) { self::$hookMethods[$className]['after'][] = $method->getName(); } if (self::isAfterClassMethod($method)) { self::$hookMethods[$className]['afterClass'][] = $method->getName(); } } } catch (ReflectionException $e) { } } return self::$hookMethods[$className]; } private static function emptyHookMethodsArray() { return [ 'beforeClass' => ['setUpBeforeClass'], 'before' => ['setUp'], 'after' => ['tearDown'], 'afterClass' => ['tearDownAfterClass'] ]; } private static function getBooleanAnnotationSetting($className, $methodName, $settingName) { $annotations = self::parseTestMethodAnnotations( $className, $methodName ); $result = null; if (isset($annotations['class'][$settingName])) { if ($annotations['class'][$settingName][0] == 'enabled') { $result = true; } elseif ($annotations['class'][$settingName][0] == 'disabled') { $result = false; } } if (isset($annotations['method'][$settingName])) { if ($annotations['method'][$settingName][0] == 'enabled') { $result = true; } elseif ($annotations['method'][$settingName][0] == 'disabled') { $result = false; } } return $result; } private static function resolveElementToReflectionObjects($element) { $codeToCoverList = []; if (strpos($element, '\\') !== false && function_exists($element)) { $codeToCoverList[] = new ReflectionFunction($element); } elseif (strpos($element, '::') !== false) { list($className, $methodName) = explode('::', $element); if (isset($methodName[0]) && $methodName[0] == '<') { $classes = [$className]; foreach ($classes as $className) { if (!class_exists($className) && !interface_exists($className) && !trait_exists($className)) { throw new PHPUnit_Framework_InvalidCoversTargetException( sprintf( 'Trying to @cover or @use not existing class or ' . 'interface "%s".', $className ) ); } $class = new ReflectionClass($className); $methods = $class->getMethods(); $inverse = isset($methodName[1]) && $methodName[1] == '!'; if (strpos($methodName, 'protected')) { $visibility = 'isProtected'; } elseif (strpos($methodName, 'private')) { $visibility = 'isPrivate'; } elseif (strpos($methodName, 'public')) { $visibility = 'isPublic'; } foreach ($methods as $method) { if ($inverse && !$method->$visibility()) { $codeToCoverList[] = $method; } elseif (!$inverse && $method->$visibility()) { $codeToCoverList[] = $method; } } } } else { $classes = [$className]; foreach ($classes as $className) { if ($className == '' && function_exists($methodName)) { $codeToCoverList[] = new ReflectionFunction( $methodName ); } else { if (!((class_exists($className) || interface_exists($className) || trait_exists($className)) && method_exists($className, $methodName))) { throw new PHPUnit_Framework_InvalidCoversTargetException( sprintf( 'Trying to @cover or @use not existing method "%s::%s".', $className, $methodName ) ); } $codeToCoverList[] = new ReflectionMethod( $className, $methodName ); } } } } else { $extended = false; if (strpos($element, '') !== false) { $element = str_replace('', '', $element); $extended = true; } $classes = [$element]; if ($extended) { $classes = array_merge( $classes, class_implements($element), class_parents($element) ); } foreach ($classes as $className) { if (!class_exists($className) && !interface_exists($className) && !trait_exists($className)) { throw new PHPUnit_Framework_InvalidCoversTargetException( sprintf( 'Trying to @cover or @use not existing class or ' . 'interface "%s".', $className ) ); } $codeToCoverList[] = new ReflectionClass($className); } } return $codeToCoverList; } private static function resolveReflectionObjectsToLines(array $reflectors) { $result = []; foreach ($reflectors as $reflector) { $filename = $reflector->getFileName(); if (!isset($result[$filename])) { $result[$filename] = []; } $result[$filename] = array_merge( $result[$filename], range($reflector->getStartLine(), $reflector->getEndLine()) ); } foreach ($result as $filename => $lineNumbers) { $result[$filename] = array_keys(array_flip($lineNumbers)); } return $result; } private static function isBeforeClassMethod(ReflectionMethod $method) { return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false; } private static function isBeforeMethod(ReflectionMethod $method) { return preg_match('/@before\b/', $method->getDocComment()); } private static function isAfterClassMethod(ReflectionMethod $method) { return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false; } private static function isAfterMethod(ReflectionMethod $method) { return preg_match('/@after\b/', $method->getDocComment()); } } 0; $i--) { $file = $files[$i]; if ($prefix !== false && strpos($file, $prefix) === 0) { continue; } if (preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { continue; } if (!$blacklist->isBlacklisted($file) && is_file($file)) { $result = 'require_once \'' . $file . "';\n" . $result; } } return $result; } public static function getIniSettingsAsString() { $result = ''; $iniSettings = ini_get_all(null, false); foreach ($iniSettings as $key => $value) { $result .= sprintf( '@ini_set(%s, %s);' . "\n", self::exportVariable($key), self::exportVariable($value) ); } return $result; } public static function getConstantsAsString() { $constants = get_defined_constants(true); $result = ''; if (isset($constants['user'])) { foreach ($constants['user'] as $name => $value) { $result .= sprintf( 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, self::exportVariable($value) ); } } return $result; } public static function getGlobalsAsString() { $result = ''; $superGlobalArrays = self::getSuperGlobalArrays(); foreach ($superGlobalArrays as $superGlobalArray) { if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { foreach (array_keys($GLOBALS[$superGlobalArray]) as $key) { if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { continue; } $result .= sprintf( '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", $superGlobalArray, $key, self::exportVariable($GLOBALS[$superGlobalArray][$key]) ); } } } $blacklist = $superGlobalArrays; $blacklist[] = 'GLOBALS'; foreach (array_keys($GLOBALS) as $key) { if (!in_array($key, $blacklist) && !$GLOBALS[$key] instanceof Closure) { $result .= sprintf( '$GLOBALS[\'%s\'] = %s;' . "\n", $key, self::exportVariable($GLOBALS[$key]) ); } } return $result; } protected static function getSuperGlobalArrays() { if (ini_get('register_long_arrays') == '1') { return array_merge( self::$superGlobalArrays, self::$superGlobalArraysLong ); } else { return self::$superGlobalArrays; } } protected static function exportVariable($variable) { if (is_scalar($variable) || is_null($variable) || (is_array($variable) && self::arrayOnlyContainsScalars($variable))) { return var_export($variable, true); } return 'unserialize(' . var_export(serialize($variable), true) . ')'; } protected static function arrayOnlyContainsScalars(array $array) { $result = true; foreach ($array as $element) { if (is_array($element)) { $result = self::arrayOnlyContainsScalars($element); } elseif (!is_scalar($element) && !is_null($element)) { $result = false; } if ($result === false) { break; } } return $result; } } tests = $testSuite->tests(); } public function rewind() { $this->position = 0; } public function valid() { return $this->position < count($this->tests); } public function key() { return $this->position; } public function current() { return $this->valid() ? $this->tests[$this->position] : null; } public function next() { $this->position++; } public function getChildren() { return new self( $this->tests[$this->position] ); } public function hasChildren() { return $this->tests[$this->position] instanceof PHPUnit_Framework_TestSuite; } } write($this->currentTestClassPrettified . "\n"); } protected function onTest($name, $success = true) { if ($success) { $this->write(' [x] '); } else { $this->write(' [ ] '); } $this->write($name . "\n"); } protected function endClass($name) { $this->write("\n"); } } document = new DOMDocument('1.0', 'UTF-8'); $this->document->formatOutput = true; $this->root = $this->document->createElement('tests'); $this->document->appendChild($this->root); $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier; parent::__construct($out); } public function flush() { $this->write($this->document->saveXML()); parent::flush(); } public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->exception = $e; } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { $this->exception = $e; } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { } public function startTest(PHPUnit_Framework_Test $test) { $this->exception = null; } public function endTest(PHPUnit_Framework_Test $test, $time) { if (!$test instanceof PHPUnit_Framework_TestCase) { return; } $groups = array_filter( $test->getGroups(), function ($group) { if ($group == 'small' || $group == 'medium' || $group == 'large') { return false; } return true; } ); $node = $this->document->createElement('test'); $node->setAttribute('className', get_class($test)); $node->setAttribute('methodName', $test->getName()); $node->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(get_class($test))); $node->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestMethod($test->getName())); $node->setAttribute('status', $test->getStatus()); $node->setAttribute('time', $time); $node->setAttribute('size', $test->getSize()); $node->setAttribute('groups', implode(',', $groups)); $inlineAnnotations = PHPUnit_Util_Test::getInlineAnnotations(get_class($test), $test->getName()); if (isset($inlineAnnotations['given']) && isset($inlineAnnotations['when']) && isset($inlineAnnotations['then'])) { $node->setAttribute('given', $inlineAnnotations['given']['value']); $node->setAttribute('givenStartLine', $inlineAnnotations['given']['line']); $node->setAttribute('when', $inlineAnnotations['when']['value']); $node->setAttribute('whenStartLine', $inlineAnnotations['when']['line']); $node->setAttribute('then', $inlineAnnotations['then']['value']); $node->setAttribute('thenStartLine', $inlineAnnotations['then']['line']); } if ($this->exception !== null) { if ($this->exception instanceof PHPUnit_Framework_Exception) { $steps = $this->exception->getSerializableTrace(); } else { $steps = $this->exception->getTrace(); } $class = new ReflectionClass($test); $file = $class->getFileName(); foreach ($steps as $step) { if (isset($step['file']) && $step['file'] == $file) { $node->setAttribute('exceptionLine', $step['line']); break; } } $node->setAttribute('exceptionMessage', $this->exception->getMessage()); } $this->root->appendChild($node); } } Test Documentation EOT; private $classHeader = <<%s
    EOT; private $classFooter = << EOT; private $pageFooter = << EOT; protected function startRun() { $this->write($this->pageHeader); } protected function startClass($name) { $this->write( sprintf( $this->classHeader, $name, $this->currentTestClassPrettified ) ); } protected function onTest($name, $success = true) { $this->write( sprintf( "
  • %s %s
  • \n", $success ? '#555753' : '#ef2929', $success ? '✓' : '❌', $name ) ); } protected function endClass($name) { $this->write($this->classFooter); } protected function endRun() { $this->write($this->pageFooter); } } groups = $groups; $this->excludeGroups = $excludeGroups; $this->prettifier = new PHPUnit_Util_TestDox_NamePrettifier; $this->startRun(); } public function flush() { $this->doEndClass(); $this->endRun(); parent::flush(); } public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { if (!$this->isOfInterest($test)) { return; } $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR; $this->failed++; } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { if (!$this->isOfInterest($test)) { return; } $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_WARNING; $this->warned++; } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { if (!$this->isOfInterest($test)) { return; } $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE; $this->failed++; } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { if (!$this->isOfInterest($test)) { return; } $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE; $this->incomplete++; } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { if (!$this->isOfInterest($test)) { return; } $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY; $this->risky++; } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { if (!$this->isOfInterest($test)) { return; } $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED; $this->skipped++; } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { } public function startTest(PHPUnit_Framework_Test $test) { if (!$this->isOfInterest($test)) { return; } $class = get_class($test); if ($this->testClass != $class) { if ($this->testClass != '') { $this->doEndClass(); } $classAnnotations = PHPUnit_Util_Test::parseTestMethodAnnotations($class); if (isset($classAnnotations['class']['testdox'][0])) { $this->currentTestClassPrettified = $classAnnotations['class']['testdox'][0]; } else { $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class); } $this->startClass($class); $this->testClass = $class; $this->tests = []; } $annotations = $test->getAnnotations(); if (isset($annotations['method']['testdox'][0])) { $this->currentTestMethodPrettified = $annotations['method']['testdox'][0]; } else { $this->currentTestMethodPrettified = $this->prettifier->prettifyTestMethod($test->getName(false)); } if ($test instanceof PHPUnit_Framework_TestCase && $test->usesDataProvider()) { $this->currentTestMethodPrettified .= ' ' . $test->dataDescription(); } $this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_PASSED; } public function endTest(PHPUnit_Framework_Test $test, $time) { if (!$this->isOfInterest($test)) { return; } if (!isset($this->tests[$this->currentTestMethodPrettified])) { if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { $this->tests[$this->currentTestMethodPrettified]['success'] = 1; $this->tests[$this->currentTestMethodPrettified]['failure'] = 0; } else { $this->tests[$this->currentTestMethodPrettified]['success'] = 0; $this->tests[$this->currentTestMethodPrettified]['failure'] = 1; } } else { if ($this->testStatus == PHPUnit_Runner_BaseTestRunner::STATUS_PASSED) { $this->tests[$this->currentTestMethodPrettified]['success']++; } else { $this->tests[$this->currentTestMethodPrettified]['failure']++; } } $this->currentTestClassPrettified = null; $this->currentTestMethodPrettified = null; } protected function doEndClass() { foreach ($this->tests as $name => $data) { $this->onTest($name, $data['failure'] == 0); } $this->endClass($this->testClass); } protected function startRun() { } protected function startClass($name) { } protected function onTest($name, $success = true) { } protected function endClass($name) { } protected function endRun() { } private function isOfInterest(PHPUnit_Framework_Test $test) { if (!$test instanceof PHPUnit_Framework_TestCase) { return false; } if ($test instanceof PHPUnit_Framework_WarningTestCase) { return false; } if (!empty($this->groups)) { foreach ($test->getGroups() as $group) { if (in_array($group, $this->groups)) { return true; } } return false; } if (!empty($this->excludeGroups)) { foreach ($test->getGroups() as $group) { if (in_array($group, $this->excludeGroups)) { return false; } } return true; } return true; } } suffix !== null && $this->suffix == substr($name, -1 * strlen($this->suffix))) { $title = substr($title, 0, strripos($title, $this->suffix)); } if ($this->prefix !== null && $this->prefix == substr($name, 0, strlen($this->prefix))) { $title = substr($title, strlen($this->prefix)); } if (substr($title, 0, 1) == '\\') { $title = substr($title, 1); } return $title; } public function prettifyTestMethod($name) { $buffer = ''; if (!is_string($name) || strlen($name) == 0) { return $buffer; } $string = preg_replace('#\d+$#', '', $name, -1, $count); if (in_array($string, $this->strings)) { $name = $string; } elseif ($count == 0) { $this->strings[] = $string; } if (substr($name, 0, 4) == 'test') { $name = substr($name, 4); } if (strlen($name) == 0) { return $buffer; } $name[0] = strtoupper($name[0]); if (strpos($name, '_') !== false) { return trim(str_replace('_', ' ', $name)); } $max = strlen($name); $wasNumeric = false; for ($i = 0; $i < $max; $i++) { if ($i > 0 && ord($name[$i]) >= 65 && ord($name[$i]) <= 90) { $buffer .= ' ' . strtolower($name[$i]); } else { $isNumeric = is_numeric($name[$i]); if (!$wasNumeric && $isNumeric) { $buffer .= ' '; $wasNumeric = true; } if ($wasNumeric && !$isNumeric) { $wasNumeric = false; } $buffer .= $name[$i]; } } return $buffer; } public function setPrefix($prefix) { $this->prefix = $prefix; } public function setSuffix($suffix) { $this->suffix = $suffix; } } codeCoverageFilter = $filter; $this->loader = $loader; $this->runtime = new Runtime; } public static function run($test, array $arguments = []) { if ($test instanceof ReflectionClass) { $test = new PHPUnit_Framework_TestSuite($test); } if ($test instanceof PHPUnit_Framework_Test) { $aTestRunner = new self; return $aTestRunner->doRun( $test, $arguments ); } else { throw new PHPUnit_Framework_Exception( 'No test case or test suite found.' ); } } protected function createTestResult() { return new PHPUnit_Framework_TestResult; } private function processSuiteFilters(PHPUnit_Framework_TestSuite $suite, array $arguments) { if (!$arguments['filter'] && empty($arguments['groups']) && empty($arguments['excludeGroups'])) { return; } $filterFactory = new PHPUnit_Runner_Filter_Factory(); if (!empty($arguments['excludeGroups'])) { $filterFactory->addFilter( new ReflectionClass('PHPUnit_Runner_Filter_Group_Exclude'), $arguments['excludeGroups'] ); } if (!empty($arguments['groups'])) { $filterFactory->addFilter( new ReflectionClass('PHPUnit_Runner_Filter_Group_Include'), $arguments['groups'] ); } if ($arguments['filter']) { $filterFactory->addFilter( new ReflectionClass('PHPUnit_Runner_Filter_Test'), $arguments['filter'] ); } $suite->injectFilter($filterFactory); } public function doRun(PHPUnit_Framework_Test $suite, array $arguments = [], $exit = true) { if (isset($arguments['configuration'])) { $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; } $this->handleConfiguration($arguments); $this->processSuiteFilters($suite, $arguments); if (isset($arguments['bootstrap'])) { $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; } if ($arguments['backupGlobals'] === false) { $suite->setBackupGlobals(false); } if ($arguments['backupStaticAttributes'] === true) { $suite->setBackupStaticAttributes(true); } if ($arguments['beStrictAboutChangesToGlobalState'] === true) { $suite->setBeStrictAboutChangesToGlobalState(true); } if (is_int($arguments['repeat'])) { $test = new PHPUnit_Extensions_RepeatedTest( $suite, $arguments['repeat'], $arguments['processIsolation'] ); $suite = new PHPUnit_Framework_TestSuite(); $suite->addTest($test); } $result = $this->createTestResult(); if (!$arguments['convertErrorsToExceptions']) { $result->convertErrorsToExceptions(false); } if (!$arguments['convertNoticesToExceptions']) { PHPUnit_Framework_Error_Notice::$enabled = false; } if (!$arguments['convertWarningsToExceptions']) { PHPUnit_Framework_Error_Warning::$enabled = false; } if ($arguments['stopOnError']) { $result->stopOnError(true); } if ($arguments['stopOnFailure']) { $result->stopOnFailure(true); } if ($arguments['stopOnWarning']) { $result->stopOnWarning(true); } if ($arguments['stopOnIncomplete']) { $result->stopOnIncomplete(true); } if ($arguments['stopOnRisky']) { $result->stopOnRisky(true); } if ($arguments['stopOnSkipped']) { $result->stopOnSkipped(true); } if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) { $result->setRegisterMockObjectsFromTestArgumentsRecursively(true); } if ($this->printer === null) { if (isset($arguments['printer']) && $arguments['printer'] instanceof PHPUnit_Util_Printer) { $this->printer = $arguments['printer']; } else { $printerClass = 'PHPUnit_TextUI_ResultPrinter'; if (isset($arguments['printer']) && is_string($arguments['printer']) && class_exists($arguments['printer'], false)) { $class = new ReflectionClass($arguments['printer']); if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) { $printerClass = $arguments['printer']; } } $this->printer = new $printerClass( (isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null, $arguments['verbose'], $arguments['colors'], $arguments['debug'], $arguments['columns'], $arguments['reverseList'] ); } } if (!$this->printer instanceof PHPUnit_Util_Log_TAP) { $this->printer->write( PHPUnit_Runner_Version::getVersionString() . "\n" ); self::$versionStringPrinted = true; if ($arguments['verbose']) { $runtime = $this->runtime->getNameWithVersion(); if ($this->runtime->hasXdebug()) { $runtime .= sprintf( ' with Xdebug %s', phpversion('xdebug') ); } $this->writeMessage('Runtime', $runtime); if (isset($arguments['configuration'])) { $this->writeMessage( 'Configuration', $arguments['configuration']->getFilename() ); } foreach ($arguments['loadedExtensions'] as $extension) { $this->writeMessage( 'Extension', $extension ); } foreach ($arguments['notLoadedExtensions'] as $extension) { $this->writeMessage( 'Extension', $extension ); } } if (isset($arguments['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'])) { $this->writeMessage('Warning', 'Deprecated configuration setting "checkForUnintentionallyCoveredCode" used'); } if (isset($arguments['tapLogfile'])) { $this->writeMessage('Warning', 'Deprecated TAP test listener used'); } if (isset($arguments['jsonLogfile'])) { $this->writeMessage('Warning', 'Deprecated JSON test listener used'); } } foreach ($arguments['listeners'] as $listener) { $result->addListener($listener); } $result->addListener($this->printer); if (isset($arguments['testdoxHTMLFile'])) { $result->addListener( new PHPUnit_Util_TestDox_ResultPrinter_HTML( $arguments['testdoxHTMLFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'] ) ); } if (isset($arguments['testdoxTextFile'])) { $result->addListener( new PHPUnit_Util_TestDox_ResultPrinter_Text( $arguments['testdoxTextFile'], $arguments['testdoxGroups'], $arguments['testdoxExcludeGroups'] ) ); } if (isset($arguments['testdoxXMLFile'])) { $result->addListener( new PHPUnit_Util_TestDox_ResultPrinter_XML( $arguments['testdoxXMLFile'] ) ); } $codeCoverageReports = 0; if (isset($arguments['coverageClover'])) { $codeCoverageReports++; } if (isset($arguments['coverageCrap4J'])) { $codeCoverageReports++; } if (isset($arguments['coverageHtml'])) { $codeCoverageReports++; } if (isset($arguments['coveragePHP'])) { $codeCoverageReports++; } if (isset($arguments['coverageText'])) { $codeCoverageReports++; } if (isset($arguments['coverageXml'])) { $codeCoverageReports++; } if (isset($arguments['noCoverage'])) { $codeCoverageReports = 0; } if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) { $this->writeMessage('Error', 'No code coverage driver is available'); $codeCoverageReports = 0; } if (!$this->printer instanceof PHPUnit_Util_Log_TAP) { $this->printer->write("\n"); } if ($codeCoverageReports > 0) { $codeCoverage = new CodeCoverage( null, $this->codeCoverageFilter ); $codeCoverage->setUnintentionallyCoveredSubclassesWhitelist( [SebastianBergmann\Comparator\Comparator::class] ); $codeCoverage->setCheckForUnintentionallyCoveredCode( $arguments['strictCoverage'] ); $codeCoverage->setCheckForMissingCoversAnnotation( $arguments['strictCoverage'] ); if (isset($arguments['forceCoversAnnotation'])) { $codeCoverage->setForceCoversAnnotation( $arguments['forceCoversAnnotation'] ); } if (isset($arguments['disableCodeCoverageIgnore'])) { $codeCoverage->setDisableIgnoredLines(true); } if (isset($arguments['whitelist'])) { $this->codeCoverageFilter->addDirectoryToWhitelist($arguments['whitelist']); } if (isset($arguments['configuration'])) { $filterConfiguration = $arguments['configuration']->getFilterConfiguration(); $codeCoverage->setAddUncoveredFilesFromWhitelist( $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist'] ); $codeCoverage->setProcessUncoveredFilesFromWhitelist( $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist'] ); foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) { $this->codeCoverageFilter->addDirectoryToWhitelist( $dir['path'], $dir['suffix'], $dir['prefix'] ); } foreach ($filterConfiguration['whitelist']['include']['file'] as $file) { $this->codeCoverageFilter->addFileToWhitelist($file); } foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) { $this->codeCoverageFilter->removeDirectoryFromWhitelist( $dir['path'], $dir['suffix'], $dir['prefix'] ); } foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) { $this->codeCoverageFilter->removeFileFromWhitelist($file); } } if (!$this->codeCoverageFilter->hasWhitelist()) { $this->writeMessage('Error', 'No whitelist configured, no code coverage will be generated'); $codeCoverageReports = 0; unset($codeCoverage); } } if (isset($codeCoverage)) { $result->setCodeCoverage($codeCoverage); if ($codeCoverageReports > 1 && isset($arguments['cacheTokens'])) { $codeCoverage->setCacheTokens($arguments['cacheTokens']); } } if (isset($arguments['jsonLogfile'])) { $result->addListener( new PHPUnit_Util_Log_JSON($arguments['jsonLogfile']) ); } if (isset($arguments['tapLogfile'])) { $result->addListener( new PHPUnit_Util_Log_TAP($arguments['tapLogfile']) ); } if (isset($arguments['teamcityLogfile'])) { $result->addListener( new PHPUnit_Util_Log_TeamCity($arguments['teamcityLogfile']) ); } if (isset($arguments['junitLogfile'])) { $result->addListener( new PHPUnit_Util_Log_JUnit( $arguments['junitLogfile'], $arguments['logIncompleteSkipped'] ) ); } $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); $result->enforceTimeLimit($arguments['enforceTimeLimit']); $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']); $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']); $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']); if ($suite instanceof PHPUnit_Framework_TestSuite) { $suite->setRunTestInSeparateProcess($arguments['processIsolation']); } $suite->run($result); unset($suite); $result->flushListeners(); if ($this->printer instanceof PHPUnit_TextUI_ResultPrinter) { $this->printer->printResult($result); } if (isset($codeCoverage)) { if (isset($arguments['coverageClover'])) { $this->printer->write( "\nGenerating code coverage report in Clover XML format ..." ); try { $writer = new CloverReport(); $writer->process($codeCoverage, $arguments['coverageClover']); $this->printer->write(" done\n"); unset($writer); } catch (CodeCoverageException $e) { $this->printer->write( " failed\n" . $e->getMessage() . "\n" ); } } if (isset($arguments['coverageCrap4J'])) { $this->printer->write( "\nGenerating Crap4J report XML file ..." ); try { $writer = new Crap4jReport($arguments['crap4jThreshold']); $writer->process($codeCoverage, $arguments['coverageCrap4J']); $this->printer->write(" done\n"); unset($writer); } catch (CodeCoverageException $e) { $this->printer->write( " failed\n" . $e->getMessage() . "\n" ); } } if (isset($arguments['coverageHtml'])) { $this->printer->write( "\nGenerating code coverage report in HTML format ..." ); try { $writer = new HtmlReport( $arguments['reportLowUpperBound'], $arguments['reportHighLowerBound'], sprintf( ' and PHPUnit %s', PHPUnit_Runner_Version::id() ) ); $writer->process($codeCoverage, $arguments['coverageHtml']); $this->printer->write(" done\n"); unset($writer); } catch (CodeCoverageException $e) { $this->printer->write( " failed\n" . $e->getMessage() . "\n" ); } } if (isset($arguments['coveragePHP'])) { $this->printer->write( "\nGenerating code coverage report in PHP format ..." ); try { $writer = new PhpReport(); $writer->process($codeCoverage, $arguments['coveragePHP']); $this->printer->write(" done\n"); unset($writer); } catch (CodeCoverageException $e) { $this->printer->write( " failed\n" . $e->getMessage() . "\n" ); } } if (isset($arguments['coverageText'])) { if ($arguments['coverageText'] == 'php://stdout') { $outputStream = $this->printer; $colors = $arguments['colors'] && $arguments['colors'] != PHPUnit_TextUI_ResultPrinter::COLOR_NEVER; } else { $outputStream = new PHPUnit_Util_Printer($arguments['coverageText']); $colors = false; } $processor = new TextReport( $arguments['reportLowUpperBound'], $arguments['reportHighLowerBound'], $arguments['coverageTextShowUncoveredFiles'], $arguments['coverageTextShowOnlySummary'] ); $outputStream->write( $processor->process($codeCoverage, $colors) ); } if (isset($arguments['coverageXml'])) { $this->printer->write( "\nGenerating code coverage report in PHPUnit XML format ..." ); try { $writer = new XmlReport; $writer->process($codeCoverage, $arguments['coverageXml']); $this->printer->write(" done\n"); unset($writer); } catch (CodeCoverageException $e) { $this->printer->write( " failed\n" . $e->getMessage() . "\n" ); } } } if ($exit) { if ($result->wasSuccessful(false)) { if ($arguments['failOnRisky'] && !$result->allHarmless()) { exit(self::FAILURE_EXIT); } if ($arguments['failOnWarning'] && $result->warningCount() > 0) { exit(self::FAILURE_EXIT); } exit(self::SUCCESS_EXIT); } if ($result->errorCount() > 0) { exit(self::EXCEPTION_EXIT); } if ($result->failureCount() > 0) { exit(self::FAILURE_EXIT); } } return $result; } public function setPrinter(PHPUnit_TextUI_ResultPrinter $resultPrinter) { $this->printer = $resultPrinter; } protected function runFailed($message) { $this->write($message . PHP_EOL); exit(self::FAILURE_EXIT); } protected function write($buffer) { if (PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg') { $buffer = htmlspecialchars($buffer); } if ($this->printer !== null) { $this->printer->write($buffer); } else { print $buffer; } } public function getLoader() { if ($this->loader === null) { $this->loader = new PHPUnit_Runner_StandardTestSuiteLoader; } return $this->loader; } protected function handleConfiguration(array &$arguments) { if (isset($arguments['configuration']) && !$arguments['configuration'] instanceof PHPUnit_Util_Configuration) { $arguments['configuration'] = PHPUnit_Util_Configuration::getInstance( $arguments['configuration'] ); } $arguments['debug'] = isset($arguments['debug']) ? $arguments['debug'] : false; $arguments['filter'] = isset($arguments['filter']) ? $arguments['filter'] : false; $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : []; if (isset($arguments['configuration'])) { $arguments['configuration']->handlePHPConfiguration(); $phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration(); if (isset($phpunitConfiguration['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'])) { $arguments['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'] = true; } if (isset($phpunitConfiguration['backupGlobals']) && !isset($arguments['backupGlobals'])) { $arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals']; } if (isset($phpunitConfiguration['backupStaticAttributes']) && !isset($arguments['backupStaticAttributes'])) { $arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes']; } if (isset($phpunitConfiguration['beStrictAboutChangesToGlobalState']) && !isset($arguments['beStrictAboutChangesToGlobalState'])) { $arguments['beStrictAboutChangesToGlobalState'] = $phpunitConfiguration['beStrictAboutChangesToGlobalState']; } if (isset($phpunitConfiguration['bootstrap']) && !isset($arguments['bootstrap'])) { $arguments['bootstrap'] = $phpunitConfiguration['bootstrap']; } if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) { $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens']; } if (isset($phpunitConfiguration['colors']) && !isset($arguments['colors'])) { $arguments['colors'] = $phpunitConfiguration['colors']; } if (isset($phpunitConfiguration['convertErrorsToExceptions']) && !isset($arguments['convertErrorsToExceptions'])) { $arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions']; } if (isset($phpunitConfiguration['convertNoticesToExceptions']) && !isset($arguments['convertNoticesToExceptions'])) { $arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions']; } if (isset($phpunitConfiguration['convertWarningsToExceptions']) && !isset($arguments['convertWarningsToExceptions'])) { $arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions']; } if (isset($phpunitConfiguration['processIsolation']) && !isset($arguments['processIsolation'])) { $arguments['processIsolation'] = $phpunitConfiguration['processIsolation']; } if (isset($phpunitConfiguration['stopOnError']) && !isset($arguments['stopOnError'])) { $arguments['stopOnError'] = $phpunitConfiguration['stopOnError']; } if (isset($phpunitConfiguration['stopOnFailure']) && !isset($arguments['stopOnFailure'])) { $arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure']; } if (isset($phpunitConfiguration['stopOnWarning']) && !isset($arguments['stopOnWarning'])) { $arguments['stopOnWarning'] = $phpunitConfiguration['stopOnWarning']; } if (isset($phpunitConfiguration['stopOnIncomplete']) && !isset($arguments['stopOnIncomplete'])) { $arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete']; } if (isset($phpunitConfiguration['stopOnRisky']) && !isset($arguments['stopOnRisky'])) { $arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky']; } if (isset($phpunitConfiguration['stopOnSkipped']) && !isset($arguments['stopOnSkipped'])) { $arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped']; } if (isset($phpunitConfiguration['failOnWarning']) && !isset($arguments['failOnWarning'])) { $arguments['failOnWarning'] = $phpunitConfiguration['failOnWarning']; } if (isset($phpunitConfiguration['failOnRisky']) && !isset($arguments['failOnRisky'])) { $arguments['failOnRisky'] = $phpunitConfiguration['failOnRisky']; } if (isset($phpunitConfiguration['timeoutForSmallTests']) && !isset($arguments['timeoutForSmallTests'])) { $arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests']; } if (isset($phpunitConfiguration['timeoutForMediumTests']) && !isset($arguments['timeoutForMediumTests'])) { $arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests']; } if (isset($phpunitConfiguration['timeoutForLargeTests']) && !isset($arguments['timeoutForLargeTests'])) { $arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests']; } if (isset($phpunitConfiguration['reportUselessTests']) && !isset($arguments['reportUselessTests'])) { $arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests']; } if (isset($phpunitConfiguration['strictCoverage']) && !isset($arguments['strictCoverage'])) { $arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage']; } if (isset($phpunitConfiguration['disallowTestOutput']) && !isset($arguments['disallowTestOutput'])) { $arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput']; } if (isset($phpunitConfiguration['enforceTimeLimit']) && !isset($arguments['enforceTimeLimit'])) { $arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit']; } if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) && !isset($arguments['disallowTodoAnnotatedTests'])) { $arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests']; } if (isset($phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']) && !isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])) { $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']; } if (isset($phpunitConfiguration['verbose']) && !isset($arguments['verbose'])) { $arguments['verbose'] = $phpunitConfiguration['verbose']; } if (isset($phpunitConfiguration['reverseDefectList']) && !isset($arguments['reverseList'])) { $arguments['reverseList'] = $phpunitConfiguration['reverseDefectList']; } if (isset($phpunitConfiguration['forceCoversAnnotation']) && !isset($arguments['forceCoversAnnotation'])) { $arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation']; } if (isset($phpunitConfiguration['disableCodeCoverageIgnore']) && !isset($arguments['disableCodeCoverageIgnore'])) { $arguments['disableCodeCoverageIgnore'] = $phpunitConfiguration['disableCodeCoverageIgnore']; } if (isset($phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']) && !isset($arguments['registerMockObjectsFromTestArgumentsRecursively'])) { $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']; } $groupCliArgs = []; if (!empty($arguments['groups'])) { $groupCliArgs = $arguments['groups']; } $groupConfiguration = $arguments['configuration']->getGroupConfiguration(); if (!empty($groupConfiguration['include']) && !isset($arguments['groups'])) { $arguments['groups'] = $groupConfiguration['include']; } if (!empty($groupConfiguration['exclude']) && !isset($arguments['excludeGroups'])) { $arguments['excludeGroups'] = array_diff($groupConfiguration['exclude'], $groupCliArgs); } foreach ($arguments['configuration']->getListenerConfiguration() as $listener) { if (!class_exists($listener['class'], false) && $listener['file'] !== '') { require_once $listener['file']; } if (!class_exists($listener['class'])) { throw new PHPUnit_Framework_Exception( sprintf( 'Class "%s" does not exist', $listener['class'] ) ); } $listenerClass = new ReflectionClass($listener['class']); if (!$listenerClass->implementsInterface(PHPUnit_Framework_TestListener::class)) { throw new PHPUnit_Framework_Exception( sprintf( 'Class "%s" does not implement the PHPUnit_Framework_TestListener interface', $listener['class'] ) ); } if (count($listener['arguments']) == 0) { $listener = new $listener['class']; } else { $listener = $listenerClass->newInstanceArgs( $listener['arguments'] ); } $arguments['listeners'][] = $listener; } $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration(); if (isset($loggingConfiguration['coverage-clover']) && !isset($arguments['coverageClover'])) { $arguments['coverageClover'] = $loggingConfiguration['coverage-clover']; } if (isset($loggingConfiguration['coverage-crap4j']) && !isset($arguments['coverageCrap4J'])) { $arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j']; if (isset($loggingConfiguration['crap4jThreshold']) && !isset($arguments['crap4jThreshold'])) { $arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold']; } } if (isset($loggingConfiguration['coverage-html']) && !isset($arguments['coverageHtml'])) { if (isset($loggingConfiguration['lowUpperBound']) && !isset($arguments['reportLowUpperBound'])) { $arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound']; } if (isset($loggingConfiguration['highLowerBound']) && !isset($arguments['reportHighLowerBound'])) { $arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound']; } $arguments['coverageHtml'] = $loggingConfiguration['coverage-html']; } if (isset($loggingConfiguration['coverage-php']) && !isset($arguments['coveragePHP'])) { $arguments['coveragePHP'] = $loggingConfiguration['coverage-php']; } if (isset($loggingConfiguration['coverage-text']) && !isset($arguments['coverageText'])) { $arguments['coverageText'] = $loggingConfiguration['coverage-text']; if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) { $arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles']; } else { $arguments['coverageTextShowUncoveredFiles'] = false; } if (isset($loggingConfiguration['coverageTextShowOnlySummary'])) { $arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary']; } else { $arguments['coverageTextShowOnlySummary'] = false; } } if (isset($loggingConfiguration['coverage-xml']) && !isset($arguments['coverageXml'])) { $arguments['coverageXml'] = $loggingConfiguration['coverage-xml']; } if (isset($loggingConfiguration['json']) && !isset($arguments['jsonLogfile'])) { $arguments['jsonLogfile'] = $loggingConfiguration['json']; } if (isset($loggingConfiguration['plain'])) { $arguments['listeners'][] = new PHPUnit_TextUI_ResultPrinter( $loggingConfiguration['plain'], true ); } if (isset($loggingConfiguration['tap']) && !isset($arguments['tapLogfile'])) { $arguments['tapLogfile'] = $loggingConfiguration['tap']; } if (isset($loggingConfiguration['teamcity']) && !isset($arguments['teamcityLogfile'])) { $arguments['teamcityLogfile'] = $loggingConfiguration['teamcity']; } if (isset($loggingConfiguration['junit']) && !isset($arguments['junitLogfile'])) { $arguments['junitLogfile'] = $loggingConfiguration['junit']; if (isset($loggingConfiguration['logIncompleteSkipped']) && !isset($arguments['logIncompleteSkipped'])) { $arguments['logIncompleteSkipped'] = $loggingConfiguration['logIncompleteSkipped']; } } if (isset($loggingConfiguration['testdox-html']) && !isset($arguments['testdoxHTMLFile'])) { $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html']; } if (isset($loggingConfiguration['testdox-text']) && !isset($arguments['testdoxTextFile'])) { $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text']; } if (isset($loggingConfiguration['testdox-xml']) && !isset($arguments['testdoxXMLFile'])) { $arguments['testdoxXMLFile'] = $loggingConfiguration['testdox-xml']; } $testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration(); if (isset($testdoxGroupConfiguration['include']) && !isset($arguments['testdoxGroups'])) { $arguments['testdoxGroups'] = $testdoxGroupConfiguration['include']; } if (isset($testdoxGroupConfiguration['exclude']) && !isset($arguments['testdoxExcludeGroups'])) { $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration['exclude']; } } $arguments['addUncoveredFilesFromWhitelist'] = isset($arguments['addUncoveredFilesFromWhitelist']) ? $arguments['addUncoveredFilesFromWhitelist'] : true; $arguments['processUncoveredFilesFromWhitelist'] = isset($arguments['processUncoveredFilesFromWhitelist']) ? $arguments['processUncoveredFilesFromWhitelist'] : false; $arguments['backupGlobals'] = isset($arguments['backupGlobals']) ? $arguments['backupGlobals'] : null; $arguments['backupStaticAttributes'] = isset($arguments['backupStaticAttributes']) ? $arguments['backupStaticAttributes'] : null; $arguments['beStrictAboutChangesToGlobalState'] = isset($arguments['beStrictAboutChangesToGlobalState']) ? $arguments['beStrictAboutChangesToGlobalState'] : null; $arguments['cacheTokens'] = isset($arguments['cacheTokens']) ? $arguments['cacheTokens'] : false; $arguments['columns'] = isset($arguments['columns']) ? $arguments['columns'] : 80; $arguments['colors'] = isset($arguments['colors']) ? $arguments['colors'] : PHPUnit_TextUI_ResultPrinter::COLOR_DEFAULT; $arguments['convertErrorsToExceptions'] = isset($arguments['convertErrorsToExceptions']) ? $arguments['convertErrorsToExceptions'] : true; $arguments['convertNoticesToExceptions'] = isset($arguments['convertNoticesToExceptions']) ? $arguments['convertNoticesToExceptions'] : true; $arguments['convertWarningsToExceptions'] = isset($arguments['convertWarningsToExceptions']) ? $arguments['convertWarningsToExceptions'] : true; $arguments['excludeGroups'] = isset($arguments['excludeGroups']) ? $arguments['excludeGroups'] : []; $arguments['groups'] = isset($arguments['groups']) ? $arguments['groups'] : []; $arguments['logIncompleteSkipped'] = isset($arguments['logIncompleteSkipped']) ? $arguments['logIncompleteSkipped'] : false; $arguments['processIsolation'] = isset($arguments['processIsolation']) ? $arguments['processIsolation'] : false; $arguments['repeat'] = isset($arguments['repeat']) ? $arguments['repeat'] : false; $arguments['reportHighLowerBound'] = isset($arguments['reportHighLowerBound']) ? $arguments['reportHighLowerBound'] : 90; $arguments['reportLowUpperBound'] = isset($arguments['reportLowUpperBound']) ? $arguments['reportLowUpperBound'] : 50; $arguments['crap4jThreshold'] = isset($arguments['crap4jThreshold']) ? $arguments['crap4jThreshold'] : 30; $arguments['stopOnError'] = isset($arguments['stopOnError']) ? $arguments['stopOnError'] : false; $arguments['stopOnFailure'] = isset($arguments['stopOnFailure']) ? $arguments['stopOnFailure'] : false; $arguments['stopOnWarning'] = isset($arguments['stopOnWarning']) ? $arguments['stopOnWarning'] : false; $arguments['stopOnIncomplete'] = isset($arguments['stopOnIncomplete']) ? $arguments['stopOnIncomplete'] : false; $arguments['stopOnRisky'] = isset($arguments['stopOnRisky']) ? $arguments['stopOnRisky'] : false; $arguments['stopOnSkipped'] = isset($arguments['stopOnSkipped']) ? $arguments['stopOnSkipped'] : false; $arguments['failOnWarning'] = isset($arguments['failOnWarning']) ? $arguments['failOnWarning'] : false; $arguments['failOnRisky'] = isset($arguments['failOnRisky']) ? $arguments['failOnRisky'] : false; $arguments['timeoutForSmallTests'] = isset($arguments['timeoutForSmallTests']) ? $arguments['timeoutForSmallTests'] : 1; $arguments['timeoutForMediumTests'] = isset($arguments['timeoutForMediumTests']) ? $arguments['timeoutForMediumTests'] : 10; $arguments['timeoutForLargeTests'] = isset($arguments['timeoutForLargeTests']) ? $arguments['timeoutForLargeTests'] : 60; $arguments['reportUselessTests'] = isset($arguments['reportUselessTests']) ? $arguments['reportUselessTests'] : false; $arguments['strictCoverage'] = isset($arguments['strictCoverage']) ? $arguments['strictCoverage'] : false; $arguments['disallowTestOutput'] = isset($arguments['disallowTestOutput']) ? $arguments['disallowTestOutput'] : false; $arguments['enforceTimeLimit'] = isset($arguments['enforceTimeLimit']) ? $arguments['enforceTimeLimit'] : false; $arguments['disallowTodoAnnotatedTests'] = isset($arguments['disallowTodoAnnotatedTests']) ? $arguments['disallowTodoAnnotatedTests'] : false; $arguments['beStrictAboutResourceUsageDuringSmallTests'] = isset($arguments['beStrictAboutResourceUsageDuringSmallTests']) ? $arguments['beStrictAboutResourceUsageDuringSmallTests'] : false; $arguments['reverseList'] = isset($arguments['reverseList']) ? $arguments['reverseList'] : false; $arguments['registerMockObjectsFromTestArgumentsRecursively'] = isset($arguments['registerMockObjectsFromTestArgumentsRecursively']) ? $arguments['registerMockObjectsFromTestArgumentsRecursively'] : false; $arguments['verbose'] = isset($arguments['verbose']) ? $arguments['verbose'] : false; $arguments['testdoxExcludeGroups'] = isset($arguments['testdoxExcludeGroups']) ? $arguments['testdoxExcludeGroups'] : []; $arguments['testdoxGroups'] = isset($arguments['testdoxGroups']) ? $arguments['testdoxGroups'] : []; } private function writeMessage($type, $message) { if (!$this->messagePrinted) { $this->write("\n"); } $this->write( sprintf( "%-15s%s\n", $type . ':', $message ) ); $this->messagePrinted = true; } } 1, 'fg-black' => 30, 'fg-red' => 31, 'fg-green' => 32, 'fg-yellow' => 33, 'fg-blue' => 34, 'fg-magenta' => 35, 'fg-cyan' => 36, 'fg-white' => 37, 'bg-black' => 40, 'bg-red' => 41, 'bg-green' => 42, 'bg-yellow' => 43, 'bg-blue' => 44, 'bg-magenta' => 45, 'bg-cyan' => 46, 'bg-white' => 47 ]; protected $column = 0; protected $maxColumn; protected $lastTestFailed = false; protected $numAssertions = 0; protected $numTests = -1; protected $numTestsRun = 0; protected $numTestsWidth; protected $colors = false; protected $debug = false; protected $verbose = false; private $numberOfColumns; private $reverse = false; private $defectListPrinted = false; public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80, $reverse = false) { parent::__construct($out); if (!is_bool($verbose)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'boolean'); } $availableColors = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS]; if (!in_array($colors, $availableColors)) { throw PHPUnit_Util_InvalidArgumentHelper::factory( 3, vsprintf('value from "%s", "%s" or "%s"', $availableColors) ); } if (!is_bool($debug)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean'); } if (!is_int($numberOfColumns) && $numberOfColumns != 'max') { throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'integer or "max"'); } if (!is_bool($reverse)) { throw PHPUnit_Util_InvalidArgumentHelper::factory(6, 'boolean'); } $console = new Console; $maxNumberOfColumns = $console->getNumberOfColumns(); if ($numberOfColumns == 'max' || $numberOfColumns > $maxNumberOfColumns) { $numberOfColumns = $maxNumberOfColumns; } $this->numberOfColumns = $numberOfColumns; $this->verbose = $verbose; $this->debug = $debug; $this->reverse = $reverse; if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) { $this->colors = true; } else { $this->colors = (self::COLOR_ALWAYS === $colors); } } public function printResult(PHPUnit_Framework_TestResult $result) { $this->printHeader(); $this->printErrors($result); $this->printWarnings($result); $this->printFailures($result); if ($this->verbose) { $this->printRisky($result); $this->printIncompletes($result); $this->printSkipped($result); } $this->printFooter($result); } protected function printDefects(array $defects, $type) { $count = count($defects); if ($count == 0) { return; } if ($this->defectListPrinted) { $this->write("\n--\n\n"); } $this->write( sprintf( "There %s %d %s%s:\n", ($count == 1) ? 'was' : 'were', $count, $type, ($count == 1) ? '' : 's' ) ); $i = 1; if ($this->reverse) { $defects = array_reverse($defects); } foreach ($defects as $defect) { $this->printDefect($defect, $i++); } $this->defectListPrinted = true; } protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count) { $this->printDefectHeader($defect, $count); $this->printDefectTrace($defect); } protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count) { $this->write( sprintf( "\n%d) %s\n", $count, $defect->getTestName() ) ); } protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect) { $e = $defect->thrownException(); $this->write((string) $e); while ($e = $e->getPrevious()) { $this->write("\nCaused by\n" . $e); } } protected function printErrors(PHPUnit_Framework_TestResult $result) { $this->printDefects($result->errors(), 'error'); } protected function printFailures(PHPUnit_Framework_TestResult $result) { $this->printDefects($result->failures(), 'failure'); } protected function printWarnings(PHPUnit_Framework_TestResult $result) { $this->printDefects($result->warnings(), 'warning'); } protected function printIncompletes(PHPUnit_Framework_TestResult $result) { $this->printDefects($result->notImplemented(), 'incomplete test'); } protected function printRisky(PHPUnit_Framework_TestResult $result) { $this->printDefects($result->risky(), 'risky test'); } protected function printSkipped(PHPUnit_Framework_TestResult $result) { $this->printDefects($result->skipped(), 'skipped test'); } protected function printHeader() { $this->write("\n\n" . PHP_Timer::resourceUsage() . "\n\n"); } protected function printFooter(PHPUnit_Framework_TestResult $result) { if (count($result) === 0) { $this->writeWithColor( 'fg-black, bg-yellow', 'No tests executed!' ); return; } if ($result->wasSuccessful() && $result->allHarmless() && $result->allCompletelyImplemented() && $result->noneSkipped()) { $this->writeWithColor( 'fg-black, bg-green', sprintf( 'OK (%d test%s, %d assertion%s)', count($result), (count($result) == 1) ? '' : 's', $this->numAssertions, ($this->numAssertions == 1) ? '' : 's' ) ); } else { if ($result->wasSuccessful()) { $color = 'fg-black, bg-yellow'; if ($this->verbose) { $this->write("\n"); } $this->writeWithColor( $color, 'OK, but incomplete, skipped, or risky tests!' ); } else { $this->write("\n"); if ($result->errorCount()) { $color = 'fg-white, bg-red'; $this->writeWithColor( $color, 'ERRORS!' ); } elseif ($result->failureCount()) { $color = 'fg-white, bg-red'; $this->writeWithColor( $color, 'FAILURES!' ); } elseif ($result->warningCount()) { $color = 'fg-black, bg-yellow'; $this->writeWithColor( $color, 'WARNINGS!' ); } } $this->writeCountString(count($result), 'Tests', $color, true); $this->writeCountString($this->numAssertions, 'Assertions', $color, true); $this->writeCountString($result->errorCount(), 'Errors', $color); $this->writeCountString($result->failureCount(), 'Failures', $color); $this->writeCountString($result->warningCount(), 'Warnings', $color); $this->writeCountString($result->skippedCount(), 'Skipped', $color); $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color); $this->writeCountString($result->riskyCount(), 'Risky', $color); $this->writeWithColor($color, '.', true); } } public function printWaitPrompt() { $this->write("\n to continue\n"); } public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeProgressWithColor('fg-red, bold', 'E'); $this->lastTestFailed = true; } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { $this->writeProgressWithColor('bg-red, fg-white', 'F'); $this->lastTestFailed = true; } public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time) { $this->writeProgressWithColor('fg-yellow, bold', 'W'); $this->lastTestFailed = true; } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeProgressWithColor('fg-yellow, bold', 'I'); $this->lastTestFailed = true; } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeProgressWithColor('fg-yellow, bold', 'R'); $this->lastTestFailed = true; } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { $this->writeProgressWithColor('fg-cyan, bold', 'S'); $this->lastTestFailed = true; } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { if ($this->numTests == -1) { $this->numTests = count($suite); $this->numTestsWidth = strlen((string) $this->numTests); $this->maxColumn = $this->numberOfColumns - strlen(' / (XXX%)') - (2 * $this->numTestsWidth); } } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { } public function startTest(PHPUnit_Framework_Test $test) { if ($this->debug) { $this->write( sprintf( "\nStarting test '%s'.\n", PHPUnit_Util_Test::describe($test) ) ); } } public function endTest(PHPUnit_Framework_Test $test, $time) { if (!$this->lastTestFailed) { $this->writeProgress('.'); } if ($test instanceof PHPUnit_Framework_TestCase) { $this->numAssertions += $test->getNumAssertions(); } elseif ($test instanceof PHPUnit_Extensions_PhptTestCase) { $this->numAssertions++; } $this->lastTestFailed = false; if ($test instanceof PHPUnit_Framework_TestCase) { if (!$test->hasExpectationOnOutput()) { $this->write($test->getActualOutput()); } } } protected function writeProgress($progress) { $this->write($progress); $this->column++; $this->numTestsRun++; if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests ) { if ($this->numTestsRun == $this->numTests) { $this->write(str_repeat(' ', $this->maxColumn - $this->column)); } $this->write( sprintf( ' %' . $this->numTestsWidth . 'd / %' . $this->numTestsWidth . 'd (%3s%%)', $this->numTestsRun, $this->numTests, floor(($this->numTestsRun / $this->numTests) * 100) ) ); if ($this->column == $this->maxColumn) { $this->writeNewLine(); } } } protected function writeNewLine() { $this->column = 0; $this->write("\n"); } protected function formatWithColor($color, $buffer) { if (!$this->colors) { return $buffer; } $codes = array_map('trim', explode(',', $color)); $lines = explode("\n", $buffer); $padding = max(array_map('strlen', $lines)); $styles = []; foreach ($codes as $code) { $styles[] = self::$ansiCodes[$code]; } $style = sprintf("\x1b[%sm", implode(';', $styles)); $styledLines = []; foreach ($lines as $line) { $styledLines[] = $style . str_pad($line, $padding) . "\x1b[0m"; } return implode("\n", $styledLines); } protected function writeWithColor($color, $buffer, $lf = true) { $this->write($this->formatWithColor($color, $buffer)); if ($lf) { $this->write("\n"); } } protected function writeProgressWithColor($color, $buffer) { $buffer = $this->formatWithColor($color, $buffer); $this->writeProgress($buffer); } private function writeCountString($count, $name, $color, $always = false) { static $first = true; if ($always || $count > 0) { $this->writeWithColor( $color, sprintf( '%s%s: %d', !$first ? ', ' : '', $name, $count ), false ); $first = false; } } } false, 'listSuites' => false, 'loader' => null, 'useDefaultConfiguration' => true, 'loadedExtensions' => [], 'notLoadedExtensions' => [] ]; protected $options = []; protected $longOptions = [ 'atleast-version=' => null, 'bootstrap=' => null, 'colors==' => null, 'columns=' => null, 'configuration=' => null, 'coverage-clover=' => null, 'coverage-crap4j=' => null, 'coverage-html=' => null, 'coverage-php=' => null, 'coverage-text==' => null, 'coverage-xml=' => null, 'debug' => null, 'disallow-test-output' => null, 'disallow-resource-usage' => null, 'disallow-todo-tests' => null, 'enforce-time-limit' => null, 'exclude-group=' => null, 'filter=' => null, 'generate-configuration' => null, 'group=' => null, 'help' => null, 'include-path=' => null, 'list-groups' => null, 'list-suites' => null, 'loader=' => null, 'log-json=' => null, 'log-junit=' => null, 'log-tap=' => null, 'log-teamcity=' => null, 'no-configuration' => null, 'no-coverage' => null, 'no-extensions' => null, 'no-globals-backup' => null, 'printer=' => null, 'process-isolation' => null, 'repeat=' => null, 'report-useless-tests' => null, 'reverse-list' => null, 'static-backup' => null, 'stderr' => null, 'stop-on-error' => null, 'stop-on-failure' => null, 'stop-on-warning' => null, 'stop-on-incomplete' => null, 'stop-on-risky' => null, 'stop-on-skipped' => null, 'fail-on-warning' => null, 'fail-on-risky' => null, 'strict-coverage' => null, 'disable-coverage-ignore' => null, 'strict-global-state' => null, 'tap' => null, 'teamcity' => null, 'testdox' => null, 'testdox-group=' => null, 'testdox-exclude-group=' => null, 'testdox-html=' => null, 'testdox-text=' => null, 'testdox-xml=' => null, 'test-suffix=' => null, 'testsuite=' => null, 'verbose' => null, 'version' => null, 'whitelist=' => null ]; private $versionStringPrinted = false; public static function main($exit = true) { $command = new static; return $command->run($_SERVER['argv'], $exit); } public function run(array $argv, $exit = true) { $this->handleArguments($argv); $runner = $this->createRunner(); if (is_object($this->arguments['test']) && $this->arguments['test'] instanceof PHPUnit_Framework_Test) { $suite = $this->arguments['test']; } else { $suite = $runner->getTest( $this->arguments['test'], $this->arguments['testFile'], $this->arguments['testSuffixes'] ); } if ($this->arguments['listGroups']) { $this->printVersionString(); print "Available test group(s):\n"; $groups = $suite->getGroups(); sort($groups); foreach ($groups as $group) { print " - $group\n"; } if ($exit) { exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); } else { return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; } } if ($this->arguments['listSuites']) { $this->printVersionString(); print "Available test suite(s):\n"; $configuration = PHPUnit_Util_Configuration::getInstance( $this->arguments['configuration'] ); $suiteNames = $configuration->getTestSuiteNames(); foreach ($suiteNames as $suiteName) { print " - $suiteName\n"; } if ($exit) { exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); } else { return PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; } } unset($this->arguments['test']); unset($this->arguments['testFile']); try { $result = $runner->doRun($suite, $this->arguments, $exit); } catch (PHPUnit_Framework_Exception $e) { print $e->getMessage() . "\n"; } $return = PHPUnit_TextUI_TestRunner::FAILURE_EXIT; if (isset($result) && $result->wasSuccessful(false)) { $return = PHPUnit_TextUI_TestRunner::SUCCESS_EXIT; } elseif (!isset($result) || $result->errorCount() > 0) { $return = PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT; } if ($exit) { exit($return); } return $return; } protected function createRunner() { return new PHPUnit_TextUI_TestRunner($this->arguments['loader']); } protected function handleArguments(array $argv) { if (defined('__PHPUNIT_PHAR__')) { $this->longOptions['check-version'] = null; $this->longOptions['selfupdate'] = null; $this->longOptions['self-update'] = null; $this->longOptions['selfupgrade'] = null; $this->longOptions['self-upgrade'] = null; } try { $this->options = PHPUnit_Util_Getopt::getopt( $argv, 'd:c:hv', array_keys($this->longOptions) ); } catch (PHPUnit_Framework_Exception $e) { $this->showError($e->getMessage()); } foreach ($this->options[0] as $option) { switch ($option[0]) { case '--colors': $this->arguments['colors'] = $option[1] ?: PHPUnit_TextUI_ResultPrinter::COLOR_AUTO; break; case '--bootstrap': $this->arguments['bootstrap'] = $option[1]; break; case '--columns': if (is_numeric($option[1])) { $this->arguments['columns'] = (int) $option[1]; } elseif ($option[1] == 'max') { $this->arguments['columns'] = 'max'; } break; case 'c': case '--configuration': $this->arguments['configuration'] = $option[1]; break; case '--coverage-clover': $this->arguments['coverageClover'] = $option[1]; break; case '--coverage-crap4j': $this->arguments['coverageCrap4J'] = $option[1]; break; case '--coverage-html': $this->arguments['coverageHtml'] = $option[1]; break; case '--coverage-php': $this->arguments['coveragePHP'] = $option[1]; break; case '--coverage-text': if ($option[1] === null) { $option[1] = 'php://stdout'; } $this->arguments['coverageText'] = $option[1]; $this->arguments['coverageTextShowUncoveredFiles'] = false; $this->arguments['coverageTextShowOnlySummary'] = false; break; case '--coverage-xml': $this->arguments['coverageXml'] = $option[1]; break; case 'd': $ini = explode('=', $option[1]); if (isset($ini[0])) { if (isset($ini[1])) { ini_set($ini[0], $ini[1]); } else { ini_set($ini[0], true); } } break; case '--debug': $this->arguments['debug'] = true; break; case 'h': case '--help': $this->showHelp(); exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); break; case '--filter': $this->arguments['filter'] = $option[1]; break; case '--testsuite': $this->arguments['testsuite'] = $option[1]; break; case '--generate-configuration': $this->printVersionString(); printf( "Generating phpunit.xml in %s\n\n", getcwd() ); print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; $bootstrapScript = trim(fgets(STDIN)); print 'Tests directory (relative to path shown above; default: tests): '; $testsDirectory = trim(fgets(STDIN)); print 'Source directory (relative to path shown above; default: src): '; $src = trim(fgets(STDIN)); if ($bootstrapScript == '') { $bootstrapScript = 'vendor/autoload.php'; } if ($testsDirectory == '') { $testsDirectory = 'tests'; } if ($src == '') { $src = 'src'; } $generator = new PHPUnit_Util_ConfigurationGenerator; file_put_contents( 'phpunit.xml', $generator->generateDefaultConfiguration( PHPUnit_Runner_Version::series(), $bootstrapScript, $testsDirectory, $src ) ); printf( "\nGenerated phpunit.xml in %s\n", getcwd() ); exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); break; case '--group': $this->arguments['groups'] = explode(',', $option[1]); break; case '--exclude-group': $this->arguments['excludeGroups'] = explode( ',', $option[1] ); break; case '--test-suffix': $this->arguments['testSuffixes'] = explode( ',', $option[1] ); break; case '--include-path': $includePath = $option[1]; break; case '--list-groups': $this->arguments['listGroups'] = true; break; case '--list-suites': $this->arguments['listSuites'] = true; break; case '--printer': $this->arguments['printer'] = $option[1]; break; case '--loader': $this->arguments['loader'] = $option[1]; break; case '--log-json': $this->arguments['jsonLogfile'] = $option[1]; break; case '--log-junit': $this->arguments['junitLogfile'] = $option[1]; break; case '--log-tap': $this->arguments['tapLogfile'] = $option[1]; break; case '--log-teamcity': $this->arguments['teamcityLogfile'] = $option[1]; break; case '--process-isolation': $this->arguments['processIsolation'] = true; break; case '--repeat': $this->arguments['repeat'] = (int) $option[1]; break; case '--stderr': $this->arguments['stderr'] = true; break; case '--stop-on-error': $this->arguments['stopOnError'] = true; break; case '--stop-on-failure': $this->arguments['stopOnFailure'] = true; break; case '--stop-on-warning': $this->arguments['stopOnWarning'] = true; break; case '--stop-on-incomplete': $this->arguments['stopOnIncomplete'] = true; break; case '--stop-on-risky': $this->arguments['stopOnRisky'] = true; break; case '--stop-on-skipped': $this->arguments['stopOnSkipped'] = true; break; case '--fail-on-warning': $this->arguments['failOnWarning'] = true; break; case '--fail-on-risky': $this->arguments['failOnRisky'] = true; break; case '--tap': $this->arguments['printer'] = 'PHPUnit_Util_Log_TAP'; break; case '--teamcity': $this->arguments['printer'] = 'PHPUnit_Util_Log_TeamCity'; break; case '--testdox': $this->arguments['printer'] = 'PHPUnit_Util_TestDox_ResultPrinter_Text'; break; case '--testdox-group': $this->arguments['testdoxGroups'] = explode( ',', $option[1] ); break; case '--testdox-exclude-group': $this->arguments['testdoxExcludeGroups'] = explode( ',', $option[1] ); break; case '--testdox-html': $this->arguments['testdoxHTMLFile'] = $option[1]; break; case '--testdox-text': $this->arguments['testdoxTextFile'] = $option[1]; break; case '--testdox-xml': $this->arguments['testdoxXMLFile'] = $option[1]; break; case '--no-configuration': $this->arguments['useDefaultConfiguration'] = false; break; case '--no-extensions': $this->arguments['noExtensions'] = true; break; case '--no-coverage': $this->arguments['noCoverage'] = true; break; case '--no-globals-backup': $this->arguments['backupGlobals'] = false; break; case '--static-backup': $this->arguments['backupStaticAttributes'] = true; break; case 'v': case '--verbose': $this->arguments['verbose'] = true; break; case '--atleast-version': exit(version_compare(PHPUnit_Runner_Version::id(), $option[1], '>=') ? PHPUnit_TextUI_TestRunner::SUCCESS_EXIT : PHPUnit_TextUI_TestRunner::FAILURE_EXIT ); break; case '--version': $this->printVersionString(); exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); break; case '--report-useless-tests': $this->arguments['reportUselessTests'] = true; break; case '--strict-coverage': $this->arguments['strictCoverage'] = true; break; case '--disable-coverage-ignore': $this->arguments['disableCodeCoverageIgnore'] = true; break; case '--strict-global-state': $this->arguments['beStrictAboutChangesToGlobalState'] = true; break; case '--disallow-test-output': $this->arguments['disallowTestOutput'] = true; break; case '--disallow-resource-usage': $this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true; break; case '--enforce-time-limit': $this->arguments['enforceTimeLimit'] = true; break; case '--disallow-todo-tests': $this->arguments['disallowTodoAnnotatedTests'] = true; break; case '--reverse-list': $this->arguments['reverseList'] = true; break; case '--check-version': $this->handleVersionCheck(); break; case '--selfupdate': case '--self-update': $this->handleSelfUpdate(); break; case '--selfupgrade': case '--self-upgrade': $this->handleSelfUpdate(true); break; case '--whitelist': $this->arguments['whitelist'] = $option[1]; break; default: $optionName = str_replace('--', '', $option[0]); $handler = null; if (isset($this->longOptions[$optionName])) { $handler = $this->longOptions[$optionName]; } elseif (isset($this->longOptions[$optionName . '='])) { $handler = $this->longOptions[$optionName . '=']; } if (isset($handler) && is_callable([$this, $handler])) { $this->$handler($option[1]); } } } $this->handleCustomTestSuite(); if (!isset($this->arguments['test'])) { if (isset($this->options[1][0])) { $this->arguments['test'] = $this->options[1][0]; } if (isset($this->options[1][1])) { $this->arguments['testFile'] = realpath($this->options[1][1]); } else { $this->arguments['testFile'] = ''; } if (isset($this->arguments['test']) && is_file($this->arguments['test']) && substr($this->arguments['test'], -5, 5) != '.phpt') { $this->arguments['testFile'] = realpath($this->arguments['test']); $this->arguments['test'] = substr($this->arguments['test'], 0, strrpos($this->arguments['test'], '.')); } } if (!isset($this->arguments['testSuffixes'])) { $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; } if (isset($includePath)) { ini_set( 'include_path', $includePath . PATH_SEPARATOR . ini_get('include_path') ); } if ($this->arguments['loader'] !== null) { $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); } if (isset($this->arguments['configuration']) && is_dir($this->arguments['configuration'])) { $configurationFile = $this->arguments['configuration'] . '/phpunit.xml'; if (file_exists($configurationFile)) { $this->arguments['configuration'] = realpath( $configurationFile ); } elseif (file_exists($configurationFile . '.dist')) { $this->arguments['configuration'] = realpath( $configurationFile . '.dist' ); } } elseif (!isset($this->arguments['configuration']) && $this->arguments['useDefaultConfiguration']) { if (file_exists('phpunit.xml')) { $this->arguments['configuration'] = realpath('phpunit.xml'); } elseif (file_exists('phpunit.xml.dist')) { $this->arguments['configuration'] = realpath( 'phpunit.xml.dist' ); } } if (isset($this->arguments['configuration'])) { try { $configuration = PHPUnit_Util_Configuration::getInstance( $this->arguments['configuration'] ); } catch (Throwable $e) { print $e->getMessage() . "\n"; exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); } catch (Exception $e) { print $e->getMessage() . "\n"; exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); } $phpunitConfiguration = $configuration->getPHPUnitConfiguration(); $configuration->handlePHPConfiguration(); if (isset($this->arguments['bootstrap'])) { $this->handleBootstrap($this->arguments['bootstrap']); } elseif (isset($phpunitConfiguration['bootstrap'])) { $this->handleBootstrap($phpunitConfiguration['bootstrap']); } if (isset($phpunitConfiguration['stderr']) && ! isset($this->arguments['stderr'])) { $this->arguments['stderr'] = $phpunitConfiguration['stderr']; } if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && extension_loaded('phar')) { $this->handleExtensions($phpunitConfiguration['extensionsDirectory']); } if (isset($phpunitConfiguration['columns']) && ! isset($this->arguments['columns'])) { $this->arguments['columns'] = $phpunitConfiguration['columns']; } if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) { if (isset($phpunitConfiguration['printerFile'])) { $file = $phpunitConfiguration['printerFile']; } else { $file = ''; } $this->arguments['printer'] = $this->handlePrinter( $phpunitConfiguration['printerClass'], $file ); } if (isset($phpunitConfiguration['testSuiteLoaderClass'])) { if (isset($phpunitConfiguration['testSuiteLoaderFile'])) { $file = $phpunitConfiguration['testSuiteLoaderFile']; } else { $file = ''; } $this->arguments['loader'] = $this->handleLoader( $phpunitConfiguration['testSuiteLoaderClass'], $file ); } if (!isset($this->arguments['test'])) { $testSuite = $configuration->getTestSuiteConfiguration(isset($this->arguments['testsuite']) ? $this->arguments['testsuite'] : null); if ($testSuite !== null) { $this->arguments['test'] = $testSuite; } } } elseif (isset($this->arguments['bootstrap'])) { $this->handleBootstrap($this->arguments['bootstrap']); } if (isset($this->arguments['printer']) && is_string($this->arguments['printer'])) { $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); } if (isset($this->arguments['test']) && is_string($this->arguments['test']) && substr($this->arguments['test'], -5, 5) == '.phpt') { $test = new PHPUnit_Extensions_PhptTestCase($this->arguments['test']); $this->arguments['test'] = new PHPUnit_Framework_TestSuite; $this->arguments['test']->addTest($test); } if (!isset($this->arguments['test']) || (isset($this->arguments['testDatabaseLogRevision']) && !isset($this->arguments['testDatabaseDSN']))) { $this->showHelp(); exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); } } protected function handleLoader($loaderClass, $loaderFile = '') { if (!class_exists($loaderClass, false)) { if ($loaderFile == '') { $loaderFile = PHPUnit_Util_Filesystem::classNameToFilename( $loaderClass ); } $loaderFile = stream_resolve_include_path($loaderFile); if ($loaderFile) { require $loaderFile; } } if (class_exists($loaderClass, false)) { $class = new ReflectionClass($loaderClass); if ($class->implementsInterface('PHPUnit_Runner_TestSuiteLoader') && $class->isInstantiable()) { return $class->newInstance(); } } if ($loaderClass == 'PHPUnit_Runner_StandardTestSuiteLoader') { return; } $this->showError( sprintf( 'Could not use "%s" as loader.', $loaderClass ) ); } protected function handlePrinter($printerClass, $printerFile = '') { if (!class_exists($printerClass, false)) { if ($printerFile == '') { $printerFile = PHPUnit_Util_Filesystem::classNameToFilename( $printerClass ); } $printerFile = stream_resolve_include_path($printerFile); if ($printerFile) { require $printerFile; } } if (class_exists($printerClass)) { $class = new ReflectionClass($printerClass); if ($class->implementsInterface('PHPUnit_Framework_TestListener') && $class->isSubclassOf('PHPUnit_Util_Printer') && $class->isInstantiable()) { if ($class->isSubclassOf('PHPUnit_TextUI_ResultPrinter')) { return $printerClass; } $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; return $class->newInstance($outputStream); } } $this->showError( sprintf( 'Could not use "%s" as printer.', $printerClass ) ); } protected function handleBootstrap($filename) { try { PHPUnit_Util_Fileloader::checkAndLoad($filename); } catch (PHPUnit_Framework_Exception $e) { $this->showError($e->getMessage()); } } protected function handleSelfUpdate($upgrade = false) { $this->printVersionString(); if ($upgrade) { print "Warning: Deprecated --self-upgrade used\n\n"; } else { print "Warning: Deprecated --self-update used\n\n"; } $localFilename = realpath($_SERVER['argv'][0]); if (!is_writable($localFilename)) { print 'No write permission to update ' . $localFilename . "\n"; exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); } if (!extension_loaded('openssl')) { print "The OpenSSL extension is not loaded.\n"; exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT); } if (!$upgrade) { $remoteFilename = sprintf( 'https://phar.phpunit.de/phpunit-%s.phar', file_get_contents( sprintf( 'https://phar.phpunit.de/latest-version-of/phpunit-%s', PHPUnit_Runner_Version::series() ) ) ); } else { $remoteFilename = sprintf( 'https://phar.phpunit.de/phpunit%s.phar', PHPUnit_Runner_Version::getReleaseChannel() ); } $tempFilename = tempnam(sys_get_temp_dir(), 'phpunit') . '.phar'; $caFile = dirname($tempFilename) . '/ca.pem'; copy(__PHPUNIT_PHAR_ROOT__ . '/ca.pem', $caFile); print 'Updating the PHPUnit PHAR ... '; $options = [ 'ssl' => [ 'allow_self_signed' => false, 'cafile' => $caFile, 'verify_peer' => true ] ]; file_put_contents( $tempFilename, file_get_contents( $remoteFilename, false, stream_context_create($options) ) ); chmod($tempFilename, 0777 & ~umask()); try { $phar = new Phar($tempFilename); unset($phar); rename($tempFilename, $localFilename); unlink($caFile); } catch (Throwable $_e) { $e = $_e; } catch (Exception $_e) { $e = $_e; } if (isset($e)) { unlink($caFile); unlink($tempFilename); print " done\n\n" . $e->getMessage() . "\n"; exit(2); } print " done\n"; exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); } protected function handleVersionCheck() { $this->printVersionString(); $latestVersion = file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); $isOutdated = version_compare($latestVersion, PHPUnit_Runner_Version::id(), '>'); if ($isOutdated) { print "You are not using the latest version of PHPUnit.\n"; print 'Use "phpunit --self-upgrade" to install PHPUnit ' . $latestVersion . "\n"; } else { print "You are using the latest version of PHPUnit.\n"; } exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT); } protected function showHelp() { $this->printVersionString(); print << Code Coverage Options: --coverage-clover Generate code coverage report in Clover XML format. --coverage-crap4j Generate code coverage report in Crap4J XML format. --coverage-html Generate code coverage report in HTML format. --coverage-php Export PHP_CodeCoverage object to file. --coverage-text= Generate code coverage report in text format. Default: Standard output. --coverage-xml Generate code coverage report in PHPUnit XML format. --whitelist Whitelist for code coverage analysis. --disable-coverage-ignore Disable annotations for ignoring code coverage. Logging Options: --log-junit Log test execution in JUnit XML format to file. --log-teamcity Log test execution in TeamCity format to file. --testdox-html Write agile documentation in HTML format to file. --testdox-text Write agile documentation in Text format to file. --testdox-xml Write agile documentation in XML format to file. --reverse-list Print defects in reverse order Test Selection Options: --filter Filter which tests to run. --testsuite Filter which testsuite to run. --group ... Only runs tests from the specified group(s). --exclude-group ... Exclude tests from the specified group(s). --list-groups List available test groups. --list-suites List available test suites. --test-suffix ... Only search for test in files with specified suffix(es). Default: Test.php,.phpt Test Execution Options: --report-useless-tests Be strict about tests that do not test anything. --strict-coverage Be strict about @covers annotation usage. --strict-global-state Be strict about changes to global state --disallow-test-output Be strict about output during tests. --disallow-resource-usage Be strict about resource usage during small tests. --enforce-time-limit Enforce time limit based on test size. --disallow-todo-tests Disallow @todo-annotated tests. --process-isolation Run each test in a separate PHP process. --no-globals-backup Do not backup and restore \$GLOBALS for each test. --static-backup Backup and restore static attributes for each test. --colors= Use colors in output ("never", "auto" or "always"). --columns Number of columns to use for progress output. --columns max Use maximum number of columns for progress output. --stderr Write to STDERR instead of STDOUT. --stop-on-error Stop execution upon first error. --stop-on-failure Stop execution upon first error or failure. --stop-on-warning Stop execution upon first warning. --stop-on-risky Stop execution upon first risky test. --stop-on-skipped Stop execution upon first skipped test. --stop-on-incomplete Stop execution upon first incomplete test. --fail-on-warning Treat tests with warnings as failures. --fail-on-risky Treat risky tests as failures. -v|--verbose Output more verbose information. --debug Display debugging information during test execution. --loader TestSuiteLoader implementation to use. --repeat Runs the test(s) repeatedly. --teamcity Report test execution progress in TeamCity format. --testdox Report test execution progress in TestDox format. --testdox-group Only include tests from the specified group(s). --testdox-exclude-group Exclude tests from the specified group(s). --printer TestListener implementation to use. Configuration Options: --bootstrap A "bootstrap" PHP file that is run before the tests. -c|--configuration Read configuration from XML file. --no-configuration Ignore default configuration file (phpunit.xml). --no-coverage Ignore code coverage configuration. --no-extensions Do not load PHPUnit extensions. --include-path Prepend PHP's include_path with given path(s). -d key[=value] Sets a php.ini value. --generate-configuration Generate configuration file with suggested settings. Miscellaneous Options: -h|--help Prints this usage information. --version Prints the version and exits. --atleast-version Checks that version is greater than min and exits. EOT; if (defined('__PHPUNIT_PHAR__')) { print "\n --check-version Check whether PHPUnit is the latest version."; } } protected function handleCustomTestSuite() { } private function printVersionString() { if ($this->versionStringPrinted) { return; } print PHPUnit_Runner_Version::getVersionString() . "\n\n"; $this->versionStringPrinted = true; } private function showError($message) { $this->printVersionString(); print $message . "\n"; exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT); } private function handleExtensions($directory) { $facade = new File_Iterator_Facade; foreach ($facade->getFilesAsArray($directory, '.phar') as $file) { require $file; $this->arguments['loadedExtensions'][] = $file; } } } 'PHP_Token_OPEN_BRACKET', ')' => 'PHP_Token_CLOSE_BRACKET', '[' => 'PHP_Token_OPEN_SQUARE', ']' => 'PHP_Token_CLOSE_SQUARE', '{' => 'PHP_Token_OPEN_CURLY', '}' => 'PHP_Token_CLOSE_CURLY', ';' => 'PHP_Token_SEMICOLON', '.' => 'PHP_Token_DOT', ',' => 'PHP_Token_COMMA', '=' => 'PHP_Token_EQUAL', '<' => 'PHP_Token_LT', '>' => 'PHP_Token_GT', '+' => 'PHP_Token_PLUS', '-' => 'PHP_Token_MINUS', '*' => 'PHP_Token_MULT', '/' => 'PHP_Token_DIV', '?' => 'PHP_Token_QUESTION_MARK', '!' => 'PHP_Token_EXCLAMATION_MARK', ':' => 'PHP_Token_COLON', '"' => 'PHP_Token_DOUBLE_QUOTES', '@' => 'PHP_Token_AT', '&' => 'PHP_Token_AMPERSAND', '%' => 'PHP_Token_PERCENT', '|' => 'PHP_Token_PIPE', '$' => 'PHP_Token_DOLLAR', '^' => 'PHP_Token_CARET', '~' => 'PHP_Token_TILDE', '`' => 'PHP_Token_BACKTICK' ); protected $filename; protected $tokens = array(); protected $position = 0; protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0); protected $classes; protected $functions; protected $includes; protected $interfaces; protected $traits; protected $lineToFunctionMap = array(); public function __construct($sourceCode) { if (is_file($sourceCode)) { $this->filename = $sourceCode; $sourceCode = file_get_contents($sourceCode); } $this->scan($sourceCode); } public function __destruct() { $this->tokens = array(); } public function __toString() { $buffer = ''; foreach ($this as $token) { $buffer .= $token; } return $buffer; } public function getFilename() { return $this->filename; } protected function scan($sourceCode) { $id = 0; $line = 1; $tokens = token_get_all($sourceCode); $numTokens = count($tokens); $lastNonWhitespaceTokenWasDoubleColon = false; for ($i = 0; $i < $numTokens; ++$i) { $token = $tokens[$i]; $skip = 0; if (is_array($token)) { $name = substr(token_name($token[0]), 2); $text = $token[1]; if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') { $name = 'CLASS_NAME_CONSTANT'; } elseif ($name == 'USE' && isset($tokens[$i+2][0]) && $tokens[$i+2][0] == T_FUNCTION) { $name = 'USE_FUNCTION'; $text .= $tokens[$i+1][1] . $tokens[$i+2][1]; $skip = 2; } $tokenClass = 'PHP_Token_' . $name; } else { $text = $token; $tokenClass = self::$customTokens[$token]; } $this->tokens[] = new $tokenClass($text, $line, $this, $id++); $lines = substr_count($text, "\n"); $line += $lines; if ($tokenClass == 'PHP_Token_HALT_COMPILER') { break; } elseif ($tokenClass == 'PHP_Token_COMMENT' || $tokenClass == 'PHP_Token_DOC_COMMENT') { $this->linesOfCode['cloc'] += $lines + 1; } if ($name == 'DOUBLE_COLON') { $lastNonWhitespaceTokenWasDoubleColon = true; } elseif ($name != 'WHITESPACE') { $lastNonWhitespaceTokenWasDoubleColon = false; } $i += $skip; } $this->linesOfCode['loc'] = substr_count($sourceCode, "\n"); $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] - $this->linesOfCode['cloc']; } public function count() { return count($this->tokens); } public function tokens() { return $this->tokens; } public function getClasses() { if ($this->classes !== null) { return $this->classes; } $this->parse(); return $this->classes; } public function getFunctions() { if ($this->functions !== null) { return $this->functions; } $this->parse(); return $this->functions; } public function getInterfaces() { if ($this->interfaces !== null) { return $this->interfaces; } $this->parse(); return $this->interfaces; } public function getTraits() { if ($this->traits !== null) { return $this->traits; } $this->parse(); return $this->traits; } public function getIncludes($categorize = false, $category = null) { if ($this->includes === null) { $this->includes = array( 'require_once' => array(), 'require' => array(), 'include_once' => array(), 'include' => array() ); foreach ($this->tokens as $token) { switch (get_class($token)) { case 'PHP_Token_REQUIRE_ONCE': case 'PHP_Token_REQUIRE': case 'PHP_Token_INCLUDE_ONCE': case 'PHP_Token_INCLUDE': $this->includes[$token->getType()][] = $token->getName(); break; } } } if (isset($this->includes[$category])) { $includes = $this->includes[$category]; } elseif ($categorize === false) { $includes = array_merge( $this->includes['require_once'], $this->includes['require'], $this->includes['include_once'], $this->includes['include'] ); } else { $includes = $this->includes; } return $includes; } public function getFunctionForLine($line) { $this->parse(); if (isset($this->lineToFunctionMap[$line])) { return $this->lineToFunctionMap[$line]; } } protected function parse() { $this->interfaces = array(); $this->classes = array(); $this->traits = array(); $this->functions = array(); $class = array(); $classEndLine = array(); $trait = false; $traitEndLine = false; $interface = false; $interfaceEndLine = false; foreach ($this->tokens as $token) { switch (get_class($token)) { case 'PHP_Token_HALT_COMPILER': return; case 'PHP_Token_INTERFACE': $interface = $token->getName(); $interfaceEndLine = $token->getEndLine(); $this->interfaces[$interface] = array( 'methods' => array(), 'parent' => $token->getParent(), 'keywords' => $token->getKeywords(), 'docblock' => $token->getDocblock(), 'startLine' => $token->getLine(), 'endLine' => $interfaceEndLine, 'package' => $token->getPackage(), 'file' => $this->filename ); break; case 'PHP_Token_CLASS': case 'PHP_Token_TRAIT': $tmp = array( 'methods' => array(), 'parent' => $token->getParent(), 'interfaces'=> $token->getInterfaces(), 'keywords' => $token->getKeywords(), 'docblock' => $token->getDocblock(), 'startLine' => $token->getLine(), 'endLine' => $token->getEndLine(), 'package' => $token->getPackage(), 'file' => $this->filename ); if ($token instanceof PHP_Token_CLASS) { $class[] = $token->getName(); $classEndLine[] = $token->getEndLine(); if ($class[count($class)-1] != 'anonymous class') { $this->classes[$class[count($class)-1]] = $tmp; } } else { $trait = $token->getName(); $traitEndLine = $token->getEndLine(); $this->traits[$trait] = $tmp; } break; case 'PHP_Token_FUNCTION': $name = $token->getName(); $tmp = array( 'docblock' => $token->getDocblock(), 'keywords' => $token->getKeywords(), 'visibility'=> $token->getVisibility(), 'signature' => $token->getSignature(), 'startLine' => $token->getLine(), 'endLine' => $token->getEndLine(), 'ccn' => $token->getCCN(), 'file' => $this->filename ); if (empty($class) && $trait === false && $interface === false) { $this->functions[$name] = $tmp; $this->addFunctionToMap( $name, $tmp['startLine'], $tmp['endLine'] ); } elseif (!empty($class) && $class[count($class)-1] != 'anonymous class') { $this->classes[$class[count($class)-1]]['methods'][$name] = $tmp; $this->addFunctionToMap( $class[count($class)-1] . '::' . $name, $tmp['startLine'], $tmp['endLine'] ); } elseif ($trait !== false) { $this->traits[$trait]['methods'][$name] = $tmp; $this->addFunctionToMap( $trait . '::' . $name, $tmp['startLine'], $tmp['endLine'] ); } else { $this->interfaces[$interface]['methods'][$name] = $tmp; } break; case 'PHP_Token_CLOSE_CURLY': if (!empty($classEndLine) && $classEndLine[count($classEndLine)-1] == $token->getLine()) { array_pop($classEndLine); array_pop($class); } elseif ($traitEndLine !== false && $traitEndLine == $token->getLine()) { $trait = false; $traitEndLine = false; } elseif ($interfaceEndLine !== false && $interfaceEndLine == $token->getLine()) { $interface = false; $interfaceEndLine = false; } break; } } } public function getLinesOfCode() { return $this->linesOfCode; } public function rewind() { $this->position = 0; } public function valid() { return isset($this->tokens[$this->position]); } public function key() { return $this->position; } public function current() { return $this->tokens[$this->position]; } public function next() { $this->position++; } public function offsetExists($offset) { return isset($this->tokens[$offset]); } public function offsetGet($offset) { if (!$this->offsetExists($offset)) { throw new OutOfBoundsException( sprintf( 'No token at position "%s"', $offset ) ); } return $this->tokens[$offset]; } public function offsetSet($offset, $value) { $this->tokens[$offset] = $value; } public function offsetUnset($offset) { if (!$this->offsetExists($offset)) { throw new OutOfBoundsException( sprintf( 'No token at position "%s"', $offset ) ); } unset($this->tokens[$offset]); } public function seek($position) { $this->position = $position; if (!$this->valid()) { throw new OutOfBoundsException( sprintf( 'No token at position "%s"', $this->position ) ); } } private function addFunctionToMap($name, $startLine, $endLine) { for ($line = $startLine; $line <= $endLine; $line++) { $this->lineToFunctionMap[$line] = $name; } } } text = $text; $this->line = $line; $this->tokenStream = $tokenStream; $this->id = $id; } public function __toString() { return $this->text; } public function getLine() { return $this->line; } } abstract class PHP_TokenWithScope extends PHP_Token { protected $endTokenId; public function getDocblock() { $tokens = $this->tokenStream->tokens(); $currentLineNumber = $tokens[$this->id]->getLine(); $prevLineNumber = $currentLineNumber - 1; for ($i = $this->id - 1; $i; $i--) { if (!isset($tokens[$i])) { return; } if ($tokens[$i] instanceof PHP_Token_FUNCTION || $tokens[$i] instanceof PHP_Token_CLASS || $tokens[$i] instanceof PHP_Token_TRAIT) { break; } $line = $tokens[$i]->getLine(); if ($line == $currentLineNumber || ($line == $prevLineNumber && $tokens[$i] instanceof PHP_Token_WHITESPACE)) { continue; } if ($line < $currentLineNumber && !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) { break; } return (string)$tokens[$i]; } } public function getEndTokenId() { $block = 0; $i = $this->id; $tokens = $this->tokenStream->tokens(); while ($this->endTokenId === null && isset($tokens[$i])) { if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY || $tokens[$i] instanceof PHP_Token_CURLY_OPEN) { $block++; } elseif ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) { $block--; if ($block === 0) { $this->endTokenId = $i; } } elseif (($this instanceof PHP_Token_FUNCTION || $this instanceof PHP_Token_NAMESPACE) && $tokens[$i] instanceof PHP_Token_SEMICOLON) { if ($block === 0) { $this->endTokenId = $i; } } $i++; } if ($this->endTokenId === null) { $this->endTokenId = $this->id; } return $this->endTokenId; } public function getEndLine() { return $this->tokenStream[$this->getEndTokenId()]->getLine(); } } abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope { public function getVisibility() { $tokens = $this->tokenStream->tokens(); for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { if (isset($tokens[$i]) && ($tokens[$i] instanceof PHP_Token_PRIVATE || $tokens[$i] instanceof PHP_Token_PROTECTED || $tokens[$i] instanceof PHP_Token_PUBLIC)) { return strtolower( str_replace('PHP_Token_', '', get_class($tokens[$i])) ); } if (isset($tokens[$i]) && !($tokens[$i] instanceof PHP_Token_STATIC || $tokens[$i] instanceof PHP_Token_FINAL || $tokens[$i] instanceof PHP_Token_ABSTRACT)) { break; } } } public function getKeywords() { $keywords = array(); $tokens = $this->tokenStream->tokens(); for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { if (isset($tokens[$i]) && ($tokens[$i] instanceof PHP_Token_PRIVATE || $tokens[$i] instanceof PHP_Token_PROTECTED || $tokens[$i] instanceof PHP_Token_PUBLIC)) { continue; } if (isset($tokens[$i]) && ($tokens[$i] instanceof PHP_Token_STATIC || $tokens[$i] instanceof PHP_Token_FINAL || $tokens[$i] instanceof PHP_Token_ABSTRACT)) { $keywords[] = strtolower( str_replace('PHP_Token_', '', get_class($tokens[$i])) ); } } return implode(',', $keywords); } } abstract class PHP_Token_Includes extends PHP_Token { protected $name; protected $type; public function getName() { if ($this->name === null) { $this->process(); } return $this->name; } public function getType() { if ($this->type === null) { $this->process(); } return $this->type; } private function process() { $tokens = $this->tokenStream->tokens(); if ($tokens[$this->id+2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) { $this->name = trim($tokens[$this->id+2], "'\""); $this->type = strtolower( str_replace('PHP_Token_', '', get_class($tokens[$this->id])) ); } } } class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility { protected $arguments; protected $ccn; protected $name; protected $signature; public function getArguments() { if ($this->arguments !== null) { return $this->arguments; } $this->arguments = array(); $tokens = $this->tokenStream->tokens(); $typeDeclaration = null; $i = $this->id + 2; while (!$tokens[$i-1] instanceof PHP_Token_OPEN_BRACKET) { $i++; } while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { if ($tokens[$i] instanceof PHP_Token_STRING) { $typeDeclaration = (string)$tokens[$i]; } elseif ($tokens[$i] instanceof PHP_Token_VARIABLE) { $this->arguments[(string)$tokens[$i]] = $typeDeclaration; $typeDeclaration = null; } $i++; } return $this->arguments; } public function getName() { if ($this->name !== null) { return $this->name; } $tokens = $this->tokenStream->tokens(); for ($i = $this->id + 1; $i < count($tokens); $i++) { if ($tokens[$i] instanceof PHP_Token_STRING) { $this->name = (string)$tokens[$i]; break; } elseif ($tokens[$i] instanceof PHP_Token_AMPERSAND && $tokens[$i+1] instanceof PHP_Token_STRING) { $this->name = (string)$tokens[$i+1]; break; } elseif ($tokens[$i] instanceof PHP_Token_OPEN_BRACKET) { $this->name = 'anonymous function'; break; } } if ($this->name != 'anonymous function') { for ($i = $this->id; $i; --$i) { if ($tokens[$i] instanceof PHP_Token_NAMESPACE) { $this->name = $tokens[$i]->getName() . '\\' . $this->name; break; } if ($tokens[$i] instanceof PHP_Token_INTERFACE) { break; } } } return $this->name; } public function getCCN() { if ($this->ccn !== null) { return $this->ccn; } $this->ccn = 1; $end = $this->getEndTokenId(); $tokens = $this->tokenStream->tokens(); for ($i = $this->id; $i <= $end; $i++) { switch (get_class($tokens[$i])) { case 'PHP_Token_IF': case 'PHP_Token_ELSEIF': case 'PHP_Token_FOR': case 'PHP_Token_FOREACH': case 'PHP_Token_WHILE': case 'PHP_Token_CASE': case 'PHP_Token_CATCH': case 'PHP_Token_BOOLEAN_AND': case 'PHP_Token_LOGICAL_AND': case 'PHP_Token_BOOLEAN_OR': case 'PHP_Token_LOGICAL_OR': case 'PHP_Token_QUESTION_MARK': $this->ccn++; break; } } return $this->ccn; } public function getSignature() { if ($this->signature !== null) { return $this->signature; } if ($this->getName() == 'anonymous function') { $this->signature = 'anonymous function'; $i = $this->id + 1; } else { $this->signature = ''; $i = $this->id + 2; } $tokens = $this->tokenStream->tokens(); while (isset($tokens[$i]) && !$tokens[$i] instanceof PHP_Token_OPEN_CURLY && !$tokens[$i] instanceof PHP_Token_SEMICOLON) { $this->signature .= $tokens[$i++]; } $this->signature = trim($this->signature); return $this->signature; } } class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility { protected $interfaces; public function getName() { return (string)$this->tokenStream[$this->id + 2]; } public function hasParent() { return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS; } public function getPackage() { $className = $this->getName(); $docComment = $this->getDocblock(); $result = array( 'namespace' => '', 'fullPackage' => '', 'category' => '', 'package' => '', 'subpackage' => '' ); for ($i = $this->id; $i; --$i) { if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) { $result['namespace'] = $this->tokenStream[$i]->getName(); break; } } if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) { $result['category'] = $matches[1]; } if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) { $result['package'] = $matches[1]; $result['fullPackage'] = $matches[1]; } if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) { $result['subpackage'] = $matches[1]; $result['fullPackage'] .= '.' . $matches[1]; } if (empty($result['fullPackage'])) { $result['fullPackage'] = $this->arrayToName( explode('_', str_replace('\\', '_', $className)), '.' ); } return $result; } protected function arrayToName(array $parts, $join = '\\') { $result = ''; if (count($parts) > 1) { array_pop($parts); $result = join($join, $parts); } return $result; } public function getParent() { if (!$this->hasParent()) { return false; } $i = $this->id + 6; $tokens = $this->tokenStream->tokens(); $className = (string)$tokens[$i]; while (isset($tokens[$i+1]) && !$tokens[$i+1] instanceof PHP_Token_WHITESPACE) { $className .= (string)$tokens[++$i]; } return $className; } public function hasInterfaces() { return (isset($this->tokenStream[$this->id + 4]) && $this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) || (isset($this->tokenStream[$this->id + 8]) && $this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS); } public function getInterfaces() { if ($this->interfaces !== null) { return $this->interfaces; } if (!$this->hasInterfaces()) { return ($this->interfaces = false); } if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) { $i = $this->id + 3; } else { $i = $this->id + 7; } $tokens = $this->tokenStream->tokens(); while (!$tokens[$i+1] instanceof PHP_Token_OPEN_CURLY) { $i++; if ($tokens[$i] instanceof PHP_Token_STRING) { $this->interfaces[] = (string)$tokens[$i]; } } return $this->interfaces; } } class PHP_Token_ABSTRACT extends PHP_Token {} class PHP_Token_AMPERSAND extends PHP_Token {} class PHP_Token_AND_EQUAL extends PHP_Token {} class PHP_Token_ARRAY extends PHP_Token {} class PHP_Token_ARRAY_CAST extends PHP_Token {} class PHP_Token_AS extends PHP_Token {} class PHP_Token_AT extends PHP_Token {} class PHP_Token_BACKTICK extends PHP_Token {} class PHP_Token_BAD_CHARACTER extends PHP_Token {} class PHP_Token_BOOLEAN_AND extends PHP_Token {} class PHP_Token_BOOLEAN_OR extends PHP_Token {} class PHP_Token_BOOL_CAST extends PHP_Token {} class PHP_Token_BREAK extends PHP_Token {} class PHP_Token_CARET extends PHP_Token {} class PHP_Token_CASE extends PHP_Token {} class PHP_Token_CATCH extends PHP_Token {} class PHP_Token_CHARACTER extends PHP_Token {} class PHP_Token_CLASS extends PHP_Token_INTERFACE { public function getName() { $next = $this->tokenStream[$this->id + 1]; if ($next instanceof PHP_Token_WHITESPACE) { $next = $this->tokenStream[$this->id + 2]; } if ($next instanceof PHP_Token_STRING) { return (string) $next; } if ($next instanceof PHP_Token_OPEN_CURLY || $next instanceof PHP_Token_EXTENDS || $next instanceof PHP_Token_IMPLEMENTS) { return 'anonymous class'; } } } class PHP_Token_CLASS_C extends PHP_Token {} class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token {} class PHP_Token_CLONE extends PHP_Token {} class PHP_Token_CLOSE_BRACKET extends PHP_Token {} class PHP_Token_CLOSE_CURLY extends PHP_Token {} class PHP_Token_CLOSE_SQUARE extends PHP_Token {} class PHP_Token_CLOSE_TAG extends PHP_Token {} class PHP_Token_COLON extends PHP_Token {} class PHP_Token_COMMA extends PHP_Token {} class PHP_Token_COMMENT extends PHP_Token {} class PHP_Token_CONCAT_EQUAL extends PHP_Token {} class PHP_Token_CONST extends PHP_Token {} class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token {} class PHP_Token_CONTINUE extends PHP_Token {} class PHP_Token_CURLY_OPEN extends PHP_Token {} class PHP_Token_DEC extends PHP_Token {} class PHP_Token_DECLARE extends PHP_Token {} class PHP_Token_DEFAULT extends PHP_Token {} class PHP_Token_DIV extends PHP_Token {} class PHP_Token_DIV_EQUAL extends PHP_Token {} class PHP_Token_DNUMBER extends PHP_Token {} class PHP_Token_DO extends PHP_Token {} class PHP_Token_DOC_COMMENT extends PHP_Token {} class PHP_Token_DOLLAR extends PHP_Token {} class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token {} class PHP_Token_DOT extends PHP_Token {} class PHP_Token_DOUBLE_ARROW extends PHP_Token {} class PHP_Token_DOUBLE_CAST extends PHP_Token {} class PHP_Token_DOUBLE_COLON extends PHP_Token {} class PHP_Token_DOUBLE_QUOTES extends PHP_Token {} class PHP_Token_ECHO extends PHP_Token {} class PHP_Token_ELSE extends PHP_Token {} class PHP_Token_ELSEIF extends PHP_Token {} class PHP_Token_EMPTY extends PHP_Token {} class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token {} class PHP_Token_ENDDECLARE extends PHP_Token {} class PHP_Token_ENDFOR extends PHP_Token {} class PHP_Token_ENDFOREACH extends PHP_Token {} class PHP_Token_ENDIF extends PHP_Token {} class PHP_Token_ENDSWITCH extends PHP_Token {} class PHP_Token_ENDWHILE extends PHP_Token {} class PHP_Token_END_HEREDOC extends PHP_Token {} class PHP_Token_EQUAL extends PHP_Token {} class PHP_Token_EVAL extends PHP_Token {} class PHP_Token_EXCLAMATION_MARK extends PHP_Token {} class PHP_Token_EXIT extends PHP_Token {} class PHP_Token_EXTENDS extends PHP_Token {} class PHP_Token_FILE extends PHP_Token {} class PHP_Token_FINAL extends PHP_Token {} class PHP_Token_FOR extends PHP_Token {} class PHP_Token_FOREACH extends PHP_Token {} class PHP_Token_FUNC_C extends PHP_Token {} class PHP_Token_GLOBAL extends PHP_Token {} class PHP_Token_GT extends PHP_Token {} class PHP_Token_IF extends PHP_Token {} class PHP_Token_IMPLEMENTS extends PHP_Token {} class PHP_Token_INC extends PHP_Token {} class PHP_Token_INCLUDE extends PHP_Token_Includes {} class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes {} class PHP_Token_INLINE_HTML extends PHP_Token {} class PHP_Token_INSTANCEOF extends PHP_Token {} class PHP_Token_INT_CAST extends PHP_Token {} class PHP_Token_ISSET extends PHP_Token {} class PHP_Token_IS_EQUAL extends PHP_Token {} class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token {} class PHP_Token_IS_IDENTICAL extends PHP_Token {} class PHP_Token_IS_NOT_EQUAL extends PHP_Token {} class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token {} class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token {} class PHP_Token_LINE extends PHP_Token {} class PHP_Token_LIST extends PHP_Token {} class PHP_Token_LNUMBER extends PHP_Token {} class PHP_Token_LOGICAL_AND extends PHP_Token {} class PHP_Token_LOGICAL_OR extends PHP_Token {} class PHP_Token_LOGICAL_XOR extends PHP_Token {} class PHP_Token_LT extends PHP_Token {} class PHP_Token_METHOD_C extends PHP_Token {} class PHP_Token_MINUS extends PHP_Token {} class PHP_Token_MINUS_EQUAL extends PHP_Token {} class PHP_Token_MOD_EQUAL extends PHP_Token {} class PHP_Token_MULT extends PHP_Token {} class PHP_Token_MUL_EQUAL extends PHP_Token {} class PHP_Token_NEW extends PHP_Token {} class PHP_Token_NUM_STRING extends PHP_Token {} class PHP_Token_OBJECT_CAST extends PHP_Token {} class PHP_Token_OBJECT_OPERATOR extends PHP_Token {} class PHP_Token_OPEN_BRACKET extends PHP_Token {} class PHP_Token_OPEN_CURLY extends PHP_Token {} class PHP_Token_OPEN_SQUARE extends PHP_Token {} class PHP_Token_OPEN_TAG extends PHP_Token {} class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token {} class PHP_Token_OR_EQUAL extends PHP_Token {} class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token {} class PHP_Token_PERCENT extends PHP_Token {} class PHP_Token_PIPE extends PHP_Token {} class PHP_Token_PLUS extends PHP_Token {} class PHP_Token_PLUS_EQUAL extends PHP_Token {} class PHP_Token_PRINT extends PHP_Token {} class PHP_Token_PRIVATE extends PHP_Token {} class PHP_Token_PROTECTED extends PHP_Token {} class PHP_Token_PUBLIC extends PHP_Token {} class PHP_Token_QUESTION_MARK extends PHP_Token {} class PHP_Token_REQUIRE extends PHP_Token_Includes {} class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes {} class PHP_Token_RETURN extends PHP_Token {} class PHP_Token_SEMICOLON extends PHP_Token {} class PHP_Token_SL extends PHP_Token {} class PHP_Token_SL_EQUAL extends PHP_Token {} class PHP_Token_SR extends PHP_Token {} class PHP_Token_SR_EQUAL extends PHP_Token {} class PHP_Token_START_HEREDOC extends PHP_Token {} class PHP_Token_STATIC extends PHP_Token {} class PHP_Token_STRING extends PHP_Token {} class PHP_Token_STRING_CAST extends PHP_Token {} class PHP_Token_STRING_VARNAME extends PHP_Token {} class PHP_Token_SWITCH extends PHP_Token {} class PHP_Token_THROW extends PHP_Token {} class PHP_Token_TILDE extends PHP_Token {} class PHP_Token_TRY extends PHP_Token {} class PHP_Token_UNSET extends PHP_Token {} class PHP_Token_UNSET_CAST extends PHP_Token {} class PHP_Token_USE extends PHP_Token {} class PHP_Token_USE_FUNCTION extends PHP_Token {} class PHP_Token_VAR extends PHP_Token {} class PHP_Token_VARIABLE extends PHP_Token {} class PHP_Token_WHILE extends PHP_Token {} class PHP_Token_WHITESPACE extends PHP_Token {} class PHP_Token_XOR_EQUAL extends PHP_Token {} class PHP_Token_HALT_COMPILER extends PHP_Token {} class PHP_Token_DIR extends PHP_Token {} class PHP_Token_GOTO extends PHP_Token {} class PHP_Token_NAMESPACE extends PHP_TokenWithScope { public function getName() { $tokens = $this->tokenStream->tokens(); $namespace = (string)$tokens[$this->id+2]; for ($i = $this->id + 3;; $i += 2) { if (isset($tokens[$i]) && $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) { $namespace .= '\\' . $tokens[$i+1]; } else { break; } } return $namespace; } } class PHP_Token_NS_C extends PHP_Token {} class PHP_Token_NS_SEPARATOR extends PHP_Token {} class PHP_Token_CALLABLE extends PHP_Token {} class PHP_Token_INSTEADOF extends PHP_Token {} class PHP_Token_TRAIT extends PHP_Token_INTERFACE {} class PHP_Token_TRAIT_C extends PHP_Token {} class PHP_Token_FINALLY extends PHP_Token {} class PHP_Token_YIELD extends PHP_Token {} class PHP_Token_ELLIPSIS extends PHP_Token {} class PHP_Token_POW extends PHP_Token {} class PHP_Token_POW_EQUAL extends PHP_Token {} class PHP_Token_COALESCE extends PHP_Token {} class PHP_Token_SPACESHIP extends PHP_Token {} class PHP_Token_YIELD_FROM extends PHP_Token {} class PHP_Token_ASYNC extends PHP_Token {} class PHP_Token_AWAIT extends PHP_Token {} class PHP_Token_COMPILER_HALT_OFFSET extends PHP_Token {} class PHP_Token_ENUM extends PHP_Token {} class PHP_Token_EQUALS extends PHP_Token {} class PHP_Token_IN extends PHP_Token {} class PHP_Token_JOIN extends PHP_Token {} class PHP_Token_LAMBDA_ARROW extends PHP_Token {} class PHP_Token_LAMBDA_CP extends PHP_Token {} class PHP_Token_LAMBDA_OP extends PHP_Token {} class PHP_Token_ONUMBER extends PHP_Token {} class PHP_Token_NULLSAFE_OBJECT_OPERATOR extends PHP_Token {} class PHP_Token_SHAPE extends PHP_Token {} class PHP_Token_SUPER extends PHP_Token {} class PHP_Token_TYPE extends PHP_Token {} class PHP_Token_TYPELIST_GT extends PHP_Token {} class PHP_Token_TYPELIST_LT extends PHP_Token {} class PHP_Token_WHERE extends PHP_Token {} class PHP_Token_XHP_ATTRIBUTE extends PHP_Token {} class PHP_Token_XHP_CATEGORY extends PHP_Token {} class PHP_Token_XHP_CATEGORY_LABEL extends PHP_Token {} class PHP_Token_XHP_CHILDREN extends PHP_Token {} class PHP_Token_XHP_LABEL extends PHP_Token {} class PHP_Token_XHP_REQUIRED extends PHP_Token {} class PHP_Token_XHP_TAG_GT extends PHP_Token {} class PHP_Token_XHP_TAG_LT extends PHP_Token {} class PHP_Token_XHP_TEXT extends PHP_Token {} =') && !ini_get('xdebug.coverage_enable')) { throw new RuntimeException( 'xdebug.coverage_enable=On has to be set in php.ini' ); } } public function start($determineUnusedAndDead = true) { if ($determineUnusedAndDead) { xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); } else { xdebug_start_code_coverage(); } } public function stop() { $data = xdebug_get_code_coverage(); xdebug_stop_code_coverage(); return $this->cleanup($data); } private function cleanup(array $data) { foreach (array_keys($data) as $file) { unset($data[$file][0]); if (strpos($file, 'xdebug://debug-eval') !== 0 && file_exists($file)) { $numLines = $this->getNumberOfLinesInFile($file); foreach (array_keys($data[$file]) as $line) { if ($line > $numLines) { unset($data[$file][$line]); } } } } return $data; } private function getNumberOfLinesInFile($file) { if (!isset($this->cacheNumLines[$file])) { $buffer = file_get_contents($file); $lines = substr_count($buffer, "\n"); if (substr($buffer, -1) !== "\n") { $lines++; } $this->cacheNumLines[$file] = $lines; } return $this->cacheNumLines[$file]; } } $newFiles] ); } else { $sourceLines = []; } } foreach ($sourceLines as $file => $lines) { foreach ($lines as $lineNo => $numExecuted) { $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; } } $fetchedLines = array_merge($fetchedLines, $sourceLines); return $this->detectExecutedLines($fetchedLines, $dbgData); } private function detectExecutedLines(array $sourceLines, array $dbgData) { foreach ($dbgData as $file => $coveredLines) { foreach ($coveredLines as $lineNo => $numExecuted) { if (isset($sourceLines[$file][$lineNo])) { $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; } } } return $sourceLines; } } 0) { $percent = ($a / $b) * 100; } else { $percent = 100; } if ($asString) { if ($fixedWidth) { return sprintf('%6.2F%%', $percent); } return sprintf('%01.2F%%', $percent); } else { return $percent; } } } selectDriver(); } if ($filter === null) { $filter = new Filter; } $this->driver = $driver; $this->filter = $filter; $this->wizard = new Wizard; } public function getReport() { $builder = new Builder; return $builder->build($this); } public function clear() { $this->isInitialized = false; $this->currentId = null; $this->data = []; $this->tests = []; } public function filter() { return $this->filter; } public function getData($raw = false) { if (!$raw && $this->addUncoveredFilesFromWhitelist) { $this->addUncoveredFilesFromWhitelist(); } return $this->data; } public function setData(array $data) { $this->data = $data; } public function getTests() { return $this->tests; } public function setTests(array $tests) { $this->tests = $tests; } public function start($id, $clear = false) { if (!is_bool($clear)) { throw InvalidArgumentException::create( 1, 'boolean' ); } if ($clear) { $this->clear(); } if ($this->isInitialized === false) { $this->initializeData(); } $this->currentId = $id; $this->driver->start($this->shouldCheckForDeadAndUnused); } public function stop($append = true, $linesToBeCovered = [], array $linesToBeUsed = []) { if (!is_bool($append)) { throw InvalidArgumentException::create( 1, 'boolean' ); } if (!is_array($linesToBeCovered) && $linesToBeCovered !== false) { throw InvalidArgumentException::create( 2, 'array or false' ); } $data = $this->driver->stop(); $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed); $this->currentId = null; return $data; } public function append(array $data, $id = null, $append = true, $linesToBeCovered = [], array $linesToBeUsed = []) { if ($id === null) { $id = $this->currentId; } if ($id === null) { throw new RuntimeException; } $this->applyListsFilter($data); $this->applyIgnoredLinesFilter($data); $this->initializeFilesThatAreSeenTheFirstTime($data); if (!$append) { return; } if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') { $this->applyCoversAnnotationFilter( $data, $linesToBeCovered, $linesToBeUsed ); } if (empty($data)) { return; } $size = 'unknown'; $status = null; if ($id instanceof \PHPUnit_Framework_TestCase) { $_size = $id->getSize(); if ($_size == \PHPUnit_Util_Test::SMALL) { $size = 'small'; } elseif ($_size == \PHPUnit_Util_Test::MEDIUM) { $size = 'medium'; } elseif ($_size == \PHPUnit_Util_Test::LARGE) { $size = 'large'; } $status = $id->getStatus(); $id = get_class($id) . '::' . $id->getName(); } elseif ($id instanceof \PHPUnit_Extensions_PhptTestCase) { $size = 'large'; $id = $id->getName(); } $this->tests[$id] = ['size' => $size, 'status' => $status]; foreach ($data as $file => $lines) { if (!$this->filter->isFile($file)) { continue; } foreach ($lines as $k => $v) { if ($v == Driver::LINE_EXECUTED) { if (empty($this->data[$file][$k]) || !in_array($id, $this->data[$file][$k])) { $this->data[$file][$k][] = $id; } } } } } public function merge(CodeCoverage $that) { $this->filter->setWhitelistedFiles( array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) ); foreach ($that->data as $file => $lines) { if (!isset($this->data[$file])) { if (!$this->filter->isFiltered($file)) { $this->data[$file] = $lines; } continue; } foreach ($lines as $line => $data) { if ($data !== null) { if (!isset($this->data[$file][$line])) { $this->data[$file][$line] = $data; } else { $this->data[$file][$line] = array_unique( array_merge($this->data[$file][$line], $data) ); } } } } $this->tests = array_merge($this->tests, $that->getTests()); } public function setCacheTokens($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->cacheTokens = $flag; } public function getCacheTokens() { return $this->cacheTokens; } public function setCheckForUnintentionallyCoveredCode($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->checkForUnintentionallyCoveredCode = $flag; } public function setForceCoversAnnotation($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->forceCoversAnnotation = $flag; } public function setCheckForMissingCoversAnnotation($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->checkForMissingCoversAnnotation = $flag; } public function setCheckForUnexecutedCoveredCode($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->checkForUnexecutedCoveredCode = $flag; } public function setMapTestClassNameToCoveredClassName($flag) { } public function setAddUncoveredFilesFromWhitelist($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->addUncoveredFilesFromWhitelist = $flag; } public function setProcessUncoveredFilesFromWhitelist($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->processUncoveredFilesFromWhitelist = $flag; } public function setDisableIgnoredLines($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->disableIgnoredLines = $flag; } public function setIgnoreDeprecatedCode($flag) { if (!is_bool($flag)) { throw InvalidArgumentException::create( 1, 'boolean' ); } $this->ignoreDeprecatedCode = $flag; } public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist) { $this->unintentionallyCoveredSubclassesWhitelist = $whitelist; } private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed) { if ($linesToBeCovered === false || ($this->forceCoversAnnotation && empty($linesToBeCovered))) { if ($this->checkForMissingCoversAnnotation) { throw new MissingCoversAnnotationException; } $data = []; return; } if (empty($linesToBeCovered)) { return; } if ($this->checkForUnintentionallyCoveredCode && (!$this->currentId instanceof \PHPUnit_Framework_TestCase || (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { $this->performUnintentionallyCoveredCodeCheck( $data, $linesToBeCovered, $linesToBeUsed ); } if ($this->checkForUnexecutedCoveredCode) { $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); } $data = array_intersect_key($data, $linesToBeCovered); foreach (array_keys($data) as $filename) { $_linesToBeCovered = array_flip($linesToBeCovered[$filename]); $data[$filename] = array_intersect_key( $data[$filename], $_linesToBeCovered ); } } private function applyListsFilter(array &$data) { foreach (array_keys($data) as $filename) { if ($this->filter->isFiltered($filename)) { unset($data[$filename]); } } } private function applyIgnoredLinesFilter(array &$data) { foreach (array_keys($data) as $filename) { if (!$this->filter->isFile($filename)) { continue; } foreach ($this->getLinesToBeIgnored($filename) as $line) { unset($data[$filename][$line]); } } } private function initializeFilesThatAreSeenTheFirstTime(array $data) { foreach ($data as $file => $lines) { if ($this->filter->isFile($file) && !isset($this->data[$file])) { $this->data[$file] = []; foreach ($lines as $k => $v) { $this->data[$file][$k] = $v == -2 ? null : []; } } } } private function addUncoveredFilesFromWhitelist() { $data = []; $uncoveredFiles = array_diff( $this->filter->getWhitelist(), array_keys($this->data) ); foreach ($uncoveredFiles as $uncoveredFile) { if (!file_exists($uncoveredFile)) { continue; } if (!$this->processUncoveredFilesFromWhitelist) { $data[$uncoveredFile] = []; $lines = count(file($uncoveredFile)); for ($i = 1; $i <= $lines; $i++) { $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED; } } } $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); } private function getLinesToBeIgnored($filename) { if (!is_string($filename)) { throw InvalidArgumentException::create( 1, 'string' ); } if (!isset($this->ignoredLines[$filename])) { $this->ignoredLines[$filename] = []; if ($this->disableIgnoredLines) { return $this->ignoredLines[$filename]; } $ignore = false; $stop = false; $lines = file($filename); $numLines = count($lines); foreach ($lines as $index => $line) { if (!trim($line)) { $this->ignoredLines[$filename][] = $index + 1; } } if ($this->cacheTokens) { $tokens = \PHP_Token_Stream_CachingFactory::get($filename); } else { $tokens = new \PHP_Token_Stream($filename); } $classes = array_merge($tokens->getClasses(), $tokens->getTraits()); $tokens = $tokens->tokens(); foreach ($tokens as $token) { switch (get_class($token)) { case 'PHP_Token_COMMENT': case 'PHP_Token_DOC_COMMENT': $_token = trim($token); $_line = trim($lines[$token->getLine() - 1]); if ($_token == '// @codeCoverageIgnore' || $_token == '//@codeCoverageIgnore') { $ignore = true; $stop = true; } elseif ($_token == '// @codeCoverageIgnoreStart' || $_token == '//@codeCoverageIgnoreStart') { $ignore = true; } elseif ($_token == '// @codeCoverageIgnoreEnd' || $_token == '//@codeCoverageIgnoreEnd') { $stop = true; } if (!$ignore) { $start = $token->getLine(); $end = $start + substr_count($token, "\n"); if (0 !== strpos($_token, $_line)) { $start++; } for ($i = $start; $i < $end; $i++) { $this->ignoredLines[$filename][] = $i; } if (isset($lines[$i-1]) && 0 === strpos($_token, '/*') && '*/' === substr(trim($lines[$i-1]), -2)) { $this->ignoredLines[$filename][] = $i; } } break; case 'PHP_Token_INTERFACE': case 'PHP_Token_TRAIT': case 'PHP_Token_CLASS': case 'PHP_Token_FUNCTION': $docblock = $token->getDocblock(); $this->ignoredLines[$filename][] = $token->getLine(); if (strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && strpos($docblock, '@deprecated'))) { $endLine = $token->getEndLine(); for ($i = $token->getLine(); $i <= $endLine; $i++) { $this->ignoredLines[$filename][] = $i; } } elseif ($token instanceof \PHP_Token_INTERFACE || $token instanceof \PHP_Token_TRAIT || $token instanceof \PHP_Token_CLASS) { if (empty($classes[$token->getName()]['methods'])) { for ($i = $token->getLine(); $i <= $token->getEndLine(); $i++) { $this->ignoredLines[$filename][] = $i; } } else { $firstMethod = array_shift( $classes[$token->getName()]['methods'] ); do { $lastMethod = array_pop( $classes[$token->getName()]['methods'] ); } while ($lastMethod !== null && substr($lastMethod['signature'], 0, 18) == 'anonymous function'); if ($lastMethod === null) { $lastMethod = $firstMethod; } for ($i = $token->getLine(); $i < $firstMethod['startLine']; $i++) { $this->ignoredLines[$filename][] = $i; } for ($i = $token->getEndLine(); $i > $lastMethod['endLine']; $i--) { $this->ignoredLines[$filename][] = $i; } } } break; case 'PHP_Token_NAMESPACE': $this->ignoredLines[$filename][] = $token->getEndLine(); case 'PHP_Token_DECLARE': case 'PHP_Token_OPEN_TAG': case 'PHP_Token_CLOSE_TAG': case 'PHP_Token_USE': $this->ignoredLines[$filename][] = $token->getLine(); break; } if ($ignore) { $this->ignoredLines[$filename][] = $token->getLine(); if ($stop) { $ignore = false; $stop = false; } } } $this->ignoredLines[$filename][] = $numLines + 1; $this->ignoredLines[$filename] = array_unique( $this->ignoredLines[$filename] ); sort($this->ignoredLines[$filename]); } return $this->ignoredLines[$filename]; } private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed) { $allowedLines = $this->getAllowedLines( $linesToBeCovered, $linesToBeUsed ); $unintentionallyCoveredUnits = []; foreach ($data as $file => $_data) { foreach ($_data as $line => $flag) { if ($flag == 1 && !isset($allowedLines[$file][$line])) { $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); } } } $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits); if (!empty($unintentionallyCoveredUnits)) { throw new UnintentionallyCoveredCodeException( $unintentionallyCoveredUnits ); } } private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed) { $expectedLines = $this->getAllowedLines( $linesToBeCovered, $linesToBeUsed ); foreach ($data as $file => $_data) { foreach (array_keys($_data) as $line) { if (!isset($expectedLines[$file][$line])) { continue; } unset($expectedLines[$file][$line]); } } $message = ''; foreach ($expectedLines as $file => $lines) { if (empty($lines)) { continue; } foreach (array_keys($lines) as $line) { $message .= sprintf('- %s:%d' . PHP_EOL, $file, $line); } } if (!empty($message)) { throw new CoveredCodeNotExecutedException($message); } } private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed) { $allowedLines = []; foreach (array_keys($linesToBeCovered) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } $allowedLines[$file] = array_merge( $allowedLines[$file], $linesToBeCovered[$file] ); } foreach (array_keys($linesToBeUsed) as $file) { if (!isset($allowedLines[$file])) { $allowedLines[$file] = []; } $allowedLines[$file] = array_merge( $allowedLines[$file], $linesToBeUsed[$file] ); } foreach (array_keys($allowedLines) as $file) { $allowedLines[$file] = array_flip( array_unique($allowedLines[$file]) ); } return $allowedLines; } private function selectDriver() { $runtime = new Runtime; if (!$runtime->canCollectCodeCoverage()) { throw new RuntimeException('No code coverage driver available'); } if ($runtime->isHHVM()) { return new HHVM; } elseif ($runtime->isPHPDBG()) { return new PHPDBG; } else { return new Xdebug; } } private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits) { $unintentionallyCoveredUnits = array_unique($unintentionallyCoveredUnits); sort($unintentionallyCoveredUnits); foreach (array_keys($unintentionallyCoveredUnits) as $k => $v) { $unit = explode('::', $unintentionallyCoveredUnits[$k]); if (count($unit) != 2) { continue; } $class = new \ReflectionClass($unit[0]); foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) { if ($class->isSubclassOf($whitelisted)) { unset($unintentionallyCoveredUnits[$k]); break; } } } return array_values($unintentionallyCoveredUnits); } protected function initializeData() { $this->isInitialized = true; if ($this->processUncoveredFilesFromWhitelist) { $this->shouldCheckForDeadAndUnused = false; $this->driver->start(true); foreach ($this->filter->getWhitelist() as $file) { if ($this->filter->isFile($file)) { include_once($file); } } $data = []; $coverage = $this->driver->stop(); foreach ($coverage as $file => $fileCoverage) { if ($this->filter->isFiltered($file)) { continue; } foreach (array_keys($fileCoverage) as $key) { if ($fileCoverage[$key] == Driver::LINE_EXECUTED) { $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED; } } $data[$file] = $fileCoverage; } $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); } } } numFiles == -1) { $this->numFiles = 0; foreach ($this->children as $child) { $this->numFiles += count($child); } } return $this->numFiles; } public function getIterator() { return new \RecursiveIteratorIterator( new Iterator($this), \RecursiveIteratorIterator::SELF_FIRST ); } public function addDirectory($name) { $directory = new self($name, $this); $this->children[] = $directory; $this->directories[] = &$this->children[count($this->children) - 1]; return $directory; } public function addFile($name, array $coverageData, array $testData, $cacheTokens) { $file = new File( $name, $this, $coverageData, $testData, $cacheTokens ); $this->children[] = $file; $this->files[] = &$this->children[count($this->children) - 1]; $this->numExecutableLines = -1; $this->numExecutedLines = -1; return $file; } public function getDirectories() { return $this->directories; } public function getFiles() { return $this->files; } public function getChildNodes() { return $this->children; } public function getClasses() { if ($this->classes === null) { $this->classes = []; foreach ($this->children as $child) { $this->classes = array_merge( $this->classes, $child->getClasses() ); } } return $this->classes; } public function getTraits() { if ($this->traits === null) { $this->traits = []; foreach ($this->children as $child) { $this->traits = array_merge( $this->traits, $child->getTraits() ); } } return $this->traits; } public function getFunctions() { if ($this->functions === null) { $this->functions = []; foreach ($this->children as $child) { $this->functions = array_merge( $this->functions, $child->getFunctions() ); } } return $this->functions; } public function getLinesOfCode() { if ($this->linesOfCode === null) { $this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; foreach ($this->children as $child) { $linesOfCode = $child->getLinesOfCode(); $this->linesOfCode['loc'] += $linesOfCode['loc']; $this->linesOfCode['cloc'] += $linesOfCode['cloc']; $this->linesOfCode['ncloc'] += $linesOfCode['ncloc']; } } return $this->linesOfCode; } public function getNumExecutableLines() { if ($this->numExecutableLines == -1) { $this->numExecutableLines = 0; foreach ($this->children as $child) { $this->numExecutableLines += $child->getNumExecutableLines(); } } return $this->numExecutableLines; } public function getNumExecutedLines() { if ($this->numExecutedLines == -1) { $this->numExecutedLines = 0; foreach ($this->children as $child) { $this->numExecutedLines += $child->getNumExecutedLines(); } } return $this->numExecutedLines; } public function getNumClasses() { if ($this->numClasses == -1) { $this->numClasses = 0; foreach ($this->children as $child) { $this->numClasses += $child->getNumClasses(); } } return $this->numClasses; } public function getNumTestedClasses() { if ($this->numTestedClasses == -1) { $this->numTestedClasses = 0; foreach ($this->children as $child) { $this->numTestedClasses += $child->getNumTestedClasses(); } } return $this->numTestedClasses; } public function getNumTraits() { if ($this->numTraits == -1) { $this->numTraits = 0; foreach ($this->children as $child) { $this->numTraits += $child->getNumTraits(); } } return $this->numTraits; } public function getNumTestedTraits() { if ($this->numTestedTraits == -1) { $this->numTestedTraits = 0; foreach ($this->children as $child) { $this->numTestedTraits += $child->getNumTestedTraits(); } } return $this->numTestedTraits; } public function getNumMethods() { if ($this->numMethods == -1) { $this->numMethods = 0; foreach ($this->children as $child) { $this->numMethods += $child->getNumMethods(); } } return $this->numMethods; } public function getNumTestedMethods() { if ($this->numTestedMethods == -1) { $this->numTestedMethods = 0; foreach ($this->children as $child) { $this->numTestedMethods += $child->getNumTestedMethods(); } } return $this->numTestedMethods; } public function getNumFunctions() { if ($this->numFunctions == -1) { $this->numFunctions = 0; foreach ($this->children as $child) { $this->numFunctions += $child->getNumFunctions(); } } return $this->numFunctions; } public function getNumTestedFunctions() { if ($this->numTestedFunctions == -1) { $this->numTestedFunctions = 0; foreach ($this->children as $child) { $this->numTestedFunctions += $child->getNumTestedFunctions(); } } return $this->numTestedFunctions; } } nodes = $node->getChildNodes(); } public function rewind() { $this->position = 0; } public function valid() { return $this->position < count($this->nodes); } public function key() { return $this->position; } public function current() { return $this->valid() ? $this->nodes[$this->position] : null; } public function next() { $this->position++; } public function getChildren() { return new self( $this->nodes[$this->position] ); } public function hasChildren() { return $this->nodes[$this->position] instanceof Directory; } } getData(); $commonPath = $this->reducePaths($files); $root = new Directory( $commonPath, null ); $this->addItems( $root, $this->buildDirectoryStructure($files), $coverage->getTests(), $coverage->getCacheTokens() ); return $root; } private function addItems(Directory $root, array $items, array $tests, $cacheTokens) { foreach ($items as $key => $value) { if (substr($key, -2) == '/f') { $key = substr($key, 0, -2); if (file_exists($root->getPath() . DIRECTORY_SEPARATOR . $key)) { $root->addFile($key, $value, $tests, $cacheTokens); } } else { $child = $root->addDirectory($key); $this->addItems($child, $value, $tests, $cacheTokens); } } } private function buildDirectoryStructure($files) { $result = []; foreach ($files as $path => $file) { $path = explode('/', $path); $pointer = &$result; $max = count($path); for ($i = 0; $i < $max; $i++) { if ($i == ($max - 1)) { $type = '/f'; } else { $type = ''; } $pointer = &$pointer[$path[$i] . $type]; } $pointer = $file; } return $result; } private function reducePaths(&$files) { if (empty($files)) { return '.'; } $commonPath = ''; $paths = array_keys($files); if (count($files) == 1) { $commonPath = dirname($paths[0]) . '/'; $files[basename($paths[0])] = $files[$paths[0]]; unset($files[$paths[0]]); return $commonPath; } $max = count($paths); for ($i = 0; $i < $max; $i++) { if (strpos($paths[$i], 'phar://') === 0) { $paths[$i] = substr($paths[$i], 7); $paths[$i] = strtr($paths[$i], '/', DIRECTORY_SEPARATOR); } $paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]); if (empty($paths[$i][0])) { $paths[$i][0] = DIRECTORY_SEPARATOR; } } $done = false; $max = count($paths); while (!$done) { for ($i = 0; $i < $max - 1; $i++) { if (!isset($paths[$i][0]) || !isset($paths[$i+1][0]) || $paths[$i][0] != $paths[$i+1][0]) { $done = true; break; } } if (!$done) { $commonPath .= $paths[0][0]; if ($paths[0][0] != DIRECTORY_SEPARATOR) { $commonPath .= DIRECTORY_SEPARATOR; } for ($i = 0; $i < $max; $i++) { array_shift($paths[$i]); } } } $original = array_keys($files); $max = count($original); for ($i = 0; $i < $max; $i++) { $files[implode('/', $paths[$i])] = $files[$original[$i]]; unset($files[$original[$i]]); } ksort($files); return substr($commonPath, 0, -1); } } name = $name; $this->parent = $parent; } public function getName() { return $this->name; } public function getId() { if ($this->id === null) { $parent = $this->getParent(); if ($parent === null) { $this->id = 'index'; } else { $parentId = $parent->getId(); if ($parentId == 'index') { $this->id = str_replace(':', '_', $this->name); } else { $this->id = $parentId . '/' . $this->name; } } } return $this->id; } public function getPath() { if ($this->path === null) { if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) { $this->path = $this->name; } else { $this->path = $this->parent->getPath() . '/' . $this->name; } } return $this->path; } public function getPathAsArray() { if ($this->pathArray === null) { if ($this->parent === null) { $this->pathArray = []; } else { $this->pathArray = $this->parent->getPathAsArray(); } $this->pathArray[] = $this; } return $this->pathArray; } public function getParent() { return $this->parent; } public function getTestedClassesPercent($asString = true) { return Util::percent( $this->getNumTestedClasses(), $this->getNumClasses(), $asString ); } public function getTestedTraitsPercent($asString = true) { return Util::percent( $this->getNumTestedTraits(), $this->getNumTraits(), $asString ); } public function getTestedClassesAndTraitsPercent($asString = true) { return Util::percent( $this->getNumTestedClassesAndTraits(), $this->getNumClassesAndTraits(), $asString ); } public function getTestedMethodsPercent($asString = true) { return Util::percent( $this->getNumTestedMethods(), $this->getNumMethods(), $asString ); } public function getLineExecutedPercent($asString = true) { return Util::percent( $this->getNumExecutedLines(), $this->getNumExecutableLines(), $asString ); } public function getNumClassesAndTraits() { return $this->getNumClasses() + $this->getNumTraits(); } public function getNumTestedClassesAndTraits() { return $this->getNumTestedClasses() + $this->getNumTestedTraits(); } public function getClassesAndTraits() { return array_merge($this->getClasses(), $this->getTraits()); } abstract public function getClasses(); abstract public function getTraits(); abstract public function getFunctions(); abstract public function getLinesOfCode(); abstract public function getNumExecutableLines(); abstract public function getNumExecutedLines(); abstract public function getNumClasses(); abstract public function getNumTestedClasses(); abstract public function getNumTraits(); abstract public function getNumTestedTraits(); abstract public function getNumMethods(); abstract public function getNumTestedMethods(); abstract public function getNumFunctions(); abstract public function getNumTestedFunctions(); } coverageData = $coverageData; $this->testData = $testData; $this->cacheTokens = $cacheTokens; $this->calculateStatistics(); } public function count() { return 1; } public function getCoverageData() { return $this->coverageData; } public function getTestData() { return $this->testData; } public function getClasses() { return $this->classes; } public function getTraits() { return $this->traits; } public function getFunctions() { return $this->functions; } public function getLinesOfCode() { return $this->linesOfCode; } public function getNumExecutableLines() { return $this->numExecutableLines; } public function getNumExecutedLines() { return $this->numExecutedLines; } public function getNumClasses() { if ($this->numClasses === null) { $this->numClasses = 0; foreach ($this->classes as $class) { foreach ($class['methods'] as $method) { if ($method['executableLines'] > 0) { $this->numClasses++; continue 2; } } } } return $this->numClasses; } public function getNumTestedClasses() { return $this->numTestedClasses; } public function getNumTraits() { if ($this->numTraits === null) { $this->numTraits = 0; foreach ($this->traits as $trait) { foreach ($trait['methods'] as $method) { if ($method['executableLines'] > 0) { $this->numTraits++; continue 2; } } } } return $this->numTraits; } public function getNumTestedTraits() { return $this->numTestedTraits; } public function getNumMethods() { if ($this->numMethods === null) { $this->numMethods = 0; foreach ($this->classes as $class) { foreach ($class['methods'] as $method) { if ($method['executableLines'] > 0) { $this->numMethods++; } } } foreach ($this->traits as $trait) { foreach ($trait['methods'] as $method) { if ($method['executableLines'] > 0) { $this->numMethods++; } } } } return $this->numMethods; } public function getNumTestedMethods() { if ($this->numTestedMethods === null) { $this->numTestedMethods = 0; foreach ($this->classes as $class) { foreach ($class['methods'] as $method) { if ($method['executableLines'] > 0 && $method['coverage'] == 100) { $this->numTestedMethods++; } } } foreach ($this->traits as $trait) { foreach ($trait['methods'] as $method) { if ($method['executableLines'] > 0 && $method['coverage'] == 100) { $this->numTestedMethods++; } } } } return $this->numTestedMethods; } public function getNumFunctions() { return count($this->functions); } public function getNumTestedFunctions() { if ($this->numTestedFunctions === null) { $this->numTestedFunctions = 0; foreach ($this->functions as $function) { if ($function['executableLines'] > 0 && $function['coverage'] == 100) { $this->numTestedFunctions++; } } } return $this->numTestedFunctions; } protected function calculateStatistics() { $classStack = $functionStack = []; if ($this->cacheTokens) { $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath()); } else { $tokens = new \PHP_Token_Stream($this->getPath()); } $this->processClasses($tokens); $this->processTraits($tokens); $this->processFunctions($tokens); $this->linesOfCode = $tokens->getLinesOfCode(); unset($tokens); for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) { if (isset($this->startLines[$lineNumber])) { if (isset($this->startLines[$lineNumber]['className'])) { if (isset($currentClass)) { $classStack[] = &$currentClass; } $currentClass = &$this->startLines[$lineNumber]; } elseif (isset($this->startLines[$lineNumber]['traitName'])) { $currentTrait = &$this->startLines[$lineNumber]; } elseif (isset($this->startLines[$lineNumber]['methodName'])) { $currentMethod = &$this->startLines[$lineNumber]; } elseif (isset($this->startLines[$lineNumber]['functionName'])) { if (isset($currentFunction)) { $functionStack[] = &$currentFunction; } $currentFunction = &$this->startLines[$lineNumber]; } } if (isset($this->coverageData[$lineNumber])) { if (isset($currentClass)) { $currentClass['executableLines']++; } if (isset($currentTrait)) { $currentTrait['executableLines']++; } if (isset($currentMethod)) { $currentMethod['executableLines']++; } if (isset($currentFunction)) { $currentFunction['executableLines']++; } $this->numExecutableLines++; if (count($this->coverageData[$lineNumber]) > 0) { if (isset($currentClass)) { $currentClass['executedLines']++; } if (isset($currentTrait)) { $currentTrait['executedLines']++; } if (isset($currentMethod)) { $currentMethod['executedLines']++; } if (isset($currentFunction)) { $currentFunction['executedLines']++; } $this->numExecutedLines++; } } if (isset($this->endLines[$lineNumber])) { if (isset($this->endLines[$lineNumber]['className'])) { unset($currentClass); if ($classStack) { end($classStack); $key = key($classStack); $currentClass = &$classStack[$key]; unset($classStack[$key]); } } elseif (isset($this->endLines[$lineNumber]['traitName'])) { unset($currentTrait); } elseif (isset($this->endLines[$lineNumber]['methodName'])) { unset($currentMethod); } elseif (isset($this->endLines[$lineNumber]['functionName'])) { unset($currentFunction); if ($functionStack) { end($functionStack); $key = key($functionStack); $currentFunction = &$functionStack[$key]; unset($functionStack[$key]); } } } } foreach ($this->traits as &$trait) { foreach ($trait['methods'] as &$method) { if ($method['executableLines'] > 0) { $method['coverage'] = ($method['executedLines'] / $method['executableLines']) * 100; } else { $method['coverage'] = 100; } $method['crap'] = $this->crap( $method['ccn'], $method['coverage'] ); $trait['ccn'] += $method['ccn']; } if ($trait['executableLines'] > 0) { $trait['coverage'] = ($trait['executedLines'] / $trait['executableLines']) * 100; if ($trait['coverage'] == 100) { $this->numTestedClasses++; } } else { $trait['coverage'] = 100; } $trait['crap'] = $this->crap( $trait['ccn'], $trait['coverage'] ); } foreach ($this->classes as &$class) { foreach ($class['methods'] as &$method) { if ($method['executableLines'] > 0) { $method['coverage'] = ($method['executedLines'] / $method['executableLines']) * 100; } else { $method['coverage'] = 100; } $method['crap'] = $this->crap( $method['ccn'], $method['coverage'] ); $class['ccn'] += $method['ccn']; } if ($class['executableLines'] > 0) { $class['coverage'] = ($class['executedLines'] / $class['executableLines']) * 100; if ($class['coverage'] == 100) { $this->numTestedClasses++; } } else { $class['coverage'] = 100; } $class['crap'] = $this->crap( $class['ccn'], $class['coverage'] ); } } protected function processClasses(\PHP_Token_Stream $tokens) { $classes = $tokens->getClasses(); unset($tokens); $link = $this->getId() . '.html#'; foreach ($classes as $className => $class) { $this->classes[$className] = [ 'className' => $className, 'methods' => [], 'startLine' => $class['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'package' => $class['package'], 'link' => $link . $class['startLine'] ]; $this->startLines[$class['startLine']] = &$this->classes[$className]; $this->endLines[$class['endLine']] = &$this->classes[$className]; foreach ($class['methods'] as $methodName => $method) { $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName]; $this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName]; } } } protected function processTraits(\PHP_Token_Stream $tokens) { $traits = $tokens->getTraits(); unset($tokens); $link = $this->getId() . '.html#'; foreach ($traits as $traitName => $trait) { $this->traits[$traitName] = [ 'traitName' => $traitName, 'methods' => [], 'startLine' => $trait['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => 0, 'coverage' => 0, 'crap' => 0, 'package' => $trait['package'], 'link' => $link . $trait['startLine'] ]; $this->startLines[$trait['startLine']] = &$this->traits[$traitName]; $this->endLines[$trait['endLine']] = &$this->traits[$traitName]; foreach ($trait['methods'] as $methodName => $method) { $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); $this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName]; $this->endLines[$method['endLine']] = &$this->traits[$traitName]['methods'][$methodName]; } } } protected function processFunctions(\PHP_Token_Stream $tokens) { $functions = $tokens->getFunctions(); unset($tokens); $link = $this->getId() . '.html#'; foreach ($functions as $functionName => $function) { $this->functions[$functionName] = [ 'functionName' => $functionName, 'signature' => $function['signature'], 'startLine' => $function['startLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => $function['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $function['startLine'] ]; $this->startLines[$function['startLine']] = &$this->functions[$functionName]; $this->endLines[$function['endLine']] = &$this->functions[$functionName]; } } protected function crap($ccn, $coverage) { if ($coverage == 0) { return (string) (pow($ccn, 2) + $ccn); } if ($coverage >= 95) { return (string) $ccn; } return sprintf( '%01.2F', pow($ccn, 2) * pow(1 - $coverage/100, 3) + $ccn ); } private function newMethod($methodName, array $method, $link) { return [ 'methodName' => $methodName, 'visibility' => $method['visibility'], 'signature' => $method['signature'], 'startLine' => $method['startLine'], 'endLine' => $method['endLine'], 'executableLines' => 0, 'executedLines' => 0, 'ccn' => $method['ccn'], 'coverage' => 0, 'crap' => 0, 'link' => $link . $method['startLine'], ]; } } getFilesAsArray($directory, $suffix, $prefix); foreach ($files as $file) { $this->addFileToWhitelist($file); } } public function addFileToWhitelist($filename) { $this->whitelistedFiles[realpath($filename)] = true; } public function addFilesToWhitelist(array $files) { foreach ($files as $file) { $this->addFileToWhitelist($file); } } public function removeDirectoryFromWhitelist($directory, $suffix = '.php', $prefix = '') { $facade = new \File_Iterator_Facade; $files = $facade->getFilesAsArray($directory, $suffix, $prefix); foreach ($files as $file) { $this->removeFileFromWhitelist($file); } } public function removeFileFromWhitelist($filename) { $filename = realpath($filename); unset($this->whitelistedFiles[$filename]); } public function isFile($filename) { if ($filename == '-' || strpos($filename, 'vfs://') === 0 || strpos($filename, 'xdebug://debug-eval') !== false || strpos($filename, 'eval()\'d code') !== false || strpos($filename, 'runtime-created function') !== false || strpos($filename, 'runkit created function') !== false || strpos($filename, 'assert code') !== false || strpos($filename, 'regexp code') !== false) { return false; } return file_exists($filename); } public function isFiltered($filename) { if (!$this->isFile($filename)) { return true; } $filename = realpath($filename); return !isset($this->whitelistedFiles[$filename]); } public function getWhitelist() { return array_keys($this->whitelistedFiles); } public function hasWhitelist() { return !empty($this->whitelistedFiles); } public function getWhitelistedFiles() { return $this->whitelistedFiles; } public function setWhitelistedFiles($whitelistedFiles) { $this->whitelistedFiles = $whitelistedFiles; } } "\x1b[30;42m", 'yellow' => "\x1b[30;43m", 'red' => "\x1b[37;41m", 'header' => "\x1b[1;37;40m", 'reset' => "\x1b[0m", 'eol' => "\x1b[2K", ]; public function __construct($lowUpperBound = 50, $highLowerBound = 90, $showUncoveredFiles = false, $showOnlySummary = false) { $this->lowUpperBound = $lowUpperBound; $this->highLowerBound = $highLowerBound; $this->showUncoveredFiles = $showUncoveredFiles; $this->showOnlySummary = $showOnlySummary; } public function process(CodeCoverage $coverage, $showColors = false) { $output = PHP_EOL . PHP_EOL; $report = $coverage->getReport(); unset($coverage); $colors = [ 'header' => '', 'classes' => '', 'methods' => '', 'lines' => '', 'reset' => '', 'eol' => '' ]; if ($showColors) { $colors['classes'] = $this->getCoverageColor( $report->getNumTestedClassesAndTraits(), $report->getNumClassesAndTraits() ); $colors['methods'] = $this->getCoverageColor( $report->getNumTestedMethods(), $report->getNumMethods() ); $colors['lines'] = $this->getCoverageColor( $report->getNumExecutedLines(), $report->getNumExecutableLines() ); $colors['reset'] = $this->colors['reset']; $colors['header'] = $this->colors['header']; $colors['eol'] = $this->colors['eol']; } $classes = sprintf( ' Classes: %6s (%d/%d)', Util::percent( $report->getNumTestedClassesAndTraits(), $report->getNumClassesAndTraits(), true ), $report->getNumTestedClassesAndTraits(), $report->getNumClassesAndTraits() ); $methods = sprintf( ' Methods: %6s (%d/%d)', Util::percent( $report->getNumTestedMethods(), $report->getNumMethods(), true ), $report->getNumTestedMethods(), $report->getNumMethods() ); $lines = sprintf( ' Lines: %6s (%d/%d)', Util::percent( $report->getNumExecutedLines(), $report->getNumExecutableLines(), true ), $report->getNumExecutedLines(), $report->getNumExecutableLines() ); $padding = max(array_map('strlen', [$classes, $methods, $lines])); if ($this->showOnlySummary) { $title = 'Code Coverage Report Summary:'; $padding = max($padding, strlen($title)); $output .= $this->format($colors['header'], $padding, $title); } else { $date = date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']); $title = 'Code Coverage Report:'; $output .= $this->format($colors['header'], $padding, $title); $output .= $this->format($colors['header'], $padding, $date); $output .= $this->format($colors['header'], $padding, ''); $output .= $this->format($colors['header'], $padding, ' Summary:'); } $output .= $this->format($colors['classes'], $padding, $classes); $output .= $this->format($colors['methods'], $padding, $methods); $output .= $this->format($colors['lines'], $padding, $lines); if ($this->showOnlySummary) { return $output . PHP_EOL; } $classCoverage = []; foreach ($report as $item) { if (!$item instanceof File) { continue; } $classes = $item->getClassesAndTraits(); foreach ($classes as $className => $class) { $classStatements = 0; $coveredClassStatements = 0; $coveredMethods = 0; $classMethods = 0; foreach ($class['methods'] as $method) { if ($method['executableLines'] == 0) { continue; } $classMethods++; $classStatements += $method['executableLines']; $coveredClassStatements += $method['executedLines']; if ($method['coverage'] == 100) { $coveredMethods++; } } if (!empty($class['package']['namespace'])) { $namespace = '\\' . $class['package']['namespace'] . '::'; } elseif (!empty($class['package']['fullPackage'])) { $namespace = '@' . $class['package']['fullPackage'] . '::'; } else { $namespace = ''; } $classCoverage[$namespace . $className] = [ 'namespace' => $namespace, 'className ' => $className, 'methodsCovered' => $coveredMethods, 'methodCount' => $classMethods, 'statementsCovered' => $coveredClassStatements, 'statementCount' => $classStatements, ]; } } ksort($classCoverage); $methodColor = ''; $linesColor = ''; $resetColor = ''; foreach ($classCoverage as $fullQualifiedPath => $classInfo) { if ($classInfo['statementsCovered'] != 0 || $this->showUncoveredFiles) { if ($showColors) { $methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']); $linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']); $resetColor = $colors['reset']; } $output .= PHP_EOL . $fullQualifiedPath . PHP_EOL . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' ' . ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor ; } } return $output . PHP_EOL; } protected function getCoverageColor($numberOfCoveredElements, $totalNumberOfElements) { $coverage = Util::percent( $numberOfCoveredElements, $totalNumberOfElements ); if ($coverage >= $this->highLowerBound) { return $this->colors['green']; } elseif ($coverage > $this->lowUpperBound) { return $this->colors['yellow']; } return $this->colors['red']; } protected function printCoverageCounts($numberOfCoveredElements, $totalNumberOfElements, $precision) { $format = '%' . $precision . 's'; return Util::percent( $numberOfCoveredElements, $totalNumberOfElements, true, true ) . ' (' . sprintf($format, $numberOfCoveredElements) . '/' . sprintf($format, $totalNumberOfElements) . ')'; } private function format($color, $padding, $string) { $reset = $color ? $this->colors['reset'] : ''; return $color . str_pad($string, $padding) . $reset . PHP_EOL; } } filter(); $output = sprintf( 'setData(%s); $coverage->setTests(%s); $filter = $coverage->filter(); $filter->setWhitelistedFiles(%s); return $coverage;', var_export($coverage->getData(true), 1), var_export($coverage->getTests(), 1), var_export($filter->getWhitelistedFiles(), 1) ); if ($target !== null) { return file_put_contents($target, $output); } else { return $output; } } } container = $container; $dom = $container->ownerDocument; $this->linesNode = $dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'lines' ); $this->methodsNode = $dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'methods' ); $this->functionsNode = $dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'functions' ); $this->classesNode = $dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'classes' ); $this->traitsNode = $dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'traits' ); $container->appendChild($this->linesNode); $container->appendChild($this->methodsNode); $container->appendChild($this->functionsNode); $container->appendChild($this->classesNode); $container->appendChild($this->traitsNode); } public function getContainer() { return $this->container; } public function setNumLines($loc, $cloc, $ncloc, $executable, $executed) { $this->linesNode->setAttribute('total', $loc); $this->linesNode->setAttribute('comments', $cloc); $this->linesNode->setAttribute('code', $ncloc); $this->linesNode->setAttribute('executable', $executable); $this->linesNode->setAttribute('executed', $executed); $this->linesNode->setAttribute( 'percent', Util::percent($executed, $executable, true) ); } public function setNumClasses($count, $tested) { $this->classesNode->setAttribute('count', $count); $this->classesNode->setAttribute('tested', $tested); $this->classesNode->setAttribute( 'percent', Util::percent($tested, $count, true) ); } public function setNumTraits($count, $tested) { $this->traitsNode->setAttribute('count', $count); $this->traitsNode->setAttribute('tested', $tested); $this->traitsNode->setAttribute( 'percent', Util::percent($tested, $count, true) ); } public function setNumMethods($count, $tested) { $this->methodsNode->setAttribute('count', $count); $this->methodsNode->setAttribute('tested', $tested); $this->methodsNode->setAttribute( 'percent', Util::percent($tested, $count, true) ); } public function setNumFunctions($count, $tested) { $this->functionsNode->setAttribute('count', $count); $this->functionsNode->setAttribute('tested', $tested); $this->functionsNode->setAttribute( 'percent', Util::percent($tested, $count, true) ); } } setContextNode($context); } protected function setContextNode(\DOMElement $context) { $this->dom = $context->ownerDocument; $this->contextNode = $context; } public function getDom() { return $this->dom; } protected function getContextNode() { return $this->contextNode; } public function getTotals() { $totalsContainer = $this->getContextNode()->firstChild; if (!$totalsContainer) { $totalsContainer = $this->getContextNode()->appendChild( $this->dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'totals' ) ); } return new Totals($totalsContainer); } public function addDirectory($name) { $dirNode = $this->getDom()->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'directory' ); $dirNode->setAttribute('name', $name); $this->getContextNode()->appendChild($dirNode); return new Directory($dirNode); } public function addFile($name, $href) { $fileNode = $this->getDom()->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'file' ); $fileNode->setAttribute('name', $name); $fileNode->setAttribute('href', $href); $this->getContextNode()->appendChild($fileNode); return new File($fileNode); } } dom = new \DOMDocument; $this->dom->loadXML(''); $this->contextNode = $this->dom->getElementsByTagNameNS( 'http://schema.phpunit.de/coverage/1.0', 'file' )->item(0); $this->setName($name); } private function setName($name) { $this->contextNode->setAttribute('name', $name); } public function asDom() { return $this->dom; } public function getFunctionObject($name) { $node = $this->contextNode->appendChild( $this->dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'function' ) ); return new Method($node, $name); } public function getClassObject($name) { return $this->getUnitObject('class', $name); } public function getTraitObject($name) { return $this->getUnitObject('trait', $name); } private function getUnitObject($tagName, $name) { $node = $this->contextNode->appendChild( $this->dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', $tagName ) ); return new Unit($node, $name); } } target = $target; $this->initTargetDirectory($target); $report = $coverage->getReport(); $this->project = new Project( $coverage->getReport()->getName() ); $this->processTests($coverage->getTests()); $this->processDirectory($report, $this->project); $index = $this->project->asDom(); $index->formatOutput = true; $index->preserveWhiteSpace = false; $index->save($target . '/index.xml'); } private function initTargetDirectory($directory) { if (file_exists($directory)) { if (!is_dir($directory)) { throw new RuntimeException( "'$directory' exists but is not a directory." ); } if (!is_writable($directory)) { throw new RuntimeException( "'$directory' exists but is not writable." ); } } elseif (!@mkdir($directory, 0777, true)) { throw new RuntimeException( "'$directory' could not be created." ); } } private function processDirectory(DirectoryNode $directory, Node $context) { $dirObject = $context->addDirectory($directory->getName()); $this->setTotals($directory, $dirObject->getTotals()); foreach ($directory->getDirectories() as $node) { $this->processDirectory($node, $dirObject); } foreach ($directory->getFiles() as $node) { $this->processFile($node, $dirObject); } } private function processFile(FileNode $file, Directory $context) { $fileObject = $context->addFile( $file->getName(), $file->getId() . '.xml' ); $this->setTotals($file, $fileObject->getTotals()); $fileReport = new Report($file->getName()); $this->setTotals($file, $fileReport->getTotals()); foreach ($file->getClassesAndTraits() as $unit) { $this->processUnit($unit, $fileReport); } foreach ($file->getFunctions() as $function) { $this->processFunction($function, $fileReport); } foreach ($file->getCoverageData() as $line => $tests) { if (!is_array($tests) || count($tests) == 0) { continue; } $coverage = $fileReport->getLineCoverage($line); foreach ($tests as $test) { $coverage->addTest($test); } $coverage->finalize(); } $this->initTargetDirectory( $this->target . dirname($file->getId()) . '/' ); $fileDom = $fileReport->asDom(); $fileDom->formatOutput = true; $fileDom->preserveWhiteSpace = false; $fileDom->save($this->target . $file->getId() . '.xml'); } private function processUnit($unit, Report $report) { if (isset($unit['className'])) { $unitObject = $report->getClassObject($unit['className']); } else { $unitObject = $report->getTraitObject($unit['traitName']); } $unitObject->setLines( $unit['startLine'], $unit['executableLines'], $unit['executedLines'] ); $unitObject->setCrap($unit['crap']); $unitObject->setPackage( $unit['package']['fullPackage'], $unit['package']['package'], $unit['package']['subpackage'], $unit['package']['category'] ); $unitObject->setNamespace($unit['package']['namespace']); foreach ($unit['methods'] as $method) { $methodObject = $unitObject->addMethod($method['methodName']); $methodObject->setSignature($method['signature']); $methodObject->setLines($method['startLine'], $method['endLine']); $methodObject->setCrap($method['crap']); $methodObject->setTotals( $method['executableLines'], $method['executedLines'], $method['coverage'] ); } } private function processFunction($function, Report $report) { $functionObject = $report->getFunctionObject($function['functionName']); $functionObject->setSignature($function['signature']); $functionObject->setLines($function['startLine']); $functionObject->setCrap($function['crap']); $functionObject->setTotals($function['executableLines'], $function['executedLines'], $function['coverage']); } private function processTests(array $tests) { $testsObject = $this->project->getTests(); foreach ($tests as $test => $result) { if ($test == 'UNCOVERED_FILES_FROM_WHITELIST') { continue; } $testsObject->addTest($test, $result); } } private function setTotals(AbstractNode $node, Totals $totals) { $loc = $node->getLinesOfCode(); $totals->setNumLines( $loc['loc'], $loc['cloc'], $loc['ncloc'], $node->getNumExecutableLines(), $node->getNumExecutedLines() ); $totals->setNumClasses( $node->getNumClasses(), $node->getNumTestedClasses() ); $totals->setNumTraits( $node->getNumTraits(), $node->getNumTestedTraits() ); $totals->setNumMethods( $node->getNumMethods(), $node->getNumTestedMethods() ); $totals->setNumFunctions( $node->getNumFunctions(), $node->getNumTestedFunctions() ); } } contextNode = $context; $this->writer = new \XMLWriter; $this->writer->openMemory(); $this->writer->startElementNs(null, $context->nodeName, 'http://schema.phpunit.de/coverage/1.0'); $this->writer->writeAttribute('nr', $line); } public function addTest($test) { if ($this->finalized) { throw new RuntimeException('Coverage Report already finalized'); } $this->writer->startElement('covered'); $this->writer->writeAttribute('by', $test); $this->writer->endElement(); } public function finalize() { $this->writer->endElement(); $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); $fragment->appendXML($this->writer->outputMemory()); $this->contextNode->parentNode->replaceChild( $fragment, $this->contextNode ); $this->finalized = true; } } contextNode = $context; $this->setName($name); } private function setName($name) { $this->contextNode->setAttribute('name', $name); } public function setLines($start, $executable, $executed) { $this->contextNode->setAttribute('start', $start); $this->contextNode->setAttribute('executable', $executable); $this->contextNode->setAttribute('executed', $executed); } public function setCrap($crap) { $this->contextNode->setAttribute('crap', $crap); } public function setPackage($full, $package, $sub, $category) { $node = $this->contextNode->getElementsByTagNameNS( 'http://schema.phpunit.de/coverage/1.0', 'package' )->item(0); if (!$node) { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'package' ) ); } $node->setAttribute('full', $full); $node->setAttribute('name', $package); $node->setAttribute('sub', $sub); $node->setAttribute('category', $category); } public function setNamespace($namespace) { $node = $this->contextNode->getElementsByTagNameNS( 'http://schema.phpunit.de/coverage/1.0', 'namespace' )->item(0); if (!$node) { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'namespace' ) ); } $node->setAttribute('name', $namespace); } public function addMethod($name) { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'method' ) ); return new Method($node, $name); } } contextNode = $context; $this->setName($name); } private function setName($name) { $this->contextNode->setAttribute('name', $name); } public function setSignature($signature) { $this->contextNode->setAttribute('signature', $signature); } public function setLines($start, $end = null) { $this->contextNode->setAttribute('start', $start); if ($end !== null) { $this->contextNode->setAttribute('end', $end); } } public function setTotals($executable, $executed, $coverage) { $this->contextNode->setAttribute('executable', $executable); $this->contextNode->setAttribute('executed', $executed); $this->contextNode->setAttribute('coverage', $coverage); } public function setCrap($crap) { $this->contextNode->setAttribute('crap', $crap); } } dom = $context->ownerDocument; $this->contextNode = $context; } public function getTotals() { $totalsContainer = $this->contextNode->firstChild; if (!$totalsContainer) { $totalsContainer = $this->contextNode->appendChild( $this->dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'totals' ) ); } return new Totals($totalsContainer); } public function getLineCoverage($line) { $coverage = $this->contextNode->getElementsByTagNameNS( 'http://schema.phpunit.de/coverage/1.0', 'coverage' )->item(0); if (!$coverage) { $coverage = $this->contextNode->appendChild( $this->dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'coverage' ) ); } $lineNode = $coverage->appendChild( $this->dom->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'line' ) ); return new Coverage($lineNode, $line); } } 'PASSED', 1 => 'SKIPPED', 2 => 'INCOMPLETE', 3 => 'FAILURE', 4 => 'ERROR', 5 => 'RISKY', 6 => 'WARNING' ]; public function __construct(\DOMElement $context) { $this->contextNode = $context; } public function addTest($test, array $result) { $node = $this->contextNode->appendChild( $this->contextNode->ownerDocument->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'test' ) ); $node->setAttribute('name', $test); $node->setAttribute('size', $result['size']); $node->setAttribute('result', (int) $result['status']); $node->setAttribute('status', $this->codeMap[(int) $result['status']]); } } init(); $this->setProjectName($name); } private function init() { $dom = new \DOMDocument; $dom->loadXML(''); $this->setContextNode( $dom->getElementsByTagNameNS( 'http://schema.phpunit.de/coverage/1.0', 'project' )->item(0) ); } private function setProjectName($name) { $this->getContextNode()->setAttribute('name', $name); } public function getTests() { $testsNode = $this->getContextNode()->getElementsByTagNameNS( 'http://schema.phpunit.de/coverage/1.0', 'tests' )->item(0); if (!$testsNode) { $testsNode = $this->getContextNode()->appendChild( $this->getDom()->createElementNS( 'http://schema.phpunit.de/coverage/1.0', 'tests' ) ); } return new Tests($testsNode); } public function asDom() { return $this->getDom(); } } templatePath = $templatePath; $this->generator = $generator; $this->date = $date; $this->lowUpperBound = $lowUpperBound; $this->highLowerBound = $highLowerBound; $this->version = $version->getVersion(); } protected function renderItemTemplate(\Text_Template $template, array $data) { $numSeparator = ' / '; if (isset($data['numClasses']) && $data['numClasses'] > 0) { $classesLevel = $this->getColorLevel($data['testedClassesPercent']); $classesNumber = $data['numTestedClasses'] . $numSeparator . $data['numClasses']; $classesBar = $this->getCoverageBar( $data['testedClassesPercent'] ); } else { $classesLevel = ''; $classesNumber = '0' . $numSeparator . '0'; $classesBar = ''; $data['testedClassesPercentAsString'] = 'n/a'; } if ($data['numMethods'] > 0) { $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); $methodsNumber = $data['numTestedMethods'] . $numSeparator . $data['numMethods']; $methodsBar = $this->getCoverageBar( $data['testedMethodsPercent'] ); } else { $methodsLevel = ''; $methodsNumber = '0' . $numSeparator . '0'; $methodsBar = ''; $data['testedMethodsPercentAsString'] = 'n/a'; } if ($data['numExecutableLines'] > 0) { $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); $linesNumber = $data['numExecutedLines'] . $numSeparator . $data['numExecutableLines']; $linesBar = $this->getCoverageBar( $data['linesExecutedPercent'] ); } else { $linesLevel = ''; $linesNumber = '0' . $numSeparator . '0'; $linesBar = ''; $data['linesExecutedPercentAsString'] = 'n/a'; } $template->setVar( [ 'icon' => isset($data['icon']) ? $data['icon'] : '', 'crap' => isset($data['crap']) ? $data['crap'] : '', 'name' => $data['name'], 'lines_bar' => $linesBar, 'lines_executed_percent' => $data['linesExecutedPercentAsString'], 'lines_level' => $linesLevel, 'lines_number' => $linesNumber, 'methods_bar' => $methodsBar, 'methods_tested_percent' => $data['testedMethodsPercentAsString'], 'methods_level' => $methodsLevel, 'methods_number' => $methodsNumber, 'classes_bar' => $classesBar, 'classes_tested_percent' => isset($data['testedClassesPercentAsString']) ? $data['testedClassesPercentAsString'] : '', 'classes_level' => $classesLevel, 'classes_number' => $classesNumber ] ); return $template->render(); } protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node) { $template->setVar( [ 'id' => $node->getId(), 'full_path' => $node->getPath(), 'path_to_root' => $this->getPathToRoot($node), 'breadcrumbs' => $this->getBreadcrumbs($node), 'date' => $this->date, 'version' => $this->version, 'runtime' => $this->getRuntimeString(), 'generator' => $this->generator, 'low_upper_bound' => $this->lowUpperBound, 'high_lower_bound' => $this->highLowerBound ] ); } protected function getBreadcrumbs(AbstractNode $node) { $breadcrumbs = ''; $path = $node->getPathAsArray(); $pathToRoot = []; $max = count($path); if ($node instanceof FileNode) { $max--; } for ($i = 0; $i < $max; $i++) { $pathToRoot[] = str_repeat('../', $i); } foreach ($path as $step) { if ($step !== $node) { $breadcrumbs .= $this->getInactiveBreadcrumb( $step, array_pop($pathToRoot) ); } else { $breadcrumbs .= $this->getActiveBreadcrumb($step); } } return $breadcrumbs; } protected function getActiveBreadcrumb(AbstractNode $node) { $buffer = sprintf( '
  • %s
  • ' . "\n", $node->getName() ); if ($node instanceof DirectoryNode) { $buffer .= '
  • (Dashboard)
  • ' . "\n"; } return $buffer; } protected function getInactiveBreadcrumb(AbstractNode $node, $pathToRoot) { return sprintf( '
  • %s
  • ' . "\n", $pathToRoot, $node->getName() ); } protected function getPathToRoot(AbstractNode $node) { $id = $node->getId(); $depth = substr_count($id, '/'); if ($id != 'index' && $node instanceof DirectoryNode) { $depth++; } return str_repeat('../', $depth); } protected function getCoverageBar($percent) { $level = $this->getColorLevel($percent); $template = new \Text_Template( $this->templatePath . 'coverage_bar.html', '{{', '}}' ); $template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]); return $template->render(); } protected function getColorLevel($percent) { if ($percent <= $this->lowUpperBound) { return 'danger'; } elseif ($percent > $this->lowUpperBound && $percent < $this->highLowerBound) { return 'warning'; } else { return 'success'; } } private function getRuntimeString() { $runtime = new Runtime; $buffer = sprintf( '%s %s', $runtime->getVendorUrl(), $runtime->getName(), $runtime->getVersion() ); if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) { $buffer .= sprintf( ' with Xdebug %s', phpversion('xdebug') ); } return $buffer; } } generator = $generator; $this->highLowerBound = $highLowerBound; $this->lowUpperBound = $lowUpperBound; $this->templatePath = __DIR__ . '/Renderer/Template/'; } public function process(CodeCoverage $coverage, $target) { $target = $this->getDirectory($target); $report = $coverage->getReport(); unset($coverage); if (!isset($_SERVER['REQUEST_TIME'])) { $_SERVER['REQUEST_TIME'] = time(); } $date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']); $dashboard = new Dashboard( $this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound ); $directory = new Directory( $this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound ); $file = new File( $this->templatePath, $this->generator, $date, $this->lowUpperBound, $this->highLowerBound ); $directory->render($report, $target . 'index.html'); $dashboard->render($report, $target . 'dashboard.html'); foreach ($report as $node) { $id = $node->getId(); if ($node instanceof DirectoryNode) { if (!file_exists($target . $id)) { mkdir($target . $id, 0777, true); } $directory->render($node, $target . $id . '/index.html'); $dashboard->render($node, $target . $id . '/dashboard.html'); } else { $dir = dirname($target . $id); if (!file_exists($dir)) { mkdir($dir, 0777, true); } $file->render($node, $target . $id . '.html'); } } $this->copyFiles($target); } private function copyFiles($target) { $dir = $this->getDirectory($target . 'css'); copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css'); copy($this->templatePath . 'css/style.css', $dir . 'style.css'); $dir = $this->getDirectory($target . 'fonts'); copy($this->templatePath . 'fonts/glyphicons-halflings-regular.eot', $dir . 'glyphicons-halflings-regular.eot'); copy($this->templatePath . 'fonts/glyphicons-halflings-regular.svg', $dir . 'glyphicons-halflings-regular.svg'); copy($this->templatePath . 'fonts/glyphicons-halflings-regular.ttf', $dir . 'glyphicons-halflings-regular.ttf'); copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff', $dir . 'glyphicons-halflings-regular.woff'); copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff2', $dir . 'glyphicons-halflings-regular.woff2'); $dir = $this->getDirectory($target . 'js'); copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js'); copy($this->templatePath . 'js/holder.min.js', $dir . 'holder.min.js'); copy($this->templatePath . 'js/html5shiv.min.js', $dir . 'html5shiv.min.js'); copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); copy($this->templatePath . 'js/respond.min.js', $dir . 'respond.min.js'); } private function getDirectory($directory) { if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) { $directory .= DIRECTORY_SEPARATOR; } if (is_dir($directory)) { return $directory; } if (@mkdir($directory, 0777, true)) { return $directory; } throw new RuntimeException( sprintf( 'Directory "%s" does not exist.', $directory ) ); } } templatePath . 'directory.html', '{{', '}}'); $this->setCommonTemplateVariables($template, $node); $items = $this->renderItem($node, true); foreach ($node->getDirectories() as $item) { $items .= $this->renderItem($item); } foreach ($node->getFiles() as $item) { $items .= $this->renderItem($item); } $template->setVar( [ 'id' => $node->getId(), 'items' => $items ] ); $template->renderTo($file); } protected function renderItem(Node $node, $total = false) { $data = [ 'numClasses' => $node->getNumClassesAndTraits(), 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), 'numMethods' => $node->getNumMethods(), 'numTestedMethods' => $node->getNumTestedMethods(), 'linesExecutedPercent' => $node->getLineExecutedPercent(false), 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), 'numExecutedLines' => $node->getNumExecutedLines(), 'numExecutableLines' => $node->getNumExecutableLines(), 'testedMethodsPercent' => $node->getTestedMethodsPercent(false), 'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(), 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent() ]; if ($total) { $data['name'] = 'Total'; } else { if ($node instanceof DirectoryNode) { $data['name'] = sprintf( '%s', $node->getName(), $node->getName() ); $data['icon'] = ' '; } else { $data['name'] = sprintf( '%s', $node->getName(), $node->getName() ); $data['icon'] = ' '; } } return $this->renderItemTemplate( new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'), $data ); } } getClassesAndTraits(); $template = new \Text_Template( $this->templatePath . 'dashboard.html', '{{', '}}' ); $this->setCommonTemplateVariables($template, $node); $baseLink = $node->getId() . '/'; $complexity = $this->complexity($classes, $baseLink); $coverageDistribution = $this->coverageDistribution($classes); $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink); $projectRisks = $this->projectRisks($classes, $baseLink); $template->setVar( [ 'insufficient_coverage_classes' => $insufficientCoverage['class'], 'insufficient_coverage_methods' => $insufficientCoverage['method'], 'project_risks_classes' => $projectRisks['class'], 'project_risks_methods' => $projectRisks['method'], 'complexity_class' => $complexity['class'], 'complexity_method' => $complexity['method'], 'class_coverage_distribution' => $coverageDistribution['class'], 'method_coverage_distribution' => $coverageDistribution['method'] ] ); $template->renderTo($file); } protected function complexity(array $classes, $baseLink) { $result = ['class' => [], 'method' => []]; foreach ($classes as $className => $class) { foreach ($class['methods'] as $methodName => $method) { if ($className != '*') { $methodName = $className . '::' . $methodName; } $result['method'][] = [ $method['coverage'], $method['ccn'], sprintf( '%s', str_replace($baseLink, '', $method['link']), $methodName ) ]; } $result['class'][] = [ $class['coverage'], $class['ccn'], sprintf( '%s', str_replace($baseLink, '', $class['link']), $className ) ]; } return [ 'class' => json_encode($result['class']), 'method' => json_encode($result['method']) ]; } protected function coverageDistribution(array $classes) { $result = [ 'class' => [ '0%' => 0, '0-10%' => 0, '10-20%' => 0, '20-30%' => 0, '30-40%' => 0, '40-50%' => 0, '50-60%' => 0, '60-70%' => 0, '70-80%' => 0, '80-90%' => 0, '90-100%' => 0, '100%' => 0 ], 'method' => [ '0%' => 0, '0-10%' => 0, '10-20%' => 0, '20-30%' => 0, '30-40%' => 0, '40-50%' => 0, '50-60%' => 0, '60-70%' => 0, '70-80%' => 0, '80-90%' => 0, '90-100%' => 0, '100%' => 0 ] ]; foreach ($classes as $class) { foreach ($class['methods'] as $methodName => $method) { if ($method['coverage'] == 0) { $result['method']['0%']++; } elseif ($method['coverage'] == 100) { $result['method']['100%']++; } else { $key = floor($method['coverage'] / 10) * 10; $key = $key . '-' . ($key + 10) . '%'; $result['method'][$key]++; } } if ($class['coverage'] == 0) { $result['class']['0%']++; } elseif ($class['coverage'] == 100) { $result['class']['100%']++; } else { $key = floor($class['coverage'] / 10) * 10; $key = $key . '-' . ($key + 10) . '%'; $result['class'][$key]++; } } return [ 'class' => json_encode(array_values($result['class'])), 'method' => json_encode(array_values($result['method'])) ]; } protected function insufficientCoverage(array $classes, $baseLink) { $leastTestedClasses = []; $leastTestedMethods = []; $result = ['class' => '', 'method' => '']; foreach ($classes as $className => $class) { foreach ($class['methods'] as $methodName => $method) { if ($method['coverage'] < $this->highLowerBound) { if ($className != '*') { $key = $className . '::' . $methodName; } else { $key = $methodName; } $leastTestedMethods[$key] = $method['coverage']; } } if ($class['coverage'] < $this->highLowerBound) { $leastTestedClasses[$className] = $class['coverage']; } } asort($leastTestedClasses); asort($leastTestedMethods); foreach ($leastTestedClasses as $className => $coverage) { $result['class'] .= sprintf( ' %s%d%%' . "\n", str_replace($baseLink, '', $classes[$className]['link']), $className, $coverage ); } foreach ($leastTestedMethods as $methodName => $coverage) { list($class, $method) = explode('::', $methodName); $result['method'] .= sprintf( ' %s%d%%' . "\n", str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $coverage ); } return $result; } protected function projectRisks(array $classes, $baseLink) { $classRisks = []; $methodRisks = []; $result = ['class' => '', 'method' => '']; foreach ($classes as $className => $class) { foreach ($class['methods'] as $methodName => $method) { if ($method['coverage'] < $this->highLowerBound && $method['ccn'] > 1) { if ($className != '*') { $key = $className . '::' . $methodName; } else { $key = $methodName; } $methodRisks[$key] = $method['crap']; } } if ($class['coverage'] < $this->highLowerBound && $class['ccn'] > count($class['methods'])) { $classRisks[$className] = $class['crap']; } } arsort($classRisks); arsort($methodRisks); foreach ($classRisks as $className => $crap) { $result['class'] .= sprintf( ' %s%d' . "\n", str_replace($baseLink, '', $classes[$className]['link']), $className, $crap ); } foreach ($methodRisks as $methodName => $crap) { list($class, $method) = explode('::', $methodName); $result['method'] .= sprintf( ' %s%d' . "\n", str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), $methodName, $method, $crap ); } return $result; } protected function getActiveBreadcrumb(AbstractNode $node) { return sprintf( '
  • %s
  • ' . "\n" . '
  • (Dashboard)
  • ' . "\n", $node->getName() ); } } Code Coverage for {{full_path}}
    {{items}}
     
    Code Coverage
     
    Classes and Traits
    Functions and Methods
    Lines
    {{lines}}
    Dashboard for {{full_path}}

    Classes

    Coverage Distribution

    Complexity

    Insufficient Coverage

    {{insufficient_coverage_classes}}
    Class Coverage

    Project Risks

    {{project_risks_classes}}
    Class CRAP

    Methods

    Coverage Distribution

    Complexity

    Insufficient Coverage

    {{insufficient_coverage_methods}}
    Method Coverage

    Project Risks

    {{project_risks_methods}}
    Method CRAP
    {{name}} {{methods_bar}}
    {{methods_tested_percent}}
    {{methods_number}}
    {{crap}} {{lines_bar}}
    {{lines_executed_percent}}
    {{lines_number}}
    Code Coverage for {{full_path}}
    {{items}}
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits

    Legend

    Low: 0% to {{low_upper_bound}}% Medium: {{low_upper_bound}}% to {{high_lower_bound}}% High: {{high_lower_bound}}% to 100%

    Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}.

    {{name}} {{classes_bar}}
    {{classes_tested_percent}}
    {{classes_number}}
    {{methods_bar}}
    {{methods_tested_percent}}
    {{methods_number}}
    {{crap}} {{lines_bar}}
    {{lines_executed_percent}}
    {{lines_number}}
    /*! * Bootstrap v3.3.7 (http://getbootstrap.com) * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} /*# sourceMappingURL=bootstrap.min.css.map */body { padding-top: 10px; } .popover { max-width: none; } .glyphicon { margin-right:.25em; } .table-bordered>thead>tr>td { border-bottom-width: 1px; } .table tbody>tr>td, .table thead>tr>td { padding-top: 3px; padding-bottom: 3px; } .table-condensed tbody>tr>td { padding-top: 0; padding-bottom: 0; } .table .progress { margin-bottom: inherit; } .table-borderless th, .table-borderless td { border: 0 !important; } .table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { background-color: #dff0d8; } .table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { background-color: #c3e3b5; } .table tbody tr.covered-by-small-tests, li.covered-by-small-tests { background-color: #99cb84; } .table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { background-color: #f2dede; } .table tbody td.warning, li.warning, span.warning { background-color: #fcf8e3; } .table tbody td.info { background-color: #d9edf7; } td.big { width: 117px; } td.small { } td.codeLine { font-family: monospace; white-space: pre; } td span.comment { color: #888a85; } td span.default { color: #2e3436; } td span.html { color: #888a85; } td span.keyword { color: #2e3436; font-weight: bold; } pre span.string { color: #2e3436; } span.success, span.warning, span.danger { margin-right: 2px; padding-left: 10px; padding-right: 10px; text-align: center; } #classCoverageDistribution, #classComplexity { height: 200px; width: 475px; } #toplink { position: fixed; left: 5px; bottom: 5px; outline: 0; } svg text { font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; font-size: 11px; color: #666; fill: #666; } .scrollbox { height:245px; overflow-x:hidden; overflow-y:scroll; } .nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc} wOFF[\FFTMXm*GDEFt DOS/2E`gkcmaprڭcvt (gaspglyfM}]oheadQ46M/hheaQ$ DhmtxROt `locaS`'0omaxpU jnameU,postWH- Ѻ5webf[xTP=vuvsxc`d``b `b`d`d,`HJxc`fft! B3.a0b ?@u" @aF$% 1 x?hSAiSm߽44,qPK q XE](2 .ԩ] "ED i]DԡZJ\8wwV"FpUԯ.Χ(gK4O n;NR{g`'!PMUHEՠJʫ*Yq9c<U9!QIYׅ-KC+ դU)Q94JYp]Nq9.qyVV n)9[{vVכ־FWb++{>׍a|*gQ,K<'W@Ex̢D&Ud# & x Mx2c 5*.lN/h]GtT(xŽ |յ0>wm#Ye[%Y-YR'rYjD% ,@BKZjHڤ@b-R+nhK~룼$;h^fܹsn{ι ˴0 kb8Fd:%Lה"1AՔ AY>,ؔ#pZ4؟5made ?Ȝy=I:C D(nIxL .1!P'JDtHj@L4Ph' )b)vHX,f1c\'cGu>1 ~t?!xT_q?qBF#L%Dћ"?Yǯj??8>NSkemAYDb4 J);@jP$ 'qh8`;aX6CF*dYc"'?hLV㗌,>ce3eVh =C~xC\((qb@ 4xK&hׁ 4\2DZ6N1|-;j Yu@jѫxi䊧mK ٍDEwq3̷.cAw@4t.gkgr{~Wl~{lW2} 276a2\6oz@$HSH gbtX70Ktc1,7B oLƏ66[,%iZ ,l>TpKSGg\> #A#3Eyk6v;u3!ZI8Mk?8CWq{`C*h>H1_skh)ojOO' !~dXgB(0< kOYxeƧĭ5k =d ϧ> +tC-o Ǫ/_koܶs+fOztpu7-}d9 se \9.H4!0S\ ʱk2"?ip7\2zlްt=W\!KyOXimUnov 6: 2 LZkAA^qCޔ &PaFI0>&Q #FQl> A·q*OȦ_@27l,sf 6p7ܩ?M1vA2]$j";vlk~va0gjzRD:gc6yw%g(þ#'uB#=_@?>FVb0a!aL4tXv:Fh9j^xތz}Wn}7}jΚiHitKSaXEEbbBQ1ftxFȮ -"dqA\~F`6i䁕+ Ԣ^Ȳ}ש׆k&Ĺ<- \;g1>w00v^x 7l#Ot^5+xe.^]׼G8^ m(t1 sbfJ %<4H@e8C,5<(kc5YIA]|ךl6+=HVcbKՋB6i4 #_|&>NvQk#pW=u7HɰR$ [5싙 g %19}&@$&l=1RI}9#ςz??1z&ı_ac|PI[:u;l->k4GYm|Zw }HnR=-B ~m.ِ .Mz^,0%8EG**|sg|ozO֬0sz.WN^ yHk<v3t{8-|' ea~H94xA-@y bT4@0b#]DDljDSio:AgSP z:;-|yH"r {B{\5RLi6AAtM]taRKC!1CgC샂 +1EG!Xzٛnzv@x-#i^x*$)W=O\f[WX~V? `Lei::v4$?=Ra#c]8YFJb&'{%LCE Cf]^$/fߪM;À; 6CXV#X~ F< :vCcyBpLv1Fv#9 /8VF01_K?x>}#G7т\Wp!.@bwɡ+{o#ԍPQҮnī66 cZD(. u;nM}?vtxF{+` ="rPπlDV̶?Z@H䰅][35%O )\^ Z;>Ftf-IzӮ yu1uo<:oa:uqwykk ⋜}0?jvX+}VG$s ?26YI5c$Cfb!X*|F^$p7p55߶6[mjgl>* KO& 8ܝ:ǰokKm~oS-*4E}P/% k:e"1AJCAX8= LŢ>ܱav{|K.3 :\Bxwbeb>1ۿvH?f58 %6$ɲ'pL^HXbpIVqnA8Kg'i!UzSEI5N=hpV?(E Vr?޴7Vڋɿ.O;p 4NRZm.O> MuL'j5`;MtAQܶMyV<` $m)yڳXDa:݁q1JFq15-l\3~X-2pFDe/f!2i:=h{%{t^ *PBͽ]YD3jd *w|GLϽ}ˑk7Ç=06oz*zo1~Jw00SePw%#@BJB %+ ';%!& )Hq 7fqH.!Eǎf,9՚$9 H{~i Z)O|!"D.KQ a2 %2Wɂ\{*B{7,9.'ew U^W&$r9rcGBwll<ʷSQゅh! iѨvJ :Y?#_m4q[ },EA{VПP|Dg?9MId?{)/ /\[ Jҏ[f4G>QK^ m O -7w]„<U3jƏ,:Yq~0/mŵ@CCFq{,Θ쬷ΘQSo lsɿh?A2q`5Z&*X1L5:6ς+O]uej%?ۼ&aW?{2[}W?JbΙk-\b7sIkf&Λfx~nO-9V ~cW"ȗy)b\)2MrWf;MU7'[-c/.ؾuMl&.9) G!!W* 60Cф#qrqOKZOWq,8́/XpTȑg<>¤)[J8o` ;S\S%h~p|J˾F~K=E0NQX*8;D7Q1QC% *Eyy} UG?>I`>'6<+3IVgϮyOQ$WBvH v[Ϗ 2+ 'ø6N߆<ɕ 2S娚9X1\┣df>B~-t>W]pPrZ['+ƌl9]8qC!' @AAOuШ !?M\JMͭfǞ)ߕ=w?AN>¼}jQ<ǏpǠ^(}1+2q F4RiHďITr8^!gm>'ڸhE`s̊ol!(9~ o%#)~ƃj$@ՔLpGOa{߿fé)zؔY<~^cs潺ݴNRURTY%8Ks3qd]^QTb' zx)HFҩPmUZjQ&XƁo<0jYGz]$8c&hyݼwΞ{9^sf߹m[vӣ!(ZAsۧyB8RiԣBg6{UmtyW!bpǮd n/ŷʼ@v/%cxEn:4Y²,yZ-krcH&^ȩC'Ȯ'^T5r)((IJU&#݌! +YM.JEX^|Lw@ھZsgY洺\xԟxyLCyo?eV"_[Q/5Y|qI/\9diEBh$v wOL fpa ,?HgHf2RbL v >USo^1/,ēvcYGmŨ~Amz ?/40yj̸pk2H eERb/"M 75ul[drC&Y͐&I `!>p;J-b--.VM4>Fj/5σt5}>C*<'d?,cdGf2ҁ0w6Lh"fKζp;ǿ϶Pdc1EOi%Ř(DCWV2I)TiMFTz0U S7V mBW6;nYZUzSTg>(hF"޽T뽷R]L۶|Lx[s,'NU|E<4)Rp*vU#g*gjə*=~܃ASēA JHw3@NurbwȀʌx}[`7ZtPlh L.)NU}kq'vFQr׷{ˤS]ZL(@*Sf^+uPe_k#.8ɂ%ՠ,@TKх t`ߑXAD;b|pA7}q2 @Y`~iԬK0jY( R~^ҧ8>=F"˜A[DqvQCX|ZsO \/f.F;kPbdz7ԐeͶ-6bybaWjnh7YLF!4wssFCnh_0> MZ nC *#5/OUN\(3o@[7`Mg8xge;f\y|f֤ޑ]i5q5q&>'353kYꭑ=W7+΋yxIeOYǏs(p6[B/t爁*̠-n: <Ц) +ް~q_}oxt>LV FG@d9[2?2ȳ8笞={fgcsCmre#E>45qo:JX^ioP,xf:/yn9VѥS7=u-\%KϦUv,ⳀZ=vkN*+_.ڊ֞iڃ=w @lmr>Oo,VԲɝz &:'45!9pI 0@I[PU""sInvR>A9t$3/|k8yiE c8E!Q\ۂ} %Af4s*A8A΀>D=5uwjnG z?2Q/I=fH4n]澀YmG"2PEHfvZn<šPiA_q/PDտ $$~%NyhrOdM\-m(@\#ƼNJO>a+ uJ*(%¢FPJW,$))} B\_wV] 0TOCÊQ}5{Ho*;;葞rǨMc54S : M7(kY:z`gp Jstˉv'eG^~iD16dA @'N ֭N.?f…1bzJD V o@7R@6<%IF0mj= [}Nۊ57pyv4@<mЭ9Tp?R70қQG[jzib~/)wC? רa-/Cn.ĕH j63pKrhXIƎj o19 f\~:-ѓK47BY̆y%DC~em@]%rs4T G-Ug>HOpVB]{9&^6|m _PLLI7ǒi "'T }? 4|[Fǭtu/_y;Z?HK0Wzc#)~.rĥ+B&JG0[.ΡrOk;VCoX K۝S߳rt:zX\xmJhxNh5 K`;ydp.Ec4XD<-llip.^p: u/.Y[rl_4kz$~Dq]7/T_<菵4K$Ɩ &w S7|K^7MsMGhw㢴0]?fja5aiЦ6C2no• f=)d^v qNcԎl=u]?;f-E~nv}5%Oջd덿=Z%v  nKu ̓*J#1hu1Hr o}SZu=w;nϗU `FȶEn?߫k&l9YdgA8NSGD09MAK{ހK3݊[_]%W4zۈu9\~n3~zir X3k`Psn=m]ԃJksT9deYN`}/]U#b;Rt,lh*#JB+ (iGx\}~IֳFv@Tu֭J @-LwzYgw`wx-(d٢]F3_XcYmQԃWb-F K5d-0b球—֨T+_Zxcj*`}|x~LF*S*oMتAT1p71?R t>R'"Ey)oP7%$rv QeE+nzlVlFrkt''?R'ZCEIKy ga0^}pE;Kq{T/?i"%1ޒb-Ծqƛ˵+ 8]rIڣV{dȪ͜\AQvOS]0.NX9svb?OE~FPU}o[YKrA̓U%7Dw q b/h AhPbQؓJB8I ?I%=XtO;(PhLd S 'hݱ>|TV?,O"\`7.2>D fmg;-C'u, zA`-ټ$x vck2[xp\cbl΀ihsivaÛM,gĨlMz7JvˑVRWϋNo4(-XB^Cl&Vnnn D4[k6N&}f3YQw@$U$(Ǫo:-ZG#&/} ?N}ƥ7A!MhW>?iXprA١b?uϱι-h6;SB#/@ѿJ !%Q)Dq:{JI^ޑˡPY7UG(h?HmъvREH=N`P)QG9FMSMG@2E$Q $s~TkN"9Ն8cF^"?+G٠ ^*gUlFVxUpoC.XCƵ׵͉qK[k[K(l; ӡn%^Rj,$) 1n.G:Cf(,;ĴR—F_~^;իD;6|/jGGSSGGӎļDzbR/X?Up14u$`[ߜH477I~~Irߙs#6+heW6@wK̸h6, 1C"=meA =@z sls];kklr^"s青>&Մ-[{JiҴ9[ݵȩ-]dޢc An۹g}ꒇ6hTɖ?3s^kLcY 1Zn[bݴE߆դwk3f> fMDՠaD ~}&@5u gnOȢ<'` &bӬ-6;X"d*awYvtLXָkUߩa=HR_@+j2T*£%/͸oƤy 19/7 ~7_o+$DүsIH:r yiF:v(dO":omdM8 ;Z9uʩHCg\K/*ԙg*-I_ERqR'[f?GUAovb A$e]/Կo?|ԐQm4G7G833+ 74z*)$݋JpDNj5pqeDf/>%gW{U:g,nlU\t'%E}͝uCꘒܻߺp}U+^b'o(5gVBIOEm>5yzg}AP-P/ޫ6)x5/t;1p1L9Aܳ|)X]mkFEH/4}:,oLMo6]YM50u[yҫfVh?E-A_i﫝j . 6|5`#Z-svfqӟs͚>w7C{ A]Bz,iH'dv?`E x,mz`F[2avhp%(̒ʂ5Ԧ;Gюh\y";|"ٝʖrxzsPHCTvP$ly}iyhvMCr)#x-.(t%fu€(ۅeUUo pqeˡ啗syi Xk`>X@2P. 2͌>n|,/4} ?A&Jr+ɐCV]{Z0- A= F$+%UZyޗٲR B)wT8(aRΣ*-sr5v !^tZ:/K,'F  9=G<Cu"$-FS2(F 0Q+Xw,]=bh[qBQI ;)"Ō926r?}lV =b[j4AzKkQ?T[%$KQ-l_@l/ &;차Dr?P_dE1~z^I~breufP/պ# E+S\G-R4 SSV俑; *`G*5'dL ~ 5Fhb` ꁜ4[b$~GNAX$~ }[W}_z×6m&~O%j/r&|_Sy<-*Lϛ,JQzͤ𫷣|V|GVW~z  HE YnH4r7P?99ߡ|O-5 %4 dzO/4L_PsT>LQD( J8F+)jCb Mu2Xc8$t}&@Qr-֤U_o6q7P1ˤ+rc6I \ (*v24Uc(A ̣93]z;0'=*,e56Va,qh*P@wȬG/Oj|FIm #Pz;Jwʎ}< z Tt~`ȱGP%;? 5((u# vՊI#9,?Gb4K]Qgԟ]E[ phʯG+`Ęp?@>!}" ҽr=CD5 62ZY? iA T(E UJu;"}պ#LcӗVWO&CIԙu8*烞QaQ^*z(L|Jӏ^fp104~CUx*rV*N9π׳Pūsp_L3Z"}&rO|l~kC/Wj><SxMbSg(]J(Z#x\$OC68-f:{Sҳ蚨o4:)Wb"uiuh~d%BAM sWH.gv%4v+=¿ SGϋjWHWu>[B{[uɶs;laziW߭\zC|\fte&ߕ+Bk/t  CM /@S>Tm G`v`?G(,zb" eAAi7QR<"iX:I܋(aV;4R]}^1vԵ7=p|[Jοeµ{)e#ief0KJq"*F#(GjJFhX#шݍk5ERP΋ ^pCeoe:{6۬5͝sƙ8X K6V[=}V+hͧJlZZ5W;TeV-@HID<͙[)֐l^bXeNN"K]@b?.HH gzXaْA}MOeXHNrڟW;htgttOyu3=*פؿCFGsh9JͽZ-k]L-~hii.49Qr5I,Vݓ^jf_},Q6?5NV ޞˍYٜN%ezqƨ>Z Nt1 a %= yhޙ HJZ? hvrk@mY`^insF\*|Lz!/?)(0 MS4(ȗh{-'ho7cCҞ?6'|ubգ@!bÙf{tz1UA?=@ t%䕉iu[ NiD GT@:p<(cXUm2ϱ7zOM^FϴYUfwGs#t:/~Os]Fݑ((^?L$Sʽ WzT>m'_d:5Lh;H7WgzgZZb3{2d5Jj9c+\vqzDbbƶg "l@צpQBbS Q>+d p%}L!cdwHopx(Tpxp#:dvQ qdAQFdLKmPR pU?l zg-jPbGaR&^q>u8p&Ӯф `MGSܵaoWܛZaâٟݰV5Rs2NX qGB OKg BW)Sg\ӡl]z<߲o-_- AKMqӭ!æSigy۰]K;ST'kPqee7cZT{~*7b\H?jٵl3P оwT2jY;)l DueytOTjöUHXgɬ,WϢ^u![]vF| QGh`(# R'5XDQqM6gc'bu:'H( ?yյ6~.e[n *UyZst9R!GMM$xz$]{L<}4JZ~MVՕhy >@u +]2FqO8jѥWCQqrw.䄫ޥ\_y\On)IKGRHŁqI. d+u@ϴ kŤ}9Tv6*xge7?ì}S-AU OMlJ pժݧYwhi6\fAZc,rjFTMj8kO51TqW_n`7%KWsd0:`OXs$4?:SI1W-Pr}² 9.&P^f 8(WI``@5a}ziV pPԽ+:d\j"=aj)W$q{͜p)V|7hj$L֡9\ځn[ k{lG.m m~TEbȭm` wnyP&:PLJY_pNWzVS׃]7Ed%i癬| EWM7r HB6`UGZ 9N2l2ɅHY(ŗiwݓ[`cZR;Yz=TrvH9c. ֲG6*p΅'[:/ҪXCYхMt-']n,{@ cObIN.xN F9뛝NK[Xr=Wm ݏƦY+?sJgXuP%ȗV^[ W;W xvi/XS3ȼ2ԩZ f2/y?8M@Q*˄CXk?MzTy?ZYu׳)]͕1-a7j~ .d  'VztXK2k̹d?zzK.>,BZ`q'kHqy5j>a\C#H;#p7l4} IR7ފ0$=V#_.vs{g>h!Ab/p7=zmi%͟3)^Oj<_UNY63dsIr8EjU* 33|v ;OB@,,\cwd}6k.ukF9'26D]exGJK.׽}S$@ t";2ɩ*41_x7QbjX9Q;#{9eI -奐br B<9dpzIVQ:l+si #=T+R(MDC$ a̱ ONgj19gqXk}FdcG,&..^ɷwwc>E_]3U|t{Jf窂u_.\*W=}lNo+^Ṿ vP>~sTjWz~_ogS}-DTd -TAaYf3,PATcm ռ4g}mE$BwŪ8>9JW⁩O/9PJCXA{,@c,tEJTj98Q& HPl~K%ƞ1ѻ -eD zxNXuz.9}Mc&:Z5ә8% յսmomCB:l8~ܦEjTYHYvnV^IN]]ŽCXkg#s cSB$Ý=$k}cG&/z}_v6<7IVGGg*l\RXST)šE%Yu~Q~>XЅ`9Wk*@_ՊpM]0*%a3X팁KM|{FԔ 췾d7[nlͬD@m8e cż#gHdd@~.jllɛeRcxE(( Km¼GXA7S@[l.%գnMDs]n_Q 5i?zGTG3T@e i,r O2<l+/,%m ۚXn|E]lí[m<|#z+5 7&\5S-{AE^tK M^rq]FmC%2vJ)W-}OM"`9l+=%"T'8zH3QҐѩYP~VزNi 7ۛ ?w1xc`d```d?oAePBYt?;"@.Hc xc`d`` &]aA_x}SJAS<` b)6 >@D"X\o!ι{,_oggg #JVYp>uC4&*<=$g9W@.0q- ;:pt"HUe5 Vg([Ax9!޴EMߗ4N&ӞwjtԞeσLp>w>Gpfz`|^aż>)o oMg+RmRq,RJ1XTN7t{IE\F8U mb:fN&j9Yxc``ЂM /^0Kؘژ0=avcca>bĒIJk ."/ I888qqpnǥ5w)^-8 ||||[5? JPKLpPa) "Z"WDmDWc3K O~/cLuNN+9K8;99/p>"k676-nܷ0h8)iʋK+s9@.xڭNAwh /"TD#J$rqr|!'O3XFާ0wY 1fg;73;3xE0C q=qX4GA$x ZB8ڃ Dw!IaSX w.0?oN؍gڍ@\A`sb k`sݡ},0Ya DȵȵMyFMvYdS20~>/qJG i<#c0C~G9ee Kvв[ڷ{&V(Ө1j1MZqr7,gKܥX0QY{ MYжz=a:[jEݢ BZZ=ns`+ȍxmUSgFB]9I$uw-J;mPwwwwwwwwlޕ]<3)e׿7R^ VV_@$zГ^З~g`0m[czf`(3233 23s2s32  eD*954XXeXX14i++ kk [[ ۲3Qfvd ;1qgg& nLdOboa_c@`PpHhXxNDNdNarsgrgsrsrs rsWrWs rs7r7s rswrwsrOO // oo __ ?? f,˺eݳYϬW;MelP68s䘉GE{RαM 7nܺp;ڛZ[ݛƵ? ѵֵykx~yj?\3V+wE5=QMjzTӣ(vN؉k/셽d/Kd/Kdbbbbbbbbbbjjjjjjjjjj/r{^n/+v ;NaS)ԼffffffffnnnnnnnnnnaaaaaaaChQN-ܩ?C?C?C?C?݇C}>t݇C}C?C?C?C?vNjHMp[qn???????>>=<<<<<:::::::U>::::::::=;;;;;;;;;;;;}VhSoTPpFFTMm*GDEFD OS/2gk8`cmapڭrcvt ( gaspglyf}]oheadM/6hhea D$hmtx `tlocao0maxpj name,post5 webfTPT=vuvs Z 2UKWN@ { , h, h@( + / _ "#%&&' ' )9IY`iy )9FIYiy !'9IY` * / _ "#%&&' ' 0@P`bp 0@HP`p !#0@P`fbߵiY!     |vpjdc]WQKED 5 *+  / / _ _  ""##%%&&&&' ' '' !& )009:@IDPYN``XbiYpyaku } )09@FHIPY`ipy !!#'09@IPY `` ((h ./<2<2/<2<23!%3#(@ (ddLL[27>32+&/#"&/.=/&6?#"&'&546?>;'.?654676X& j  j )"& j  j )L j )"& j  j )"& j LL#32!2#!+"&5!"&=463!46^^L^^p@LE32!2+!2++"&=!"&?>;5!"&?>;&'&6;22?69  x } x }  x } x v L   d    d  l d;2#4.#"!!!!32>53#"'.'#7367#73>76p<#4@9+820{dd 09B49@4#bkv$B dpd>uhi-K0! .O2d22dJtB+"0J+ku0wd/5dW%{L>G!2+!2++"&=!"&?>;5!"&?>;4632654&#^CjB0  0BjC x  x u x u@--@$?2O*$ $*P2@%d    d   BVT@L!2#!"&=46 %A+32!546;5467.=#"&=!54&'.467>=2cQQc22cQQc2A7 7AA7 7Ad[##[[##[dd76!' Pԇ $ op zy#%**%$ pdL #7!2"'&6&546 6'&4#!"&7622?62~   \l lL 7  &   l 2'7' & c_"fn &\`tfjpO32!546;! 22&&L%6.676.67646p'0SFO$WOHBXAO$WOHB"7Q)mr *`)nq&* )2"'#'".4>"2>4&ȶNN;)wdNNrVVVVNdy%:MNȶ[VVVdXD>.54>0{xuX6Cy>>xC8ZvxyDH-Sv@9yUUy9@vS-H^{62!2'%&7%&63 a o  ^{"62!2'%&7%&63#7'7#'JJN a o  d⋌&2##!"&=467%>="&=46X|>& f   f &>|.hK  ]  ]  Kh.| L#'+/37GKOSW!2#!"&54635)"3!2654&33535!3535!35!"3!2654&35!3535!35~  Ud  & sdd dd d  & d dd dL   ddd  ^ ddddddddddd  ^ dddddddddLL/?!2#!"&546)2#!"&546!2#!"&546)2#!"&5462pmppmpLpppp LL/?O_o32+"&=46!32+"&=46!32+"&=4632+"&=46!32+"&=46!32+"&=4632+"&=46!32+"&=46!32+"&=462LppL/?O_32+"&=46)2#!"&=4632+"&=46)2#!"&=4632+"&=46)2#!"&=462DDDLpp&,  62"'&4?622;;nnBB# "' "/&47 &4?62 62    ;    %I2"'#".4>"2>4&3232++"&=#"&=46;546ijMN,mwbMMoXXXX K  K K  KMbyl+MMijMXXX# K K  K K %52"'#".4>"2>4&!2#!"&=46ijMN,mwbMMoXXXXX^  Mbyl+MMijMXXX  -32+"&5465".5472>54&&dd[֛[ҧg|rr|p>ٸu֛[[u'>7xtrrtxd/?32+"&54632+"&54632+"&54632+"&=46  ޖ  ޖ  ޖ    ~ p     >     GO27'#"/&/&'7'&/&54?6?'6776?6"264X!)&1-=+PP08,2&+!)&1-<,P  P/:-1&+x~~~P09,1&+"(&1,=,QQ09-0&* !(&0-=,P~~~d!%)-1!2!2!5463!546!5#!"&53333333,);  ;),,;)D);dddddddd;)d KK d);ddd);;) dDDDD 62++"&5!+"&5#"&l`    j`  w  ? d3!#!"&5463#"&=X;),Rp);vLp02".4>"2>4&3232+"&546֛[[֛[[rrrr|2   [֛[[֛;rrr   2  ^  )#!3333))p,p,d/3232"'&6;4632#!"&546;2!546& & T2   2 >p  ^  12".4>"2>4&3232"'&6;46֛[[֛[[rrrr|  & [֛[[֛;rrr   12".4>"2>4&%++"&5#"&762֛[[֛[[rrrr   &[֛[[֛;rrr  9!2#!"&'&547>!";2;26?>;26'.    W & & W tW    >     '2".4>"2>4&&546֛[[֛[[rrrr[֛[[֛;rrr] $  (76#!"&?&#"2>53".4>32  mtrrr[֛[[u$  Lrrrtu֛[[֛[576#!"&?&#"#4>323#"'&5463!232>  ntr[u[u  h ntr$  Krtu֛[u֛[v h  Lr d/?O_o!2#!"&546!"3!2654&32+"&=463!2#!"&=4632+"&=463!2#!"&=4632+"&=463!2#!"&=4632+"&=463!2#!"&=46}    R 2  2   > 2  2   > 2  2   > 2  2   >   ~   R d 2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2 L#54&#!"#"3!2654&#!546;2uSRvd);;));;) SuvR;));;)X);dLL 732#462#".'.#"#"'&5>763276}2 d!C@1?*'),GUKx;(.9)-EgPL 3 0[;P$ 97W W!1A2+"&54. +"&54>32+"&546!32+"&546ޣc 2  2 c*  `  ct  ,rr  ,tޣ 4  4  G9%6'%&+"&546;2762"/"/&4?'&4?62A   Xx"xx"xx"ww".   ^ x"xx"ww"xx"r/%6'%&+"&546;2%3"/.7654'&6?6A    `Z  HN.   ^ d  g~jb1K3#"/.7654&'&6?6%6'%&+"&546;2%3"/.7654'&6?6 D@  *o;7 *    `Z  HN iT "ZG !   ^ d  g~j  !%-;?CGKO3#!#!#3!##5!!!!#53#533!3533##5#535#5!!#53#53#53!5!ddpddX,,ddddD dddd,D,ddddd dd,dddX d,,d,,ddd dddddd,dddddd  #7#3#3#3#3#3!5!#53#53#53ddddddd,,dddd,Pdd[[[[[   "'463&"260V C;S;;S;V0 ;;T;;  ! "'463!"/ &"260V 08D;S;;S;V0 V08;;T;;d&!2&54&#!"3!2#!"&54?6,9K@  D@   K|@  @  J  L !2 46 >>CEU!"3!26?6'.#"#!"&/.+";26=463!2;2654&!"3!26/.6D N9  >SV N N      & X & l l- p  v       dL!)13232#!"&546;>35"264$2"&48]4$);;));;) '3]dϾV<?!(% _5,Ry:" *28 T2*BBW-ޑY". BB % Zd'2;#!5>54.'52%32654.+32654&+50;*7Xml0 ); !9uc>--Ni*S>vPR}^3:R.CuN7Y3(;  G)IsC3[:+ 1aJ);4ePZo!56764.'&'5mSB ,J   95(1(aaR@ 9%/#4.+!52>5#"#!#3'3#72 &2p"& 2KK}}KK} dd R ,১ !%/#4.+!52>5#"#!5!'7!5L2 &2p"& 2C১  vdd  ,}KK}}KKL/?!2#!"&=46!2#!"&=46!2#!"&=46!2#!"&=462X LLddddddddL/?!2#!"&=46!2#!"&=46!2#!"&=46!2#!"&=46DLDLLddddddddL/?5463!2#!"&5463!2#!"&5463!2#!"&5463!2#!"&Xp LddddddddL/?!2#!"&=46!2#!"&=46!2#!"&=46!2#!"&=462LLLLLddddddddL/?O_o32+"&=46)2#!"&=4632+"&=46)2#!"&=4632+"&=46)2#!"&=4632+"&=46)2#!"&=462ddA ddA ddA ddA LddddddddddddddddL#*:J!#;2+"&=46!2#!"&=465#535!2#!"&=46!2#!"&=46dddd ,XLdddd}KdKddddL#*:J32+"&=46#3!2#!"&=463#'7!2#!"&=46!2#!"&=462ddgdd /ȧ,XLddLdddK}}dddd!2#!"&546 K,,,,,,v,,,D,,L!2#!"&5467'2"&4,XJ*J%pNNpNL d>tNoOOo62.'&54>"264usFE66 !^Xm)!fhHuXyHÂ2".4>"֛[[֛[[Ktrr[֛[[֛oVrru5.54>6?6&'.'&76#&*IOWN>%3Vp}?T|J$?LWPI)(!1 )  HuwsuEG^F&:cYEvsxv!K:%A'# " A)Y l */7>%!2!"3!26=7#!"&546 7l l27);;));Ȼp87cs* s ;) );;)2cL6!#"3!2657#!"&546&'5&>75>^i4);;));ȹpS 9dTX .9I@F* L6;) );;)g  0!;bA4 L5!2!"3!26=7#!"&546 62"/&4?622^^  Ȫ   ȯ  ȭ   ȭ   L326'+"&546d0dLJJL#3266''+"&5462d00dLJJJJ3''&47660J*J36 &546.2   d32+"&546!32+"&546  dL#!"&5463!2L  346&5&5460d * ;O#72#"&5&5&5464646dd12N: 9  > =,L32+"&5&54646Rdd0L;;dH  #!"&762!2#!"&=46  *9HdduJ  u`((&;(J ' 7(a#aa32".4>#"#";;26=326=4&+54&֛[[֛[[}dd[֛[[֛dd2".4>!"3!26=4&֛[[֛[[E [֛[[֛~dd32".4>"'&"2?2?64/764/֛[[֛[[ xx  xx  xx  xx [֛[[֛ xx  xx  xx  xx  $2".4>'&"2764/&"֛[[֛[[Tw[֛[[֛1Uw;K2".4>";7>32";2>54.#";26=4&֛[[֛[[?2".4>#";26=4&#";#"3!26=4&+4&֛[[֛[[    KK  ^  K[֛[[֛V   2  2  2  /_3232++"&=.'#"&=46;>7546+"&=32+546;2>7#"&=46;. g  g g  g Df  fD Df  f g g  g g ͨ  fD Df  fD Df?2".4>"2>4&"/"/&4?'&4?62762֛[[֛[[rrrr@||@||@||@||[֛[[֛;rrrZ@||@||@||@||02".4>"2>4&"/&4?62762֛[[֛[[rrrrjjO[֛[[֛;rrr}jjO!2".4>"&32>54֛[[֛[[KtrAKihstr[֛[[֛;rtxiKA>rtsS6!2#!'&4' &F   &S &5!"&=463!46 &U & U ## ] #!+"&5!"&762   && ]32!2"'&63!46&# U & U # &] &5>746 ^$,[~UU & U #$DuMiqF +!2/"/&4?'&6!"&546762R,^j^!^j^^j^P,^j^IIgg+#!"&546762!2/"/&4?'&6j^^ ,^j^`j^,^^j^/2".4>#";2676&#";26=4&֛[[֛[[:#6#:1  [֛[[֛.   IUaho276?67632;2+"!#!54&+"&=46;2654?67>;26/.'&;26!"&5)#!  &0  =  2 pp 2  =   353  X  v  v !{,  2  ,ԯ  2 0y    r w  +I6.'&&&547>7>'.>7>&67>7>7>-BlabD8=3*U  :1'Ra\{%&=>8\tYR-!q[Fak[)ȕX1 "@&J<7_?3J5%#/D &/q!!6ROg58<'([@1%@_U2]rO.>7'&767>.'&'.'&>77>.'&>' '8GB    `H  >JS>H7 '+" NA 5M[`/Pg!;('2"&"IbYCe\D9$ 886#1%)*J7gG:    8G\au9hoK$]54<&"&5476&2>76&'&6?6&'&'.{nO9:On{{nO:9On{FZ  2Z__Z2  Z# %8-#,- "F-I\b\I*I\b\I--I\b\I*I\b\I9>||;7Es1$F^D10E^E$1u$/D0 "%,I';L!#7.54>327377>76&'&%7.5476&6?'&'.P[vY,9On{R=A &/l'PjR.Mv&  6QFZ  *HLh5)k|# %8- ,- "xatzbI\b\I-yRU4Zrnc1?1FrEs11) ]@ @] )1ES>L'+/37;?CGKOSW[_c3232!546;546;2!546#!"&5353353353353353533533533533535335335335335Rd22ddddddddddd|ddddddddd|ddddddddd2222pddddddddddddddddddddddddddddddw%7&=#!"&=46;3546'#"&=463!&=#'73546oXz#z*dXzdM*zL!2#!#"&546d);;)d);;L;));,;)X);dL ?32!546!32!546".5!2>&54=(LffL(, '6B6'p)IjV\>((>\VjI), +'%! !%'*L 'L'a'M 7 Maa'aQd_)!232"/&6;!%+!!"&5#"&?62**p&032!2#!!2+"&=!"&=#"&/#"&468^&d,!02**6%%+*2222 *L !53463!2!!P;),);DPdd);;)L 3463!2!!;),*:,P, pX);;)dDEk+32"/&6;#"&?62{**YDk&=!/&4?6!546X`)  )   !.#!"!"3!26=4&53353$`$-);;));;ddd-(d;)d);;)d);dddddL #12"&54%##"+"&'=454>;%".=4>7i**d]&/T7 " LRQ  )2( Jf,53232#"./.46;7>7'&6327"&)^Sz?vdjO9t\U>/ v?zS$2451 7F8%M)(  ()GM~ 1==7'''7'7'7'77 N괴N--N괴N-N--N괴N--N괴d!-=32!2+"&/#"&54?>335!7532+"&5462(<H(<,F=-7` 1dd>2vddQ,}Q,d-!2$'$(ddw} L 0<32#!+"&/&546;632+"&546!#35'!5X,<(<(21 `7-=|dd_dd22L!-d,Qv,Q($'$dd dԯ}wdO7G%6!2+#!"&5467!>;26&#!*.'&?'32+"&546dkn  T.TlnTj:d%8   VOddip &yLN(  % H YS(22S dO6F#!"&'#"&463!'&6?6*#!32!7%32+"&546n jUmlT.U  nJ   %&jPddO (SNLy& pd(Y aL7G2#!"&/&?>454&/!7%.!2#!"&=46ސNS( % p &y22SY( nTjkn  T.T8   Vd% dd-I!26=4&#!""&5&/&7>3!2766=467%'^ NLy& p  (S22(SYLddjTnlT.T  nk V   8%d%2".4>%&!"3!7%64֛[[֛[[  [֛[[֛9   &%2".4> 6=!26=4&#!54&֛[[֛[[%  [֛[[֛ &   %2".4>&";;265326֛[[֛[[K &   [֛[[֛@  %2".4>#"#"276&+4&֛[[֛[[  & [֛[[֛  2".4>%&277>7.'.'"'&65.'6.'&767>'&>7>7&72267.'4>&'?6.'.'>72>՛\\՛\\d+: =?1 " "/ ?9 #hu!$ 0 E.(,3)  (     *!A 7 ,8 !?*  \՛\\՛  ' "r"v G  .&* r$>   #1    %  *  '"  $  g2( % 67'"/&47&6PM<;+oX"O\e~Y+" n+We`#'7;!2#!"&=46#3!2#!"&=46!!!2#!"&=46!!d);;));;);;));; );;));;,;)d);;)d);dd;)d);;)d);dd;)d);;)d);dddL !2#!"&46!|;**Dd%32!2!5#!463!54635#!"&=);,); ;),;);));;)d;)pdd);d);dddD);;)+AW!2"/&546)2/"/&4?'&6#!"&54676276#!"&?'&4?622,^j^5,^j^/j^^^^j^j^,^j^&j^,^^^j#;CK2".4>"2>4&$2"&4$2#"'"&546?&542"&4$2"&4ݟ__ݠ^^oooo-- - L- 73H3)z - - - - _ݠ^^ݟWooo -!!- -! $33$ 1~ - - - -Z[%676&'&#"3276'.#"&477>32#"&'&6767632'."[v_"A0?! -  Y7J3$$ )G"#A.,= # (wnkV8@Fv"0DG([kPHNg8B*[eb2!5(7>B3$$' )M"#!7)/c# *xnfL@9NDH7!$W]B$&dXDD>.54>"".#"2>767>54&0{xuX6Cy>>xC8Zvxy#!?2-*!')-?"CoA23:+1! "3)@ +)?jDH-Sv@9yUUy9@vS-H-&65&&56&oM8J41<*.0(@  )*D*2Om9w.2&/7'/&477"/&4?BB8"._{iBBi BBBBBB7._BB^*k"5._{jBBFi BBBBBB77/_2#!"&54>!"264d:;));XV==V=.2G);;)3-D=V==V "/''!'&462*$3, #**#4$*' 2@K#.'#5&'.'3'.54>75>4.&ER<, 3'@" MOW(kVMbO/9X6FpH*M6&+  4C4%dfJ2#4.#"3#>36327#".'>7>'#53&'.>761T^'<;%T)-6"b "S5268 jt&'V7  0 $ݦ -$aPN(?",9J0* d2>2 ""   7Gd/9+DAL!X32"/&6;3+##"&?62*Ȗ*,|%#5##!32"/&6;3353!57#5!ddd,*dc,dd|ddd!%32"/&6;33!57#5!#5##!35*X,ddd,d,ddPdddL32"/&6;3##53#5#!35*Xdddd,d, dPddL32"/&6;3#5#!35##53*d,ddd, ddd32"/&6;3#53!5!!5!!5!*d,dpd , 32"/&6;3!5!!5!!5!#53* dpd,d, LL!2#!"&546!"3!2654&^pg );;));;Lp;) );;));LL+!2#!"&546!"3!2654&&546^pd );;));;oLp;) );;)); $  LL+!2#!"&546!"3!2654&!2"/&6^pg );;));; $ Lp;) );;));LL+!2#!"&546!"3!2654&#!"&?62^pg );;));; p $Lp;) );;));L5!2#!"&=463!2654&#!"&=46&=#"&=46;546&p);;)>DLpd;));d&  #%2"+'&7>?!"'&766763 ,  P'' K    S#  nnV/L5!2#!"3!2#!"&546&=#"&=46;546^>);;)pDLd;) );d&  1!2/"/&47'&6#"3!26=7#!"&5463!m)8m);;));Ȼp,pm)8m;) );;)֥#2".4>"2>4&2"&4ٝ]]ٝ]]qqqq{rrr]ٝ]]ٝGqqqsrrrL#3232"'&6;46!2!54635 ' gdV^|d22L# ++"&=#"&7>!2!54635Gz " 'gdM !d22LK" 62"'&4?62!2!54635qgdq#d22L #'762'&476#"&?'7!2!54635*MMК=gdML*Л:d22L#'/'7'&6"/&4?!2!54635^WЛԛL*MgdКԚPM*MXd22% ! q3gqdL+!#"&546;!3#53LDdddp,E/'&"!#"&546;!3#53"/&4?6262L_  Ȗdddj\jO)_ p,j[jO) >'.!#"&546;!3#53"/"/&4?'&4?62762Lg%dddFF))FF))gp,F))FF))F/!"!#"&546;!3#533232"/&6;546L dddd*p,/'&"!#"&546;!3#53++"&=#"&?62L*ndddd*pp,L !2!546#!"&5!52LPdL&}-1;&=!5!546#"&=46;#5376!!/&4#5;2+p/22ddpddd33*ȖdȖ*yddQ%6+"&5.546%2+"&5.54>323<>3234>^%"% "  d d 1t5gD >?1) A..@  ^  ^ dL3"!5265!3!52>54&/5!"!4"2pK Kp"2KKL8 88 %v% 88 x88 %v% 8LL  $(4!2#5'!7!!2#!"&546!55%!5#!!'!73wipdw%,);;));;),p,ddibbd;) );;));dfdd&767>".'.7.wfw3 .1LOefx;JwF2 1vev/ 5Cc;J|sU@L#A2/.=& &=>2#!"&=46754>ud?,  1;ftpR&mm&L!((" """" '$+  222/2 ! '!'3353353!2+!7#"&46!2!546L J LP*dd*22dL #"!4&#"!4&!46;2d);,;gd);,;;)d);L;));;)D););;)L%)!2#!"&546!#3!535#!#33||D| ,dddL| |||Dddd,ddd,L%)!2#!"&546!#5##3353#33||D| dddddddddL| |||Dddd,L#!2#!"&546!#3!!#3!!||D| ,,L| |||DdddL!2#!"&546!- ||D| ,L| |||D ,L )!2#!"&546!!!#";32654&#||D|dDd&96) )69&L| |||DdVAAT,TAAVL%)!2#!"&546!#3!535#!##53#53||D| ,ddddL| |||Dddd, d dL#'!2#!"&546!3!3##5335#53||D|DdXddd,ddL| |||Dp ddL"&!2#!"&546!#575#5!##53#53||D| d,ddddL| |||Dp2Ȗd d d %2".4>"2>4&!!!'57!۞^^۞^^qqqql,dd,^۞^^۞Lqqqddd '+2".4>"2>4&#'##!35۞^^۞^^qqqql2dddd,^۞^^۞Lqqqd2d2dddddA 62632+54&#!"#"&5467&54>3232"/&6;46n,,.xxPpVAbz  & AwasOEkdb  A32632&"#"&5467&54>++"&5#"&76762n,+.yxZ % OqVAb   AwaxchsOEkdc  dLm%5!33 33!#"!54&#Ԫ2dd,,Md22y7/2#"'2!54635#"&547.546324&546X^Y{;2 iJ7--7Ji/9iJqYZ=gJi22iJX5Jit'*BJb{"&'&7>2"3276767>/&'&"327>7>/&'&&"267"327>76&/&"327>76&/&oOOoSSoOOoS=y" $GF`   Pu "Q9   ccccVQ:   Pu "GF`   y" $ooSWWSo++oSWW"y  `FG # uP  :Q # cccc:Q # uP  $`FG # "y  d "!#5!!463!#53'353!"&5+, ?,dԢdu       d !! 463!#5##5#7!"&=)+5, ?,>dԪ |  ^G |d 77 P#3!#732!!34>3!!ddԢ!,d!s, d,+$d$+ppLL293232#!"&=46;54652#!"'74633!265#535d22s);;);)X>,>XL2dd2;));FD);>XXԢddL6=3232#!"&=46;54652#3#!"&54633!265#535d22s);!);;)X>,>XL2dd2;) $+;) );>XXԢd  #!"&762#";2676&35} ,, }@D:#6#:&77&P'L. dd LL/?O_o32+"&=4632+"&=46!32+"&=4632+"&=46!32+"&=46!32+"&=4632+"&=46!32+"&=46!32+"&=46                  L                  )33#!2!&/&63!5#5353!2+!7#"&46!2!546dd^>1B)(()B1>^dd> J LPdO7S33S7Odd|*dd*22+52#4!!2!'&63!&54!2+!%5#"&46!2!5460P9<:H)"Z" )HJLP;))%&!!&**22$.2"&432!65463!2+!7#"&46!2!546 jjj."+''+# J LPjjj9:LkkL:9r*dd*22,62"&5477'632!65463!2+!7#"&46!2!546X/[3oo"o"."+''+# J LPk6NooN>Qo 9:LkkL:9r*dd*22",!!.54>7!2+!7#"&46!2!546X,%??M<=BmJ J LP9fQ?HSTTvK~*dd*22)2!546754!2#3#3#3#!"&546/R;.6p6.d6\uSpSuu;)N\6226\N)G6.dddddSuuSSudLL/3!2#!"&546!2#!"/!"&4?!"&=46!'|  % XW & dDdL D 2  % XX %  2 dddL#-7!2#4&+"#4&+"#546!2!46+"&=!+"&= Sud;));d;));du);P;ddLuS);;));;)Su ;),); 2222  !&4762 !2!546 'YV/ |UYY(n0U22!/.#!"3!26=326!546;546;33232!'p'q*}20/222,2 "!#!5463!#5!#!"&5463!#5,  w,, v  w, O,T    dGFV32676'&7>++"&?+"'+"&?&/.=46;67'&6;6#";26=4&KjI C   )V=>8'"d 1*) "dT,| -otE  GAkI ! "% ,=?W7|&F@Je5&2WO_e_ 2  2 ~ $4<Rb%6%32!2&'&#!"&=46#";2654&'&"2647>?&/&6%?6'.'.. +jCHf7" *:>XXP* @--@- -?0 !3P/|)( )f!% =  &* x"62&CX>>X83 D-@--@ۂ # =I+E( //}X&+ 5!H d9Q`o322#+"&=#+"&=#"&=46;#"&=46;546;23546!2>574.#!2>574.#q Oh ..40:*"6-@# d   KK   d)  )k)  ) m!mJ.M-(2N-;]<* K  KK  K X K  KK  "p "),!2#!"&'.546"!7.# Vz$RR(z }VG+0 )IU!zV`3BBWwvXZ3Vz&--% ,(1#32#!"&546+"&=ۖgT)>)TH66g )TT)g6633#!"&546+"&=`T)>)TH66B)TT)g66 %'5754&>?' %5%Ndd/\^^<ǔȖ  (Abd 2"&4$2"&4$2"&4|XX|X|XX|X|XX|X X|XX|XX|XX|XX|XX|L2"&42"&42"&4|XX|XX|XX|XX|XX|XLX|XX|X|XX|X|XX|ddLL/!2#!"&=46!2#!"&=46!2#!"&=46}  J    J    J L  p  p  /3!2#!"&546!"3!2654&!2#!"&546!5^ );;)X);; G ;));;)X);d,dddL;!2+32+32+32#!"&46;5#"&46;5#"&46;5#"&46222222222222L********, *.62"&%#462"&%#46"&=32W??WW??||||||*(CBB||||԰||||ӐB76+2+"47&"+".543#"&'&676/!'.6E*  '?) T 0I' *L #3{,# n  6F82 *5#"#!#4.+3#525#"#5!2 &2p"& 2D d 2d  dd R , W 22 L 05"'./#!5"&?!##!"&=463!2E  1;E%= !'y,2 " # 22+."A2VddGJ!2#!"&546#"3!26=4&#"'&?!#"3!26=4&'"'&'#&#2LFF &  7 ? 9   9 gLR   2 2  2 2 $ #'!5!!2#!"&546)2#!"&546!PpmpG,Ld|pd,#'!2#!"&546!2#!"&546!!5!2pmpG,P| pd,dd'+!235463!23##!"&=##!"&546!2dddpdp,d ,'3#3!2#!"&546!!2#!"&546dddpG,|dpd, pdL'+32+!2#!"&5463!5#"&546;53!X|^d,Lpdpdd,'!#3!2#!"&546!!2#!"&546ddvpG,|dpd, p,0o #"&54632a5*A2~ 6'&4O**{))*2A~ !2"'&6d)***2,~o #!"&762{))*a**( 5-5!5!Lc d 1#3!35#5!34>;!5".5323!,P2 &d2"d& 2dd,dd  dd & ,L%1#4.+!52>5#"#!#3!35#5! 2 &d2p"d& 2 ,, dd & ,dd,ddfrJ32 +"'&476 0  ) J 00   >fJ32+"&7 &6S )  0 J ))   fJr"'&=46 4 ))  w  )  0f>J ' &=4762j  00  )  0  =:#463267>"&#""'./.>'&6|Vd&O "(P3G*+*3M, :I G79_7&%*>7F1 ||5KmCKG\JBktl$#?hI7 !2+&5#"&546!5X,p dddL!2%!#4675'=DXDd dQ,[u}4]ddMo__<vsvsQQ(dpEHEd{ d&ndd ddddd5d!u ,d;I]ddQEJadd9'dddd dy'dddddddd,d,A22>ff****NNNNNNNNNNNNNN"~Fn2b\r bb 6 ( L 0  X * ^ h(T*v 8|t*<6`R.j(h6h^2Dl.vb F !2!v!"@""##"#8#z##$$0$^$$%4%`%&&~&'P''(4(p())*&*J*+ +z,,h,,---.(.f..//F/~//0>0011`112$2^223"3>3h344`445,556>6|677N7788B889 9J99::l::;;<:>>?(?n??@H@@AA~BBBCCBCvCCDD`DDEZEFFtFFG6GvGGHH2HNHjHHII8I^IIJJ.JR@. j (|  L 8 x6 6   $ $4 $X | 0 www.glyphicons.comCopyright 2014 by Jan Kovarik. All rights reserved.GLYPHICONS HalflingsRegular1.009;UKWN;GLYPHICONSHalflings-RegularGLYPHICONS Halflings RegularVersion 1.009;PS 001.009;hotconv 1.0.70;makeotf.lib2.5.58329GLYPHICONSHalflings-RegularJan KovarikJan Kovarikwww.glyphicons.comwww.glyphicons.comwww.glyphicons.comWebfont 1.0Wed Oct 29 06:36:07 2014Font Squirrel2       !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     glyph1glyph2uni00A0uni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni202Funi205FEurouni20BDuni231Buni25FCuni2601uni26FAuni2709uni270FuniE001uniE002uniE003uniE005uniE006uniE007uniE008uniE009uniE010uniE011uniE012uniE013uniE014uniE015uniE016uniE017uniE018uniE019uniE020uniE021uniE022uniE023uniE024uniE025uniE026uniE027uniE028uniE029uniE030uniE031uniE032uniE033uniE034uniE035uniE036uniE037uniE038uniE039uniE040uniE041uniE042uniE043uniE044uniE045uniE046uniE047uniE048uniE049uniE050uniE051uniE052uniE053uniE054uniE055uniE056uniE057uniE058uniE059uniE060uniE062uniE063uniE064uniE065uniE066uniE067uniE068uniE069uniE070uniE071uniE072uniE073uniE074uniE075uniE076uniE077uniE078uniE079uniE080uniE081uniE082uniE083uniE084uniE085uniE086uniE087uniE088uniE089uniE090uniE091uniE092uniE093uniE094uniE095uniE096uniE097uniE101uniE102uniE103uniE104uniE105uniE106uniE107uniE108uniE109uniE110uniE111uniE112uniE113uniE114uniE115uniE116uniE117uniE118uniE119uniE120uniE121uniE122uniE123uniE124uniE125uniE126uniE127uniE128uniE129uniE130uniE131uniE132uniE133uniE134uniE135uniE136uniE137uniE138uniE139uniE140uniE141uniE142uniE143uniE144uniE145uniE146uniE148uniE149uniE150uniE151uniE152uniE153uniE154uniE155uniE156uniE157uniE158uniE159uniE160uniE161uniE162uniE163uniE164uniE165uniE166uniE167uniE168uniE169uniE170uniE171uniE172uniE173uniE174uniE175uniE176uniE177uniE178uniE179uniE180uniE181uniE182uniE183uniE184uniE185uniE186uniE187uniE188uniE189uniE190uniE191uniE192uniE193uniE194uniE195uniE197uniE198uniE199uniE200uniE201uniE202uniE203uniE204uniE205uniE206uniE209uniE210uniE211uniE212uniE213uniE214uniE215uniE216uniE218uniE219uniE221uniE223uniE224uniE225uniE226uniE227uniE230uniE231uniE232uniE233uniE234uniE235uniE236uniE237uniE238uniE239uniE240uniE241uniE242uniE243uniE244uniE245uniE246uniE247uniE248uniE249uniE250uniE251uniE252uniE253uniE254uniE255uniE256uniE257uniE258uniE259uniE260uniF8FFu1F511u1F6AATPNAMLP',(GLYPHICONS HalflingsRegularxVersion 1.009;PS 001.009;hotconv 1.0.70;makeotf.lib2.5.583298GLYPHICONS Halflings RegularBSGPMMF٣(uʌ<0DB/X N CC^ rmR2skPJ"5+glW*iW/E4#ԣU~fUDĹJ1/!/s7k(hN8od$yq19@-HGS"Fjؠ6C3&W51BaQaRU/{*=@dh$1Tۗnc+cA Zɀ@Qcal2>Km' CHMĬfBX,Ype U*Ҕz miO1nE. hx!aC XTV‹ R%|IHP5"bN=r/_R_ %҄uzҘ52ġP)F7SqF{nia@Ds;}9⬥?ź R{Tk;޵ǜU\NZQ-^s7f 0S3A _n`W7Ppi!g/_pZ-=ץ~WZ#/4 KF` z0| Dѵ&däIÏ;M{'omm I !wi9|H:ۧ{~qO, L]&J09/9&Y 蓰{;'3`e@vHyDZ$3Dx28 W Cx5xwB`$C$'ElyhԀ DJ $(pQAA܉A@'$ hp0V0 `se$4$"t2=f4A{Tk0|rH`L&sh]A<`R'!1N;_t3# V *veF`E O${)W=p:F`22ړC^.ćG<.pNe2ִ+Ysl:˼ ܫu5tu^86ȄTmyQ%u~%~1rҘawߚ^_ZZa0!N`. uqYB\ᨀ[e:@J'Eہ,3ubj@pfeW9( ޅ=lG7gj SM609OˑlBa݁ <Bՙ(VRApf^+g9qMt]تpEr@]@VkV ud^X R@?EY2]#Ǽ4JK'dPC|mmn#$+48u'e&[n[L%{BCDL:^!bƙ:&g3-3ub iLZڂWFSId6.k5Pl77UzT:NN.")['|U"AIvwptdk9嫫9nDmq7I|6Kbc]MBABȪ_JT q  6@Fhd`GT:M7'L,IhFP ~j $¡„ 3hA-S^چ-%qe~Qqln"i&Qe?FlK"As(3Y;"Let'RzM1 0{=) K%$C 9M4c EotjVGD)l8,\w !%$3t TBzҴ iUJ[xgdBr$!eq"J> )\~3(^ R€8#>bHG'7_ fӫcκtDoAA߃(qB<``VΫ֘*buP4v@+.Qԥ$V@C0 RܐP[z:XH#e s>?EWO>@I$|si ES)0A?9ab,@K̩o&Q% ϞLu+ +H|Ɛ?NK4CnPt 'OT.j5Ĵ8vw֜I&+`yScaO[#gQd[KI矗`ČLP # )27aTi@c\ސ 0nCpߖ運4͵x*RzYbT[\kUvHʈqp঄IIŗ) bB XPNtz 2 I== ;}bqjiކa#" >11Ap1POOuxQ Fϲ(h݄O'MDxLK$ȵh& 14SirHJPtDM;rM+ *ؗ5u2$f3K %ѳb (@,2f,~"7R;E;HX(42Z'Tۿ2J+^!#oY~4-׃GW*!A0&8f{`W=DP8'= R g}iP>#4EBRY^4eN8V,[BĨD#X],LBsNC> +o^x jC.4Ya_{eA2=r+9POA!! }YPJeGn%x1/}RgHa ^3- 5 |qSaWK{ 1al`I1 Qf_yyCZ)L3X] W6@DMT<.uGK8DsбWr\7Z\V"ISd>CUjeD 3MtWcPӉ6#3QnቩJ\7#磱`؀K lV6 &T ~l. @61`! ` wYk/a0A¹ԁYhdxk:fEao̟^<IwYgq7s[ -y1ع5aMKאRBYFq}8*Nt'.YbZvK (]&ɜ(ՙ2:0 oΏхPKiBH4UX,[$ 0mXش f50VR 8%ާDtUs`-BPzPsvI8z-t1DiB "˶YTJ .?07jLN[2tĮ̎ #6?E׻:ɞY;A&qSIR)ss 9*x0Bj)mHAhyЏhMm&4Ŋ4 gV&tYOCS0Yd7MvNj)wA(o "͢[ E`7ezď-Q]6+Bca@^I:һ=sSnc 6 OB4LGpBq/>O pwj A*@JC[h&3B Qbϩ8 :%f~v/lS00a"B8(f uGoǚgyt_y~͔ %mL !I$Xt0~ePz]Ug Н=_?.j#+`li BM5 őGp7a ֒%Y[UG9@\bDY{{ED0 $Q+FvC`ݨ3Q E\uC9![$l 6DoDgG*+X!%#Cq ?8ZUB)U@opgީZq89|uccAќW;@">Ph_9}.6V/O:3}ZS {:~ykcO6;OB=bV. Rk o ^GV= }oI"+ ]wFzϷ`<30h3]Rf859s`KM8 XUq<\ZOssM&j&  .%PBL~^Gˈ3pD:Z<\ǠiW̆"(:zX~0PG]8RQMNTqfW~!0R%Ց0xvGFy/F-wu/*+ \8@6c<L;c[º nr QS'oQuT{qҐ_ͿSdA*ð:m8Yuz2PB Hh`lkpLLh cEb6eۏҋ ?!>| *=VK@rx0G`%ryr[6Y37 f**n%9df11ޢځ^'] Rq.,^%l e#wWs56!=!q[ %Ԯ]5^:m5)?V b|u7fw,:Ye R% [ o gFAzFPx{dxíw8ٔ{{L> d2CLL,L,(mS$=|%֝lu& ą83 N Xx \VnJ[)Iw/鹻 |GźYDH*Sp60cJ2@W%Ѧc_^$#*:G6n>D;~`9hXB UJB_вˈ%w'$v|#T<68KMϑ-5U+'B ĪNbJOv'|+*Mk(d }C˱@q&aR%} !VЃs3w2a2awHz/Q0F ]~;ä NDP mK3xke_ S!V&=v_PL9؃Yi NU_)J69f*S  17F|BR$y,Ʊ.&=uqsODBR=ɳeؽɇBH 2lu'h7^#S)Xi2..Pe/@FK$](%|2Y1pC8tI11N//+\pjdWmI=߽YZxMЉP81/ JG^U ,Pd1O^ypql2h$jvI%]V .'[+WU8[D,߻-=[ O wE)3J&dقݶR¡S\. 5J$I&oHȳ~ lz> Ux/Hu;?Gt{?;TH L|F8}{p:2t͆aѧp65Y"LD.rVS_ k]n&Hz~9æ p $4ق'{&M\ΰч!qi (.h' B T|{I6cL.빍iI꫿\!;g`1 j%C o3*60E؎]t.-%0 YK_nft] *VFCtJT+\WZ8gF^ ޞf 5I=#6.@2z;W`B/ęQghjyJNAX3, K66ڲM0T@O{4kj|"ftџۄU<-a5b)^R8:ilKa6@!]buvΏ$ oUœ~:.Lte JξP l$S[z~Rq39钺9Q/m"%ʤ7 5MKL鑧"IߏG XTގXLFݧV jp^/Mgۻ{w *9Oʈ<"aAq.M2@mp^'wߕmkxO8 $[&|YZy`2_|%r/J?QṈl3ÞKE$wvCh a@U1M%0?1* $GZ{!|ʿ$ە-٪Ev;͓:`Bl˸쌧ɬoQ0&,F?^s,ch˕$Ecl0w`⏺ň@/r^l8cT3k@JݔuP&ʪNdJjTKi *uX{tj~ɡ}i\BKenȵ|N u#]@lCZ$iPa㸩t04y20 s֪,Au!QBϖ^@Vsɑ\Za7쾉ш6-TrU u~1HJ(<αbRԖqi J?eG *jVħ ":Y);-Fd!HG~ux cb6m)&;0dU?8X~12ۼtIx5{(z '[ŃkZЅi,b1̇`(mHNeK/ [(#QGduT^m%!(7KgP=hϕkɐU+.[eC"GDΨ<*Ř 9&܂?)\<&Ŏ5 LJu@Y,냲ھ_w0^17p޻*>D8_)$UźR!jOF>{ t,-bP,m`D"/zA ͔إQZG&U]xejxLwv~=)@B6?!;53/ps@tOZS7ؙnlxZ?Zj a{6L41 2Qi&֥l]o=7ļ ofЖr MEV@H/aD٦HlK5)ŒZ OE3IG'г;D'zl(E$.ٜ-W R'\w+)w3꺾 @%R).~9;].šg+)%ȝk҉^NW>b1z:soD K2w[|>9vWMFu`axchիU`*ʆe]OV'6xd?H]_rA+zdFH ʋ<ǴkUsFzaH9-gvb=L/E).x9j%B)$AB t b.bAEZRbH(Jya9Wj0fF'Xz $DQ6q` o i={#4FYH@J3 3i~tYТhkHP17YD"pĦ;'16fpu>FoDQin̒- @P# hj ނŀfC 7°T5HVXpklĭ]yXr)?ͺBNJ B#9e&&_0=pZ6h) ̗a b=(p);.N,W^ *hԺCm}E7i6aIvͲxp*Ac#4N&`)ĉHWey7jloEh_n3 jp?4p2WE'kT_ &!ȖjVlHӻ_kɚʳaY s@[G"bYLܫXi Cq8&zVaY{#I@2m!d[1 AƢnKeם/>dmuX:xʷ\pNl+H+ctSǶC[~3e}6 \,Ʉ|Yݧv]'|&M2 ddsx-((76aXm=ӊQ<$Q†\ qiH阇i'i$"{S*VwF/tfQCWUZ{S;Nx}H&* 9׸qU1 a`(M-aG}n̽0 pmcn ɘ_\l} 9FvHþkJZNO mZQҤ aSf )QC+2 d[ H"t* c*bڢq,#S#u'Ҭ:4asCDMF|ɸm_1L]Y\*X>tgDd@&[)8;<{8<+VG\H^aae-4sJA \ hM[\`#pD5Z97g;BWmqTXX%0v&]E 4]FIJ&S_4R0D+meY gO+M{03v'ͅft:;ر Nn\ǔ^,)1laBZZ[  ZSUYh߆wS\/*?zQЋ`X4gr[CWG.Y0Q|RԃE[wy),ш$NK@c/b -#ZI G$ƗtmH#)XwPZAD|S ofTH)>M1b 7ɆSuq jK4[s xL Ǣ]5 !M!AdƧN><:ǻZ(8)e /W| bDDŃtT7rur0Ң`ܴh5 5S}4hrvalc!ZjB]xDbTxzYS6_)op>#@PS*bS\q ƋxYfQ><" Y6IEr_7Ұ VH!IrEL6!Nq"'daqMvA% v n.;A/2ʲa8D$GWv#̏ 9k'o؟o@ (]gk+}/ (nqK(f Ɵиp23YwpDdGq2$}KӯA"E&Ntg'Nes!Ю4qo}쿝S,ojr/s TMT&Qf\12h'&ctN'Tx7]2 ;G ʅ|T++:%/ 1T  ˀ<4͔˗ ,0~!WO' :suҦن(^ﮎ )7fmlҹ1ūtZh L0 6X"J҂ 49 ֩B}ԭ``Ӓ #Jn_F H|$OK=œi17o-Hqp[ɫ%%:Ɉi3۠G CLL4S:dBj|pYSDP>pv5KLe{t0yEND$*;z5NBIgn.N|׶nRaSZJcH mXek;_ 6,yb0#ZA e|wG U1lLD7ÄVqt[xuEQULPBlZSh.1Q0Uٱ8Rip;{H#GON!?t>Q |pkq!gT,j2sǍ4툊tjnƛ/IOE!ˋnF4M&1x$ew+vS  bm]e%8 P !s_06)Q2JB [t9'Ԝ,[fÆג]BB@r&Bs|Q gOC1J D&LJiC`A^#X8tH?daĖTSTaH0@U)^e}Jb7%ܔ%:ƿ@M+ysqL Y00ÔGD >ĩAW 2I:F 32ʠq:6S]K"g[ ϑHB5VEqLJX{CB!PIq9Llxʪ7>֤]@!@9H!pə$ ?)܎l/"́+@`}}:\ 8zQgS+򒤿C}R:HUF\Xg/AZ%c1wlETwX ZNhyf2D ø&vLq47z\iJyJ-kN3 -sJ5)V0N0d\ӛd0d-E[mf\UmxCR<(`ѕp4^!hQ `!l ~ƙ:JɠlW9˸ZXB=l)`jeVJUG!s1?Ƽ3Ê.}bIa6ʕ t?SxZJ'p i,.R2T`5-R BxrWH JPe#Bb|-[PEh‹(5Sfr/]IƊ dE#OS39ӻ]eۮɹ.9_beM9b#e(- 0Ra9"U,%~X܀z۽{'6[@t[W%* .d'vR {h!AedCE}x=E[|B$7J* B- ,=k7[_-I J5e̶{ ( ;WMw`~pAz 8f))(@ Īم<.a%N n@bz>%T*?lgbd<ĵw9Na8;<^*%y:tDҕZ<@0q4l\ 1`/$IJ ғsN);:A;)$ו Wwy%KrIv\bV\nd{6tv/~*O 7U>8rAC<jE-j牷xs)D1Ì/qp**̸$ّ,  Bȼpk MhpK7U]h&-$鎻Y;q6wzW˄֭AhD^R"s5fw +Q&/9ȂwNbz{Y> ]NEc,ߞ# BF:0/-EȾŒ׃F\I{tAZCORuk i)ytkdN&vA P{P'>xƆ`.%,;:Կ:aFoTQ}v#ףQk's~z5hMQʒY>CʍiUNF#J0uC8k! fv {E/IKIE> pyde ʾ=z:@7J|5g8x 3O 3H1؄F.yfzWIM j[.w%i?҆Uf|}@+[8k7CxSEOޯp$Q+:<]K3T-y[Nz;y-HZY^.M*'h8A.N2rLB 7:Or}CS˚S9Jq#WI}*8D!# g#Y>8` В ?a2H,^'?^nhOƒi<Ya2+6aFaMG-Gkè1TbL `*ـVX *xe§֊Z*c`VSbJU*6TK@zqPhg*ߔU(QU49L cM*TR!R,BȅE*C|TzpF@4*텰جXbL.T2y`Upb T,%@` #?@tGLŞS)ÿztϲFy׎ 14Lhfe(.)pK@\ Xe@TbvhD&0-IbD d@ZD1@ DyѧCN| 94Ӛ#Nc l;, `cX@(2$0 "@- $B@<$А8p7C b(@ PA@F 0tGORIJITySMW52\ToRKV0Ȏ( -$ !6wHGO r~e~/]V~/P~7SzKFv`;`9v# JBN,ӭ'`'`\LTApBs)r! ( i`wOF2Fl\F M?FFTM `r $e6$t 0 "Q?webfe5옏@? t,3+2q FYO&>bm5ZH$Y{H jd Չ %٧y"+@]e{vNc)n?~?萤h_&iѝ?>^K v-cۍ12Ky,'n(3EwiB& Tlh0M҆dYrﲬnti]yurVXsjgMnәHW r2>iT`V7R(+o6'cB4ι㿚T ]a[Qd<3wq8,rTI80>E?*E痦#7'S ocʷ_7&#*+)+4aA6cy٣f(bF$;{ YA1vP-tG"Cf- WԙuKְK#*K< (Z`٫ [%YT{%Ɋ$s{oջvt"p4`ߩϤ}o `'ne> G5sz_N PKӦvmU ɾ{z"3`l W#Ԑ^@+,ckoAOpnuzzJ)Υ1}O=xR`J`qUs/+kv1xljlEl\nDƶVjg{Zdz7 5!xm5o[u&1ڂHBkAqrR (\gh7Ҋy=HZUPh$8RgzgͭN:1u$܅>R]"f7 K^'3+E/^YU5]NB.ʋ8+͏8,|{M|Aua|a˅՝% lKGP,Nukc8mX@d̘?Y&{?P(G]Or-\LF9,&y8r3ܟ?p>~sDz1?\U5q=tzԒ&Znj%mM"}tkDwh-=mB76&:һqt" 1:Еu;"K_/Jdc0l0'^B8VCzg[ ;d Ybȃuu;@*}y| .'C>\g=9VŐ[o|g^ >d 9 *E|A*M[[*mOQz?Pn?R)YoT&[U*5S MB [ oYDh{,}1f?NN ]O/^;\J BEsJrĚ'g/B%o Cn7:|yKt&$s|wP\i]$Z@+ Հ90x]r%+RUEm+ܰ;wu9/I77զQlu\yWN)8ܰvY*umm( fEG8 j#IRz #q߷ )Y$ Лc_%m-{!0-` ;公hyV]Hv! ta\K[1{"j 6@3T0%Θ"ԙZIGS.ΣpӬS1eٓ؛ Yv8d\BlSR)ӆ {Iӆ%>0Ўڦ\'cg2%4QD 0͒3B"MՎ&ۊhIڧRgME I(5UD] }b8$8>X h"l΀j.%ۀHH- Iݸ#1C4Y7YݖV o>P]6O47f ~AJdYF€.oy) 8l 22e1H[t@!ȅ 2\@5ٓ%Zkޒa@.`n3OFR(󅥶ZkLkF HWjY I5*6eSbk.5F,.N0ԙ|V||~N( 4],Jp|~xeA5/ڻSvy?'_v|rXHQēB@= XB94TBBcHP+_YH#$`FB;+BPR4̼ t:t"ZEJ^!XǓq4_dTW(5܀IUŇAz@U6n.WGXHRK&'swMjʎ<3)`#F@  F Ԣvob$x +u&}|X&[٪8F-E&/>/G.az^/})'x$O=<zoA9M؝&~3r3g'8ң\-MDzk5A G9|1-! 87[,mRu|57 =X,aJ^tN4\fЄ]AzH^7F&k"LU>}>rBX(ۂT% JdhKPKTFaA3HHC[r;ad54 lLkjG{8h~ fR@9wB0 zS'a7@@Nƹlbj3hNXF/es'DsQjw}Jz^:V.:ڋ{ͼ(ȲBɦx<Db#"S{PHuN/{r6;wUsPО p8+6g_2lΡ6H džH: dBtGNmx@j |{s9=wR/oDJs5z>;'xEq^r^=G?9AA_K%Dɮ:uikjkIeG՝#*)jm|t}`JZ؈H=4{g߁)qXMA,H71V"o,Y#hݨS_;a_ԗZ^cn4HE?} ȝ٤=}BWvުUehGF;@2S@f n2#fY:]JyH]-G׌wgv'|0e _7Ґn+fٸY<( ?y%wm+j&&!c^u'b&hm6¤*2 ?AIƲ5FWؙ[ƜBUzIE!m:xheǮnz|]% mrUFگ1 };!n F&gP;&$$F).tBQ3(C=Xes;iي@~NΡE SRh\BeobTnΒju g@'qQ딎nx.u6bVU& ];!C_  5*zɺmRQuqPZ0}mn^nOrT:U'h0nZp^R|DF_b\@mDE8{oGM᠜q}Sd C,iܚE/Ë[d8],MCI_u,]Vc"pg@`"y),;B^el2'.(Ęy>-|hw;jՍiԽ_o|!@)ɢ=̌SPz*!z})|ƧT}jEtCZný*՞4ۆ׽[ 9Юݓz`Wmeo|j8j59@.EV/ZW@|f_\"${v/;a:Sei3TG*]ơ/h2C32$1}DNXt?Fϝ~n,Pj9.>ף{ 9EN-v|3hCиE XT;P$=J-gݕigz~q(A<:h193N̽Q}CLWߧ׎~ b"|4u}cy62[ \d,ҎճbkD%0Tx{=;Է(i LS13Nh/6?'E^~P{sZZKĞB{Dt&z)Uoa5Q3ȗr~ F]$<tm(} MB@[GxFh8#},#u Laz(Qh4%xm`Uչ.Ev1a4_'/[d{FxI59 D<&8VEFg 芘#I䟍2S_]QqAn_Q>bޘ4g-0&E#ci8 vR/4rP7KsOWN3ՏvE\bqQ5ZڽVy5]h/ i)-/kNю#e)"P {KSQx>a&, _g-mc<n]Ч-52cz 7d PzVOPvfR Rఓ9Z -dC`,at=k?v4#P Bإ/[s.-bH)ɺz '}׶w!rXZ .:Vn;->: 6rUcs4kVW{#5ߑ0B`ܝ0u".QdB0Cr]#Q9lqN^ֳh~NU\ 16 ~SnTl\THҲڛ-~G~)$oQ7-C}q%/avO|[q4~Bc-$N76w{V餃.&(o*n NeRi4!3R"4nbm-y[X."!QKE\N4gՠםaNp >k)90BZBs yrer)vDtrv\v[>rJm a̼~uՏ>rMZcB<`)\yt|ۍr'<>[Îh7Z8caI! p⢟̮,G k5@`iw nО8pv *'O A[.rhT pR?+;\*HsLqUf:ql-ć *6!h+ˬ{h- jgkMMP#:}{/VŶC]옙&[W$ګ^#4fWa\ 5躺M[6)T3~ :. Z`si(RQ|/` il^L#f-;-C;_*{@EMCooÂ_7TrqzF%ׯ|UEƫUs^ݜv{fQ<ĐVPTfͦ?mpP*&QG{cJEPe2)xP0AMɪZHj"׻"AC+zqmVzᖞU%C:@1W [y)J@ob% jA>)Nǀi$At`>?f0gH36p6D|M 4N 4JJڃ jƇ\ p38Я6pV?:$sDNƹ2n,HO\[ոK-)W~im?T:޺UeY-#dJe)Z5?$\dW<,Ɇ;ط5SոTT̄f(PYv=Q ~DX*8辩s- ˨΀55 XRl QC l|5{ӦT\t꼕+en۸Psl3UO[ZS3*,:ÛZLS'̵**@ı~xgno2- WV;pZ9?~$6҄xJ>\QA_Cihbl] 64*A˯ɰqX7YX.-ոaɇVhiKgqNRĆN(r']%٘@3̀jZJ.;nm,S0xͻOF33ҧ<$'GE+}'1f3y5/&Z\RB7dm]8\3߂Ȫ@oT3eu^W@e7l!B,s1$Z&?dC (YЦSm>J"&pt܈P㇄BF4G5 t^Ć$j-a㠍g^ʐCAsT=kTS,|r9IBϘЬ'vGA@thQNj&T=xt;2]P|T- LÞe1ݽWZŚ*MrH5?=o"9K5='k-*AE| qҔ_?\7%|M6f++S*}W_]3fmܮ˳m w!.R#鬪;qq71$•ݙկ_iK&JάMemV5P0> Q5WHIh&4ҍIlE7}sm[cȾ|d^ %Uv1D>.T7*=tZ_㟾1Х:=0pZ6ҋNt(uƝ; B]$kڌ.{F*/UZN砦|oqKG;^侞9NexK \wh~ZpHb䉸 [k8k.bX.QXpxYa^"#Bwnbum5F~>8bN:p4 [gv^ BFUz)?60F8/2C8>N8G%l%5FH{46h4%# 7x oN t\'Ȩ E0#jNãVӹd?WlcW žֵu-}22EN}#䵵2H^a3rqs-S3&f퇣fwl.=W8,cHjcTWנs90ZDMC2ZMdjt"8:g{.Ʊ1Fb618"yԦ> W9 V `jT򔔑r,ni d qN .g+ S Q KaB?_QE rjh>Eӛ;C׭7^q `Ue#-;oJċԝ>) ;Jg׭9R;OgiI7}8Kہqjeؓ+ٗ'nϷk3eFρ0V#pMAzb^PVu~1uғwn ^.II_vdW[Q,+Lbćq 9V} ΏVw4qU3&jıHYb ttT7ρarBwP9?)uT/aA19kM \Psq+=[5͔?9W+^o^E8s)f 2aQxi& NE>"^Naa;f9]NE& t^CLz'e8ZRs&67_ãcyJ1 @TZ?SD2 |POӌ\dR7zH9iQ#zrc.4GR4qx<2~Xhnੳ2auBNC+kX0 aj5n>މe3vާ<>_ uH:XR%~9!4oѼ38? 1d#A&{A!i6 /Xa㇤=W;|) g~ ?*悽 }ڧKt>5|E.A Q6 (6 6є7<9_C f1Ўi8, V4$uti,.`v6r P gFBɎ t C3; ,oÂx| /KMp1S_X.fV#U>Ȓ#B] AIVoІϵGTV1nr+OXS% ³fOZ[_9P߰ {Gln%#hdwH= ye/W>,IP,*MV~ºK&eċM콣=)qFS"GTF*LX,h[wweWQEx ?{^چExhiׂJH|^͓e*^Я.uxEb#;ԝ<]z]\wNhochqE=4Q17W̓lÕ6᧿HE_̣qy YR۫9~l4sVy`Uߛ,#_u+DeM~hq벇#Yz$; 5ͯ9$ z> *jO$$O/xRtf-}*oɦ|3M;xިUl/.~XǎY4x3&x";$KI5dڭ ~w[M9O%4Q}S^t@w[Y;-s;bwH-* imI-1e/~TNN.p)H$W~ƦO (9, ]gM6r+#%/swA$q4O> d9}+$s?0a,>yڈs<=,c_*\D}2MT8/4g'ڦ8'}"C*\9#Y>z$7c[s|"$} ymzQx 5%o$jkp)x-:И|?ofgFr2SZq}q o,wyOgCF1l'L5T33yM92"s5uD6-JUbs O)wR -2/5frϛf@=BFCB&'F}@&yubC?'S49+ÓCIî+f/RU C Fu:C*} T:}{ݽⲷue[!>? ڸ"M 8gz0\HkZ:h~@+#N fjyio!B R'5>`[!T`mC Iѝ}n >W!M}Uav43)!kcȂm? dwv!ה;Xϡۨ}8vt"Ӽ# kvXJ[l[ZݙMÀXC3l[ TaVjʻѬ"œ t:(<cZveQTqHi{銀Q埓'ÖiP■mKAIBF =Tᅽ(&TS?/؁A:ַОV(@wFa^]o]*99Ri_2vM`Pf{QYH#V7v7Ұq>@~uɘ׆Ax/xB3Ġtyb0nG` EDٍA: PwI7nW2ED}.(h"U]9Ih_V@GZ0C pb :L 3tN*N 2!3 Cayn.ɋW`̳}QBCi 8*{57O#aTBUoi0 _^ ChrU}~rL 1z>..=%GG o EuPPsؘ޸8Pu&;*|i&Pbțh;[|y*cVhҼ(~_AqU2GIQ3`^v=@K'ЇZ#4sJ=:sY sڥbyj S_E܃"@~>86#y[cSŬ#SJGZyvvSя扝pwaT/, 9'Jkv%%.~o[ 衧RBjSȀ*$'腁pçSu +9\_f+8u\,tpэkخJ0h(]NQvW7 86:ݣ WcY_i>"R(e]6RA%U6&F]7@̳k3X h?KQ2Bk[?..KKAb65ke+]FeWHU0Oק5 e3Hco>l]02cH9{Z {sO!A,7?ŷ3w俎A Fj8B&8U$G$Y5FL5n1> q2.6e +@/kb{(7i={l͍݂濦81g(%h/EfMҍt5̼vgo ~ਜ਼WKi父UأݖwRSEFT% `=|*=1*SX^w)lfQH(YSSˌK1W]f7ך^&p@T'.%3 5zaTf6A5LX̡|L-ηTg{A)F."hjA;.~o% G#}&]׾c`ChH9xnNY lc\+v\EƧ1D9KX)2b.NWQש$/|6tð32ԛ72иyu0e)Nuh'd~xY ># b"k3 :9v$ПC:)H> զz;ed\jmfOa%9cKxۥ!k%HDn{Y"{n_} )9= _/Z(>lYVgQ#߭:Qbw$zwٮ#U?|Ghz{o$wϜ)|Vh? ZV7%Go/׆E"KӲlp76-z !l4n>$\zV?szqejQ]m^=^ !lHB4sL i9}2^K5OB)O v^~݀xrm\K&G^5CL}&FB]Kn3|sGjykObsܽaW?R6Jfh2 lBS\=jV*Y^˺^E)*\ rr(a@6nԌ?}dLgIvqNcaƮkmLcA!hdVwc=憖s_:җsLg>1*4-%&0Ub)Eܬ*b51 ++;<`!qfM*,[/GK+{,>CLR%%c~'EGAG=h䟔8:IDN)W̻AF)ucw'qhXèL@a~6Pc2L"A2bU & 9A#QLO:E9kfKFb93tL$cˬpLz5dp۰>$`.~X=?NͰ/LPNo0p b8AR4r Jj} Ӳ04ˋquۏAFP 'HfXDIVTM7Lv\(N,/ʪnڮi^m?~ QU Ӳ04ˋquۏb$tV&gϖr> {{icon}}{{name}} {{lines_bar}}
    {{lines_executed_percent}}
    {{lines_number}}
    {{methods_bar}}
    {{methods_tested_percent}}
    {{methods_number}}
    {{classes_bar}}
    {{classes_tested_percent}}
    {{classes_number}}
    /*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("
    {header} {scenarios}

    Summary

    Successful scenarios: {successfulScenarios}
    Failed scenarios: {failedScenarios}
    Skipped scenarios: {skippedScenarios}
    Incomplete scenarios: {incompleteScenarios}
    Useless scenarios: {uselessScenarios}

    {suite} Tests

    + {metaStep}

    {steps}

    {name} {status} ({time}s)

        {action} {fail} templatePath = sprintf( '%s%stemplate%s', __DIR__, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR ); } /** * Handler for 'start class' event. * * @param string $name */ protected function startClass($name) { } public function endTest(\PHPUnit\Framework\Test $test, $time) { $steps = []; $success = ($this->testStatus == \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED); if ($success) { $this->successful++; } if ($test instanceof ScenarioDriven) { $steps = $test->getScenario()->getSteps(); } $this->timeTaken += $time; switch ($this->testStatus) { case \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE: $scenarioStatus = 'scenarioFailed'; break; case \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED: $scenarioStatus = 'scenarioSkipped'; break; case \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE: $scenarioStatus = 'scenarioIncomplete'; break; case \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR: $scenarioStatus = 'scenarioFailed'; break; default: $scenarioStatus = 'scenarioSuccess'; } $stepsBuffer = ''; $subStepsRendered = []; foreach ($steps as $step) { if ($step->getMetaStep()) { $key = $step->getMetaStep()->getLine() . $step->getMetaStep()->getAction(); $subStepsRendered[$key][] = $this->renderStep($step); } } foreach ($steps as $step) { if ($step->getMetaStep()) { $key = $step->getMetaStep()->getLine() . $step->getMetaStep()->getAction(); if (! empty($subStepsRendered[$key])) { $subStepsBuffer = implode('', $subStepsRendered[$key]); unset($subStepsRendered[$key]); $stepsBuffer .= $this->renderSubsteps($step->getMetaStep(), $subStepsBuffer); } } else { $stepsBuffer .= $this->renderStep($step); } } $scenarioTemplate = new \Text_Template( $this->templatePath . 'scenario.html' ); $failures = ''; $name = Descriptor::getTestSignatureUnique($test); if (isset($this->failures[$name])) { $failTemplate = new \Text_Template( $this->templatePath . 'fail.html' ); foreach ($this->failures[$name] as $failure) { $failTemplate->setVar(['fail' => nl2br($failure)]); $failures .= $failTemplate->render() . PHP_EOL; } $this->failures[$name] = []; } $png = ''; $html = ''; if ($test instanceof TestInterface) { $reports = $test->getMetadata()->getReports(); if (isset($reports['png'])) { $localPath = PathResolver::getRelativeDir($reports['png'], codecept_output_dir()); $png = "
    failure screenshot
    "; } if (isset($reports['html'])) { $localPath = PathResolver::getRelativeDir($reports['html'], codecept_output_dir()); $html = "See HTML snapshot of a failed page"; } } $toggle = $stepsBuffer ? '+' : ''; $testString = htmlspecialchars(ucfirst(Descriptor::getTestAsString($test))); $testString = preg_replace('~^([\s\w\\\]+):\s~', '$1 » ', $testString); $scenarioTemplate->setVar( [ 'id' => ++$this->id, 'name' => $testString, 'scenarioStatus' => $scenarioStatus, 'steps' => $stepsBuffer, 'toggle' => $toggle, 'failure' => $failures, 'png' => $png, 'html' => $html, 'time' => round($time, 2) ] ); $this->scenarios .= $scenarioTemplate->render(); } public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) { $suiteTemplate = new \Text_Template( $this->templatePath . 'suite.html' ); if (!$suite->getName()) { return; } $suiteTemplate->setVar(['suite' => ucfirst($suite->getName())]); $this->scenarios .= $suiteTemplate->render(); } /** * Handler for 'end run' event. */ protected function endRun() { $scenarioHeaderTemplate = new \Text_Template( $this->templatePath . 'scenario_header.html' ); $status = !$this->failed ? 'OK' : 'FAILED'; $scenarioHeaderTemplate->setVar( [ 'name' => 'Codeception Results', 'status' => $status, 'time' => round($this->timeTaken, 1) ] ); $header = $scenarioHeaderTemplate->render(); $scenariosTemplate = new \Text_Template( $this->templatePath . 'scenarios.html' ); $scenariosTemplate->setVar( [ 'header' => $header, 'scenarios' => $this->scenarios, 'successfulScenarios' => $this->successful, 'failedScenarios' => $this->failed, 'skippedScenarios' => $this->skipped, 'incompleteScenarios' => $this->incomplete, 'uselessScenarios' => $this->risky, ] ); $this->write($scenariosTemplate->render()); } /** * An error occurred. * * @param \PHPUnit\Framework\Test $test * @param \Exception $e * @param float $time */ public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->failures[Descriptor::getTestSignatureUnique($test)][] = $this->cleanMessage($e); parent::addError($test, $e, $time); } /** * A failure occurred. * * @param \PHPUnit\Framework\Test $test * @param \PHPUnit\Framework\AssertionFailedError $e * @param float $time */ public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) { $this->failures[Descriptor::getTestSignatureUnique($test)][] = $this->cleanMessage($e); parent::addFailure($test, $e, $time); } /** * Starts test. * * @param \PHPUnit\Framework\Test $test */ public function startTest(\PHPUnit\Framework\Test $test) { $name = Descriptor::getTestSignatureUnique($test); if (isset($this->failures[$name])) { // test failed in before hook return; } // start test and mark initialize as passed parent::startTest($test); } /** * @param $step * @return string */ protected function renderStep(Step $step) { $stepTemplate = new \Text_Template($this->templatePath . 'step.html'); $stepTemplate->setVar(['action' => $step->getHtml(), 'error' => $step->hasFailed() ? 'failedStep' : '']); return $stepTemplate->render(); } /** * @param $metaStep * @param $substepsBuffer * @return string */ protected function renderSubsteps(Meta $metaStep, $substepsBuffer) { $metaTemplate = new \Text_Template($this->templatePath . 'substeps.html'); $metaTemplate->setVar(['metaStep' => $metaStep->getHtml(), 'error' => $metaStep->hasFailed() ? 'failedStep' : '', 'steps' => $substepsBuffer, 'id' => uniqid()]); return $metaTemplate->render(); } private function cleanMessage($exception) { $msg = $exception->getMessage(); $msg = str_replace(['','','',''], ['','','',''], $msg); return htmlentities($msg); } } OutputInterface::VERBOSITY_NORMAL, $options['colors'] ? 'always' : 'never'); $this->dispatcher = $dispatcher; } protected function printDefect(\PHPUnit\Framework\TestFailure $defect, $count) { $this->write("\n---------\n"); $this->dispatch( $this->dispatcher, Events::TEST_FAIL_PRINT, new FailEvent($defect->failedTest(), null, $defect->thrownException(), $count) ); } /** * @param \PHPUnit\Framework\TestFailure $defect */ protected function printDefectTrace(\PHPUnit\Framework\TestFailure $defect) { $this->write($defect->getExceptionAsString()); $this->writeNewLine(); $stackTrace = \PHPUnit\Util\Filter::getFilteredStacktrace($defect->thrownException(), false); foreach ($stackTrace as $i => $frame) { if (!isset($frame['file'])) { continue; } $this->write( sprintf( "#%d %s(%s)", $i + 1, $frame['file'], isset($frame['line']) ? $frame['line'] : '?' ) ); $this->writeNewLine(); } } public function startTest(\PHPUnit\Framework\Test $test) { if ($test instanceof Unit) { parent::startTest($test); } } public function endTest(\PHPUnit\Framework\Test $test, $time) { if ($test instanceof \PHPUnit\Framework\TestCase or $test instanceof \Codeception\Test\Test) { $this->numAssertions += $test->getNumAssertions(); } $this->lastTestFailed = false; } public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->lastTestFailed = true; } public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) { $this->lastTestFailed = true; } public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time) { $this->lastTestFailed = true; } public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->lastTestFailed = true; } public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->lastTestFailed = true; } } dispatcher = $dispatcher; } /** * Risky test. * * @param PHPUnit\Framework\Test $test * @param Exception $e * @param float $time * @since Method available since Release 4.0.0 */ public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->unsuccessfulTests[] = spl_object_hash($test); $this->fire('test.useless', new FailEvent($test, $time, $e)); } public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) { $this->unsuccessfulTests[] = spl_object_hash($test); $this->fire(Events::TEST_FAIL, new FailEvent($test, $time, $e)); } public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->unsuccessfulTests[] = spl_object_hash($test); $this->fire(Events::TEST_ERROR, new FailEvent($test, $time, $e)); } // This method was added in PHPUnit 6 public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time) { $this->unsuccessfulTests[] = spl_object_hash($test); $this->fire(Events::TEST_WARNING, new FailEvent($test, $time, $e)); } public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { if (in_array(spl_object_hash($test), $this->skippedTests)) { return; } $this->unsuccessfulTests[] = spl_object_hash($test); $this->fire(Events::TEST_INCOMPLETE, new FailEvent($test, $time, $e)); $this->skippedTests[] = spl_object_hash($test); } public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { if (in_array(spl_object_hash($test), $this->skippedTests)) { return; } $this->unsuccessfulTests[] = spl_object_hash($test); $this->fire(Events::TEST_SKIPPED, new FailEvent($test, $time, $e)); $this->skippedTests[] = spl_object_hash($test); } public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) { $this->dispatch($this->dispatcher, 'suite.start', new SuiteEvent($suite)); } public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) { $this->dispatch($this->dispatcher, 'suite.end', new SuiteEvent($suite)); } public function startTest(\PHPUnit\Framework\Test $test) { $this->dispatch($this->dispatcher, Events::TEST_START, new TestEvent($test)); if (!$test instanceof TestInterface) { return; } if ($test->getMetadata()->isBlocked()) { return; } try { $this->startedTests[] = spl_object_hash($test); $this->fire(Events::TEST_BEFORE, new TestEvent($test)); } catch (\PHPUnit\Framework\IncompleteTestError $e) { $test->getTestResultObject()->addFailure($test, $e, 0); } catch (\PHPUnit\Framework\SkippedTestError $e) { $test->getTestResultObject()->addFailure($test, $e, 0); } catch (\Exception $e) { $test->getTestResultObject()->addError($test, $e, 0); } } public function endTest(\PHPUnit\Framework\Test $test, $time) { $hash = spl_object_hash($test); if (!in_array($hash, $this->unsuccessfulTests)) { $this->fire(Events::TEST_SUCCESS, new TestEvent($test, $time)); } if (in_array($hash, $this->startedTests)) { $this->fire(Events::TEST_AFTER, new TestEvent($test, $time)); } $this->dispatch($this->dispatcher, Events::TEST_END, new TestEvent($test, $time)); } protected function fire($event, TestEvent $eventType) { $test = $eventType->getTest(); if ($test instanceof TestInterface) { foreach ($test->getMetadata()->getGroups() as $group) { $this->dispatch($this->dispatcher, $event . '.' . $group, $eventType); } } $this->dispatch($this->dispatcher, $event, $eventType); } } dispatch($eventObject, $eventType); } else { //Symfony 4.2 or lower $dispatcher->dispatch($eventType, $eventObject); } } } string) { throw new \PHPUnit\Framework\ExpectationFailedException( "Element $selector was found", $comparisonFailure ); } $output = "There was $selector element"; $output .= $this->uriMessage("on page"); $output .= $this->nodesList($nodes, $this->string); $output .= "\ncontaining '{$this->string}'"; throw new \PHPUnit\Framework\ExpectationFailedException( $output, $comparisonFailure ); } public function toString() { if ($this->string) { return 'that contains text "' . $this->string . '"'; } } } expected = $expected; } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * * @param mixed $other Value or object to evaluate. * * @return bool */ protected function matches($other) { $jsonResponseArray = new JsonArray($other); if (!is_array($jsonResponseArray->toArray())) { throw new \PHPUnit\Framework\AssertionFailedError('JSON response is not an array: ' . $other); } if ($jsonResponseArray->containsArray($this->expected)) { return true; } $comparator = new ArrayComparator(); $comparator->setFactory(new Factory); try { $comparator->assertEquals($this->expected, $jsonResponseArray->toArray()); } catch (ComparisonFailure $failure) { throw new \PHPUnit\Framework\ExpectationFailedException( "Response JSON does not contain the provided JSON\n", $failure ); } } /** * Returns a string representation of the constraint. * * @return string */ public function toString() { //unused return ''; } protected function failureDescription($other) { //unused return ''; } } string = $this->normalizeText((string)$string); $this->uri = $uri; } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * * @param mixed $other Value or object to evaluate. * * @return bool */ protected function matches($other) { $other = $this->normalizeText($other); return mb_stripos($other, $this->string, null, 'UTF-8') !== false; } /** * @param $text * @return string */ private function normalizeText($text) { $text = strtr($text, "\r\n", " "); return trim(preg_replace('/\\s{2,}/', ' ', $text)); } /** * Returns a string representation of the constraint. * * @return string */ public function toString() { return sprintf( 'contains "%s"', $this->string ); } protected function failureDescription($pageContent) { $message = $this->uriMessage('on page'); $message->append("\n--> "); $message->append(mb_substr($pageContent, 0, 300, 'utf-8')); if (mb_strlen($pageContent, 'utf-8') > 300) { $debugMessage = new Message( "[Content too long to display. See complete response in '" . codecept_output_dir() . "' directory]" ); $message->append("\n")->append($debugMessage); } $message->append("\n--> "); return $message->getMessage() . $this->toString(); } protected function uriMessage($onPage = "") { if (!$this->uri) { return new Message(''); } $message = new Message($this->uri); $message->prepend(" $onPage "); return $message; } } string) { throw new \PHPUnit\Framework\ExpectationFailedException( "Element '$selector' was found", $comparisonFailure ); } /** @var $nodes DomCrawler * */ $output = "There was '$selector' element"; $output .= $this->uriMessage('on page'); $output .= $this->nodesList($nodes, $this->string); $output .= "\ncontaining '{$this->string}'"; throw new \PHPUnit\Framework\ExpectationFailedException( $output, $comparisonFailure ); } public function toString() { if ($this->string) { return 'that contains text "' . $this->string . '"'; } } } jsonType = $jsonType; $this->match = $match; } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * * @param mixed $jsonArray Value or object to evaluate. * * @return bool */ protected function matches($jsonArray) { if ($jsonArray instanceof JsonArray) { $jsonArray = $jsonArray->toArray(); } $matched = (new JsonTypeUtil($jsonArray))->matches($this->jsonType); if ($this->match) { if ($matched !== true) { throw new \PHPUnit\Framework\ExpectationFailedException($matched); } } else { if ($matched === true) { throw new \PHPUnit\Framework\ExpectationFailedException('Unexpectedly response matched: ' . json_encode($jsonArray)); } } return true; } /** * Returns a string representation of the constraint. * * @return string */ public function toString() { //unused return ''; } protected function failureDescription($other) { //unused return ''; } } count()) { return false; } if ($this->string === '') { return true; } foreach ($nodes as $node) { if (parent::matches($node->nodeValue)) { return true; } } return false; } protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null) { /** @var $nodes DomCrawler * */ if (!$nodes->count()) { throw new ElementNotFound($selector, 'Element located either by name, CSS or XPath'); } $output = "Failed asserting that any element by '$selector'"; $output .= $this->uriMessage('on page'); $output .= " "; if ($nodes->count() < 10) { $output .= $this->nodesList($nodes); } else { $message = new Message("[total %s elements]"); $output .= $message->with($nodes->count())->getMessage(); } $output .= "\ncontains text '{$this->string}'"; throw new \PHPUnit\Framework\ExpectationFailedException( $output, $comparisonFailure ); } protected function failureDescription($other) { $desc = ''; foreach ($other as $o) { $desc .= parent::failureDescription($o->textContent); } return $desc; } protected function nodesList(DomCrawler $nodes, $contains = null) { $output = ""; foreach ($nodes as $node) { if ($contains && strpos($node->nodeValue, $contains) === false) { continue; } $output .= "\n+ " . $node->C14N(); } return $output; } } string === '') { return true; } foreach ($nodes as $node) { /** @var $node \WebDriverElement * */ if (!$node->isDisplayed()) { continue; } if (parent::matches(htmlspecialchars_decode($node->getText()))) { return true; } } return false; } protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null) { if (!count($nodes)) { throw new ElementNotFound($selector, 'Element located either by name, CSS or XPath'); } $output = "Failed asserting that any element by " . Locator::humanReadableString($selector); $output .= $this->uriMessage('on page'); if (count($nodes) < 5) { $output .= "\nElements: "; $output .= $this->nodesList($nodes); } else { $message = new Message("[total %s elements]"); $output .= $message->with(count($nodes)); } $output .= "\ncontains text '" . $this->string . "'"; throw new \PHPUnit\Framework\ExpectationFailedException( $output, $comparisonFailure ); } protected function failureDescription($nodes) { $desc = ''; foreach ($nodes as $node) { $desc .= parent::failureDescription($node->getText()); } return $desc; } protected function nodesList($nodes, $contains = null) { $output = ""; foreach ($nodes as $node) { if ($contains && strpos($node->getText(), $contains) === false) { continue; } /** @var $node \WebDriverElement * */ $message = new Message("\n+ <%s> %s"); $output .= $message->with($node->getTagName(), $node->getText()); } return $output; } } _setUp(); } } protected function tearDown() { if (method_exists($this, '_tearDown')) { $this->_tearDown(); } } public static function setUpBeforeClass() { if (method_exists(get_called_class(), '_setUpBeforeClass')) { static::_setUpBeforeClass(); } } public static function tearDownAfterClass() { if (method_exists(get_called_class(), '_tearDownAfterClass')) { static::_tearDownAfterClass(); } } public static function assertStringContainsString($needle, $haystack, $message = '') { if (!is_string($needle)) { throw new AssertionFailedError('Needle is not string'); } if (!is_string($haystack)) { throw new AssertionFailedError('Haystack is not string'); } \Codeception\PHPUnit\TestCase::assertContains($needle, $haystack, $message); } public static function assertStringNotContainsString($needle, $haystack, $message = '') { if (!is_string($needle)) { throw new AssertionFailedError('Needle is not string'); } if (!is_string($haystack)) { throw new AssertionFailedError('Haystack is not string'); } \Codeception\PHPUnit\TestCase::assertNotContains($needle, $haystack, $message); } public static function assertStringContainsStringIgnoringCase($needle, $haystack, $message = '') { if (!is_string($needle)) { throw new AssertionFailedError('Needle is not string'); } if (!is_string($haystack)) { throw new AssertionFailedError('Haystack is not string'); } \Codeception\PHPUnit\TestCase::assertContains($needle, $haystack, $message, true); } public static function assertStringNotContainsStringIgnoringCase($needle, $haystack, $message = '') { if (!is_string($needle)) { throw new AssertionFailedError('Needle is not string'); } if (!is_string($haystack)) { throw new AssertionFailedError('Haystack is not string'); } \Codeception\PHPUnit\TestCase::assertNotContains($needle, $haystack, $message, true); } public static function assertIsArray($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('array', $actual, $message); } public static function assertIsBool($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('bool', $actual, $message); } public static function assertIsFloat($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('float', $actual, $message); } public static function assertIsInt($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('int', $actual, $message); } public static function assertIsNumeric($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('numeric', $actual, $message); } public static function assertIsObject($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('object', $actual, $message); } public static function assertIsResource($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('resource', $actual, $message); } public static function assertIsString($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('string', $actual, $message); } public static function assertIsScalar($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('scalar', $actual, $message); } public static function assertIsCallable($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertInternalType('callable', $actual, $message); } public static function assertIsNotArray($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('array', $actual, $message); } public static function assertIsNotBool($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('bool', $actual, $message); } public static function assertIsNotFloat($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('float', $actual, $message); } public static function assertIsNotInt($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('int', $actual, $message); } public static function assertIsNotNumeric($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('numeric', $actual, $message); } public static function assertIsNotObject($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('object', $actual, $message); } public static function assertIsNotResource($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('resource', $actual, $message); } public static function assertIsNotString($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('string', $actual, $message); } public static function assertIsNotScalar($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('scalar', $actual, $message); } public static function assertIsNotCallable($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('callable', $actual, $message); } public static function assertIsNotIterable($actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotInternalType('iterable', $actual, $message); } public static function assertEqualsCanonicalizing($expected, $actual, $message = '') { \Codeception\PHPUnit\TestCase::assertEquals($expected, $actual, $message, 0.0, 10, true, false); } public static function assertNotEqualsCanonicalizing($expected, $actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotEquals($expected, $actual, $message, 0.0, 10, true, false); } public static function assertEqualsIgnoringCase($expected, $actual, $message = '') { \Codeception\PHPUnit\TestCase::assertEquals($expected, $actual, $message, 0.0, 10, false, true); } public static function assertNotEqualsIgnoringCase($expected, $actual, $message = '') { \Codeception\PHPUnit\TestCase::assertNotEquals($expected, $actual, $message, 0.0, 10, false, true); } public static function assertEqualsWithDelta($expected, $actual, $delta, $message = '') { \Codeception\PHPUnit\TestCase::assertEquals($expected, $actual, $message, $delta, 10, false, false); } public static function assertNotEqualsWithDelta($expected, $actual, $delta, $message = '') { \Codeception\PHPUnit\TestCase::assertNotEquals($expected, $actual, $message, $delta, 10, false, false); } } getFileName(); } else { $reflector = new \ReflectionClass($test); $filename = $reflector->getFileName(); } if ($filename !== $this->currentFile) { if ($this->currentFile !== null) { parent::endTestSuite(new TestSuite()); } //initialize all values to avoid warnings $this->testSuiteAssertions[self::FILE_LEVEL] = 0; $this->testSuiteTests[self::FILE_LEVEL] = 0; $this->testSuiteTimes[self::FILE_LEVEL] = 0; $this->testSuiteErrors[self::FILE_LEVEL] = 0; $this->testSuiteFailures[self::FILE_LEVEL] = 0; $this->testSuiteSkipped[self::FILE_LEVEL] = 0; $this->testSuiteLevel = self::FILE_LEVEL; $this->currentFile = $filename; $this->currentFileSuite = $this->document->createElement('testsuite'); if ($test instanceof Reported) { $reportFields = $test->getReportFields(); $class = isset($reportFields['class']) ? $reportFields['class'] : $reportFields['name']; $this->currentFileSuite->setAttribute('name', $class); } else { $this->currentFileSuite->setAttribute('name', get_class($test)); } $this->currentFileSuite->setAttribute('file', $filename); $this->testSuites[self::SUITE_LEVEL]->appendChild($this->currentFileSuite); $this->testSuites[self::FILE_LEVEL] = $this->currentFileSuite; } if (!$test instanceof Reported) { parent::startTest($test); return; } $this->currentTestCase = $this->document->createElement('testcase'); $isStrict = Configuration::config()['settings']['strict_xml']; foreach ($test->getReportFields() as $attr => $value) { if ($isStrict and !in_array($attr, $this->strictAttributes)) { continue; } $this->currentTestCase->setAttribute($attr, $value); } } public function endTest(\PHPUnit\Framework\Test $test, $time) { if ($this->currentTestCase !== null && $test instanceof Test) { $numAssertions = $test->getNumAssertions(); $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; $this->currentTestCase->setAttribute( 'assertions', $numAssertions ); } if ($test instanceof TestCase) { parent::endTest($test, $time); return; } // In PhpUnit 7.4.*, parent::endTest ignores tests that aren't instances of TestCase // so I copied this code from PhpUnit 7.3.5 $this->currentTestCase->setAttribute( 'time', \sprintf('%F', $time) ); $this->testSuites[$this->testSuiteLevel]->appendChild( $this->currentTestCase ); $this->testSuiteTests[$this->testSuiteLevel]++; $this->testSuiteTimes[$this->testSuiteLevel] += $time; $this->currentTestCase = null; } /** * Cleans the mess caused by test suite manipulation in startTest */ public function endTestSuite(TestSuite $suite) { if ($suite->getName()) { if ($this->currentFile) { //close last file in the test suite parent::endTestSuite(new TestSuite()); $this->currentFile = null; } $this->testSuiteLevel = self::SUITE_LEVEL; } parent::endTestSuite($suite); } } currentTestCase = $this->document->createElement('testcase'); $isStrict = Configuration::config()['settings']['strict_xml']; foreach ($test->getReportFields() as $attr => $value) { if ($isStrict and !in_array($attr, $this->strictAttributes)) { continue; } $this->currentTestCase->setAttribute($attr, $value); } } public function endTest(\PHPUnit\Framework\Test $test, $time) { if ($this->currentTestCase !== null and $test instanceof Test) { $numAssertions = $test->getNumAssertions(); $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; $this->currentTestCase->setAttribute( 'assertions', $numAssertions ); } parent::endTest($test, $time); } } testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_ERROR; $this->failed++; } /** * A failure occurred. * * @param \PHPUnit\Framework\Test $test * @param \PHPUnit\Framework\AssertionFailedError $e * @param float $time */ public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) { $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_FAILURE; $this->failed++; } /** * A warning occurred. * * @param \PHPUnit\Framework\Test $test * @param \PHPUnit\Framework\Warning $e * @param float $time */ public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time) { $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_WARNING; $this->warned++; } /** * Incomplete test. * * @param \PHPUnit\Framework\Test $test * @param \Exception $e * @param float $time */ public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_INCOMPLETE; $this->incomplete++; } /** * Risky test. * * @param \PHPUnit\Framework\Test $test * @param \Exception $e * @param float $time * * @since Method available since Release 4.0.0 */ public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_RISKY; $this->risky++; } /** * Skipped test. * * @param \PHPUnit\Framework\Test $test * @param \Exception $e * @param float $time * * @since Method available since Release 3.0.0 */ public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_SKIPPED; $this->skipped++; } public function startTest(\PHPUnit\Framework\Test $test) { $this->testStatus = \PHPUnit\Runner\BaseTestRunner::STATUS_PASSED; } } * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace { if (!class_exists('PHPUnit_Util_String')) { /** * String helpers. */ class PHPUnit_Util_String { /** * Converts a string to UTF-8 encoding. * * @param string $string * * @return string */ public static function convertToUtf8($string) { return mb_convert_encoding($string, 'UTF-8'); } /** * Checks a string for UTF-8 encoding. * * @param string $string * * @return bool */ protected static function isUtf8($string) { $length = strlen($string); for ($i = 0; $i < $length; $i++) { if (ord($string[$i]) < 0x80) { $n = 0; } elseif ((ord($string[$i]) & 0xE0) == 0xC0) { $n = 1; } elseif ((ord($string[$i]) & 0xF0) == 0xE0) { $n = 2; } elseif ((ord($string[$i]) & 0xF0) == 0xF0) { $n = 3; } else { return false; } for ($j = 0; $j < $n; $j++) { if ((++$i == $length) || ((ord($string[$i]) & 0xC0) != 0x80)) { return false; } } } return true; } } } } namespace PHPUnit\Util\Log { /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ /** * A TestListener that generates JSON messages. */ if (!class_exists('\PHPUnit\Util\Log\JSON')) { class JSON extends \PHPUnit\Util\Printer implements \PHPUnit\Framework\TestListener { /** * @var string */ protected $currentTestSuiteName = ''; /** * @var string */ protected $currentTestName = ''; /** * @var bool */ protected $currentTestPass = true; /** * @var array */ protected $logEvents = []; /** * An error occurred. * * @param \PHPUnit\Framework\Test $test * @param Exception $e * @param float $time */ public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->writeCase( 'error', $time, \PHPUnit\Util\Filter::getFilteredStacktrace($e, false), \PHPUnit\Framework\TestFailure::exceptionToString($e), $test ); $this->currentTestPass = false; } /** * A warning occurred. * * @param \PHPUnit\Framework\Test $test * @param \PHPUnit\Framework\Warning $e * @param float $time */ public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time) { $this->writeCase( 'warning', $time, \PHPUnit\Util\Filter::getFilteredStacktrace($e, false), \PHPUnit\Framework\TestFailure::exceptionToString($e), $test ); $this->currentTestPass = false; } /** * A failure occurred. * * @param \PHPUnit\Framework\Test $test * @param \PHPUnit\Framework\AssertionFailedError $e * @param float $time */ public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) { $this->writeCase( 'fail', $time, \PHPUnit\Util\Filter::getFilteredStacktrace($e, false), \PHPUnit\Framework\TestFailure::exceptionToString($e), $test ); $this->currentTestPass = false; } /** * Incomplete test. * * @param \PHPUnit\Framework\Test $test * @param Exception $e * @param float $time */ public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->writeCase( 'error', $time, \PHPUnit\Util\Filter::getFilteredStacktrace($e, false), 'Incomplete Test: ' . $e->getMessage(), $test ); $this->currentTestPass = false; } /** * Risky test. * * @param \PHPUnit\Framework\Test $test * @param Exception $e * @param float $time */ public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->writeCase( 'error', $time, \PHPUnit\Util\Filter::getFilteredStacktrace($e, false), 'Risky Test: ' . $e->getMessage(), $test ); $this->currentTestPass = false; } /** * Skipped test. * * @param \PHPUnit\Framework\Test $test * @param Exception $e * @param float $time */ public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->writeCase( 'error', $time, \PHPUnit\Util\Filter::getFilteredStacktrace($e, false), 'Skipped Test: ' . $e->getMessage(), $test ); $this->currentTestPass = false; } /** * A testsuite started. * * @param \PHPUnit\Framework\TestSuite $suite */ public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) { $this->currentTestSuiteName = $suite->getName(); $this->currentTestName = ''; $this->addLogEvent( [ 'event' => 'suiteStart', 'suite' => $this->currentTestSuiteName, 'tests' => count($suite) ] ); } /** * A testsuite ended. * * @param \PHPUnit\Framework\TestSuite $suite */ public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) { $this->currentTestSuiteName = ''; $this->currentTestName = ''; $this->writeAll($this->logEvents); } /** * A test started. * * @param \PHPUnit\Framework\Test $test */ public function startTest(\PHPUnit\Framework\Test $test) { $this->currentTestName = \PHPUnit\Util\Test::describe($test); $this->currentTestPass = true; $this->addLogEvent( [ 'event' => 'testStart', 'suite' => $this->currentTestSuiteName, 'test' => $this->currentTestName ] ); } /** * A test ended. * * @param \PHPUnit\Framework\Test $test * @param float $time */ public function endTest(\PHPUnit\Framework\Test $test, $time) { if ($this->currentTestPass) { $this->writeCase('pass', $time, [], '', $test); } } /** * @param string $status * @param float $time * @param array $trace * @param string $message * @param \PHPUnit\Framework\TestCase|null $test */ protected function writeCase($status, $time, array $trace = [], $message = '', $test = null) { $output = ''; // take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) { $output = $test->getActualOutput(); } $this->addLogEvent( [ 'event' => 'test', 'suite' => $this->currentTestSuiteName, 'test' => $this->currentTestName, 'status' => $status, 'time' => $time, 'trace' => $trace, 'message' => \PHPUnit_Util_String::convertToUtf8($message), 'output' => $output, ] ); } /** * @param array $event_data */ protected function addLogEvent($event_data = []) { if (count($event_data)) { array_push($this->logEvents, $event_data); } } /** * @param array $buffer */ public function writeAll($buffer) { array_walk_recursive( $buffer, function (&$input) { if (is_string($input)) { $input = \PHPUnit_Util_String::convertToUtf8($input); } } ); parent::write(json_encode($buffer, JSON_PRETTY_PRINT)); } } } /* * This file is part of PHPUnit. * * (c) Sebastian Bergmann * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (!class_exists('\PHPUnit\Util\Log\TAP')) { /** * A TestListener that generates a logfile of the * test execution using the Test Anything Protocol (TAP). */ class TAP extends \PHPUnit\Util\Printer implements \PHPUnit\Framework\TestListener { /** * @var int */ protected $testNumber = 0; /** * @var int */ protected $testSuiteLevel = 0; /** * @var bool */ protected $testSuccessful = true; /** * Constructor. * * @param mixed $out * * @throws \PHPUnit\Framework\Exception */ public function __construct($out = null) { parent::__construct($out); $this->write("TAP version 13\n"); } /** * An error occurred. * * @param \PHPUnit\Framework\Test $test * @param Exception $e * @param float $time */ public function addError(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->writeNotOk($test, 'Error'); } /** * A warning occurred. * * @param \PHPUnit\Framework\Test $test * @param \PHPUnit\Framework\Warning $e * @param float $time */ public function addWarning(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\Warning $e, $time) { $this->writeNotOk($test, 'Warning'); } /** * A failure occurred. * * @param \PHPUnit\Framework\Test $test * @param \PHPUnit\Framework\AssertionFailedError $e * @param float $time */ public function addFailure(\PHPUnit\Framework\Test $test, \PHPUnit\Framework\AssertionFailedError $e, $time) { $this->writeNotOk($test, 'Failure'); $message = explode( "\n", \PHPUnit\Framework\TestFailure::exceptionToString($e) ); $diagnostic = [ 'message' => $message[0], 'severity' => 'fail' ]; if ($e instanceof \PHPUnit\Framework\ExpectationFailedException) { $cf = $e->getComparisonFailure(); if ($cf !== null) { $diagnostic['data'] = [ 'got' => $cf->getActual(), 'expected' => $cf->getExpected() ]; } } $yaml = new \Symfony\Component\Yaml\Dumper; $this->write( sprintf( " ---\n%s ...\n", $yaml->dump($diagnostic, 2, 2) ) ); } /** * Incomplete test. * * @param \PHPUnit\Framework\Test $test * @param \Exception $e * @param float $time */ public function addIncompleteTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->writeNotOk($test, '', 'TODO Incomplete Test'); } /** * Risky test. * * @param \PHPUnit\Framework\Test $test * @param Exception $e * @param float $time */ public function addRiskyTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->write( sprintf( "ok %d - # RISKY%s\n", $this->testNumber, $e->getMessage() != '' ? ' ' . $e->getMessage() : '' ) ); $this->testSuccessful = false; } /** * Skipped test. * * @param \PHPUnit\Framework\Test $test * @param Exception $e * @param float $time */ public function addSkippedTest(\PHPUnit\Framework\Test $test, \Exception $e, $time) { $this->write( sprintf( "ok %d - # SKIP%s\n", $this->testNumber, $e->getMessage() != '' ? ' ' . $e->getMessage() : '' ) ); $this->testSuccessful = false; } /** * A testsuite started. * * @param \PHPUnit\Framework\TestSuite $suite */ public function startTestSuite(\PHPUnit\Framework\TestSuite $suite) { $this->testSuiteLevel++; } /** * A testsuite ended. * * @param \PHPUnit\Framework\TestSuite $suite */ public function endTestSuite(\PHPUnit\Framework\TestSuite $suite) { $this->testSuiteLevel--; if ($this->testSuiteLevel == 0) { $this->write(sprintf("1..%d\n", $this->testNumber)); } } /** * A test started. * * @param \PHPUnit\Framework\Test $test */ public function startTest(\PHPUnit\Framework\Test $test) { $this->testNumber++; $this->testSuccessful = true; } /** * A test ended. * * @param \PHPUnit\Framework\Test $test * @param float $time */ public function endTest(\PHPUnit\Framework\Test $test, $time) { if ($this->testSuccessful === true) { $this->write( sprintf( "ok %d - %s\n", $this->testNumber, \PHPUnit\Util\Test::describe($test) ) ); } $this->writeDiagnostics($test); } /** * @param \PHPUnit\Framework\Test $test * @param string $prefix * @param string $directive */ protected function writeNotOk(\PHPUnit\Framework\Test $test, $prefix = '', $directive = '') { $this->write( sprintf( "not ok %d - %s%s%s\n", $this->testNumber, $prefix != '' ? $prefix . ': ' : '', \PHPUnit\Util\Test::describe($test), $directive != '' ? ' # ' . $directive : '' ) ); $this->testSuccessful = false; } /** * @param \PHPUnit\Framework\Test $test */ private function writeDiagnostics(\PHPUnit\Framework\Test $test) { if (!$test instanceof \PHPUnit\Framework\TestCase) { return; } if (!$test->hasOutput()) { return; } foreach (explode("\n", trim($test->getActualOutput())) as $line) { $this->write( sprintf( "# %s\n", $line ) ); } } } } } // @codingStandardsIgnoreEnd false, 'phpunit-xml' => false, 'html' => false, 'tap' => false, 'json' => false, 'report' => false ]; protected $config = []; protected $logDir = null; public function __construct() { $this->config = Configuration::config(); $this->logDir = Configuration::outputDir(); // prepare log dir $this->phpUnitOverriders(); parent::__construct(); } public function phpUnitOverriders() { require_once __DIR__ . DIRECTORY_SEPARATOR . 'Overrides/Filter.php'; } /** * @return null|\PHPUnit\TextUI\ResultPrinter */ public function getPrinter() { return $this->printer; } public function prepareSuite(\PHPUnit\Framework\Test $suite, array &$arguments) { $this->handleConfiguration($arguments); $filterFactory = new \PHPUnit\Runner\Filter\Factory(); if ($arguments['groups']) { $filterFactory->addFilter( new \ReflectionClass('PHPUnit\Runner\Filter\IncludeGroupFilterIterator'), $arguments['groups'] ); } if ($arguments['excludeGroups']) { $filterFactory->addFilter( new \ReflectionClass('PHPUnit\Runner\Filter\ExcludeGroupFilterIterator'), $arguments['excludeGroups'] ); } if ($arguments['filter']) { $filterFactory->addFilter( new \ReflectionClass('Codeception\PHPUnit\FilterTest'), $arguments['filter'] ); } $suite->injectFilter($filterFactory); } public function doEnhancedRun( \PHPUnit\Framework\Test $suite, \PHPUnit\Framework\TestResult $result, array $arguments = [] ) { unset($GLOBALS['app']); // hook for not to serialize globals $result->convertErrorsToExceptions(false); if (isset($arguments['report_useless_tests'])) { $result->beStrictAboutTestsThatDoNotTestAnything((bool)$arguments['report_useless_tests']); } if (isset($arguments['disallow_test_output'])) { $result->beStrictAboutOutputDuringTests((bool)$arguments['disallow_test_output']); } if (empty(self::$persistentListeners)) { $this->applyReporters($result, $arguments); } if (class_exists('\Symfony\Bridge\PhpUnit\SymfonyTestsListener')) { $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : []; $listener = new \Symfony\Bridge\PhpUnit\SymfonyTestsListener(); $listener->globalListenerDisabled(); $arguments['listeners'][] = $listener; } $arguments['listeners'][] = $this->printer; // clean up listeners between suites foreach ($arguments['listeners'] as $listener) { $result->addListener($listener); } $suite->run($result); unset($suite); foreach ($arguments['listeners'] as $listener) { $result->removeListener($listener); } return $result; } /** * @param \PHPUnit\Framework\TestResult $result * @param array $arguments * * @return array */ protected function applyReporters(\PHPUnit\Framework\TestResult $result, array $arguments) { foreach ($this->defaultListeners as $listener => $value) { if (!isset($arguments[$listener])) { $arguments[$listener] = $value; } } if ($arguments['report']) { self::$persistentListeners[] = $this->instantiateReporter('report'); } if ($arguments['html']) { codecept_debug('Printing HTML report into ' . $arguments['html']); self::$persistentListeners[] = $this->instantiateReporter( 'html', [$this->absolutePath($arguments['html'])] ); } if ($arguments['xml']) { codecept_debug('Printing JUNIT report into ' . $arguments['xml']); self::$persistentListeners[] = $this->instantiateReporter( 'xml', [$this->absolutePath($arguments['xml']), (bool)$arguments['log_incomplete_skipped']] ); } if ($arguments['phpunit-xml']) { codecept_debug('Printing PHPUNIT report into ' . $arguments['phpunit-xml']); self::$persistentListeners[] = $this->instantiateReporter( 'phpunit-xml', [$this->absolutePath($arguments['phpunit-xml']), (bool)$arguments['log_incomplete_skipped']] ); } if ($arguments['tap']) { codecept_debug('Printing TAP report into ' . $arguments['tap']); self::$persistentListeners[] = $this->instantiateReporter('tap', [$this->absolutePath($arguments['tap'])]); } if ($arguments['json']) { codecept_debug('Printing JSON report into ' . $arguments['json']); self::$persistentListeners[] = $this->instantiateReporter( 'json', [$this->absolutePath($arguments['json'])] ); } foreach (self::$persistentListeners as $listener) { if ($listener instanceof ConsolePrinter) { $this->printer = $listener; continue; } $result->addListener($listener); } } protected function instantiateReporter($name, $args = []) { if (!isset($this->config['reporters'][$name])) { throw new ConfigurationException("Reporter $name not defined"); } return (new \ReflectionClass($this->config['reporters'][$name]))->newInstanceArgs($args); } private function absolutePath($path) { if ((strpos($path, '/') === 0) or (strpos($path, ':') === 1)) { // absolute path return $path; } return $this->logDir . $path; } } getInnerIterator()->current(); if ($test instanceof \PHPUnit\Framework\TestSuite) { return true; } $name = Descriptor::getTestSignature($test); $index = Descriptor::getTestDataSetIndex($test); if (!is_null($index)) { $name .= " with data set #{$index}"; } $accepted = preg_match($this->filter, $name, $matches); // This fix the issue when an invalid dataprovider method generate a warning // See issue https://github.com/Codeception/Codeception/issues/4888 if($test instanceof \PHPUnit\Framework\WarningTestCase) { $message = $test->getMessage(); $accepted = preg_match($this->filter, $message, $matches); } if ($accepted && isset($this->filterMax)) { $set = end($matches); $accepted = $set >= $this->filterMin && $set <= $this->filterMax; } return $accepted; } } getPrevious() ? $e->getPrevious()->getTrace() : $e->getTrace(); if ($e instanceof \PHPUnit\Framework\ExceptionWrapper) { $trace = $e->getSerializableTrace(); } $eFile = $e->getFile(); $eLine = $e->getLine(); if (!self::frameExists($trace, $eFile, $eLine)) { array_unshift( $trace, ['file' => $eFile, 'line' => $eLine] ); } foreach ($trace as $step) { if (self::classIsFiltered($step) and $filter) { continue; } if (self::fileIsFiltered($step) and $filter) { continue; } if (!$asString) { $stackTrace[] = $step; continue; } if (!isset($step['file'])) { continue; } $stackTrace .= $step['file'] . ':' . $step['line'] . "\n"; } return $stackTrace; } protected static function classIsFiltered($step) { if (!isset($step['class'])) { return false; } $className = $step['class']; foreach (self::$filteredClassesPattern as $filteredClassName) { if (strpos($className, $filteredClassName) === 0) { return true; } } return false; } protected static function fileIsFiltered($step) { if (!isset($step['file'])) { return false; } if (strpos($step['file'], 'codecept.phar/') !== false) { return true; } if (strpos($step['file'], 'vendor' . DIRECTORY_SEPARATOR . 'phpunit') !== false) { return true; } if (strpos($step['file'], 'vendor' . DIRECTORY_SEPARATOR . 'codeception') !== false) { return true; } $modulePath = 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR . 'Module'; if (strpos($step['file'], $modulePath) !== false) { return false; // don`t filter modules } if (strpos($step['file'], 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR) !== false) { return true; } return false; } /** * @param array $trace * @param string $file * @param int $line * * @return bool */ private static function frameExists(array $trace, $file, $line) { foreach ($trace as $frame) { if (isset($frame['file']) && $frame['file'] == $file && isset($frame['line']) && $frame['line'] == $line) { return true; } } return false; } } '*']; file_put_contents(__DIR__ . '/composer.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } public function prepareDependencies() { $config = json_decode(file_get_contents(__DIR__ . '/composer.json'), true); $config['name'] = 'codeception/phpunit-wrapper-test'; $config['require-dev']['codeception/codeception'] = getenv('CODECEPTION_VERSION'); $config['require-dev']['codeception/module-asserts'] = '^1.3.1'; $config['require-dev']['codeception/module-cli'] = '^1.0'; $config['require-dev']['codeception/module-db'] = '^1.0'; $config['require-dev']['codeception/module-filesystem'] = '^1.0'; $config['require-dev']['codeception/module-phpbrowser'] = '^1.0.3'; $config['require-dev']['codeception/util-universalframework'] = '*'; $config['replace'] = ['codeception/phpunit-wrapper' => '*']; file_put_contents(__DIR__ . '/composer.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } public function prepareTests() { $this->_copyDir(__DIR__ . '/vendor/codeception/codeception/tests', __DIR__ . '/tests'); $this->_copy(__DIR__ . '/vendor/codeception/codeception/codeception.yml', __DIR__ .'/codeception.yml'); $this->_symlink(__DIR__ . '/vendor/bin/codecept', __DIR__ . '/codecept'); } public function prepareTestAutoloading() { $config = json_decode(file_get_contents(__DIR__ . '/composer.json'), true); $config['autoload-dev'] = [ 'classmap' => [ 'tests/cli/_steps', 'tests/data/DummyClass.php', 'tests/data/claypit/tests/_data' ] ]; file_put_contents(__DIR__ . '/composer.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } public function test($params) { return $this->taskExec(__DIR__ . '/vendor/bin/codecept run ' . $params) ->dir(__DIR__ .'/vendor/codeception/codeception') ->run(); } }loadSessionSnapshot('login')) return; * * // logging in * $I->amOnPage('/login'); * $I->fillField('name', 'jon'); * $I->fillField('password', '123345'); * $I->click('Login'); * * // saving snapshot * $I->saveSessionSnapshot('login'); * } * ?> * ``` * * @param $name * @return mixed */ public function saveSessionSnapshot($name); /** * Loads cookies from a saved snapshot. * Allows to reuse same session across tests without additional login. * * See [saveSessionSnapshot](#saveSessionSnapshot) * * @param $name * @return mixed */ public function loadSessionSnapshot($name); /** * Deletes session snapshot. * * See [saveSessionSnapshot](#saveSessionSnapshot) * * @param $name * @return mixed */ public function deleteSessionSnapshot($name); } getModule('{{MODULE_NAME}}')->_saveScreenshot(codecept_output_dir().'screenshot_1.png'); * ``` * @api * @param $filename */ public function _saveScreenshot($filename); } :@ondemand.saucelabs.com' * port: 80 * browser: chrome * capabilities: * platform: 'Windows 10' * ``` * * ### BrowserStack * * 1. Create an account at [BrowserStack](https://www.browserstack.com/) to get your username and access key * 2. In the module configuration use the format `username`:`access_key`@hub.browserstack.com' for `host` * 3. Configure `os` and `os_version` under `capabilities` to define the operating System * 4. If your site is available only locally or via VPN you should use a tunnel app. In this case add `browserstack.local` capability and set it to true. * * ```yaml * modules: * enabled: * - WebDriver: * url: http://mysite.com * host: ':@hub.browserstack.com' * port: 80 * browser: chrome * capabilities: * os: Windows * os_version: 10 * browserstack.local: true # for local testing * ``` * * ### LambdaTest * * 1. Create an account at [LambdaTest](https://www.lambdatest.com/) to get your username and access key * 2. In the module configuration use the format `username`:`access key`@hub.lambdatest.com' for `host` * 3. Configure `os` and `os_version` under `capabilities` to define the operating System * 4. If your site is available only locally or via VPN you should use a tunnel app. In this case add capabilities.setCapability("tunnel",true);. * * ```yaml * modules: * enabled: * - WebDriver: * url: http://mysite.com * host: ':@hub.lambdatest.com' * build: * name: * port: 80 * browser: chrome * capabilities: * os: Windows * os_version: 10 * browser_version: 86 * resolution: 1366x768 * tunnel: true # for local testing * ``` * * ### TestingBot * * 1. Create an account at [TestingBot](https://testingbot.com/) to get your key and secret * 2. In the module configuration use the format `key`:`secret`@hub.testingbot.com' for `host` * 3. Configure `platform` under `capabilities` to define the [Operating System](https://testingbot.com/support/getting-started/browsers.html) * 4. Run [TestingBot Tunnel](https://testingbot.com/support/other/tunnel) if your site can't be accessed from Internet * * ```yaml * modules: * enabled: * - WebDriver: * url: http://mysite.com * host: ':@hub.testingbot.com' * port: 80 * browser: chrome * capabilities: * platform: Windows 10 * ``` * * ## Configuration * * * `url` *required* - Base URL for your app (amOnPage opens URLs relative to this setting). * * `browser` *required* - Browser to launch. * * `host` - Selenium server host (127.0.0.1 by default). * * `port` - Selenium server port (4444 by default). * * `restart` - Set to `false` (default) to use the same browser window for all tests, or set to `true` to create a new window for each test. In any case, when all tests are finished the browser window is closed. * * `start` - Autostart a browser for tests. Can be disabled if browser session is started with `_initializeSession` inside a Helper. * * `window_size` - Initial window size. Set to `maximize` or a dimension in the format `640x480`. * * `clear_cookies` - Set to false to keep cookies, or set to true (default) to delete all cookies between tests. * * `wait` (default: 0 seconds) - Whenever element is required and is not on page, wait for n seconds to find it before fail. * * `capabilities` - Sets Selenium [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities). Should be a key-value array. * * `connection_timeout` - timeout for opening a connection to remote selenium server (30 seconds by default). * * `request_timeout` - timeout for a request to return something from remote selenium server (30 seconds by default). * * `pageload_timeout` - amount of time to wait for a page load to complete before throwing an error (default 0 seconds). * * `http_proxy` - sets http proxy server url for testing a remote server. * * `http_proxy_port` - sets http proxy server port * * `ssl_proxy` - sets ssl(https) proxy server url for testing a remote server. * * `ssl_proxy_port` - sets ssl(https) proxy server port * * `debug_log_entries` - how many selenium entries to print with `debugWebDriverLogs` or on fail (0 by default). * * `log_js_errors` - Set to true to include possible JavaScript to HTML report, or set to false (default) to deactivate. * * `webdriver_proxy` - sets http proxy to tunnel requests to the remote Selenium WebDriver through * * `webdriver_proxy_port` - sets http proxy server port to tunnel requests to the remote Selenium WebDriver through * * Example (`acceptance.suite.yml`) * * ```yaml * modules: * enabled: * - WebDriver: * url: 'http://localhost/' * browser: firefox * window_size: 1024x768 * capabilities: * unexpectedAlertBehaviour: 'accept' * firefox_profile: '~/firefox-profiles/codeception-profile.zip.b64' * ``` * * ## Loading Parts from other Modules * * While all Codeception modules are designed to work stand-alone, it's still possible to load *several* modules at once. To use e.g. the [Asserts module](https://codeception.com/docs/modules/Asserts) in your acceptance tests, just load it like this in your `acceptance.suite.yml`: * * ```yaml * modules: * enabled: * - WebDriver * - Asserts * ``` * * However, when loading a framework module (e.g. [Symfony](https://codeception.com/docs/modules/Symfony)) like this, it would lead to a conflict: When you call `$I->amOnPage()`, Codeception wouldn't know if you want to access the page using WebDriver's `amOnPage()`, or Symfony's `amOnPage()`. That's why possibly conflicting modules are separated into "parts". Here's how to load just the "services" part from e.g. Symfony: * ```yaml * modules: * enabled: * - WebDriver * - Symfony: * part: services * ``` * To find out which parts each module has, look at the "Parts" header on the module's page. * * ## Usage * * ### Locating Elements * * Most methods in this module that operate on a DOM element (e.g. `click`) accept a locator as the first argument, * which can be either a string or an array. * * If the locator is an array, it should have a single element, * with the key signifying the locator type (`id`, `name`, `css`, `xpath`, `link`, or `class`) * and the value being the locator itself. * This is called a "strict" locator. * Examples: * * * `['id' => 'foo']` matches `
    ` * * `['name' => 'foo']` matches `
    ` * * `['css' => 'input[type=input][value=foo]']` matches `` * * `['xpath' => "//input[@type='submit'][contains(@value, 'foo')]"]` matches `` * * `['link' => 'Click here']` matches `Click here` * * `['class' => 'foo']` matches `
    ` * * Writing good locators can be tricky. * The Mozilla team has written an excellent guide titled [Writing reliable locators for Selenium and WebDriver tests](https://blog.mozilla.org/webqa/2013/09/26/writing-reliable-locators-for-selenium-and-webdriver-tests/). * * If you prefer, you may also pass a string for the locator. This is called a "fuzzy" locator. * In this case, Codeception uses a a variety of heuristics (depending on the exact method called) to determine what element you're referring to. * For example, here's the heuristic used for the `submitForm` method: * * 1. Does the locator look like an ID selector (e.g. "#foo")? If so, try to find a form matching that ID. * 2. If nothing found, check if locator looks like a CSS selector. If so, run it. * 3. If nothing found, check if locator looks like an XPath expression. If so, run it. * 4. Throw an `ElementNotFound` exception. * * Be warned that fuzzy locators can be significantly slower than strict locators. * Especially if you use Selenium WebDriver with `wait` (aka implicit wait) option. * In the example above if you set `wait` to 5 seconds and use XPath string as fuzzy locator, * `submitForm` method will wait for 5 seconds at each step. * That means 5 seconds finding the form by ID, another 5 seconds finding by CSS * until it finally tries to find the form by XPath). * If speed is a concern, it's recommended you stick with explicitly specifying the locator type via the array syntax. * * ### Get Scenario Metadata * * You can inject `\Codeception\Scenario` into your test to get information about the current configuration: * ```php * use Codeception\Scenario * public function myTest(AcceptanceTester $I, Scenario $scenario) * { * if ('firefox' === $scenario->current('browser')) { * // ... * } * } * ``` * See [Get Scenario Metadata](https://codeception.com/docs/07-AdvancedUsage#Get-Scenario-Metadata) for more information on `$scenario`. * * ## Public Properties * * * `webDriver` - instance of `\Facebook\WebDriver\Remote\RemoteWebDriver`. Can be accessed from Helper classes for complex WebDriver interactions. * * ```php * // inside Helper class * $this->getModule('WebDriver')->webDriver->getKeyboard()->sendKeys('hello, webdriver'); * ``` * */ class WebDriver extends CodeceptionModule implements WebInterface, RemoteInterface, MultiSessionInterface, SessionSnapshot, ScreenshotSaver, PageSourceSaver, ElementLocator, ConflictsWithModule, RequiresPackage { protected $requiredFields = ['browser', 'url']; protected $config = [ 'protocol' => 'http', 'host' => '127.0.0.1', 'port' => '4444', 'path' => '/wd/hub', 'start' => true, 'restart' => false, 'wait' => 0, 'clear_cookies' => true, 'window_size' => false, 'capabilities' => [], 'connection_timeout' => null, 'request_timeout' => null, 'pageload_timeout' => null, 'http_proxy' => null, 'http_proxy_port' => null, 'ssl_proxy' => null, 'ssl_proxy_port' => null, 'debug_log_entries' => 0, 'log_js_errors' => false, 'webdriver_proxy' => null, 'webdriver_proxy_port' => null, ]; protected $wdHost; protected $capabilities; protected $connectionTimeoutInMs; protected $requestTimeoutInMs; protected $test; protected $sessions = []; protected $sessionSnapshots = []; protected $webdriverProxy; protected $webdriverProxyPort; /** * @var RemoteWebDriver */ public $webDriver; /** * @var RemoteWebElement */ protected $baseElement; public function _requires() { return ['Facebook\WebDriver\Remote\RemoteWebDriver' => '"php-webdriver/webdriver": "^1.0.1"']; } /** * @return RemoteWebElement * @throws ModuleException */ protected function getBaseElement() { if (!$this->baseElement) { throw new ModuleException($this, "Page not loaded. Use `\$I->amOnPage` (or hidden API methods `_request` and `_loadPage`) to open it"); } return $this->baseElement; } public function _initialize() { $this->wdHost = sprintf('%s://%s:%s%s', $this->config['protocol'], $this->config['host'], $this->config['port'], $this->config['path']); $this->capabilities = $this->config['capabilities']; $this->capabilities[WebDriverCapabilityType::BROWSER_NAME] = $this->config['browser']; if ($proxy = $this->getProxy()) { $this->capabilities[WebDriverCapabilityType::PROXY] = $proxy; } $this->connectionTimeoutInMs = $this->config['connection_timeout'] * 1000; $this->requestTimeoutInMs = $this->config['request_timeout'] * 1000; $this->webdriverProxy = $this->config['webdriver_proxy']; $this->webdriverProxyPort = $this->config['webdriver_proxy_port']; $this->loadFirefoxProfile(); } /** * Change capabilities of WebDriver. Should be executed before starting a new browser session. * This method expects a function to be passed which returns array or [WebDriver Desired Capabilities](https://github.com/php-webdriver/php-webdriver/blob/main/lib/Remote/DesiredCapabilities.php) object. * Additional [Chrome options](https://github.com/php-webdriver/php-webdriver/wiki/ChromeOptions) (like adding extensions) can be passed as well. * * ```php * getModule('WebDriver')->_capabilities(function($currentCapabilities) { * // or new \Facebook\WebDriver\Remote\DesiredCapabilities(); * return \Facebook\WebDriver\Remote\DesiredCapabilities::firefox(); * }); * } * ``` * * to make this work load `\Helper\Acceptance` before `WebDriver` in `acceptance.suite.yml`: * * ```yaml * modules: * enabled: * - \Helper\Acceptance * - WebDriver * ``` * * For instance, [**BrowserStack** cloud service](https://www.browserstack.com/automate/capabilities) may require a test name to be set in capabilities. * This is how it can be done via `_capabilities` method from `Helper\Acceptance`: * * ```php * getMetadata()->getName(); * $this->getModule('WebDriver')->_capabilities(function($currentCapabilities) use ($name) { * $currentCapabilities['name'] = $name; * return $currentCapabilities; * }); * } * ``` * In this case, please ensure that `\Helper\Acceptance` is loaded before WebDriver so new capabilities could be applied. * * @api * @param \Closure $capabilityFunction */ public function _capabilities(\Closure $capabilityFunction) { $this->capabilities = $capabilityFunction($this->capabilities); } public function _conflicts() { return 'Codeception\Lib\Interfaces\Web'; } public function _before(TestInterface $test) { if (!isset($this->webDriver) && $this->config['start']) { $this->_initializeSession(); } $this->setBaseElement(); $test->getMetadata()->setCurrent( [ 'browser' => $this->webDriver->getCapabilities()->getBrowserName(), 'capabilities' => $this->webDriver->getCapabilities()->toArray(), ] ); } /** * Restarts a web browser. * Can be used with `_reconfigure` to open browser with different configuration * * ```php * getModule('WebDriver')->_restart(); // just restart * $this->getModule('WebDriver')->_restart(['browser' => $browser]); // reconfigure + restart * ``` * * @param array $config * @api */ public function _restart($config = []) { $this->webDriver->quit(); if (!empty($config)) { $this->_reconfigure($config); } $this->_initializeSession(); } protected function onReconfigure() { $this->_initialize(); } protected function loadFirefoxProfile() { if (!array_key_exists('firefox_profile', $this->config['capabilities'])) { return; } $firefox_profile = $this->config['capabilities']['firefox_profile']; if (file_exists($firefox_profile) === false) { throw new ModuleConfigException( __CLASS__, "Firefox profile does not exist under given path " . $firefox_profile ); } // Set firefox profile as capability $this->capabilities['firefox_profile'] = file_get_contents($firefox_profile); } protected function initialWindowSize() { if ($this->config['window_size'] == 'maximize') { $this->maximizeWindow(); return; } $size = explode('x', $this->config['window_size']); if (count($size) == 2) { $this->resizeWindow(intval($size[0]), intval($size[1])); } } public function _after(TestInterface $test) { if ($this->config['restart']) { $this->stopAllSessions(); return; } if ($this->config['clear_cookies'] && isset($this->webDriver)) { try { $this->webDriver->manage()->deleteAllCookies(); } catch (\Exception $e) { // may cause fatal errors when not handled $this->debug("Error, can't clean cookies after a test: " . $e->getMessage()); } } } public function _failed(TestInterface $test, $fail) { $this->debugWebDriverLogs($test); $filename = preg_replace('~[^a-zA-Z0-9\x80-\xff]~', '.', Descriptor::getTestSignatureUnique($test)); $outputDir = codecept_output_dir(); $this->_saveScreenshot($report = $outputDir . mb_strcut($filename, 0, 245, 'utf-8') . '.fail.png'); $test->getMetadata()->addReport('png', $report); $this->_savePageSource($report = $outputDir . mb_strcut($filename, 0, 244, 'utf-8') . '.fail.html'); $test->getMetadata()->addReport('html', $report); $this->debug("Screenshot and page source were saved into '$outputDir' dir"); } /** * Print out latest Selenium Logs in debug mode * * @param \Codeception\TestInterface $test */ public function debugWebDriverLogs(TestInterface $test = null) { if (!isset($this->webDriver)) { $this->debug('WebDriver::debugWebDriverLogs method has been called when webDriver is not set'); return; } // don't show logs if log entries not set if (!$this->config['debug_log_entries']) { return; } try { // Dump out latest Selenium logs $logs = $this->webDriver->manage()->getAvailableLogTypes(); foreach ($logs as $logType) { $logEntries = array_slice( $this->webDriver->manage()->getLog($logType), -$this->config['debug_log_entries'] ); if (empty($logEntries)) { $this->debugSection("Selenium {$logType} Logs", " EMPTY "); continue; } $this->debugSection("Selenium {$logType} Logs", "\n" . $this->formatLogEntries($logEntries)); if ($logType === 'browser' && $this->config['log_js_errors'] && ($test instanceof ScenarioDriven) ) { $this->logJSErrors($test, $logEntries); } } } catch (\Exception $e) { $this->debug('Unable to retrieve Selenium logs : ' . $e->getMessage()); } } /** * Turns an array of log entries into a human-readable string. * Each log entry is an array with the keys "timestamp", "level", and "message". * See https://code.google.com/p/selenium/wiki/JsonWireProtocol#Log_Entry_JSON_Object * * @param array $logEntries * @return string */ protected function formatLogEntries(array $logEntries) { $formattedLogs = ''; foreach ($logEntries as $logEntry) { // Timestamp is in milliseconds, but date() requires seconds. $time = date('H:i:s', $logEntry['timestamp'] / 1000) . // Append the milliseconds to the end of the time string '.' . ($logEntry['timestamp'] % 1000); $formattedLogs .= "{$time} {$logEntry['level']} - {$logEntry['message']}\n"; } return $formattedLogs; } /** * Logs JavaScript errors as comments. * * @param ScenarioDriven $test * @param array $browserLogEntries */ protected function logJSErrors(ScenarioDriven $test, array $browserLogEntries) { foreach ($browserLogEntries as $logEntry) { if (true === isset($logEntry['level']) && true === isset($logEntry['message']) && $this->isJSError($logEntry['level'], $logEntry['message']) ) { // Timestamp is in milliseconds, but date() requires seconds. $time = date('H:i:s', $logEntry['timestamp'] / 1000) . // Append the milliseconds to the end of the time string '.' . ($logEntry['timestamp'] % 1000); $test->getScenario()->comment("{$time} {$logEntry['level']} - {$logEntry['message']}"); } } } /** * Determines if the log entry is an error. * The decision is made depending on browser and log-level. * * @param string $logEntryLevel * @param string $message * @return bool */ protected function isJSError($logEntryLevel, $message) { return ( ($this->isPhantom() && $logEntryLevel != 'INFO') // phantomjs logs errors as "WARNING" || $logEntryLevel === 'SEVERE' // other browsers log errors as "SEVERE" ) && strpos($message, 'ERR_PROXY_CONNECTION_FAILED') === false; // ignore blackhole proxy } public function _afterSuite() { // this is just to make sure webDriver is cleared after suite $this->stopAllSessions(); } protected function stopAllSessions() { foreach ($this->sessions as $session) { $this->_closeSession($session); } $this->webDriver = null; $this->baseElement = null; } public function amOnSubdomain($subdomain) { $url = $this->config['url']; $url = preg_replace('~(https?:\/\/)(.*\.)(.*\.)~', "$1$3", $url); // removing current subdomain $url = preg_replace('~(https?:\/\/)(.*)~', "$1$subdomain.$2", $url); // inserting new $this->_reconfigure(['url' => $url]); } /** * Returns URL of a host. * * @api * @return mixed * @throws ModuleConfigException */ public function _getUrl() { if (!isset($this->config['url'])) { throw new ModuleConfigException( __CLASS__, "Module connection failure. The URL for client can't bre retrieved" ); } return $this->config['url']; } protected function getProxy() { $proxyConfig = []; if ($this->config['http_proxy']) { $proxyConfig['httpProxy'] = $this->config['http_proxy']; if ($this->config['http_proxy_port']) { $proxyConfig['httpProxy'] .= ':' . $this->config['http_proxy_port']; } } if ($this->config['ssl_proxy']) { $proxyConfig['sslProxy'] = $this->config['ssl_proxy']; if ($this->config['ssl_proxy_port']) { $proxyConfig['sslProxy'] .= ':' . $this->config['ssl_proxy_port']; } } if (!empty($proxyConfig)) { $proxyConfig['proxyType'] = 'manual'; return $proxyConfig; } return null; } /** * Uri of currently opened page. * @return string * @api * @throws ModuleException */ public function _getCurrentUri() { $url = $this->webDriver->getCurrentURL(); if ($url == 'about:blank' || strpos($url, 'data:') === 0) { throw new ModuleException($this, 'Current url is blank, no page was opened'); } return Uri::retrieveUri($url); } public function _saveScreenshot($filename) { if (!isset($this->webDriver)) { $this->debug('WebDriver::_saveScreenshot method has been called when webDriver is not set'); return; } try { $this->webDriver->takeScreenshot($filename); } catch (\Exception $e) { $this->debug('Unable to retrieve screenshot from Selenium : ' . $e->getMessage()); return; } } public function _saveElementScreenshot($selector, $filename) { if (!isset($this->webDriver)) { $this->debug('WebDriver::_saveElementScreenshot method has been called when webDriver is not set'); return; } try { $this->matchFirstOrFail($this->webDriver, $selector)->takeElementScreenshot($filename); } catch (\Exception $e) { $this->debug('Unable to retrieve element screenshot from Selenium : ' . $e->getMessage()); return; } } public function _findElements($locator) { return $this->match($this->webDriver, $locator); } /** * Saves HTML source of a page to a file * @param $filename */ public function _savePageSource($filename) { if (!isset($this->webDriver)) { $this->debug('WebDriver::_savePageSource method has been called when webDriver is not set'); return; } try { file_put_contents($filename, $this->webDriver->getPageSource()); } catch (\Exception $e) { $this->debug('Unable to retrieve source page from Selenium : ' . $e->getMessage()); } } /** * Takes a screenshot of the current window and saves it to `tests/_output/debug`. * * ``` php * amOnPage('/user/edit'); * $I->makeScreenshot('edit_page'); * // saved to: tests/_output/debug/edit_page.png * $I->makeScreenshot(); * // saved to: tests/_output/debug/2017-05-26_14-24-11_4b3403665fea6.png * ``` * * @param $name */ public function makeScreenshot($name = null) { if (empty($name)) { $name = uniqid(date("Y-m-d_H-i-s_")); } $debugDir = codecept_log_dir() . 'debug'; if (!is_dir($debugDir)) { mkdir($debugDir, 0777); } $screenName = $debugDir . DIRECTORY_SEPARATOR . $name . '.png'; $this->_saveScreenshot($screenName); $this->debugSection('Screenshot Saved', "file://$screenName"); } /** * Takes a screenshot of an element of the current window and saves it to `tests/_output/debug`. * * ``` php * amOnPage('/user/edit'); * $I->makeElementScreenshot('#dialog', 'edit_page'); * // saved to: tests/_output/debug/edit_page.png * $I->makeElementScreenshot('#dialog'); * // saved to: tests/_output/debug/2017-05-26_14-24-11_4b3403665fea6.png * ``` * * @param $name */ public function makeElementScreenshot($selector, $name = null) { if (empty($name)) { $name = uniqid(date("Y-m-d_H-i-s_")); } $debugDir = codecept_log_dir() . 'debug'; if (!is_dir($debugDir)) { mkdir($debugDir, 0777); } $screenName = $debugDir . DIRECTORY_SEPARATOR . $name . '.png'; $this->_saveElementScreenshot($selector, $screenName); $this->debugSection('Screenshot Saved', "file://$screenName"); } public function makeHtmlSnapshot($name = null) { if (empty($name)) { $name = uniqid(date("Y-m-d_H-i-s_")); } $debugDir = codecept_output_dir() . 'debug'; if (!is_dir($debugDir)) { mkdir($debugDir, 0777); } $fileName = $debugDir . DIRECTORY_SEPARATOR . $name . '.html'; $this->_savePageSource($fileName); $this->debugSection('Snapshot Saved', "file://$fileName"); } /** * Resize the current window. * * ``` php * resizeWindow(800, 600); * * ``` * * @param int $width * @param int $height */ public function resizeWindow($width, $height) { $this->webDriver->manage()->window()->setSize(new WebDriverDimension($width, $height)); } private function debugCookies() { $result = []; $cookies = $this->webDriver->manage()->getCookies(); foreach ($cookies as $cookie) { $result[] = is_array($cookie) ? $cookie : $cookie->toArray(); } $this->debugSection('Cookies', json_encode($result)); } public function seeCookie($cookie, array $params = []) { $cookies = $this->filterCookies($this->webDriver->manage()->getCookies(), $params); $cookies = array_map( function ($c) { return $c['name']; }, $cookies ); $this->debugCookies(); $this->assertContains($cookie, $cookies); } public function dontSeeCookie($cookie, array $params = []) { $cookies = $this->filterCookies($this->webDriver->manage()->getCookies(), $params); $cookies = array_map( function ($c) { return $c['name']; }, $cookies ); $this->debugCookies(); $this->assertNotContains($cookie, $cookies); } public function setCookie($cookie, $value, array $params = [], $showDebug = true) { $params['name'] = $cookie; $params['value'] = $value; if (isset($params['expires'])) { // PhpBrowser compatibility $params['expiry'] = $params['expires']; } // #5401 Supply defaults, otherwise chromedriver 2.46 complains. $defaults = [ 'path' => '/', 'expiry' => time() + 86400, 'secure' => false, 'httpOnly' => false, ]; foreach ($defaults as $key => $default) { if (empty($params[$key])) { $params[$key] = $default; } } $this->webDriver->manage()->addCookie($params); if ($showDebug) { $this->debugCookies(); } } public function resetCookie($cookie, array $params = []) { $this->webDriver->manage()->deleteCookieNamed($cookie); $this->debugCookies(); } public function grabCookie($cookie, array $params = []) { $params['name'] = $cookie; $cookies = $this->filterCookies($this->webDriver->manage()->getCookies(), $params); if (empty($cookies)) { return null; } $cookie = reset($cookies); return $cookie['value']; } /** * Grabs current page source code. * * @throws ModuleException if no page was opened. * * @return string Current page source code. */ public function grabPageSource() { // Make sure that some page was opened. $this->_getCurrentUri(); return $this->webDriver->getPageSource(); } protected function filterCookies($cookies, $params = []) { foreach (['domain', 'path', 'name'] as $filter) { if (!isset($params[$filter])) { continue; } $cookies = array_filter( $cookies, function ($item) use ($filter, $params) { return $item[$filter] == $params[$filter]; } ); } return $cookies; } public function amOnUrl($url) { $host = Uri::retrieveHost($url); $this->_reconfigure(['url' => $host]); $this->debugSection('Host', $host); $this->webDriver->get($url); } public function amOnPage($page) { $url = Uri::appendPath($this->config['url'], $page); $this->debugSection('GET', $url); $this->webDriver->get($url); } public function see($text, $selector = null) { if (!$selector) { return $this->assertPageContains($text); } $this->enableImplicitWait(); $nodes = $this->matchVisible($selector); $this->disableImplicitWait(); $this->assertNodesContain($text, $nodes, $selector); } public function dontSee($text, $selector = null) { if (!$selector) { return $this->assertPageNotContains($text); } $nodes = $this->matchVisible($selector); $this->assertNodesNotContain($text, $nodes, $selector); } public function seeInSource($raw) { $this->assertPageSourceContains($raw); } public function dontSeeInSource($raw) { $this->assertPageSourceNotContains($raw); } /** * Checks that the page source contains the given string. * * ```php * seeInPageSource('assertThat( $this->webDriver->getPageSource(), new PageConstraint($text, $this->_getCurrentUri()), '' ); } /** * Checks that the page source doesn't contain the given string. * * @param $text */ public function dontSeeInPageSource($text) { $this->assertThatItsNot( $this->webDriver->getPageSource(), new PageConstraint($text, $this->_getCurrentUri()), '' ); } public function click($link, $context = null) { $page = $this->webDriver; if ($context) { $page = $this->matchFirstOrFail($this->webDriver, $context); } $el = $this->_findClickable($page, $link); if (!$el) { // check one more time if this was a CSS selector we didn't match try { $els = $this->match($page, $link); } catch (MalformedLocatorException $e) { throw new ElementNotFound("name=$link", "'$link' is invalid CSS and XPath selector and Link or Button"); } $el = reset($els); } if (!$el) { throw new ElementNotFound($link, 'Link or Button or CSS or XPath'); } $el->click(); } /** * Locates a clickable element. * * Use it in Helpers or GroupObject or Extension classes: * * ```php * getModule('WebDriver'); * $page = $module->webDriver; * * // search a link or button on a page * $el = $module->_findClickable($page, 'Click Me'); * * // search a link or button within an element * $topBar = $module->_findElements('.top-bar')[0]; * $el = $module->_findClickable($topBar, 'Click Me'); * * ``` * @api * @param RemoteWebDriver $page WebDriver instance or an element to search within * @param $link a link text or locator to click * @return WebDriverElement */ public function _findClickable($page, $link) { if (is_array($link) or ($link instanceof WebDriverBy)) { return $this->matchFirstOrFail($page, $link); } // try to match by strict locators, CSS Ids or XPath if (Locator::isPrecise($link)) { return $this->matchFirstOrFail($page, $link); } $locator = static::xpathLiteral(trim($link)); // narrow $xpath = Locator::combine( ".//a[normalize-space(.)=$locator]", ".//button[normalize-space(.)=$locator]", ".//a/img[normalize-space(@alt)=$locator]/ancestor::a", ".//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=$locator]" ); $els = $page->findElements(WebDriverBy::xpath($xpath)); if (count($els)) { return reset($els); } // wide $xpath = Locator::combine( ".//a[./@href][((contains(normalize-space(string(.)), $locator)) or contains(./@title, $locator) or .//img[contains(./@alt, $locator)])]", ".//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][contains(./@value, $locator)]", ".//input[./@type = 'image'][contains(./@alt, $locator)]", ".//button[contains(normalize-space(string(.)), $locator)]", ".//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = $locator or ./@title = $locator]", ".//button[./@name = $locator or ./@title = $locator]" ); $els = $page->findElements(WebDriverBy::xpath($xpath)); if (count($els)) { return reset($els); } return null; } /** * @param $selector * @return WebDriverElement[] * @throws \Codeception\Exception\ElementNotFound */ protected function findFields($selector) { if ($selector instanceof WebDriverElement) { return [$selector]; } if (is_array($selector) || ($selector instanceof WebDriverBy)) { $fields = $this->match($this->webDriver, $selector); if (empty($fields)) { throw new ElementNotFound($selector); } return $fields; } $locator = static::xpathLiteral(trim($selector)); // by text or label $xpath = Locator::combine( // @codingStandardsIgnoreStart ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = $locator) or ./@id = //label[contains(normalize-space(string(.)), $locator)]/@for) or ./@placeholder = $locator)]", ".//label[contains(normalize-space(string(.)), $locator)]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" // @codingStandardsIgnoreEnd ); $fields = $this->webDriver->findElements(WebDriverBy::xpath($xpath)); if (!empty($fields)) { return $fields; } // by name $xpath = ".//*[self::input | self::textarea | self::select][@name = $locator]"; $fields = $this->webDriver->findElements(WebDriverBy::xpath($xpath)); if (!empty($fields)) { return $fields; } // try to match by CSS or XPath $fields = $this->match($this->webDriver, $selector, false); if (!empty($fields)) { return $fields; } throw new ElementNotFound($selector, "Field by name, label, CSS or XPath"); } /** * @param $selector * @return WebDriverElement * @throws \Codeception\Exception\ElementNotFound */ protected function findField($selector) { $arr = $this->findFields($selector); return reset($arr); } public function seeLink($text, $url = null) { $this->enableImplicitWait(); $nodes = $this->getBaseElement()->findElements(WebDriverBy::partialLinkText($text)); $this->disableImplicitWait(); $currentUri = $this->_getCurrentUri(); if (empty($nodes)) { $this->fail("No links containing text '$text' were found in page $currentUri"); } if ($url) { $nodes = $this->filterNodesByHref($url, $nodes); } $this->assertNotEmpty($nodes, "No links containing text '$text' and URL '$url' were found in page $currentUri"); } public function dontSeeLink($text, $url = null) { $nodes = $this->getBaseElement()->findElements(WebDriverBy::partialLinkText($text)); $currentUri = $this->_getCurrentUri(); if (!$url) { $this->assertEmpty($nodes, "Link containing text '$text' was found in page $currentUri"); } else { $nodes = $this->filterNodesByHref($url, $nodes); $this->assertEmpty($nodes, "Link containing text '$text' and URL '$url' was found in page $currentUri"); } } /** * @param string $url * @param $nodes * @return array */ private function filterNodesByHref($url, $nodes) { //current uri can be relative, merging it with configured base url gives absolute url $absoluteCurrentUrl = Uri::mergeUrls($this->_getUrl(), $this->_getCurrentUri()); $expectedUrl = Uri::mergeUrls($absoluteCurrentUrl, $url); $nodes = array_filter( $nodes, function (WebDriverElement $e) use ($expectedUrl, $absoluteCurrentUrl) { $elementHref = Uri::mergeUrls($absoluteCurrentUrl, $e->getAttribute('href')); return $elementHref === $expectedUrl; } ); return $nodes; } public function seeInCurrentUrl($uri) { $this->assertStringContainsString($uri, $this->_getCurrentUri()); } public function seeCurrentUrlEquals($uri) { $this->assertEquals($uri, $this->_getCurrentUri()); } public function seeCurrentUrlMatches($uri) { $this->assertRegExp($uri, $this->_getCurrentUri()); } public function dontSeeInCurrentUrl($uri) { $this->assertStringNotContainsString($uri, $this->_getCurrentUri()); } public function dontSeeCurrentUrlEquals($uri) { $this->assertNotEquals($uri, $this->_getCurrentUri()); } public function dontSeeCurrentUrlMatches($uri) { $this->assertNotRegExp($uri, $this->_getCurrentUri()); } public function grabFromCurrentUrl($uri = null) { if (!$uri) { return $this->_getCurrentUri(); } $matches = []; $res = preg_match($uri, $this->_getCurrentUri(), $matches); if (!$res) { $this->fail("Couldn't match $uri in " . $this->_getCurrentUri()); } if (!isset($matches[1])) { $this->fail("Nothing to grab. A regex parameter required. Ex: '/user/(\\d+)'"); } return $matches[1]; } public function seeCheckboxIsChecked($checkbox) { $this->assertTrue($this->findField($checkbox)->isSelected()); } public function dontSeeCheckboxIsChecked($checkbox) { $this->assertFalse($this->findField($checkbox)->isSelected()); } public function seeInField($field, $value) { $els = $this->findFields($field); $this->assert($this->proceedSeeInField($els, $value)); } public function dontSeeInField($field, $value) { $els = $this->findFields($field); $this->assertNot($this->proceedSeeInField($els, $value)); } public function seeInFormFields($formSelector, array $params) { $this->proceedSeeInFormFields($formSelector, $params, false); } public function dontSeeInFormFields($formSelector, array $params) { $this->proceedSeeInFormFields($formSelector, $params, true); } protected function proceedSeeInFormFields($formSelector, array $params, $assertNot) { $form = $this->match($this->getBaseElement(), $formSelector); if (empty($form)) { throw new ElementNotFound($formSelector, "Form via CSS or XPath"); } $form = reset($form); $els = []; foreach ($params as $name => $values) { $this->pushFormField($els, $form, $name, $values); } foreach ($els as $arrayElement) { list($el, $values) = $arrayElement; if (!is_array($values)) { $values = [$values]; } foreach ($values as $value) { $ret = $this->proceedSeeInField($el, $value); if ($assertNot) { $this->assertNot($ret); } else { $this->assert($ret); } } } } /** * Map an array element passed to seeInFormFields to its corresponding WebDriver element, * recursing through array values if the field is not found. * * @param array $els The previously found elements. * @param RemoteWebElement $form The form in which to search for fields. * @param string $name The field's name. * @param mixed $values * @return void */ protected function pushFormField(&$els, $form, $name, $values) { $el = $form->findElements(WebDriverBy::name($name)); if ($el) { $els[] = [$el, $values]; } elseif (is_array($values)) { foreach ($values as $key => $value) { $this->pushFormField($els, $form, "{$name}[$key]", $value); } } else { throw new ElementNotFound($name); } } /** * @param RemoteWebElement[] $elements * @param $value * @return array */ protected function proceedSeeInField(array $elements, $value) { $strField = reset($elements)->getAttribute('name'); if (reset($elements)->getTagName() === 'select') { $el = reset($elements); $elements = $el->findElements(WebDriverBy::xpath('.//option')); if (empty($value) && empty($elements)) { return ['True', true]; } } $currentValues = []; if (is_bool($value)) { $currentValues = [false]; } foreach ($elements as $el) { switch ($el->getTagName()) { case 'input': if ($el->getAttribute('type') === 'radio' || $el->getAttribute('type') === 'checkbox') { if ($el->getAttribute('checked')) { if (is_bool($value)) { $currentValues = [true]; break; } else { $currentValues[] = $el->getAttribute('value'); } } } else { $currentValues[] = $el->getAttribute('value'); } break; case 'option': // no break we need the trim text and the value also if (!$el->isSelected()) { break; } $currentValues[] = $el->getText(); case 'textarea': // we include trimmed and real value of textarea for check $currentValues[] = trim($el->getText()); default: $currentValues[] = $el->getAttribute('value'); // raw value break; } } return [ 'Contains', $value, $currentValues, "Failed testing for '$value' in $strField's value: '" . implode("', '", $currentValues) . "'" ]; } public function selectOption($select, $option) { $el = $this->findField($select); if ($el->getTagName() != 'select') { $els = $this->matchCheckables($select); $radio = null; foreach ($els as $el) { $radio = $this->findCheckable($el, $option, true); if ($radio) { break; } } if (!$radio) { throw new ElementNotFound($select, "Radiobutton with value or name '$option in"); } $radio->click(); return; } $wdSelect = new WebDriverSelect($el); if ($wdSelect->isMultiple()) { $wdSelect->deselectAll(); } if (!is_array($option)) { $option = [$option]; } $matched = false; if (key($option) !== 'value') { foreach ($option as $opt) { try { $wdSelect->selectByVisibleText($opt); $matched = true; } catch (NoSuchElementException $e) { } } } if ($matched) { return; } if (key($option) !== 'text') { foreach ($option as $opt) { try { $wdSelect->selectByValue($opt); $matched = true; } catch (NoSuchElementException $e) { } } } if ($matched) { return; } // partially matching foreach ($option as $opt) { try { $optElement = $el->findElement(WebDriverBy::xpath('.//option [contains (., "' . $opt . '")]')); $matched = true; if (!$optElement->isSelected()) { $optElement->click(); } } catch (NoSuchElementException $e) { // exception treated at the end } } if ($matched) { return; } throw new ElementNotFound(json_encode($option), "Option inside $select matched by name or value"); } /** * Manually starts a new browser session. * * ```php * getModule('WebDriver')->_initializeSession(); * ``` * * @api */ public function _initializeSession() { try { $this->sessions[] = $this->webDriver; $this->webDriver = RemoteWebDriver::create( $this->wdHost, $this->capabilities, $this->connectionTimeoutInMs, $this->requestTimeoutInMs, $this->webdriverProxy, $this->webdriverProxyPort ); if (!is_null($this->config['pageload_timeout'])) { $this->webDriver->manage()->timeouts()->pageLoadTimeout($this->config['pageload_timeout']); } $this->setBaseElement(); $this->initialWindowSize(); } catch (WebDriverCurlException $e) { codecept_debug('Curl error: ' . $e->getMessage()); throw new ConnectionException("Can't connect to WebDriver at {$this->wdHost}. Make sure that ChromeDriver, GeckoDriver or Selenium Server is running."); } } /** * Loads current RemoteWebDriver instance as a session * * @api * @param RemoteWebDriver $session */ public function _loadSession($session) { $this->webDriver = $session; $this->setBaseElement(); } /** * Returns current WebDriver session for saving * * @api * @return RemoteWebDriver */ public function _backupSession() { return $this->webDriver; } /** * Manually closes current WebDriver session. * * ```php * getModule('WebDriver')->_closeSession(); * * // close a specific session * $webDriver = $this->getModule('WebDriver')->webDriver; * $this->getModule('WebDriver')->_closeSession($webDriver); * ``` * * @api * @param $webDriver (optional) a specific webdriver session instance */ public function _closeSession($webDriver = null) { if (!$webDriver and $this->webDriver) { $webDriver = $this->webDriver; } if (!$webDriver) { return; } try { $webDriver->quit(); unset($webDriver); } catch (UnknownServerException $e) { // Session already closed so nothing to do } catch (UnknownErrorException $e) { // Session already closed so nothing to do } } /** * Unselect an option in the given select box. * * @param $select * @param $option */ public function unselectOption($select, $option) { $el = $this->findField($select); $wdSelect = new WebDriverSelect($el); if (!is_array($option)) { $option = [$option]; } $matched = false; foreach ($option as $opt) { try { $wdSelect->deselectByVisibleText($opt); $matched = true; } catch (NoSuchElementException $e) { // exception treated at the end } try { $wdSelect->deselectByValue($opt); $matched = true; } catch (NoSuchElementException $e) { // exception treated at the end } } if ($matched) { return; } throw new ElementNotFound(json_encode($option), "Option inside $select matched by name or value"); } /** * @param $context * @param $radioOrCheckbox * @param bool $byValue * @return mixed|null */ protected function findCheckable($context, $radioOrCheckbox, $byValue = false) { if ($radioOrCheckbox instanceof WebDriverElement) { return $radioOrCheckbox; } if (is_array($radioOrCheckbox) or ($radioOrCheckbox instanceof WebDriverBy)) { return $this->matchFirstOrFail($this->getBaseElement(), $radioOrCheckbox); } $locator = static::xpathLiteral($radioOrCheckbox); if ($context instanceof WebDriverElement && $context->getTagName() === 'input') { $contextType = $context->getAttribute('type'); if (!in_array($contextType, ['checkbox', 'radio'], true)) { return null; } $nameLiteral = static::xpathLiteral($context->getAttribute('name')); $typeLiteral = static::xpathLiteral($contextType); $inputLocatorFragment = "input[@type = $typeLiteral][@name = $nameLiteral]"; $xpath = Locator::combine( // @codingStandardsIgnoreStart "ancestor::form//{$inputLocatorFragment}[(@id = ancestor::form//label[contains(normalize-space(string(.)), $locator)]/@for) or @placeholder = $locator]", // @codingStandardsIgnoreEnd "ancestor::form//label[contains(normalize-space(string(.)), $locator)]//{$inputLocatorFragment}" ); if ($byValue) { $xpath = Locator::combine($xpath, "ancestor::form//{$inputLocatorFragment}[@value = $locator]"); } } else { $xpath = Locator::combine( // @codingStandardsIgnoreStart "//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), $locator)]/@for) or @placeholder = $locator or @name = $locator]", // @codingStandardsIgnoreEnd "//label[contains(normalize-space(string(.)), $locator)]//input[@type = 'radio' or @type = 'checkbox']" ); if ($byValue) { $xpath = Locator::combine($xpath, "//input[@type = 'checkbox' or @type = 'radio'][@value = $locator]"); } } $els = $context->findElements(WebDriverBy::xpath($xpath)); if (count($els)) { return reset($els); } $els = $context->findElements(WebDriverBy::xpath(str_replace('ancestor::form', '', $xpath))); if (count($els)) { return reset($els); } $els = $this->match($context, $radioOrCheckbox); if (count($els)) { return reset($els); } return null; } protected function matchCheckables($selector) { $els = $this->match($this->webDriver, $selector); if (!count($els)) { throw new ElementNotFound($selector, "Element containing radio by CSS or XPath"); } return $els; } public function checkOption($option) { $field = $this->findCheckable($this->webDriver, $option); if (!$field) { throw new ElementNotFound($option, "Checkbox or Radio by Label or CSS or XPath"); } if ($field->isSelected()) { return; } $field->click(); } public function uncheckOption($option) { $field = $this->findCheckable($this->getBaseElement(), $option); if (!$field) { throw new ElementNotFound($option, "Checkbox by Label or CSS or XPath"); } if (!$field->isSelected()) { return; } $field->click(); } public function fillField($field, $value) { $el = $this->findField($field); $el->clear(); $el->sendKeys((string)$value); } /** * Clears given field which isn't empty. * * ``` php * clearField('#username'); * ``` * * @param $field */ public function clearField($field) { $el = $this->findField($field); $el->clear(); } /** * Type in characters on active element. * With a second parameter you can specify delay between key presses. * * ```php * click('#input'); * * // type text in active element * $I->type('Hello world'); * * // type text with a 1sec delay between chars * $I->type('Hello World', 1); * ``` * * This might be useful when you an input reacts to typing and you need to slow it down to emulate human behavior. * For instance, this is how Credit Card fields can be filled in. * * @param $text * @param $delay [sec] */ public function type($text, $delay = 0) { $keys = str_split($text); foreach ($keys as $key) { sleep($delay); $this->webDriver->getKeyboard()->pressKey($key); } sleep($delay); } public function attachFile($field, $filename) { $el = $this->findField($field); // in order to be compatible on different OS $filePath = codecept_data_dir() . $filename; if (!file_exists($filePath)) { throw new \InvalidArgumentException("File does not exist: $filePath"); } if (!is_readable($filePath)) { throw new \InvalidArgumentException("File is not readable: $filePath"); } // in order for remote upload to be enabled $el->setFileDetector(new LocalFileDetector()); // skip file detector for phantomjs if ($this->isPhantom()) { $el->setFileDetector(new UselessFileDetector()); } $el->sendKeys(realpath($filePath)); } /** * Grabs all visible text from the current page. * * @return string */ protected function getVisibleText() { if ($this->getBaseElement() instanceof RemoteWebElement) { return $this->getBaseElement()->getText(); } $els = $this->getBaseElement()->findElements(WebDriverBy::cssSelector('body')); if (isset($els[0])) { return $els[0]->getText(); } return ''; } public function grabTextFrom($cssOrXPathOrRegex) { $els = $this->match($this->getBaseElement(), $cssOrXPathOrRegex, false); if (count($els)) { return $els[0]->getText(); } if (@preg_match($cssOrXPathOrRegex, $this->webDriver->getPageSource(), $matches)) { return $matches[1]; } throw new ElementNotFound($cssOrXPathOrRegex, 'CSS or XPath or Regex'); } public function grabAttributeFrom($cssOrXpath, $attribute) { $el = $this->matchFirstOrFail($this->getBaseElement(), $cssOrXpath); return $el->getAttribute($attribute); } public function grabValueFrom($field) { $el = $this->findField($field); // value of multiple select is the value of the first selected option if ($el->getTagName() == 'select') { $select = new WebDriverSelect($el); return $select->getFirstSelectedOption()->getAttribute('value'); } return $el->getAttribute('value'); } public function grabMultiple($cssOrXpath, $attribute = null) { $els = $this->match($this->getBaseElement(), $cssOrXpath); return array_map( function (WebDriverElement $e) use ($attribute) { if ($attribute) { return $e->getAttribute($attribute); } return $e->getText(); }, $els ); } protected function filterByAttributes($els, array $attributes) { foreach ($attributes as $attr => $value) { $els = array_filter( $els, function (WebDriverElement $el) use ($attr, $value) { return $el->getAttribute($attr) == $value; } ); } return $els; } public function seeElement($selector, $attributes = []) { $this->enableImplicitWait(); $els = $this->matchVisible($selector); $this->disableImplicitWait(); $els = $this->filterByAttributes($els, $attributes); $this->assertNotEmpty($els); } public function dontSeeElement($selector, $attributes = []) { $els = $this->matchVisible($selector); $els = $this->filterByAttributes($els, $attributes); $this->assertEmpty($els); } /** * Checks that the given element exists on the page, even it is invisible. * * ``` php * seeElementInDOM('//form/input[type=hidden]'); * ?> * ``` * * @param $selector * @param array $attributes */ public function seeElementInDOM($selector, $attributes = []) { $this->enableImplicitWait(); $els = $this->match($this->getBaseElement(), $selector); $els = $this->filterByAttributes($els, $attributes); $this->disableImplicitWait(); $this->assertNotEmpty($els); } /** * Opposite of `seeElementInDOM`. * * @param $selector * @param array $attributes */ public function dontSeeElementInDOM($selector, $attributes = []) { $els = $this->match($this->getBaseElement(), $selector); $els = $this->filterByAttributes($els, $attributes); $this->assertEmpty($els); } public function seeNumberOfElements($selector, $expected) { $counted = count($this->matchVisible($selector)); if (is_array($expected)) { list($floor, $ceil) = $expected; $this->assertTrue( $floor <= $counted && $ceil >= $counted, 'Number of elements counted differs from expected range' ); } else { $this->assertEquals( $expected, $counted, 'Number of elements counted differs from expected number' ); } } public function seeNumberOfElementsInDOM($selector, $expected) { $counted = count($this->match($this->getBaseElement(), $selector)); if (is_array($expected)) { list($floor, $ceil) = $expected; $this->assertTrue( $floor <= $counted && $ceil >= $counted, 'Number of elements counted differs from expected range' ); } else { $this->assertEquals( $expected, $counted, 'Number of elements counted differs from expected number' ); } } public function seeOptionIsSelected($selector, $optionText) { $el = $this->findField($selector); if ($el->getTagName() !== 'select') { $els = $this->matchCheckables($selector); foreach ($els as $k => $el) { $els[$k] = $this->findCheckable($el, $optionText, true); } $this->assertNotEmpty( array_filter( $els, function ($e) { return $e && $e->isSelected(); } ) ); return; } $select = new WebDriverSelect($el); $this->assertNodesContain($optionText, $select->getAllSelectedOptions(), 'option'); } public function dontSeeOptionIsSelected($selector, $optionText) { $el = $this->findField($selector); if ($el->getTagName() !== 'select') { $els = $this->matchCheckables($selector); foreach ($els as $k => $el) { $els[$k] = $this->findCheckable($el, $optionText, true); } $this->assertEmpty( array_filter( $els, function ($e) { return $e && $e->isSelected(); } ) ); return; } $select = new WebDriverSelect($el); $this->assertNodesNotContain($optionText, $select->getAllSelectedOptions(), 'option'); } public function seeInTitle($title) { $this->assertStringContainsString($title, $this->webDriver->getTitle()); } public function dontSeeInTitle($title) { $this->assertStringNotContainsString($title, $this->webDriver->getTitle()); } /** * Accepts the active JavaScript native popup window, as created by `window.alert`|`window.confirm`|`window.prompt`. * Don't confuse popups with modal windows, * as created by [various libraries](http://jster.net/category/windows-modals-popups). */ public function acceptPopup() { if ($this->isPhantom()) { throw new ModuleException($this, 'PhantomJS does not support working with popups'); } $this->webDriver->switchTo()->alert()->accept(); } /** * Dismisses the active JavaScript popup, as created by `window.alert`, `window.confirm`, or `window.prompt`. */ public function cancelPopup() { if ($this->isPhantom()) { throw new ModuleException($this, 'PhantomJS does not support working with popups'); } $this->webDriver->switchTo()->alert()->dismiss(); } /** * Checks that the active JavaScript popup, * as created by `window.alert`|`window.confirm`|`window.prompt`, contains the given string. * * @param $text * * @throws \Codeception\Exception\ModuleException */ public function seeInPopup($text) { if ($this->isPhantom()) { throw new ModuleException($this, 'PhantomJS does not support working with popups'); } $alert = $this->webDriver->switchTo()->alert(); try { $this->assertStringContainsString($text, $alert->getText()); } catch (\PHPUnit\Framework\AssertionFailedError $e) { $alert->dismiss(); throw $e; } } /** * Checks that the active JavaScript popup, * as created by `window.alert`|`window.confirm`|`window.prompt`, does NOT contain the given string. * * @param $text * * @throws \Codeception\Exception\ModuleException */ public function dontSeeInPopup($text) { if ($this->isPhantom()) { throw new ModuleException($this, 'PhantomJS does not support working with popups'); } $alert = $this->webDriver->switchTo()->alert(); try { $this->assertStringNotContainsString($text, $alert->getText()); } catch (\PHPUnit\Framework\AssertionFailedError $e) { $alert->dismiss(); throw $e; } } /** * Enters text into a native JavaScript prompt popup, as created by `window.prompt`. * * @param $keys * * @throws \Codeception\Exception\ModuleException */ public function typeInPopup($keys) { if ($this->isPhantom()) { throw new ModuleException($this, 'PhantomJS does not support working with popups'); } $this->webDriver->switchTo()->alert()->sendKeys($keys); } /** * Reloads the current page. */ public function reloadPage() { $this->webDriver->navigate()->refresh(); } /** * Moves back in history. */ public function moveBack() { $this->webDriver->navigate()->back(); $this->debug($this->_getCurrentUri()); } /** * Moves forward in history. */ public function moveForward() { $this->webDriver->navigate()->forward(); $this->debug($this->_getCurrentUri()); } protected function getSubmissionFormFieldName($name) { if (substr($name, -2) === '[]') { return substr($name, 0, -2); } return $name; } /** * Submits the given form on the page, optionally with the given form * values. Give the form fields values as an array. Note that hidden fields * can't be accessed. * * Skipped fields will be filled by their values from the page. * You don't need to click the 'Submit' button afterwards. * This command itself triggers the request to form's action. * * You can optionally specify what button's value to include * in the request with the last parameter as an alternative to * explicitly setting its value in the second parameter, as * button values are not otherwise included in the request. * * Examples: * * ``` php * submitForm('#login', [ * 'login' => 'davert', * 'password' => '123456' * ]); * // or * $I->submitForm('#login', [ * 'login' => 'davert', * 'password' => '123456' * ], 'submitButtonName'); * * ``` * * For example, given this sample "Sign Up" form: * * ``` html *
    * Login: *
    * Password: *
    * Do you agree to our terms? *
    * Select pricing plan: * * *
    * ``` * * You could write the following to submit it: * * ``` php * submitForm( * '#userForm', * [ * 'user[login]' => 'Davert', * 'user[password]' => '123456', * 'user[agree]' => true * ], * 'submitButton' * ); * ``` * Note that "2" will be the submitted value for the "plan" field, as it is * the selected option. * * Also note that this differs from PhpBrowser, in that * ```'user' => [ 'login' => 'Davert' ]``` is not supported at the moment. * Named array keys *must* be included in the name as above. * * Pair this with seeInFormFields for quick testing magic. * * ``` php * 'value', * 'field2' => 'another value', * 'checkbox1' => true, * // ... * ]; * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); * // $I->amOnPage('/path/to/form-page') may be needed * $I->seeInFormFields('//form[@id=my-form]', $form); * ?> * ``` * * Parameter values must be set to arrays for multiple input fields * of the same name, or multi-select combo boxes. For checkboxes, * either the string value can be used, or boolean values which will * be replaced by the checkbox's value in the DOM. * * ``` php * submitForm('#my-form', [ * 'field1' => 'value', * 'checkbox' => [ * 'value of first checkbox', * 'value of second checkbox', * ], * 'otherCheckboxes' => [ * true, * false, * false, * ], * 'multiselect' => [ * 'first option value', * 'second option value', * ] * ]); * ?> * ``` * * Mixing string and boolean values for a checkbox's value is not supported * and may produce unexpected results. * * Field names ending in "[]" must be passed without the trailing square * bracket characters, and must contain an array for its value. This allows * submitting multiple values with the same name, consider: * * ```php * $I->submitForm('#my-form', [ * 'field[]' => 'value', * 'field[]' => 'another value', // 'field[]' is already a defined key * ]); * ``` * * The solution is to pass an array value: * * ```php * // this way both values are submitted * $I->submitForm('#my-form', [ * 'field' => [ * 'value', * 'another value', * ] * ]); * ``` * * The `$button` parameter can be either a string, an array or an instance * of Facebook\WebDriver\WebDriverBy. When it is a string, the * button will be found by its "name" attribute. If $button is an * array then it will be treated as a strict selector and a WebDriverBy * will be used verbatim. * * For example, given the following HTML: * * ``` html * * ``` * * `$button` could be any one of the following: * - 'submitButton' * - ['name' => 'submitButton'] * - WebDriverBy::name('submitButton') * * @param $selector * @param $params * @param $button */ public function submitForm($selector, array $params, $button = null) { $form = $this->matchFirstOrFail($this->getBaseElement(), $selector); $fields = $form->findElements( WebDriverBy::cssSelector('input:enabled,textarea:enabled,select:enabled,input[type=hidden]') ); foreach ($fields as $field) { $fieldName = $this->getSubmissionFormFieldName($field->getAttribute('name')); if (!isset($params[$fieldName])) { continue; } $value = $params[$fieldName]; if (is_array($value) && $field->getTagName() !== 'select') { if ($field->getAttribute('type') === 'checkbox' || $field->getAttribute('type') === 'radio') { $found = false; foreach ($value as $index => $val) { if (!is_bool($val) && $val === $field->getAttribute('value')) { array_splice($params[$fieldName], $index, 1); $value = $val; $found = true; break; } } if (!$found && !empty($value) && is_bool(reset($value))) { $value = array_pop($params[$fieldName]); } } else { $value = array_pop($params[$fieldName]); } } if ($field->getAttribute('type') === 'checkbox' || $field->getAttribute('type') === 'radio') { if ($value === true || $value === $field->getAttribute('value')) { $this->checkOption($field); } else { $this->uncheckOption($field); } } elseif ($field->getAttribute('type') === 'button' || $field->getAttribute('type') === 'submit') { continue; } elseif ($field->getTagName() === 'select') { $this->selectOption($field, $value); } else { $this->fillField($field, $value); } } $this->debugSection( 'Uri', $form->getAttribute('action') ? $form->getAttribute('action') : $this->_getCurrentUri() ); $this->debugSection('Method', $form->getAttribute('method') ? $form->getAttribute('method') : 'GET'); $this->debugSection('Parameters', json_encode($params)); $submitted = false; if (!empty($button)) { if (is_array($button)) { $buttonSelector = $this->getStrictLocator($button); } elseif ($button instanceof WebDriverBy) { $buttonSelector = $button; } else { $buttonSelector = WebDriverBy::name($button); } $els = $form->findElements($buttonSelector); if (!empty($els)) { $el = reset($els); $el->click(); $submitted = true; } } if (!$submitted) { $form->submit(); } $this->debugSection('Page', $this->_getCurrentUri()); } /** * Waits up to $timeout seconds for the given element to change. * Element "change" is determined by a callback function which is called repeatedly * until the return value evaluates to true. * * ``` php * waitForElementChange('#menu', function(WebDriverElement $el) { * return $el->isDisplayed(); * }, 100); * ?> * ``` * * @param $element * @param \Closure $callback * @param int $timeout seconds * @throws \Codeception\Exception\ElementNotFound */ public function waitForElementChange($element, \Closure $callback, $timeout = 30) { $el = $this->matchFirstOrFail($this->getBaseElement(), $element); $checker = function () use ($el, $callback) { return $callback($el); }; $this->webDriver->wait($timeout)->until($checker); } /** * Waits up to $timeout seconds for an element to appear on the page. * If the element doesn't appear, a timeout exception is thrown. * * ``` php * waitForElement('#agree_button', 30); // secs * $I->click('#agree_button'); * ?> * ``` * * @param $element * @param int $timeout seconds * @throws \Exception */ public function waitForElement($element, $timeout = 10) { $condition = WebDriverExpectedCondition::presenceOfElementLocated($this->getLocator($element)); $this->webDriver->wait($timeout)->until($condition); } /** * Waits up to $timeout seconds for the given element to be visible on the page. * If element doesn't appear, a timeout exception is thrown. * * ``` php * waitForElementVisible('#agree_button', 30); // secs * $I->click('#agree_button'); * ?> * ``` * * @param $element * @param int $timeout seconds * @throws \Exception */ public function waitForElementVisible($element, $timeout = 10) { $condition = WebDriverExpectedCondition::visibilityOfElementLocated($this->getLocator($element)); $this->webDriver->wait($timeout)->until($condition); } /** * Waits up to $timeout seconds for the given element to become invisible. * If element stays visible, a timeout exception is thrown. * * ``` php * waitForElementNotVisible('#agree_button', 30); // secs * ?> * ``` * * @param $element * @param int $timeout seconds * @throws \Exception */ public function waitForElementNotVisible($element, $timeout = 10) { $condition = WebDriverExpectedCondition::invisibilityOfElementLocated($this->getLocator($element)); $this->webDriver->wait($timeout)->until($condition); } /** * Waits up to $timeout seconds for the given element to be clickable. * If element doesn't become clickable, a timeout exception is thrown. * * ``` php * waitForElementClickable('#agree_button', 30); // secs * $I->click('#agree_button'); * ?> * ``` * * @param $element * @param int $timeout seconds * @throws \Exception */ public function waitForElementClickable($element, $timeout = 10) { $condition = WebDriverExpectedCondition::elementToBeClickable($this->getLocator($element)); $this->webDriver->wait($timeout)->until($condition); } /** * Waits up to $timeout seconds for the given string to appear on the page. * * Can also be passed a selector to search in, be as specific as possible when using selectors. * waitForText() will only watch the first instance of the matching selector / text provided. * If the given text doesn't appear, a timeout exception is thrown. * * ``` php * waitForText('foo', 30); // secs * $I->waitForText('foo', 30, '.title'); // secs * ?> * ``` * * @param string $text * @param int $timeout seconds * @param string $selector optional * @throws \Exception */ public function waitForText($text, $timeout = 10, $selector = null) { $message = sprintf( 'Waited for %d secs but text %s still not found', $timeout, Locator::humanReadableString($text) ); if (!$selector) { $condition = WebDriverExpectedCondition::textToBePresentInElement(WebDriverBy::xpath('//body'), $text); $this->webDriver->wait($timeout)->until($condition, $message); return; } $condition = WebDriverExpectedCondition::textToBePresentInElement($this->getLocator($selector), $text); $this->webDriver->wait($timeout)->until($condition, $message); } /** * Wait for $timeout seconds. * * @param int|float $timeout secs * @throws \Codeception\Exception\TestRuntimeException */ public function wait($timeout) { if ($timeout >= 1000) { throw new TestRuntimeException( " Waiting for more then 1000 seconds: 16.6667 mins\n Please note that wait method accepts number of seconds as parameter." ); } usleep($timeout * 1000000); } /** * Low-level API method. * If Codeception commands are not enough, this allows you to use Selenium WebDriver methods directly: * * ``` php * $I->executeInSelenium(function(\Facebook\WebDriver\Remote\RemoteWebDriver $webdriver) { * $webdriver->get('http://google.com'); * }); * ``` * * This runs in the context of the * [RemoteWebDriver class](https://github.com/php-webdriver/php-webdriver/blob/master/lib/remote/RemoteWebDriver.php). * Try not to use this command on a regular basis. * If Codeception lacks a feature you need, please implement it and submit a patch. * * @param callable $function */ public function executeInSelenium(\Closure $function) { return $function($this->webDriver); } /** * Switch to another window identified by name. * * The window can only be identified by name. If the $name parameter is blank, the parent window will be used. * * Example: * ``` html * * ``` * * ``` php * click("Open window"); * # switch to another window * $I->switchToWindow("another_window"); * # switch to parent window * $I->switchToWindow(); * ?> * ``` * * If the window has no name, match it by switching to next active tab using `switchToNextTab` method. * * Or use native Selenium functions to get access to all opened windows: * * ``` php * executeInSelenium(function (\Facebook\WebDriver\Remote\RemoteWebDriver $webdriver) { * $handles=$webdriver->getWindowHandles(); * $last_window = end($handles); * $webdriver->switchTo()->window($last_window); * }); * ?> * ``` * * @param string|null $name */ public function switchToWindow($name = null) { $this->webDriver->switchTo()->window($name); } /** * Switch to another iframe on the page. * * Example: * ``` html *