#!/usr/bin/env php :   codecept.phar/vendor/nikic/php-parser/lib/PhpParser/Error.phpE E cŊ0vendor/nikic/php-parser/lib/PhpParser/Parser.phpן7vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter.php:,i8vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.phpZZc?vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.phpڤ@vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php;;zXb3vendor/nikic/php-parser/lib/PhpParser/Modifiers.php7vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.phpQ$3O4vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php  P}Avendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php)/|?vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php͇4vendor/nikic/php-parser/lib/PhpParser/PhpVersion.php5 5 {Iӈ5vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.phpgg[ĠZ5vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php$ $ c Ԥ1vendor/nikic/php-parser/lib/PhpParser/Comment.php r/5vendor/nikic/php-parser/lib/PhpParser/NodeVisitor.php''{/vendor/nikic/php-parser/lib/PhpParser/Lexer.phpDA16vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.phpG<vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php;;37vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php/v8vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php+=vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.phpcc^iBvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php"VY?Ivendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.phpmaxDvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php55􉉤Nvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.phpww=RAKvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php--& Mvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php@@pDvendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php?r٤4vendor/nikic/php-parser/lib/PhpParser/NodeFinder.phpaa>>L5vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.phpOd5vendor/nikic/php-parser/lib/PhpParser/Parser/Php8.phpH"H"T};vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.phpqqnHG>vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.phpdd %SLvendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php~M\9vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php 3@vendor/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php%k9vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.phpSvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.phpr&%*Svendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php+܀(Svendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php&Kvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php  ꍤOvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.phpgnOvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.phpYY$JV[vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.phpR)\Mvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.phpggćPvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.phpvvc%#Mvendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php))J5vendor/nikic/php-parser/lib/PhpParser/NameContext.php55W/vendor/nikic/php-parser/lib/PhpParser/Token.php PFvendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.phpppҀ;vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.phpAh7vendor/nikic/php-parser/lib/PhpParser/Node/MatchArm.phpb\>3vendor/nikic/php-parser/lib/PhpParser/Node/Name.phpI I h٤=vendor/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.phpY y:vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.php)4vendor/nikic/php-parser/lib/PhpParser/Node/Param.phpNN;vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.phpJJ?vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php >8vendor/nikic/php-parser/lib/PhpParser/Node/StaticVar.phpFF7vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php[d;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php'W@<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.phpg4,9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.phpO7 7vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php|<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.phpΤ8vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.phpzzQR;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.phpl/<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.phpᅔ_:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php..Zu:Bvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/DeclareDeclare.phpII9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.phps;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php dDq;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.phpAA뭤>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.phps>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.phpMM_>-:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.phpqmyz@vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php6?vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php VLvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpjQvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.phpzzy6=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/StaticVar.phpGG&;vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php$4:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.phpEEG)<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php%>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php|Dvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/PropertyProperty.phpJJZ{zǤ=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.phptf1>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php))E8vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.phpxxD:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php>vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.phpĽ<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php^^k9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php?G:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.phpP:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php rz{9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.phpk:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.phpHH$֗89vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php&K9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php*W:vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php:SO=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php9V9vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php<vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php <vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.phpäFvendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php=vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php+:n7vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.phpMd8vendor/nikic/php-parser/lib/PhpParser/Node/Attribute.phpBvendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.phpjjBs2vendor/nikic/php-parser/lib/PhpParser/Node/Arg.phpyyIƤ5vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php<5C3Evendor/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php03vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.phpYm";vendor/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php#8vendor/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php z(3vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php}序9vendor/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php$$ 6vendor/nikic/php-parser/lib/PhpParser/Node/UseItem.phpVV)p@vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.phpZBvendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php-G$g<vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.phpN=vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.phpAAGHvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/EncapsedStringPart.phpTTŝ'@vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.phpTT1"Ȥ=vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php  $뛤=vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/LNumber.php??t1i>vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.phpMMqO<vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.phpHvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php//":vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.phpt-äGvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.phpuGvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.phpY^,Dvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php^Kvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php''&Gvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.phpXEvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.phpkKJvendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php$$MG0Evendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php@9vendor/nikic/php-parser/lib/PhpParser/Node/Identifier.phpJJ3 ݤ5vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.phpbbkQK8vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.phpyy^:vendor/nikic/php-parser/lib/PhpParser/Node/DeclareItem.phpZZ!9vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.phpPP-g :vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php0wCvendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.phpQQN;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.phpp݇D9vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php >vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.phptAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.phpڤFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php,Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.phpgg ~=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.phpKIvendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php::UФ9vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php Avendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.phpg{Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.phpi;UBGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.phpDvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php4Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.phpN@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php  ל Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.phps@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php  w¤Cvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php|Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php?Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php t@|Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php3ϟEvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php6 L̤Fvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.phpFYEvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.phpfIvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php#2HBvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php_Kvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php  U٤Dvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php[=wGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php P@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php  /SGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php4^@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php  RIAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php  mwFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.phpߤBvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php  Kvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php  iKqܤGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php)X:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.phpX&Ȥ:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.phpg{Dn>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php0Ť>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.phpkx>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.phpX{n;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.phpHHH ?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.phpSPx<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.phpX^=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.phprM<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php눱9vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php@w<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.phpOO7L:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.phpEPդ:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.phpL謤@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.phpo@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php)`Cvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.phpfFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.phphLFvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.phps쑺Gvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.phpǤEvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.phpGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.phpff@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.phpRGvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.phpb<@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php\sAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.phpWeBvendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php!g6<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php  {?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php694@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.phpiY-?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php8@vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.phpL>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php Ǥ=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php`\?vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.phpZ8vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.phpccf5=;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php`/:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.phpy=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayItem.phpGGf>vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClosureUse.phpHH6:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php`ϤAvendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.phpvendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.phpJ1¤<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php''XI=vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php+;vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.phpV78vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.phpSS&8W<vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php>:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.phpI֤:vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.phpxz6vendor/nikic/php-parser/lib/PhpParser/NodeAbstract.phph=vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php֩:vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.phpE38vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php00,> <vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php99i7vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php>>Re8vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php>vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.phpggZ:vendor/nikic/php-parser/lib/PhpParser/Builder/Property.phpq<vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.phpVVf^6vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.phphh4ä<vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php^^+%&O7vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php((8vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php؋SФ;vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.phprr:vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.phpff|aDvendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.phpZ Z Ӫ@vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php:r11vendor/nikic/php-parser/lib/PhpParser/Builder.phprr_g>vendor/nikic/php-parser/lib/PhpParser/compatibility_tokens.php+8vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.phpggh.vendor/nikic/php-parser/lib/PhpParser/Node.phpvG]6vendor/phar-io/version/src/VersionConstraintParser.php? ? MQ,vendor/phar-io/version/src/VersionNumber.php//f,w[&vendor/phar-io/version/src/Version.php/C_5vendor/phar-io/version/src/VersionConstraintValue.php ?vendor/phar-io/version/src/constraints/AnyVersionConstraint.php^<vendor/phar-io/version/src/constraints/VersionConstraint.phpw_Cvendor/phar-io/version/src/constraints/OrVersionConstraintGroup.php$Pvendor/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php  Dvendor/phar-io/version/src/constraints/AndVersionConstraintGroup.php Ivendor/phar-io/version/src/constraints/SpecificMajorVersionConstraint.phpXxDvendor/phar-io/version/src/constraints/AbstractVersionConstraint.phpKKqBQvendor/phar-io/version/src/constraints/SpecificMajorAndMinorVersionConstraint.php&qAvendor/phar-io/version/src/constraints/ExactVersionConstraint.phpjj<1n/vendor/phar-io/version/src/PreReleaseSuffix.phpYYi,vendor/phar-io/version/src/BuildMetaData.php[[)>Evendor/phar-io/version/src/exceptions/NoPreReleaseSuffixException.phpXbpԤOvendor/phar-io/version/src/exceptions/UnsupportedVersionConstraintException.php/OBvendor/phar-io/version/src/exceptions/NoBuildMetaDataException.php3vendor/phar-io/version/src/exceptions/Exception.php~~x|Jvendor/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.phpAvendor/phar-io/version/src/exceptions/InvalidVersionException.php|.vendor/phar-io/manifest/src/ManifestLoader.php((%ZϤ;vendor/phar-io/manifest/src/xml/AuthorElementCollection.phpѤ.vendor/phar-io/manifest/src/xml/PhpElement.phpY4vendor/phar-io/manifest/src/xml/CopyrightElement.php||~2vendor/phar-io/manifest/src/xml/LicenseElement.phpi2vendor/phar-io/manifest/src/xml/BundlesElement.phpފ4vendor/phar-io/manifest/src/xml/ManifestDocument.php zߤ3vendor/phar-io/manifest/src/xml/ContainsElement.php u5vendor/phar-io/manifest/src/xml/ElementCollection.php88]a1vendor/phar-io/manifest/src/xml/AuthorElement.phpbbnU4vendor/phar-io/manifest/src/xml/ExtensionElement.php"";8vendor/phar-io/manifest/src/xml/ExtElementCollection.php4D>vendor/phar-io/manifest/src/xml/ComponentElementCollection.php 53vendor/phar-io/manifest/src/xml/RequiresElement.phpX.vendor/phar-io/manifest/src/xml/ExtElement.php4vendor/phar-io/manifest/src/xml/ComponentElement.php@"3vendor/phar-io/manifest/src/xml/ManifestElement.phpUuZ7vendor/phar-io/manifest/src/values/AuthorCollection.php ,vendor/phar-io/manifest/src/values/Email.php;L?Avendor/phar-io/manifest/src/values/BundledComponentCollection.phpI r.vendor/phar-io/manifest/src/values/License.phpJJ|N*vendor/phar-io/manifest/src/values/Url.php<vendor/phar-io/manifest/src/values/RequirementCollection.phpKKJ.vendor/phar-io/manifest/src/values/Library.phpd6vendor/phar-io/manifest/src/values/ApplicationName.php^;vendor/phar-io/manifest/src/values/CopyrightInformation.php^ݤ0vendor/phar-io/manifest/src/values/Extension.phpbͤDvendor/phar-io/manifest/src/values/RequirementCollectionIterator.phpX :Ivendor/phar-io/manifest/src/values/BundledComponentCollectionIterator.phpYڤ/vendor/phar-io/manifest/src/values/Manifest.php*07vendor/phar-io/manifest/src/values/BundledComponent.phpJ>vendor/phar-io/manifest/src/values/PhpExtensionRequirement.php((s)-vendor/phar-io/manifest/src/values/Author.phpAI<vendor/phar-io/manifest/src/values/PhpVersionRequirement.phpn2vendor/phar-io/manifest/src/values/Requirement.php``',2vendor/phar-io/manifest/src/values/Application.phpX?vendor/phar-io/manifest/src/values/AuthorCollectionIterator.phpzz$+H+vendor/phar-io/manifest/src/values/Type.phpkkJ.2vendor/phar-io/manifest/src/ManifestSerializer.phpit6vendor/phar-io/manifest/src/ManifestDocumentMapper.php. . 1Jvendor/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php!-Kvendor/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php>>dBvendor/phar-io/manifest/src/exceptions/NoEmailAddressException.phpz輤Cvendor/phar-io/manifest/src/exceptions/ManifestElementException.php&П@vendor/phar-io/manifest/src/exceptions/InvalidEmailException.php2>vendor/phar-io/manifest/src/exceptions/InvalidUrlException.phpQ,Jvendor/phar-io/manifest/src/exceptions/InvalidApplicationNameException.php~A4vendor/phar-io/manifest/src/exceptions/Exception.phpNQDvendor/phar-io/manifest/src/exceptions/ManifestDocumentException.phpUEvendor/phar-io/manifest/src/exceptions/ElementCollectionException.phppjNBvendor/phar-io/manifest/src/exceptions/ManifestLoaderException.php(Ivendor/phar-io/manifest/tools/php-cs-fixer.d/PhpdocSingleLineVarFixer.php v1vendor/behat/gherkin/src/Behat/Gherkin/Parser.php33$=vendor/behat/gherkin/src/Behat/Gherkin/Loader/ArrayLoader.php]]iȤ@vendor/behat/gherkin/src/Behat/Gherkin/Loader/YamlFileLoader.phpS oEvendor/behat/gherkin/src/Behat/Gherkin/Loader/FileLoaderInterface.php=Avendor/behat/gherkin/src/Behat/Gherkin/Loader/LoaderInterface.phpEZbAvendor/behat/gherkin/src/Behat/Gherkin/Loader/DirectoryLoader.phpܯGCvendor/behat/gherkin/src/Behat/Gherkin/Loader/GherkinFileLoader.phpۤDvendor/behat/gherkin/src/Behat/Gherkin/Loader/AbstractFileLoader.phpRIvendor/behat/gherkin/src/Behat/Gherkin/Loader/CucumberNDJsonAstLoader.phpWW0vendor/behat/gherkin/src/Behat/Gherkin/Lexer.php%% <vendor/behat/gherkin/src/Behat/Gherkin/Cache/MemoryCache.php..&cm?vendor/behat/gherkin/src/Behat/Gherkin/Cache/CacheInterface.php  E:vendor/behat/gherkin/src/Behat/Gherkin/Cache/FileCache.phpGG¤2vendor/behat/gherkin/src/Behat/Gherkin/Gherkin.php  yB;vendor/behat/gherkin/src/Behat/Gherkin/Node/OutlineNode.phpee?Avendor/behat/gherkin/src/Behat/Gherkin/Node/ArgumentInterface.phpii\;vendor/behat/gherkin/src/Behat/Gherkin/Node/FeatureNode.phpἸu=vendor/behat/gherkin/src/Behat/Gherkin/Node/NodeInterface.phpq!z@vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleTableNode.php38vendor/behat/gherkin/src/Behat/Gherkin/Node/StepNode.phpIi<vendor/behat/gherkin/src/Behat/Gherkin/Node/PyStringNode.php??O<vendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioNode.php BAvendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioInterface.phpA9p9vendor/behat/gherkin/src/Behat/Gherkin/Node/TableNode.php99$AEvendor/behat/gherkin/src/Behat/Gherkin/Node/ScenarioLikeInterface.phpxFvendor/behat/gherkin/src/Behat/Gherkin/Node/StepContainerInterface.php' ~;vendor/behat/gherkin/src/Behat/Gherkin/Node/ExampleNode.php  c|Dvendor/behat/gherkin/src/Behat/Gherkin/Node/KeywordNodeInterface.phpn>vendor/behat/gherkin/src/Behat/Gherkin/Node/BackgroundNode.php1Cvendor/behat/gherkin/src/Behat/Gherkin/Node/TaggedNodeInterface.php%,<vendor/behat/gherkin/src/Behat/Gherkin/Filter/RoleFilter.phpYY:{;vendor/behat/gherkin/src/Behat/Gherkin/Filter/TagFilter.php (>vendor/behat/gherkin/src/Behat/Gherkin/Filter/SimpleFilter.php<Hvendor/behat/gherkin/src/Behat/Gherkin/Filter/FeatureFilterInterface.php<vendor/behat/gherkin/src/Behat/Gherkin/Filter/NameFilter.phpS-Avendor/behat/gherkin/src/Behat/Gherkin/Filter/LineRangeFilter.php9 9 äAvendor/behat/gherkin/src/Behat/Gherkin/Filter/NarrativeFilter.php?vendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilter.phpee Hvendor/behat/gherkin/src/Behat/Gherkin/Filter/ComplexFilterInterface.php$$Q=ϤAvendor/behat/gherkin/src/Behat/Gherkin/Filter/FilterInterface.phpsH=vendor/behat/gherkin/src/Behat/Gherkin/Filter/PathsFilter.php{6ݤ<vendor/behat/gherkin/src/Behat/Gherkin/Filter/LineFilter.phpsBvendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsDumper.phpr-MEvendor/behat/gherkin/src/Behat/Gherkin/Keywords/KeywordsInterface.phpZZ[DDvendor/behat/gherkin/src/Behat/Gherkin/Keywords/CucumberKeywords.phpAvendor/behat/gherkin/src/Behat/Gherkin/Keywords/ArrayKeywords.phpeGvendor/behat/gherkin/src/Behat/Gherkin/Keywords/CachedArrayKeywords.phpKPCvendor/behat/gherkin/src/Behat/Gherkin/Exception/CacheException.phpJ񼩤Bvendor/behat/gherkin/src/Behat/Gherkin/Exception/NodeException.phpt>vendor/behat/gherkin/src/Behat/Gherkin/Exception/Exception.phpKKDvendor/behat/gherkin/src/Behat/Gherkin/Exception/ParserException.phpdKCvendor/behat/gherkin/src/Behat/Gherkin/Exception/LexerException.phpU vendor/behat/gherkin/libpath.phptrnǤvendor/behat/gherkin/i18n.php9/Cvendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php  PRvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/PredefinedArray.php,,15Xvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.phpTvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/AbstractRetriever.php[AGvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.phpǒRvendor/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php  Dvendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php@@53Jvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php PFFPvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php e Gvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php _Pvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php& & 78Nvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php**14NPvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.phpzzf~Pvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php%lTvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php =Svendor/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.phpG"G"@?Nvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/BaseConstraint.php wlSvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php QXvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.phpaa qäYvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php4~\vendor/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php+jNvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php\\_}Pvendor/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.phpssd ޤJvendor/justinrainbow/json-schema/src/JsonSchema/SchemaStorageInterface.phpo+};vendor/justinrainbow/json-schema/src/JsonSchema/Rfc3339.phpf4Ivendor/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.phpeAvendor/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php &z^=vendor/justinrainbow/json-schema/src/JsonSchema/Validator.phpss#Fvendor/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php{NHvendor/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.phpJKvendor/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.phpMTvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidConfigException.phpllA!LפPvendor/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.phpII%|Wvendor/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.phpoopSvendor/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php\ Tvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaException.phpll2]vendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.phpuu=h^vendor/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.phpu-#1Nvendor/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php%Rvendor/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.phpjj>Vvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.phpGQvendor/justinrainbow/json-schema/src/JsonSchema/Exception/ValidationException.phpffWvendor/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSourceUriException.phpwwN-[6vendor/theseer/tokenizer/src/NamespaceUriException.phpqq)0U0vendor/theseer/tokenizer/src/TokenCollection.phpO.vendor/theseer/tokenizer/src/XMLSerializer.php4-vendor/theseer/tokenizer/src/NamespaceUri.php X*vendor/theseer/tokenizer/src/Tokenizer.phpYYTLӤ*vendor/theseer/tokenizer/src/Exception.phpff9vendor/theseer/tokenizer/src/TokenCollectionException.phpttu&vendor/theseer/tokenizer/src/Token.phpX8vendor/psr/http-client/src/RequestExceptionInterface.phpɾ7vendor/psr/http-client/src/ClientExceptionInterface.php``f .vendor/psr/http-client/src/ClientInterface.phpo穤8vendor/psr/http-client/src/NetworkExceptionInterface.phpDd;vendor/psr/event-dispatcher/src/StoppableEventInterface.php.<vendor/psr/event-dispatcher/src/EventDispatcherInterface.phpISؤ=vendor/psr/event-dispatcher/src/ListenerProviderInterface.php7 3vendor/psr/http-factory/src/UriFactoryInterface.phpZ7vendor/psr/http-factory/src/RequestFactoryInterface.phpbߤ6vendor/psr/http-factory/src/StreamFactoryInterface.phpTT0(8vendor/psr/http-factory/src/ResponseFactoryInterface.phpC<vendor/psr/http-factory/src/UploadedFileFactoryInterface.php''.6=vendor/psr/http-factory/src/ServerRequestFactoryInterface.phpy)='vendor/psr/log/src/LoggerAwareTrait.phpx!vendor/psr/log/src/NullLogger.phpeW%vendor/psr/log/src/AbstractLogger.phppp%WФ/vendor/psr/log/src/InvalidArgumentException.php`` X1+vendor/psr/log/src/LoggerAwareInterface.phppդvendor/psr/log/src/LogLevel.phpj8"vendor/psr/log/src/LoggerTrait.php5&vendor/psr/log/src/LoggerInterface.php||*b7vendor/psr/container/src/NotFoundExceptionInterface.phpqqR8vendor/psr/container/src/ContainerExceptionInterface.phppp;Z /vendor/psr/container/src/ContainerInterface.php[d0vendor/psr/http-message/src/MessageInterface.phpVVjj0vendor/psr/http-message/src/RequestInterface.php..y1vendor/psr/http-message/src/ResponseInterface.php==#/vendor/psr/http-message/src/StreamInterface.phpě5vendor/psr/http-message/src/UploadedFileInterface.phpZOP,vendor/psr/http-message/src/UriInterface.php\\S86vendor/psr/http-message/src/ServerRequestInterface.php772_ڤ"vendor/composer/platform_check.php  j)\!vendor/composer/autoload_real.php""`ܤ%vendor/composer/InstalledVersions.phpxG!vendor/composer/autoload_psr4.php n@vendor/composer/installed.php(h(h7L#vendor/composer/autoload_static.php[%vendor/composer/autoload_classmap.php''vvendor/composer/ClassLoader.php//⒤"vendor/composer/autoload_files.php7'vendor/composer/autoload_namespaces.php~t,vendor/guzzlehttp/guzzle/src/ClientTrait.php  6vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php[I%vendor/guzzlehttp/guzzle/src/Pool.php q-vendor/guzzlehttp/guzzle/src/HandlerStack.phpxxx*vendor/guzzlehttp/guzzle/src/functions.phpvv: 2vendor/guzzlehttp/guzzle/src/functions_include.phpaaޤ1vendor/guzzlehttp/guzzle/src/MessageFormatter.php, , {/vendor/guzzlehttp/guzzle/src/RequestOptions.php4%/vendor/guzzlehttp/guzzle/src/BodySummarizer.phpK&vendor/guzzlehttp/guzzle/src/Utils.php^0vendor/guzzlehttp/guzzle/src/RetryMiddleware.php%;)0vendor/guzzlehttp/guzzle/src/ClientInterface.php-.vendor/guzzlehttp/guzzle/src/TransferStats.php44k+vendor/guzzlehttp/guzzle/src/Middleware.phpɟ3vendor/guzzlehttp/guzzle/src/RedirectMiddleware.phpyy$t'vendor/guzzlehttp/guzzle/src/Client.php-%-%5摤1vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.phpZZL8vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.phpt5vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php77s\G:vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php1vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.phps2ϤDvendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.phpee$<vendor/guzzlehttp/guzzle/src/Exception/TransferException.phpyy/;vendor/guzzlehttp/guzzle/src/Exception/RequestException.phpr r jܤ;vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php"l}:vendor/guzzlehttp/guzzle/src/Exception/ClientException.phpbbyʤ?vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php88"Vp~:vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php5O\$Cvendor/guzzlehttp/guzzle/src/Exception/InvalidArgumentException.phpco:vendor/guzzlehttp/guzzle/src/Exception/ServerException.phpbbmx8vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.phpAN3vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.phpg,4vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.phpzؘ6vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php55p9vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.phpGGe4vendor/guzzlehttp/guzzle/src/Handler/MockHandler.phpAA׉4vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.phpn;n;ӝ.vendor/guzzlehttp/guzzle/src/Handler/Proxy.php%%o=vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.phpYۤ8vendor/guzzlehttp/guzzle/src/BodySummarizerInterface.phpxD[:vendor/guzzlehttp/guzzle/src/MessageFormatterInterface.php8vendor/guzzlehttp/promises/src/CancellationException.php~~5vendor/guzzlehttp/promises/src/TaskQueueInterface.php*vendor/guzzlehttp/promises/src/Promise.phpg3vendor/guzzlehttp/promises/src/FulfilledPromise.php55mY4vendor/guzzlehttp/promises/src/PromisorInterface.php1R5vendor/guzzlehttp/promises/src/RejectionException.phpW, 3vendor/guzzlehttp/promises/src/PromiseInterface.phpuuQDvC(vendor/guzzlehttp/promises/src/Utils.php ZW .vendor/guzzlehttp/promises/src/EachPromise.phpu u l"פ,vendor/guzzlehttp/promises/src/Coroutine.php<<˭2vendor/guzzlehttp/promises/src/RejectedPromise.php__i'vendor/guzzlehttp/promises/src/Each.phpf6,vendor/guzzlehttp/promises/src/TaskQueue.php#r)vendor/guzzlehttp/promises/src/Create.phpTTc 5vendor/guzzlehttp/promises/src/AggregateException.phpRY9y%vendor/guzzlehttp/promises/src/Is.phpdd{G,vendor/guzzlehttp/psr7/src/StreamWrapper.phpw w ym,vendor/guzzlehttp/psr7/src/CachingStream.php) ) {%,vendor/guzzlehttp/psr7/src/UriNormalizer.php  oi+vendor/guzzlehttp/psr7/src/NoSeekStream.phpzzZ*+vendor/guzzlehttp/psr7/src/MessageTrait.phpHF)vendor/guzzlehttp/psr7/src/PumpStream.php+ + @%vendor/guzzlehttp/psr7/src/Stream.php BA*vendor/guzzlehttp/psr7/src/HttpFactory.php "*a*vendor/guzzlehttp/psr7/src/UriResolver.phpF2I%vendor/guzzlehttp/psr7/src/Header.phpxxJ0&vendor/guzzlehttp/psr7/src/Request.php p8"vendor/guzzlehttp/psr7/src/Uri.php , ,݇G'vendor/guzzlehttp/psr7/src/Response.phpK K P'vendor/guzzlehttp/psr7/src/MimeType.php>>g&vendor/guzzlehttp/psr7/src/Message.php  ~]&vendor/guzzlehttp/psr7/src/Rfc7230.php\!$vendor/guzzlehttp/psr7/src/Utils.php;-vendor/guzzlehttp/psr7/src/DroppingStream.phpkkC ?$vendor/guzzlehttp/psr7/src/Query.php(6,vendor/guzzlehttp/psr7/src/UriComparator.phpyפ.vendor/guzzlehttp/psr7/src/MultipartStream.phpf f #A*vendor/guzzlehttp/psr7/src/LimitStream.php:t,vendor/guzzlehttp/psr7/src/InflateStream.php  -`,vendor/guzzlehttp/psr7/src/ServerRequest.phpQq3vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.phpN+-vendor/guzzlehttp/psr7/src/LazyOpenStream.php  /^'vendor/guzzlehttp/psr7/src/FnStream.phpy y &+vendor/guzzlehttp/psr7/src/AppendStream.php .G+vendor/guzzlehttp/psr7/src/UploadedFile.phpy y *>vendor/guzzlehttp/psr7/src/Exception/MalformedUriException.php}ۤ+vendor/guzzlehttp/psr7/src/BufferStream.phpY)~Mvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPStreamConnection.php; ; c8Nvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionFactory.php Mvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionConfig.php%%w}O|Qvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySocketConnection.php&Kvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.phpä`vendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/AbstractSignalHeartbeatSender.php Wvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.php11 CUvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/SIGHeartbeatSender.php77CퟤNvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySSLConnection.php^hMJvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSSLConnection.php}S$Kvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AbstractConnection.phpIIǨMvendor/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPSocketConnection.phpvvv\[Avendor/php-amqplib/php-amqplib/PhpAmqpLib/Message/AMQPMessage.php&@vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/DebugHelper.php33r¤Ivendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol091.php55\a\Ivendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Protocol080.phpMM=^Evendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait091.phpUGJvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap080.php P6ФEvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/Wait080.php lwJvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Protocol/MethodMap091.phpvqF?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/MiscHelper.php44y;vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/Assert.php77qDvendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/SocketConstants.phpVĄߤ?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Helper/BigInteger.php\\<vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants.phpLy?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants091.php F=¤>vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.phpqq~Z?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPIOReader.phpd wCvendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPBufferReader.phpnn*L<vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPTable.phpJRIvendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPAbstractCollection.phpV!V!f=vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPWriter.php zAvendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPByteStream.phpjjd>vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/StreamIO.php|#|#aȤ>vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/SocketIO.phpŒ@vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/IO/AbstractIO.php ]?vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/Constants080.phpq q ԛ<vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPArray.phpllUۤ=vendor/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPReader.phpDGvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exchange/AMQPExchangeType.phpoEvendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php##fз;vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Frame.phpU>Avendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.php[[^Z<vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Method.php%%CuUvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionClosedException.phppph Kvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOWaitException.phpccMNvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPExceptionInterface.phpMM"WܤTvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidArgumentException.php,DΤGvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPIOException.phpwwf5Kvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPSocketException.phpccbҤSvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNotImplementedException.phpkkd4аMvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolException.php  qJvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPLogicException.phpJ_Pvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfBoundsException.phpOvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPOutOfRangeException.php|#ǤLvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPRuntimeException.phpAMvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPDataReadException.phpeew068Rvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPChannelClosedException.phpjj읦Vvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php==_{Qvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPInvalidFrameException.phpiiMTvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPProtocolChannelException.phpmmN!Lvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPTimeoutException.phpMfUvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPEmptyDeliveryTagException.phpmm֔Tvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPHeartbeatMissedException.phpuu渖Kvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPNoDataException.phpff? ,ͤPvendor/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPBasicCancelException.php__<5vendor/php-amqplib/php-amqplib/PhpAmqpLib/Package.phpuuNۤGvendor/doctrine/instantiator/src/Doctrine/Instantiator/Instantiator.phpjj5;Pvendor/doctrine/instantiator/src/Doctrine/Instantiator/InstantiatorInterface.php#Wvendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/ExceptionInterface.phpyym7]vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/InvalidArgumentException.phpvvX~B]vendor/doctrine/instantiator/src/Doctrine/Instantiator/Exception/UnexpectedValueException.phpuRvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.phpMDLvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.phpnnJvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php!\?vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.phpffZ.)ȤBvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php›qFvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.phpgLvendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php[[%MQ3vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php nEvendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.phpEvendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.phpjQAvendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.phpjjc$CEvendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.phpkOvendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php)9vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.phpmmaˤ2vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php``ĪAvendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php46>vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php__|Gؤ>vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php""G@vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php))Wt7vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php||a]ˤ;vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php6Wvendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.phphRvendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.phpCC AMvendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.phps()Bvendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.phpLtEvendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.phpxx4@vendor/predis/predis/src/Protocol/RequestSerializerInterface.php$[7vendor/predis/predis/src/Protocol/ProtocolException.phpZ{@vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php^^(Evendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.phplm;<vendor/predis/predis/src/Protocol/Text/RequestSerializer.phpPP]9vendor/predis/predis/src/Protocol/Text/ResponseReader.php\<vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php%% ^t?vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.phpQQ +CNvendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.phpc3@vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php::*%Dvendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.phpU Avendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php??㘭Kvendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.phpj=3Bvendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php+\=vendor/predis/predis/src/Protocol/ResponseReaderInterface.phpy4vendor/predis/predis/src/Connection/RelayMethods.phpgg-?vendor/predis/predis/src/Connection/NodeConnectionInterface.phpllPp#Dvendor/predis/predis/src/Connection/AggregateConnectionInterface.php|/vendor/predis/predis/src/Connection/Factory.php g;j;vendor/predis/predis/src/Connection/ParametersInterface.phpY@8vendor/predis/predis/src/Connection/FactoryInterface.php(Ў2vendor/predis/predis/src/Connection/Parameters.phpn n >S;vendor/predis/predis/src/Connection/ConnectionException.php ;vendor/predis/predis/src/Connection/ConnectionInterface.php/X)8vendor/predis/predis/src/Connection/StreamConnection.phpɽҤ7vendor/predis/predis/src/Connection/RelayConnection.php {Avendor/predis/predis/src/Connection/PhpiredisStreamConnection.php!3Avendor/predis/predis/src/Connection/CompositeStreamConnection.php%Avendor/predis/predis/src/Connection/PhpiredisSocketConnection.php/Gvendor/predis/predis/src/Connection/Replication/SentinelReplication.php**=(6Hvendor/predis/predis/src/Connection/Replication/ReplicationInterface.phpJvendor/predis/predis/src/Connection/Replication/MasterSlaveReplication.phpO O RxDvendor/predis/predis/src/Connection/CompositeConnectionInterface.phpwΤ8vendor/predis/predis/src/Connection/WebdisConnection.phpw<vendor/predis/predis/src/Connection/Cluster/RedisCluster.php$$?7C=vendor/predis/predis/src/Connection/Cluster/PredisCluster.php |B@vendor/predis/predis/src/Connection/Cluster/ClusterInterface.phppL_:vendor/predis/predis/src/Connection/AbstractConnection.php  w0vendor/predis/predis/src/ClientConfiguration.phpaan2vendor/predis/predis/src/NotSupportedException.php``L?'vendor/predis/predis/src/Autoloader.phpt%ۤ,vendor/predis/predis/src/PredisException.phpllG 9vendor/predis/predis/src/Collection/Iterator/Keyspace.php^8vendor/predis/predis/src/Collection/Iterator/ListKey.php@@w!=vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php] 8vendor/predis/predis/src/Collection/Iterator/HashKey.phpna7vendor/predis/predis/src/Collection/Iterator/SetKey.php6yDvendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.phpTTL+vendor/predis/predis/src/Response/Error.php ރ7vendor/predis/predis/src/Response/ResponseInterface.phpPPLR/N,vendor/predis/predis/src/Response/Status.phpQQ\4vendor/predis/predis/src/Response/ErrorInterface.phphG55vendor/predis/predis/src/Response/ServerException.php]] p8vendor/predis/predis/src/Response/Iterator/MultiBulk.php3&=vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.phpK'@vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.phpϻ,vendor/predis/predis/src/Session/Handler.php;; 7vendor/predis/predis/src/Transaction/MultiExecState.php`f2vendor/predis/predis/src/Transaction/MultiExec.phpwwD_Bvendor/predis/predis/src/Transaction/AbortedMultiExecException.php6q3vendor/predis/predis/src/CommunicationException.phpKK <.,vendor/predis/predis/src/ClientException.phpYY]ov,vendor/predis/predis/src/ClientInterface.php3 3 :m,vendor/predis/predis/src/PubSub/Consumer.phpI2vendor/predis/predis/src/PubSub/DispatcherLoop.phpllhG4vendor/predis/predis/src/PubSub/AbstractConsumer.php d1vendor/predis/predis/src/PubSub/RelayConsumer.phpGG-vendor/predis/predis/src/Monitor/Consumer.phpII_0=vendor/predis/predis/src/Command/Processor/ProcessorChain.php*TAvendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php%,%,BXEAvendor/predis/predis/src/Command/Processor/ProcessorInterface.php{ 5vendor/predis/predis/src/Command/Redis/PSUBSCRIBE.phpMM.m9vendor/predis/predis/src/Command/Redis/ZREVRANGEBYLEX.phpaG3vendor/predis/predis/src/Command/Redis/BZPOPMIN.php1/vendor/predis/predis/src/Command/Redis/MSET.php@J?1vendor/predis/predis/src/Command/Redis/DECRBY.php*sD2vendor/predis/predis/src/Command/Redis/CLUSTER.phpPF1/vendor/predis/predis/src/Command/Redis/EXEC.phpn5vendor/predis/predis/src/Command/Redis/BRPOPLPUSH.php5̴N1vendor/predis/predis/src/Command/Redis/BZMPOP.php/vendor/predis/predis/src/Command/Redis/XDEL.php@@)/Y46vendor/predis/predis/src/Command/Redis/PEXPIRETIME.php{3vendor/predis/predis/src/Command/Redis/ZREVRANK.phpb琤2vendor/predis/predis/src/Command/Redis/MIGRATE.php..^4vendor/predis/predis/src/Command/Redis/RPOPLPUSH.phpyO4vendor/predis/predis/src/Command/Redis/SUBSCRIBE.phpKK?Avendor/predis/predis/src/Command/Redis/BloomFilter/BFSCANDUMP.php5=vendor/predis/predis/src/Command/Redis/BloomFilter/BFMADD.phpfBڤ@vendor/predis/predis/src/Command/Redis/BloomFilter/BFRESERVE.php<<G@vendor/predis/predis/src/Command/Redis/BloomFilter/BFMEXISTS.php,f=vendor/predis/predis/src/Command/Redis/BloomFilter/BFINFO.phpbb '<vendor/predis/predis/src/Command/Redis/BloomFilter/BFADD.php(?vendor/predis/predis/src/Command/Redis/BloomFilter/BFEXISTS.php ?vendor/predis/predis/src/Command/Redis/BloomFilter/BFINSERT.phpZBvendor/predis/predis/src/Command/Redis/BloomFilter/BFLOADCHUNK.php(41vendor/predis/predis/src/Command/Redis/DBSIZE.php*/vendor/predis/predis/src/Command/Redis/HGET.phpr2vendor/predis/predis/src/Command/Redis/COMMAND.phpr0vendor/predis/predis/src/Command/Redis/SCARD.php}/vendor/predis/predis/src/Command/Redis/SPOP.phpQvQ40vendor/predis/predis/src/Command/Redis/ZCARD.phpZ .vendor/predis/predis/src/Command/Redis/DEL.php??o3vendor/predis/predis/src/Command/Redis/SENTINEL.phpA<vendor/predis/predis/src/Command/Redis/GEORADIUSBYMEMBER.php%1vendor/predis/predis/src/Command/Redis/LPUSHX.phpsɩ6vendor/predis/predis/src/Command/Redis/SRANDMEMBER.php_[1vendor/predis/predis/src/Command/Redis/SCRIPT.phph01vendor/predis/predis/src/Command/Redis/BGSAVE.php""|X~1vendor/predis/predis/src/Command/Redis/GETSET.php/vendor/predis/predis/src/Command/Redis/DECR.phpԀnt/vendor/predis/predis/src/Command/Redis/RPOP.php{2vendor/predis/predis/src/Command/Redis/RESTORE.phpy$4vendor/predis/predis/src/Command/Redis/ZREVRANGE.phpa_Y4vendor/predis/predis/src/Command/Redis/RANDOMKEY.phpЦ;vendor/predis/predis/src/Command/Redis/ZREVRANGEBYSCORE.phpn尤2vendor/predis/predis/src/Command/Redis/PEXPIRE.php/vendor/predis/predis/src/Command/Redis/AUTH.phpå3vendor/predis/predis/src/Command/Redis/SHUTDOWN.php11)k:vendor/predis/predis/src/Command/Redis/Search/FTCREATE.phph<vendor/predis/predis/src/Command/Redis/Search/FTALIASADD.php%&8vendor/predis/predis/src/Command/Redis/Search/FTINFO.php ;vendor/predis/predis/src/Command/Redis/Search/FTTAGVALS.phpl99vendor/predis/predis/src/Command/Redis/Search/FTALTER.php{2=vendor/predis/predis/src/Command/Redis/Search/FTSYNUPDATE.php  E?vendor/predis/predis/src/Command/Redis/Search/FTALIASUPDATE.php\*;vendor/predis/predis/src/Command/Redis/Search/FTEXPLAIN.phpP:vendor/predis/predis/src/Command/Redis/Search/FTSUGADD.php:vendor/predis/predis/src/Command/Redis/Search/FTSUGLEN.php6%;vendor/predis/predis/src/Command/Redis/Search/FTDICTADD.php7:vendor/predis/predis/src/Command/Redis/Search/FTCURSOR.php;Uޤ;vendor/predis/predis/src/Command/Redis/Search/FTPROFILE.phpcc0L =vendor/predis/predis/src/Command/Redis/Search/FTDROPINDEX.phpy;vendor/predis/predis/src/Command/Redis/Search/FTSYNDUMP.phpZo&`=vendor/predis/predis/src/Command/Redis/Search/FTAGGREGATE.php]>vendor/predis/predis/src/Command/Redis/Search/FTSPELLCHECK.php::vendor/predis/predis/src/Command/Redis/Search/FTSEARCH.php'_uw<vendor/predis/predis/src/Command/Redis/Search/FTALIASDEL.phpD:vendor/predis/predis/src/Command/Redis/Search/FTCONFIG.php:vendor/predis/predis/src/Command/Redis/Search/FTSUGGET.php?@ˤ;vendor/predis/predis/src/Command/Redis/Search/FTDICTDEL.phpc͑<vendor/predis/predis/src/Command/Redis/Search/FTDICTDUMP.phpR:vendor/predis/predis/src/Command/Redis/Search/FTSUGDEL.phpQ0vendor/predis/predis/src/Command/Redis/LTRIM.php2L4vendor/predis/predis/src/Command/Redis/PEXPIREAT.php/vendor/predis/predis/src/Command/Redis/ZREM.php@@d01vendor/predis/predis/src/Command/Redis/PSETEX.php/vendor/predis/predis/src/Command/Redis/KEYS.php!6vendor/predis/predis/src/Command/Redis/ZUNIONSTORE.php::$1vendor/predis/predis/src/Command/Redis/HSETNX.phpD1vendor/predis/predis/src/Command/Redis/LRANGE.php%׳1vendor/predis/predis/src/Command/Redis/BLMOVE.php~~q|H&/vendor/predis/predis/src/Command/Redis/XLEN.php31vendor/predis/predis/src/Command/Redis/BLMPOP.phpdal=vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTCDF.php?vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTMERGE.phpr@vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTCREATE.php{ƤCvendor/predis/predis/src/Command/Redis/TDigest/TDIGESTBYREVRANK.php  ѕԤ>vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTINFO.php~Fvendor/predis/predis/src/Command/Redis/TDigest/TDIGESTTRIMMED_MEAN.phppjX!=vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTMIN.phpya@vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTBYRANK.phphBvendor/predis/predis/src/Command/Redis/TDigest/TDIGESTQUANTILE.php=vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTADD.php+3=vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTMAX.phpAvendor/predis/predis/src/Command/Redis/TDigest/TDIGESTREVRANK.php޶P>vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTRANK.php#?vendor/predis/predis/src/Command/Redis/TDigest/TDIGESTRESET.phptae4vendor/predis/predis/src/Command/Redis/XREVRANGE.phpCQ2vendor/predis/predis/src/Command/Redis/PFCOUNT.phpGG^e5vendor/predis/predis/src/Command/Redis/EXPIRETIME.phpFUФ0vendor/predis/predis/src/Command/Redis/ZRANK.phpi/vendor/predis/predis/src/Command/Redis/PING.phpgG٤/vendor/predis/predis/src/Command/Redis/SAVE.php 2vendor/predis/predis/src/Command/Redis/GEODIST.php;.vendor/predis/predis/src/Command/Redis/GET.phpT2vendor/predis/predis/src/Command/Redis/PFMERGE.phpGGN%1vendor/predis/predis/src/Command/Redis/GEOADD.phpԋ0vendor/predis/predis/src/Command/Redis/SDIFF.phpCCg 1vendor/predis/predis/src/Command/Redis/SELECT.phpum=1vendor/predis/predis/src/Command/Redis/EXISTS.phpe j1vendor/predis/predis/src/Command/Redis/XRANGE.phptti4vendor/predis/predis/src/Command/Redis/ZLEXCOUNT.php-3vendor/predis/predis/src/Command/Redis/SMEMBERS.php .vendor/predis/predis/src/Command/Redis/TTL.php)j5vendor/predis/predis/src/Command/Redis/ZDIFFSTORE.phpAAYΤ1vendor/predis/predis/src/Command/Redis/RPUSHX.phpuW3vendor/predis/predis/src/Command/Redis/LASTSAVE.phpٽ\*0vendor/predis/predis/src/Command/Redis/SETNX.php]<Ƥ0vendor/predis/predis/src/Command/Redis/BLPOP.php31ؤ3vendor/predis/predis/src/Command/Redis/BITCOUNT.phpDɤ0vendor/predis/predis/src/Command/Redis/XTRIM.php&&ϲ)!5vendor/predis/predis/src/Command/Redis/SDIFFSTORE.phpp;/vendor/predis/predis/src/Command/Redis/MOVE.php;vendor/predis/predis/src/Command/Redis/ZREMRANGEBYSCORE.php 2vendor/predis/predis/src/Command/Redis/EVAL_RO.php;1vendor/predis/predis/src/Command/Redis/ZRANGE.phpf0vendor/predis/predis/src/Command/Redis/ZDIFF.php44$oX1vendor/predis/predis/src/Command/Redis/SUBSTR.phpm;r0vendor/predis/predis/src/Command/Redis/PFADD.phpBB@c1vendor/predis/predis/src/Command/Redis/PUBSUB.php55~eפ2vendor/predis/predis/src/Command/Redis/LINSERT.phpi0vendor/predis/predis/src/Command/Redis/HVALS.phpڌ]0vendor/predis/predis/src/Command/Redis/FCALL.php !0vendor/predis/predis/src/Command/Redis/HMSET.phpI2vendor/predis/predis/src/Command/Redis/PUBLISH.php6Ѥ1vendor/predis/predis/src/Command/Redis/RENAME.php!G/vendor/predis/predis/src/Command/Redis/XADD.php  J+/vendor/predis/predis/src/Command/Redis/HSET.phpRޤ/vendor/predis/predis/src/Command/Redis/DUMP.phppSA6vendor/predis/predis/src/Command/Redis/ZRANGEBYLEX.phpSSdzQ7vendor/predis/predis/src/Command/Redis/PUNSUBSCRIBE.phpQQD/vendor/predis/predis/src/Command/Redis/INCR.php]Rd0vendor/predis/predis/src/Command/Redis/BITOP.php3vendor/predis/predis/src/Command/Redis/BITFIELD.phptpg}.vendor/predis/predis/src/Command/Redis/SET.phpyM^1vendor/predis/predis/src/Command/Redis/GETBIT.php{N_2vendor/predis/predis/src/Command/Redis/GEOHASH.phpo4vendor/predis/predis/src/Command/Redis/GEORADIUS.php++s1vendor/predis/predis/src/Command/Redis/ZCOUNT.php)3vendor/predis/predis/src/Command/Redis/EXPIREAT.php}2vendor/predis/predis/src/Command/Redis/PERSIST.php6vendor/predis/predis/src/Command/Redis/ZINTERSTORE.php5vendor/predis/predis/src/Command/Redis/SMISMEMBER.phpJb+0vendor/predis/predis/src/Command/Redis/HKEYS.php/vendor/predis/predis/src/Command/Redis/ZADD.php،ˤ/vendor/predis/predis/src/Command/Redis/PTTL.phpc0vendor/predis/predis/src/Command/Redis/HMGET.phpBBEA8vendor/predis/predis/src/Command/Redis/ZRANGEBYSCORE.phpBB.Z/vendor/predis/predis/src/Command/Redis/INFO.php3vendor/predis/predis/src/Command/Redis/GETRANGE.phpݽ12vendor/predis/predis/src/Command/Redis/OBJECT_.phpn?2vendor/predis/predis/src/Command/Redis/HSTRLEN.php{1vendor/predis/predis/src/Command/Redis/SUNION.phpEEj9vendor/predis/predis/src/Command/Redis/GEOSEARCHSTORE.phpu"0vendor/predis/predis/src/Command/Redis/MULTI.phpS12vendor/predis/predis/src/Command/Redis/EVALSHA.phpk謩1vendor/predis/predis/src/Command/Redis/MSETNX.phps>0vendor/predis/predis/src/Command/Redis/ZSCAN.phpHˤ2vendor/predis/predis/src/Command/Redis/SLAVEOF.phpoo' 4vendor/predis/predis/src/Command/Redis/SISMEMBER.php!3vendor/predis/predis/src/Command/Redis/FCALL_RO.phpDĤ5vendor/predis/predis/src/Command/Redis/EVALSHA_RO.phpqX.vendor/predis/predis/src/Command/Redis/LCS.php6Ӣ1vendor/predis/predis/src/Command/Redis/SETBIT.php PfBAvendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINFO.phpŁBvendor/predis/predis/src/Command/Redis/CountMinSketch/CMSMERGE.php(yGvendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYPROB.php'a|Fvendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYDIM.phpmCvendor/predis/predis/src/Command/Redis/CountMinSketch/CMSINCRBY.phpfBvendor/predis/predis/src/Command/Redis/CountMinSketch/CMSQUERY.phpQ,Dvendor/predis/predis/src/Command/Redis/AbstractCommand/BZPOPBase.phpGG06vendor/predis/predis/src/Command/Redis/UNSUBSCRIBE.phpOO;)7vendor/predis/predis/src/Command/Redis/HINCRBYFLOAT.phpp2vendor/predis/predis/src/Command/Redis/ZINCRBY.phpg/vendor/predis/predis/src/Command/Redis/SREM.php@@aYۤ0vendor/predis/predis/src/Command/Redis/RPUSH.phpBBr_P1vendor/predis/predis/src/Command/Redis/GETDEL.php|p/vendor/predis/predis/src/Command/Redis/HDEL.php@@Li6vendor/predis/predis/src/Command/Redis/ZRANGESTORE.php53vendor/predis/predis/src/Command/Redis/FLUSHALL.php,~0vendor/predis/predis/src/Command/Redis/SMOVE.php231vendor/predis/predis/src/Command/Redis/APPEND.php0vendor/predis/predis/src/Command/Redis/TOUCH.phpCC.Ѥ0vendor/predis/predis/src/Command/Redis/EVAL_.php =2vendor/predis/predis/src/Command/Redis/DISCARD.phpn)/vendor/predis/predis/src/Command/Redis/LLEN.phpUH/vendor/predis/predis/src/Command/Redis/LREM.php,7vendor/predis/predis/src/Command/Redis/BGREWRITEAOF.php22+e:5vendor/predis/predis/src/Command/Redis/SINTERCARD.phpUUrځ2vendor/predis/predis/src/Command/Redis/HEXISTS.phpۍ!/vendor/predis/predis/src/Command/Redis/TYPE.php}(0vendor/predis/predis/src/Command/Redis/LMOVE.php$6vendor/predis/predis/src/Command/Redis/SINTERSTORE.php4bo4vendor/predis/predis/src/Command/Redis/FUNCTIONS.phpGT70vendor/predis/predis/src/Command/Redis/ECHO_.php0vendor/predis/predis/src/Command/Redis/HSCAN.php^ܤ1vendor/predis/predis/src/Command/Redis/SINTER.phpEEo򋉤0vendor/predis/predis/src/Command/Redis/SETEX.phpŨ/vendor/predis/predis/src/Command/Redis/SORT.phpD>0vendor/predis/predis/src/Command/Redis/GETEX.php$$_1vendor/predis/predis/src/Command/Redis/BITPOS.php1p4vendor/predis/predis/src/Command/Redis/GEOSEARCH.php&0vendor/predis/predis/src/Command/Redis/BRPOP.phpD 1vendor/predis/predis/src/Command/Redis/ZSCORE.php 8.vendor/predis/predis/src/Command/Redis/ACL.php}K0vendor/predis/predis/src/Command/Redis/LPUSH.phpBBҟu3vendor/predis/predis/src/Command/Redis/SETRANGE.php/vendor/predis/predis/src/Command/Redis/MGET.phpAA149vendor/predis/predis/src/Command/Redis/ZREMRANGEBYLEX.phpkF6vendor/predis/predis/src/Command/Redis/ZRANDMEMBER.php3WN1vendor/predis/predis/src/Command/Redis/ZUNION.phpff92vendor/predis/predis/src/Command/Redis/ZPOPMIN.phpƻ2vendor/predis/predis/src/Command/Redis/WAITAOF.phpT$1vendor/predis/predis/src/Command/Redis/INCRBY.php6vendor/predis/predis/src/Command/Redis/SUNIONSTORE.phplD"/vendor/predis/predis/src/Command/Redis/QUIT.phpQV0vendor/predis/predis/src/Command/Redis/SSCAN.php  (I(2vendor/predis/predis/src/Command/Redis/UNWATCH.phpΤ1vendor/predis/predis/src/Command/Redis/CLIENT.php03vendor/predis/predis/src/Command/Redis/RENAMENX.phpTdh1vendor/predis/predis/src/Command/Redis/LINDEX.php]0vendor/predis/predis/src/Command/Redis/LMPOP.phpe/vendor/predis/predis/src/Command/Redis/SCAN.php   <vendor/predis/predis/src/Command/Redis/Container/CLUSTER.php]n+Dvendor/predis/predis/src/Command/Redis/Container/Search/FTCURSOR.phpBBDvendor/predis/predis/src/Command/Redis/Container/Search/FTCONFIG.php$(Gvendor/predis/predis/src/Command/Redis/Container/ContainerInterface.phpM҅Evendor/predis/predis/src/Command/Redis/Container/ContainerFactory.php8vendor/predis/predis/src/Command/Redis/Container/ACL.php.ͤFvendor/predis/predis/src/Command/Redis/Container/AbstractContainer.php009pLCvendor/predis/predis/src/Command/Redis/Container/Json/JSONDEBUG.phpXFvendor/predis/predis/src/Command/Redis/Container/FunctionContainer.php,WU2vendor/predis/predis/src/Command/Redis/SORT_RO.phpڠ82vendor/predis/predis/src/Command/Redis/ZPOPMAX.php61vendor/predis/predis/src/Command/Redis/CONFIG.php%10vendor/predis/predis/src/Command/Redis/ZMPOP.phpkdu=vendor/predis/predis/src/Command/Redis/Json/JSONARRAPPEND.php5۱7vendor/predis/predis/src/Command/Redis/Json/JSONGET.phpZZ8@7vendor/predis/predis/src/Command/Redis/Json/JSONSET.phpqP8vendor/predis/predis/src/Command/Redis/Json/JSONTYPE.phpm:vendor/predis/predis/src/Command/Redis/Json/JSONFORGET.php:'\=vendor/predis/predis/src/Command/Redis/Json/JSONARRINSERT.phpc-g=vendor/predis/predis/src/Command/Redis/Json/JSONSTRAPPEND.php哩6:vendor/predis/predis/src/Command/Redis/Json/JSONARRLEN.phpc7vendor/predis/predis/src/Command/Redis/Json/JSONDEL.phpS9vendor/predis/predis/src/Command/Redis/Json/JSONMERGE.php3 :vendor/predis/predis/src/Command/Redis/Json/JSONTOGGLE.phpdC:vendor/predis/predis/src/Command/Redis/Json/JSONOBJLEN.php X:vendor/predis/predis/src/Command/Redis/Json/JSONSTRLEN.php,GD8vendor/predis/predis/src/Command/Redis/Json/JSONRESP.php:+;vendor/predis/predis/src/Command/Redis/Json/JSONARRTRIM.phpm-ݤ8vendor/predis/predis/src/Command/Redis/Json/JSONMSET.php=1/9vendor/predis/predis/src/Command/Redis/Json/JSONCLEAR.phpzO3<vendor/predis/predis/src/Command/Redis/Json/JSONARRINDEX.phpnj9vendor/predis/predis/src/Command/Redis/Json/JSONDEBUG.php[0 8vendor/predis/predis/src/Command/Redis/Json/JSONMGET.php0I:vendor/predis/predis/src/Command/Redis/Json/JSONARRPOP.phpX;vendor/predis/predis/src/Command/Redis/Json/JSONOBJKEYS.php<=vendor/predis/predis/src/Command/Redis/Json/JSONNUMINCRBY.php2vendor/predis/predis/src/Command/Redis/HGETALL.php2vendor/predis/predis/src/Command/Redis/HINCRBY.php>>vendor/predis/predis/src/Command/Redis/CuckooFilter/CFINFO.php[{3@vendor/predis/predis/src/Command/Redis/CuckooFilter/CFINSERT.phpU?vendor/predis/predis/src/Command/Redis/CuckooFilter/CFCOUNT.php=vendor/predis/predis/src/Command/Redis/CuckooFilter/CFADD.phpU7?vendor/predis/predis/src/Command/Redis/CuckooFilter/CFADDNX.phpH\@vendor/predis/predis/src/Command/Redis/CuckooFilter/CFEXISTS.phpwgAvendor/predis/predis/src/Command/Redis/CuckooFilter/CFRESERVE.phpwBvendor/predis/predis/src/Command/Redis/CuckooFilter/CFINSERTNX.phpJCvendor/predis/predis/src/Command/Redis/CuckooFilter/CFLOADCHUNK.php%vAvendor/predis/predis/src/Command/Redis/CuckooFilter/CFMEXISTS.phpY8Bvendor/predis/predis/src/Command/Redis/CuckooFilter/CFSCANDUMP.php8=vendor/predis/predis/src/Command/Redis/CuckooFilter/CFDEL.phpn_^2vendor/predis/predis/src/Command/Redis/MONITOR.php45vendor/predis/predis/src/Command/Redis/ZINTERCARD.php[[̤1vendor/predis/predis/src/Command/Redis/EXPIRE.phpS0vendor/predis/predis/src/Command/Redis/WATCH.phpee&~:vendor/predis/predis/src/Command/Redis/ZREMRANGEBYRANK.phpo7P2vendor/predis/predis/src/Command/Redis/FLUSHDB.phpHj/vendor/predis/predis/src/Command/Redis/COPY.php[=A/vendor/predis/predis/src/Command/Redis/LSET.php%:9vendor/predis/predis/src/Command/Redis/TopK/TOPKQUERY.php|P+7vendor/predis/predis/src/Command/Redis/TopK/TOPKADD.phpOr8vendor/predis/predis/src/Command/Redis/TopK/TOPKLIST.phpuuϰˤ8vendor/predis/predis/src/Command/Redis/TopK/TOPKINFO.php Q;vendor/predis/predis/src/Command/Redis/TopK/TOPKRESERVE.phpww:vendor/predis/predis/src/Command/Redis/TopK/TOPKINCRBY.phprOܤ1vendor/predis/predis/src/Command/Redis/ZINTER.phpffX/vendor/predis/predis/src/Command/Redis/SADD.php@@UU/vendor/predis/predis/src/Command/Redis/LPOP.phpU4/vendor/predis/predis/src/Command/Redis/TIME.phpz瀤5vendor/predis/predis/src/Command/Redis/HRANDFIELD.php¶q2vendor/predis/predis/src/Command/Redis/ZMSCORE.php~2vendor/predis/predis/src/Command/Redis/SLOWLOG.phpvendor/predis/predis/src/Command/Redis/TimeSeries/TSCREATE.phprPAvendor/predis/predis/src/Command/Redis/TimeSeries/TSMREVRANGE.php\F"Bvendor/predis/predis/src/Command/Redis/TimeSeries/TSQUERYINDEX.phpXBvendor/predis/predis/src/Command/Redis/TimeSeries/TSDELETERULE.phpDj=vendor/predis/predis/src/Command/Redis/TimeSeries/TSALTER.php$>vendor/predis/predis/src/Command/Redis/TimeSeries/TSINCRBY.phpC<vendor/predis/predis/src/Command/Redis/TimeSeries/TSMGET.phpC ;vendor/predis/predis/src/Command/Redis/TimeSeries/TSGET.php))<vendor/predis/predis/src/Command/Redis/TimeSeries/TSINFO.phpێ<vendor/predis/predis/src/Command/Redis/TimeSeries/TSMADD.php)2>vendor/predis/predis/src/Command/Redis/TimeSeries/TSDECRBY.php)d>vendor/predis/predis/src/Command/Redis/TimeSeries/TSMRANGE.php4;vendor/predis/predis/src/Command/Redis/TimeSeries/TSADD.php~=vendor/predis/predis/src/Command/Redis/TimeSeries/TSRANGE.phptBvendor/predis/predis/src/Command/Redis/TimeSeries/TSCREATERULE.phpޅ+@vendor/predis/predis/src/Command/Redis/TimeSeries/TSREVRANGE.phpˇ;vendor/predis/predis/src/Command/Redis/TimeSeries/TSDEL.phpj1vendor/predis/predis/src/Command/RedisFactory.phpKKl",vendor/predis/predis/src/Command/Factory.phpooI0פVvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/LoadStrategy.phpYY;cYvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/RestoreStrategy.php?Vvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/KillStrategy.phpWvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/StatsStrategy.php8`Wvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/FlushStrategy.phpRVvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/ListStrategy.php99ĊhVvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.phpY;Xvendor/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DeleteStrategy.phpNGvendor/predis/predis/src/Command/Strategy/StrategyResolverInterface.phpJHvendor/predis/predis/src/Command/Strategy/SubcommandStrategyResolver.php>Ivendor/predis/predis/src/Command/Strategy/SubcommandStrategyInterface.php)ܤ?vendor/predis/predis/src/Command/Argument/ArrayableArgument.phpbEvendor/predis/predis/src/Command/Argument/Server/LimitOffsetCount.phpؤCvendor/predis/predis/src/Command/Argument/Server/LimitInterface.phpWͧ7vendor/predis/predis/src/Command/Argument/Server/To.php__.JEvendor/predis/predis/src/Command/Argument/Search/ExplainArguments.phpoo<Hvendor/predis/predis/src/Command/Argument/Search/SpellcheckArguments.php))SvxDvendor/predis/predis/src/Command/Argument/Search/SugGetArguments.php++OcݤDvendor/predis/predis/src/Command/Argument/Search/SearchArguments.php1Gvendor/predis/predis/src/Command/Argument/Search/SynUpdateArguments.phpqq Cvendor/predis/predis/src/Command/Argument/Search/AlterArguments.phpmmDvendor/predis/predis/src/Command/Argument/Search/CommonArguments.phpwGDvendor/predis/predis/src/Command/Argument/Search/SugAddArguments.phpa{DOvendor/predis/predis/src/Command/Argument/Search/SchemaFields/AbstractField.php4n%Nvendor/predis/predis/src/Command/Argument/Search/SchemaFields/NumericField.phpKK"HKvendor/predis/predis/src/Command/Argument/Search/SchemaFields/TextField.phpR|oJvendor/predis/predis/src/Command/Argument/Search/SchemaFields/GeoField.phpCCucJvendor/predis/predis/src/Command/Argument/Search/SchemaFields/TagField.php)).Pvendor/predis/predis/src/Command/Argument/Search/SchemaFields/FieldInterface.phpfn1Mvendor/predis/predis/src/Command/Argument/Search/SchemaFields/VectorField.phpOO?Dvendor/predis/predis/src/Command/Argument/Search/CreateArguments.phpQQSBvendor/predis/predis/src/Command/Argument/Search/DropArguments.phpPPႸ̤Gvendor/predis/predis/src/Command/Argument/Search/AggregateArguments.phpeEvendor/predis/predis/src/Command/Argument/Search/ProfileArguments.phpQ,Dvendor/predis/predis/src/Command/Argument/Search/CursorArguments.phpzz93Cvendor/predis/predis/src/Command/Argument/Geospatial/AbstractBy.phpCvendor/predis/predis/src/Command/Argument/Geospatial/FromLonLat.php|Ǥ>vendor/predis/predis/src/Command/Argument/Geospatial/ByBox.phpwVAvendor/predis/predis/src/Command/Argument/Geospatial/ByRadius.phphhym<ҤFvendor/predis/predis/src/Command/Argument/Geospatial/FromInterface.phpDvendor/predis/predis/src/Command/Argument/Geospatial/ByInterface.php %Cvendor/predis/predis/src/Command/Argument/Geospatial/FromMember.phpCC쫤Hvendor/predis/predis/src/Command/Argument/TimeSeries/DecrByArguments.phprr=bGvendor/predis/predis/src/Command/Argument/TimeSeries/RangeArguments.phpﻋGvendor/predis/predis/src/Command/Argument/TimeSeries/AlterArguments.phpqq,Hvendor/predis/predis/src/Command/Argument/TimeSeries/IncrByArguments.phpQQkHvendor/predis/predis/src/Command/Argument/TimeSeries/CommonArguments.phpQQ7Fvendor/predis/predis/src/Command/Argument/TimeSeries/InfoArguments.phpXX'פFvendor/predis/predis/src/Command/Argument/TimeSeries/MGetArguments.phpppUHvendor/predis/predis/src/Command/Argument/TimeSeries/CreateArguments.phprrČ`ǤEvendor/predis/predis/src/Command/Argument/TimeSeries/AddArguments.phpy-SEvendor/predis/predis/src/Command/Argument/TimeSeries/GetArguments.phpooK Hvendor/predis/predis/src/Command/Argument/TimeSeries/MRangeArguments.phpŤ5vendor/predis/predis/src/Command/FactoryInterface.php?/vendor/predis/predis/src/Command/RawFactory.php<<NGv5vendor/predis/predis/src/Command/CommandInterface.php:2vendor/predis/predis/src/Command/ScriptCommand.phpO,vendor/predis/predis/src/Command/Command.phpus/vendor/predis/predis/src/Command/RawCommand.php::t3vendor/predis/predis/src/Command/Traits/BitByte.phpRX>vendor/predis/predis/src/Command/Traits/BloomFilters/Error.php##;8Cvendor/predis/predis/src/Command/Traits/BloomFilters/BucketSize.php``*Bvendor/predis/predis/src/Command/Traits/BloomFilters/Expansion.php44?>vendor/predis/predis/src/Command/Traits/BloomFilters/Items.php4Avendor/predis/predis/src/Command/Traits/BloomFilters/Capacity.phpGGP,EFvendor/predis/predis/src/Command/Traits/BloomFilters/MaxIterations.phpAvendor/predis/predis/src/Command/Traits/BloomFilters/NoCreate.phpgg\.vendor/predis/predis/src/Command/Traits/DB.phpq3vendor/predis/predis/src/Command/Traits/Weights.php5vendor/predis/predis/src/Command/Traits/LeftRight.php333vendor/predis/predis/src/Command/Traits/Sorting.php11U;a7vendor/predis/predis/src/Command/Traits/To/ServerTo.php  g;vendor/predis/predis/src/Command/Traits/By/ByLexByScore.phpV~9vendor/predis/predis/src/Command/Traits/By/ByArgument.php4vendor/predis/predis/src/Command/Traits/By/GeoBy.phpll}l\;vendor/predis/predis/src/Command/Traits/With/WithScores.php:vendor/predis/predis/src/Command/Traits/With/WithCoord.phpggIX1H9vendor/predis/predis/src/Command/Traits/With/WithHash.php@@/Q;vendor/predis/predis/src/Command/Traits/With/WithValues.phprrV9vendor/predis/predis/src/Command/Traits/With/WithDist.php@@|\/vendor/predis/predis/src/Command/Traits/Rev.php^=vendor/predis/predis/src/Command/Traits/Limit/LimitObject.php99C1Τ7vendor/predis/predis/src/Command/Traits/Limit/Limit.php5vendor/predis/predis/src/Command/Traits/Storedist.phpbb*M8vendor/predis/predis/src/Command/Traits/From/GeoFrom.php~~:vendor/predis/predis/src/Command/Traits/MinMaxModifier.phpdd~F70vendor/predis/predis/src/Command/Traits/Keys.php}3vendor/predis/predis/src/Command/Traits/Get/Get.phppp^Ȥ@vendor/predis/predis/src/Command/Traits/Expire/ExpireOptions.php咄6vendor/predis/predis/src/Command/Traits/Json/Space.php(=vendor/predis/predis/src/Command/Traits/Json/NxXxArgument.php2':8vendor/predis/predis/src/Command/Traits/Json/Newline.php[6`7vendor/predis/predis/src/Command/Traits/Json/Indent.phpdO5vendor/predis/predis/src/Command/Traits/Aggregate.phpa)1vendor/predis/predis/src/Command/Traits/Count.php@@'ڥ3vendor/predis/predis/src/Command/Traits/Replace.phpXX>n!vendor/predis/predis/autoload.phpUULA=N-vendor/sebastian/type/src/type/ObjectType.phpP.vendor/sebastian/type/src/type/UnknownType.php,vendor/sebastian/type/src/type/MixedType.php $4vendor/sebastian/type/src/type/GenericObjectType.phpTT-vendor/sebastian/type/src/type/StaticType.php}}Y-vendor/sebastian/type/src/type/SimpleType.phpօN+vendor/sebastian/type/src/type/VoidType.php``,m+,vendor/sebastian/type/src/type/FalseType.phpD@3vendor/sebastian/type/src/type/IntersectionType.php+vendor/sebastian/type/src/type/NullType.phpA/vendor/sebastian/type/src/type/IterableType.php88QZ,vendor/sebastian/type/src/type/NeverType.phpcc|+vendor/sebastian/type/src/type/TrueType.phpXQ/vendor/sebastian/type/src/type/CallableType.php  - ,vendor/sebastian/type/src/type/UnionType.phpIj;'vendor/sebastian/type/src/type/Type.php .l.vendor/sebastian/type/src/ReflectionMapper.phpk&vendor/sebastian/type/src/TypeName.php//?71vendor/sebastian/type/src/exception/Exception.php98vendor/sebastian/type/src/exception/RuntimeException.phph'vendor/sebastian/type/src/Parameter.php__6-A.vendor/sebastian/global-state/src/Restorer.php} } &2vendor/sebastian/global-state/src/CodeExporter.phpKf1vendor/sebastian/global-state/src/ExcludeList.phpJJZ.vendor/sebastian/global-state/src/Snapshot.php#ce:vendor/sebastian/global-state/src/exceptions/Exception.php*Avendor/sebastian/global-state/src/exceptions/RuntimeException.phpXB,vendor/sebastian/code-unit/src/TraitUnit.php͕b,vendor/sebastian/code-unit/src/ClassUnit.phpդ=vendor/sebastian/code-unit/src/CodeUnitCollectionIterator.php;5vendor/sebastian/code-unit/src/CodeUnitCollection.php11 t/vendor/sebastian/code-unit/src/FunctionUnit.phpa0vendor/sebastian/code-unit/src/InterfaceUnit.phpCT2vendor/sebastian/code-unit/src/TraitMethodUnit.php}1L+vendor/sebastian/code-unit/src/CodeUnit.phpW¤6vendor/sebastian/code-unit/src/InterfaceMethodUnit.phpä2vendor/sebastian/code-unit/src/ClassMethodUnit.php:]8)vendor/sebastian/code-unit/src/Mapper.php+>Avendor/sebastian/code-unit/src/exceptions/ReflectionException.phpN:7vendor/sebastian/code-unit/src/exceptions/Exception.phpT>vendor/sebastian/code-unit/src/exceptions/NoTraitException.phpFvendor/sebastian/code-unit/src/exceptions/InvalidCodeUnitException.phpzۤ2vendor/sebastian/recursion-context/src/Context.php\Ĥ4vendor/sebastian/recursion-context/src/Exception.php+DCvendor/sebastian/recursion-context/src/InvalidArgumentException.phpk08vendor/sebastian/code-unit-reverse-lookup/src/Wizard.php   /5vendor/sebastian/object-enumerator/src/Enumerator.phpO+?$4vendor/sebastian/object-enumerator/src/Exception.php1Cvendor/sebastian/object-enumerator/src/InvalidArgumentException.php@q9vendor/sebastian/object-reflector/src/ObjectReflector.php+Ob3vendor/sebastian/object-reflector/src/Exception.php3Bvendor/sebastian/object-reflector/src/InvalidArgumentException.phpW(vendor/sebastian/version/src/Version.php0Cvendor/sebastian/complexity/src/Complexity/ComplexityCollection.phpOϤKvendor/sebastian/complexity/src/Complexity/ComplexityCollectionIterator.phpuub9vendor/sebastian/complexity/src/Complexity/Complexity.phpkդ.vendor/sebastian/complexity/src/Calculator.phpVV&nHvendor/sebastian/complexity/src/Visitor/ComplexityCalculatingVisitor.phphh UARvendor/sebastian/complexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.php1頤7vendor/sebastian/complexity/src/Exception/Exception.phpҹL>vendor/sebastian/complexity/src/Exception/RuntimeException.phpbm*vendor/sebastian/exporter/src/Exporter.phpˎ_,vendor/sebastian/environment/src/Runtime.phpJ:4vendor/sebastian/environment/src/OperatingSystem.phpPPD,vendor/sebastian/environment/src/Console.phpr r k$vendor/sebastian/diff/src/Parser.php13[#vendor/sebastian/diff/src/Chunk.phpEjt"vendor/sebastian/diff/src/Diff.php++tg/Mvendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.phpRRH"vendor/sebastian/diff/src/Line.phpE_M$vendor/sebastian/diff/src/Differ.php??ZOvendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.phpjv@vendor/sebastian/diff/src/LongestCommonSubsequenceCalculator.php/1vendor/sebastian/diff/src/Exception/Exception.phpVj>vendor/sebastian/diff/src/Exception/ConfigurationException.phpJJ?@vendor/sebastian/diff/src/Exception/InvalidArgumentException.php=|?vendor/sebastian/diff/src/Output/DiffOutputBuilderInterface.php2G\+:vendor/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php&Cvendor/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php__1=vendor/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.phpb\,?vendor/sebastian/diff/src/Output/AbstractChunkOutputBuilder.phpDDtH*?vendor/sebastian/resource-operations/src/ResourceOperations.php}7vendor/sebastian/resource-operations/build/generate.phpd>vendor/sebastian/comparator/src/SplObjectStorageComparator.php.Y5vendor/sebastian/comparator/src/DOMNodeComparator.phpg|S6vendor/sebastian/comparator/src/DateTimeComparator.php+vendor/sebastian/comparator/src/Factory.php22~z3vendor/sebastian/comparator/src/ArrayComparator.php  Z2vendor/sebastian/comparator/src/TypeComparator.phpRR$k4vendor/sebastian/comparator/src/ObjectComparator.phpFƻ6vendor/sebastian/comparator/src/ResourceComparator.php}K-5vendor/sebastian/comparator/src/ComparisonFailure.php44=̤5vendor/sebastian/comparator/src/NumericComparator.php+7vendor/sebastian/comparator/src/ExceptionComparator.phpט\.vendor/sebastian/comparator/src/Comparator.phpcЇ4vendor/sebastian/comparator/src/ScalarComparator.php}})f4vendor/sebastian/comparator/src/DoubleComparator.phpxxB8vendor/sebastian/comparator/src/exceptions/Exception.phpФ?vendor/sebastian/comparator/src/exceptions/RuntimeException.php08vendor/sebastian/comparator/src/MockObjectComparator.phpětܤ*vendor/sebastian/cli-parser/src/Parser.php{ {  fGvendor/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.phpJJkwAEvendor/sebastian/cli-parser/src/exceptions/UnknownOptionException.phpCC4x8vendor/sebastian/cli-parser/src/exceptions/Exception.php}Rvendor/sebastian/cli-parser/src/exceptions/OptionDoesNotAllowArgumentException.phpcc=Uvendor/sebastian/cli-parser/src/exceptions/RequiredOptionArgumentMissingException.phpll$u:vendor/sebastian/lines-of-code/src/LineCountingVisitor.php;;hd.vendor/sebastian/lines-of-code/src/Counter.phpuZV2vendor/sebastian/lines-of-code/src/LinesOfCode.php(tGvendor/sebastian/lines-of-code/src/Exception/NegativeValueException.phpA+Ivendor/sebastian/lines-of-code/src/Exception/IllogicalValuesException.phpkCo :vendor/sebastian/lines-of-code/src/Exception/Exception.phpߟʤAvendor/sebastian/lines-of-code/src/Exception/RuntimeException.phpXmh<vendor/codeception/module-cli/src/Codeception/Module/Cli.php N>vendor/codeception/module-amqp/src/Codeception/Module/AMQP.php~,~,,:vendor/codeception/module-db/src/Codeception/Module/Db.php\\ Bvendor/codeception/module-db/src/Codeception/Lib/Interfaces/Db.phpݪ\Avendor/codeception/module-db/src/Codeception/Lib/Driver/MySql.php o>vendor/codeception/module-db/src/Codeception/Lib/Driver/Db.php%%g¤Bvendor/codeception/module-db/src/Codeception/Lib/Driver/Sqlite.php `0?vendor/codeception/module-db/src/Codeception/Lib/Driver/Oci.phpee.:Bvendor/codeception/module-db/src/Codeception/Lib/Driver/SqlSrv.php ޅʌFvendor/codeception/module-db/src/Codeception/Lib/Driver/PostgreSql.php--QR@vendor/codeception/module-db/src/Codeception/Lib/DbPopulator.php%%(դAvendor/codeception/lib-web/src/Lib/Interfaces/ScreenshotSaver.phpXX]ퟤ@vendor/codeception/lib-web/src/Lib/Interfaces/ElementLocator.php6ݤAvendor/codeception/lib-web/src/Lib/Interfaces/PageSourceSaver.phpuu[|5vendor/codeception/lib-web/src/Lib/Interfaces/Web.phphhv,Avendor/codeception/lib-web/src/Lib/Interfaces/SessionSnapshot.php͞78vendor/codeception/lib-web/src/Lib/Interfaces/Remote.phpg>vendor/codeception/lib-web/src/Lib/Interfaces/MultiSession.php77 i2vendor/codeception/lib-web/src/Constraint/Page.phpm+vendor/codeception/lib-web/src/Util/Uri.php[/vendor/codeception/lib-web/src/Util/Locator.php&&6%ѤFvendor/codeception/lib-web/src/Exception/MalformedLocatorException.phpGGIk<vendor/codeception/lib-web/src/Exception/ElementNotFound.php(񞖤<vendor/codeception/module-ftp/src/Codeception/Module/FTP.phpbWbW $ڢFvendor/codeception/module-sequence/src/Codeception/Module/Sequence.php} } G >vendor/codeception/module-sequence/src/Codeception/Util/sq.phpYEä>vendor/codeception/module-rest/src/Codeception/Step/AsJson.php9m1e>vendor/codeception/module-rest/src/Codeception/Module/REST.phpee~Fvendor/codeception/module-rest/src/Codeception/Constraint/JsonType.phpJvendor/codeception/module-rest/src/Codeception/Constraint/JsonContains.php@vendor/codeception/module-rest/src/Codeception/Util/JsonType.php""6zOvendor/codeception/module-rest/src/Codeception/Util/ArrayContainsComparator.phprrnAvendor/codeception/module-rest/src/Codeception/Util/JsonArray.phpl@vendor/codeception/module-queue/src/Codeception/Module/Queue.php,,uHvendor/codeception/module-queue/src/Codeception/Lib/Interfaces/Queue.php}}QHvendor/codeception/module-queue/src/Codeception/Lib/Driver/Beanstalk.php/5դJvendor/codeception/module-queue/src/Codeception/Lib/Driver/Pheanstalk4.php ٬BzCvendor/codeception/module-queue/src/Codeception/Lib/Driver/Iron.php ) Hvendor/codeception/module-queue/src/Codeception/Lib/Driver/AmazonSQS.phpVVuJvendor/codeception/module-phpbrowser/src/Codeception/Module/PhpBrowser.phpi(Mvendor/codeception/module-phpbrowser/src/Codeception/Lib/Connector/Guzzle.php//E24*Fvendor/codeception/module-memcache/src/Codeception/Module/Memcache.phpHHMۤEvendor/codeception/lib-innerbrowser/src/Codeception/Lib/Framework.phpHvendor/codeception/lib-innerbrowser/src/Codeception/Lib/InnerBrowser.phpEĤJvendor/codeception/lib-innerbrowser/src/Codeception/Constraint/Crawler.phpa a φMvendor/codeception/lib-innerbrowser/src/Codeception/Constraint/CrawlerNot.phpee(yZEvendor/codeception/lib-innerbrowser/src/Codeception/Util/HttpCode.php!!o=Vvendor/codeception/lib-innerbrowser/src/Codeception/Exception/ExternalUrlException.phpu]?vendor/codeception/codeception/src/Codeception/Template/Api.php{ { 6Evendor/codeception/codeception/src/Codeception/Template/Bootstrap.php..US@vendor/codeception/codeception/src/Codeception/Template/Unit.php \Fvendor/codeception/codeception/src/Codeception/Template/Acceptance.phpcI[Hvendor/codeception/codeception/src/Codeception/Template/Dependencies.php>>j⮤:vendor/codeception/codeception/src/Codeception/Example.php ̤9vendor/codeception/codeception/src/Codeception/Module.php  ?9vendor/codeception/codeception/src/Codeception/Events.phpPPG<vendor/codeception/codeception/src/Codeception/Test/Cept.php5 5 Š<vendor/codeception/codeception/src/Codeception/Test/Cest.php""(Nvendor/codeception/codeception/src/Codeception/Test/Interfaces/Descriptive.phpۤQvendor/codeception/codeception/src/Codeception/Test/Interfaces/StrictCoverage.php6KLvendor/codeception/codeception/src/Codeception/Test/Interfaces/Dependent.phpww9ӜHvendor/codeception/codeception/src/Codeception/Test/Interfaces/Plain.phpoo4;\Qvendor/codeception/codeception/src/Codeception/Test/Interfaces/ScenarioDriven.php[[Kvendor/codeception/codeception/src/Codeception/Test/Interfaces/Reported.phpp[wBvendor/codeception/codeception/src/Codeception/Test/Descriptor.php SˤCvendor/codeception/codeception/src/Codeception/Test/Loader/Cept.phpdoDCvendor/codeception/codeception/src/Codeception/Test/Loader/Cest.phpJCvendor/codeception/codeception/src/Codeception/Test/Loader/Unit.php Fvendor/codeception/codeception/src/Codeception/Test/Loader/Gherkin.php""f`Nvendor/codeception/codeception/src/Codeception/Test/Loader/LoaderInterface.php4Ӥ<vendor/codeception/codeception/src/Codeception/Test/Unit.php==9 %@vendor/codeception/codeception/src/Codeception/Test/Metadata.php--5B>vendor/codeception/codeception/src/Codeception/Test/Filter.php mvPvendor/codeception/codeception/src/Codeception/Test/Feature/AssertionCounter.phpj.Lvendor/codeception/codeception/src/Codeception/Test/Feature/CodeCoverage.phpNvendor/codeception/codeception/src/Codeception/Test/Feature/ScenarioLoader.php Qvendor/codeception/codeception/src/Codeception/Test/Feature/MetadataCollector.php88׈Wvendor/codeception/codeception/src/Codeception/Test/Feature/IgnoreIfMetadataBlocked.phpV=)?vendor/codeception/codeception/src/Codeception/Test/Gherkin.phpw w ?Gvendor/codeception/codeception/src/Codeception/Test/TestCaseWrapper.php+6ŤDvendor/codeception/codeception/src/Codeception/Test/DataProvider.phpU">vendor/codeception/codeception/src/Codeception/Test/Loader.phpNNp8<vendor/codeception/codeception/src/Codeception/Test/Test.php''4=vendor/codeception/codeception/src/Codeception/Step/TryTo.phpvendor/codeception/codeception/src/Codeception/Step/Action.php2PBvendor/codeception/codeception/src/Codeception/Step/Incomplete.phpD @vendor/codeception/codeception/src/Codeception/Step/Executor.php++F8Lvendor/codeception/codeception/src/Codeception/Step/ConditionalAssertion.php44u~<vendor/codeception/codeception/src/Codeception/Step/Skip.php5ͤ<vendor/codeception/codeception/src/Codeception/Step/Meta.phpX Avendor/codeception/codeception/src/Codeception/Step/Condition.phpACvendor/codeception/codeception/src/Codeception/ResultAggregator.php::="8vendor/codeception/codeception/src/Codeception/Suite.php'' (Dvendor/codeception/codeception/src/Codeception/Subscriber/Module.php$ $ ޤGvendor/codeception/codeception/src/Codeception/Subscriber/Bootstrap.php+ Fvendor/codeception/codeception/src/Codeception/Subscriber/FailFast.php{{jNQvendor/codeception/codeception/src/Codeception/Subscriber/GracefulTermination.php{{2Mvendor/codeception/codeception/src/Codeception/Subscriber/BeforeAfterTest.php[8Ivendor/codeception/codeception/src/Codeception/Subscriber/Deprecation.phpWJvendor/codeception/codeception/src/Codeception/Subscriber/ErrorHandler.phpiIvendor/codeception/codeception/src/Codeception/Subscriber/PrepareTest.php\cMvendor/codeception/codeception/src/Codeception/Subscriber/ExtensionLoader.phpDDzVvendor/codeception/codeception/src/Codeception/Subscriber/Shared/StaticEventsTrait.phpM Ivendor/codeception/codeception/src/Codeception/Subscriber/AutoRebuild.php |Evendor/codeception/codeception/src/Codeception/Subscriber/Console.phpii+Jvendor/codeception/codeception/src/Codeception/Subscriber/Dependencies.phpSֱNvendor/codeception/codeception/src/Codeception/Coverage/Subscriber/Printer.php~Lvendor/codeception/codeception/src/Codeception/Coverage/Subscriber/Local.php{Svendor/codeception/codeception/src/Codeception/Coverage/Subscriber/RemoteServer.php [GBRvendor/codeception/codeception/src/Codeception/Coverage/Subscriber/LocalServer.php,,gBvendor/codeception/codeception/src/Codeception/Coverage/Filter.php٤Kvendor/codeception/codeception/src/Codeception/Coverage/SuiteSubscriber.php~~Rvendor/codeception/codeception/src/Codeception/Coverage/PhpCodeCoverageFactory.php&&ߝ<vendor/codeception/codeception/src/Codeception/Extension.php  / Cvendor/codeception/codeception/src/Codeception/Lib/Notification.php=vendor/codeception/codeception/src/Codeception/Lib/Parser.php#Fvendor/codeception/codeception/src/Codeception/Lib/ModuleContainer.php>>K5^VAvendor/codeception/codeception/src/Codeception/Lib/PauseShell.phpi~CJvendor/codeception/codeception/src/Codeception/Lib/Console/ReplHistory.phpV&[Jvendor/codeception/codeception/src/Codeception/Lib/Console/DiffFactory.php(#GMvendor/codeception/codeception/src/Codeception/Lib/Console/MessageFactory.php[[)*Evendor/codeception/codeception/src/Codeception/Lib/Console/Output.php  ITFvendor/codeception/codeception/src/Codeception/Lib/Console/Message.php a=Hvendor/codeception/codeception/src/Codeception/Lib/Console/Colorizer.php<<)9Rvendor/codeception/codeception/src/Codeception/Lib/Interfaces/DoctrineProvider.php54wNvendor/codeception/codeception/src/Codeception/Lib/Interfaces/PartedModule.phps<Qvendor/codeception/codeception/src/Codeception/Lib/Interfaces/RequiresPackage.php~Nvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ActiveRecord.phpEvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ORM.php@@j{Lvendor/codeception/codeception/src/Codeception/Lib/Interfaces/DataMapper.phpVPUvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ConflictsWithModule.phpStKPvendor/codeception/codeception/src/Codeception/Lib/Interfaces/ConsolePrinter.php))yYLQvendor/codeception/codeception/src/Codeception/Lib/Interfaces/DependsOnModule.phpJJnN-Evendor/codeception/codeception/src/Codeception/Lib/Interfaces/API.phpccl ɤCvendor/codeception/codeception/src/Codeception/Lib/ParamsLoader.php44݆ܤGvendor/codeception/codeception/src/Codeception/Lib/Generator/Helper.phpA%Evendor/codeception/codeception/src/Codeception/Lib/Generator/Cest.phpԛFvendor/codeception/codeception/src/Codeception/Lib/Generator/Group.phpKK]xPvendor/codeception/codeception/src/Codeception/Lib/Generator/GherkinSnippets.phpҸ'Kvendor/codeception/codeception/src/Codeception/Lib/Generator/PageObject.php-Hvendor/codeception/codeception/src/Codeception/Lib/Generator/Actions.php))ѩHvendor/codeception/codeception/src/Codeception/Lib/Generator/Feature.phpu-Fvendor/codeception/codeception/src/Codeception/Lib/Generator/Actor.php WuKvendor/codeception/codeception/src/Codeception/Lib/Generator/StepObject.phpѤQvendor/codeception/codeception/src/Codeception/Lib/Generator/Shared/Classname.php  r=Ivendor/codeception/codeception/src/Codeception/Lib/Generator/Snapshot.phptsEvendor/codeception/codeception/src/Codeception/Lib/Generator/Test.php^^7X:Cvendor/codeception/codeception/src/Codeception/Lib/GroupManager.php55׈ۤIvendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Retry.php‰*Kvendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Comment.phpPP"Jvendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Friend.phphb>Ivendor/codeception/codeception/src/Codeception/Lib/Actor/Shared/Pause.phpY芤=vendor/codeception/codeception/src/Codeception/Lib/Friend.phpM9vendor/codeception/codeception/src/Codeception/Lib/Di.php//!{5`vendor/codeception/codeception/src/Codeception/Lib/Connector/Shared/PhpSuperGlobalsConverter.php E Ivendor/codeception/codeception/src/Codeception/Event/PrintResultEvent.phpeeU"Bvendor/codeception/codeception/src/Codeception/Event/TestEvent.phpصBvendor/codeception/codeception/src/Codeception/Event/FailEvent.phpll^Cvendor/codeception/codeception/src/Codeception/Event/SuiteEvent.php[?+Bvendor/codeception/codeception/src/Codeception/Event/StepEvent.phpvQ;vendor/codeception/codeception/src/Codeception/Codecept.php'':TbSvendor/codeception/codeception/src/Codeception/Reporter/template/substeps.html.dist##͠wSvendor/codeception/codeception/src/Codeception/Reporter/template/scenario.html.distS&Ovendor/codeception/codeception/src/Codeception/Reporter/template/fail.html.dist;;˟Tvendor/codeception/codeception/src/Codeception/Reporter/template/scenarios.html.dist/ZOvendor/codeception/codeception/src/Codeception/Reporter/template/step.html.distbbdPvendor/codeception/codeception/src/Codeception/Reporter/template/suite.html.dist33|Zvendor/codeception/codeception/src/Codeception/Reporter/template/scenario_header.html.dist22"Ivendor/codeception/codeception/src/Codeception/Reporter/ReportPrinter.phpܤKvendor/codeception/codeception/src/Codeception/Reporter/PhpUnitReporter.php LxIvendor/codeception/codeception/src/Codeception/Reporter/JUnitReporter.php--EHvendor/codeception/codeception/src/Codeception/Reporter/HtmlReporter.php\0\0\@vendor/codeception/codeception/src/Codeception/TestInterface.php  l8vendor/codeception/codeception/src/Codeception/Actor.php?vendor/codeception/codeception/src/Codeception/SuiteManager.php))~;vendor/codeception/codeception/src/Codeception/Scenario.phpwg79>vendor/codeception/codeception/src/Codeception/GroupObject.php2P>vendor/codeception/codeception/src/Codeception/Application.php99_>DȤ@vendor/codeception/codeception/src/Codeception/Configuration.phpddKvendor/codeception/codeception/src/Codeception/Command/GenerateSnapshot.php̳ʤNvendor/codeception/codeception/src/Codeception/Command/GenerateEnvironment.phpR Avendor/codeception/codeception/src/Codeception/Command/DryRun.php:cDvendor/codeception/codeception/src/Codeception/Command/Bootstrap.phpjAJvendor/codeception/codeception/src/Codeception/Command/GherkinSnippets.php q(ƤMvendor/codeception/codeception/src/Codeception/Command/GeneratePageObject.php  Z0@vendor/codeception/codeception/src/Codeception/Command/Clean.phpdd ^>vendor/codeception/codeception/src/Codeception/Command/Run.phpyysHvendor/codeception/codeception/src/Codeception/Command/GenerateGroup.php  äGvendor/codeception/codeception/src/Codeception/Command/GenerateCest.php  t&Gvendor/codeception/codeception/src/Codeception/Command/GherkinSteps.phpLvendor/codeception/codeception/src/Codeception/Command/GenerateScenarios.php'ФMvendor/codeception/codeception/src/Codeception/Command/GenerateStepObject.php  rJvendor/codeception/codeception/src/Codeception/Command/GenerateFeature.php99NSEvendor/codeception/codeception/src/Codeception/Command/SelfUpdate.php Lvendor/codeception/codeception/src/Codeception/Command/Shared/StyleTrait.phpgQvendor/codeception/codeception/src/Codeception/Command/Shared/FileSystemTrait.php$$2M Lvendor/codeception/codeception/src/Codeception/Command/Shared/ActorTrait.phpCCEMvendor/codeception/codeception/src/Codeception/Command/Shared/ConfigTrait.php; ; ^AIvendor/codeception/codeception/src/Codeception/Command/GenerateHelper.php  +CHvendor/codeception/codeception/src/Codeception/Command/GenerateSuite.phpk@Gvendor/codeception/codeception/src/Codeception/Command/GenerateTest.php44XBvendor/codeception/codeception/src/Codeception/Command/Console.php> Evendor/codeception/codeception/src/Codeception/Command/Completion.php |Mvendor/codeception/codeception/src/Codeception/Command/CompletionFallback.php?vendor/codeception/codeception/src/Codeception/Command/Init.phpNN:sIvendor/codeception/codeception/src/Codeception/Command/ConfigValidate.phpPA@vendor/codeception/codeception/src/Codeception/Command/Build.phpi@vendor/codeception/codeception/src/Codeception/Util/Fixtures.phpJHvendor/codeception/codeception/src/Codeception/Util/ReflectionHelper.phpc=vendor/codeception/codeception/src/Codeception/Util/Debug.phpDvendor/codeception/codeception/src/Codeception/Util/PathResolver.php%%JxBvendor/codeception/codeception/src/Codeception/Util/FileSystem.phpD D ?ͤ@vendor/codeception/codeception/src/Codeception/Util/Autoload.php/=Bvendor/codeception/codeception/src/Codeception/Util/Annotation.phpGGK\\Hvendor/codeception/codeception/src/Codeception/Util/StackTraceFilter.php /=vendor/codeception/codeception/src/Codeception/Util/Maybe.phpp^gIvendor/codeception/codeception/src/Codeception/Util/Shared/Namespaces.php@yj@vendor/codeception/codeception/src/Codeception/Util/Template.phpF- Fvendor/codeception/codeception/src/Codeception/Util/ActionSequence.phpߔˤ?vendor/codeception/codeception/src/Codeception/InitTemplate.phpu(u(eĤIvendor/codeception/codeception/src/Codeception/CustomCommandInterface.phpBvendor/codeception/codeception/src/Codeception/Attribute/Group.php%ufDvendor/codeception/codeception/src/Codeception/Attribute/Prepare.php\rAvendor/codeception/codeception/src/Codeception/Attribute/Then.phpU$Dvendor/codeception/codeception/src/Codeception/Attribute/Depends.php  פHvendor/codeception/codeception/src/Codeception/Attribute/BeforeClass.php(|ӤEvendor/codeception/codeception/src/Codeception/Attribute/Examples.php2dBvendor/codeception/codeception/src/Codeception/Attribute/After.phpYGvendor/codeception/codeception/src/Codeception/Attribute/Incomplete.phpqӤCvendor/codeception/codeception/src/Codeception/Attribute/Before.php%ɤGvendor/codeception/codeception/src/Codeception/Attribute/AfterClass.phpDͤIvendor/codeception/codeception/src/Codeception/Attribute/DataProvider.phpljBAvendor/codeception/codeception/src/Codeception/Attribute/Skip.php @vendor/codeception/codeception/src/Codeception/Attribute/Env.php]ŤBvendor/codeception/codeception/src/Codeception/Attribute/Given.php Avendor/codeception/codeception/src/Codeception/Attribute/When.phpx7vendor/codeception/codeception/src/Codeception/Step.php],],'G;vendor/codeception/codeception/src/Codeception/Snapshot.php]]W.Bvendor/codeception/codeception/src/Codeception/Exception/Error.phpggȏ;Ovendor/codeception/codeception/src/Codeception/Exception/InjectionException.php{DΤOvendor/codeception/codeception/src/Codeception/Exception/ExtensionException.php44٤Kvendor/codeception/codeception/src/Codeception/Exception/ParseException.php~~͡$Mvendor/codeception/codeception/src/Codeception/Exception/ThrowableWrapper.php'3khLvendor/codeception/codeception/src/Codeception/Exception/RemoteException.php,,DLvendor/codeception/codeception/src/Codeception/Exception/ContentNotFound.php¥Qvendor/codeception/codeception/src/Codeception/Exception/InvalidTestException.php{äOvendor/codeception/codeception/src/Codeception/Exception/TestParseException.php*̤Qvendor/codeception/codeception/src/Codeception/Exception/TestRuntimeException.php ȳHvendor/codeception/codeception/src/Codeception/Exception/Deprecation.phpgg>FCvendor/codeception/codeception/src/Codeception/Exception/Notice.phpbbdSvendor/codeception/codeception/src/Codeception/Exception/ModuleRequireException.phpSvendor/codeception/codeception/src/Codeception/Exception/ConfigurationException.phpuwTvendor/codeception/codeception/src/Codeception/Exception/ModuleConflictException.php aRvendor/codeception/codeception/src/Codeception/Exception/ModuleConfigException.phpK qDvendor/codeception/codeception/src/Codeception/Exception/Warning.phpcc`äWvendor/codeception/codeception/src/Codeception/Exception/ConditionalAssertionFailed.phpXHQvendor/codeception/codeception/src/Codeception/Exception/UselessTestException.php'#0Lvendor/codeception/codeception/src/Codeception/Exception/ModuleException.phpL?7vendor/codeception/codeception/src/PHPUnit/TestCase.php&Dvendor/codeception/codeception/src/PHPUnit/Wrapper/PhpUnit9/Test.php(Evendor/codeception/codeception/src/PHPUnit/Wrapper/PhpUnit10/Test.php:$,vendor/codeception/codeception/functions.phpJr&vendor/codeception/codeception/app.phpTTAC0vendor/codeception/codeception/ext/RunBefore.phpM/vendor/codeception/codeception/ext/Recorder.phpUUd0H1vendor/codeception/codeception/ext/RunProcess.php[Ϭ"0vendor/codeception/codeception/ext/RunFailed.php ؇ 2vendor/codeception/codeception/ext/DotReporter.php Fk:5vendor/codeception/codeception/ext/SimpleReporter.phpRA-vendor/codeception/codeception/ext/Logger.php7?+vendor/codeception/codeception/autoload.php22Lvendor/codeception/module-asserts/src/Codeception/Module/AbstractAsserts.phpKDvendor/codeception/module-asserts/src/Codeception/Module/Asserts.php 飤Hvendor/codeception/module-webdriver/src/Codeception/Module/WebDriver.phpLvendor/codeception/module-webdriver/src/Codeception/Constraint/WebDriver.phpV V ,٤Ovendor/codeception/module-webdriver/src/Codeception/Constraint/WebDriverNot.php))~iUvendor/codeception/module-webdriver/src/Codeception/Exception/ConnectionException.php, YJvendor/codeception/module-filesystem/src/Codeception/Module/Filesystem.php%%uS[$vendor/codeception/stub/src/Stub.phpRR *|1vendor/codeception/stub/src/Test/Feature/Stub.php&&~*-vendor/codeception/stub/src/Stub/Expected.php--*Ф3vendor/codeception/stub/src/Stub/ConsecutiveMap.phpwN2vendor/codeception/stub/src/Stub/StubMarshaler.phpYY$vendor/codeception/stub/RoboFile.phpYY]A,vendor/codeception/lib-xml/src/Util/Soap.phpxmj4+vendor/codeception/lib-xml/src/Util/Xml.php2vendor/codeception/lib-xml/src/Util/XmlBuilder.phpl4vendor/codeception/lib-xml/src/Util/XmlStructure.php  +Ovendor/codeception/lib-asserts/src/Codeception/Util/Shared/InheritedAsserts.php11ϖuFvendor/codeception/lib-asserts/src/Codeception/Util/Shared/Asserts.php$ $ g<vendor/codeception/module-apc/src/Codeception/Module/Apc.php^^s?Dvendor/codeception/module-mongodb/src/Codeception/Module/MongoDb.php99Hvendor/codeception/module-mongodb/src/Codeception/Lib/Driver/MongoDb.phpmiJ@vendor/codeception/module-redis/src/Codeception/Module/Redis.php/X/XO>vendor/codeception/module-soap/src/Codeception/Module/SOAP.php}:}:"_4*vendor/phpunit/php-invoker/src/Invoker.php@@h<ˤWvendor/phpunit/php-invoker/src/exceptions/ProcessControlExtensionNotLoadedException.php 䊤7vendor/phpunit/php-invoker/src/exceptions/Exception.php󂔤>vendor/phpunit/php-invoker/src/exceptions/TimeoutException.php00vendor/phpunit/php-file-iterator/src/Factory.phpV1vendor/phpunit/php-file-iterator/src/Iterator.php/vendor/phpunit/php-file-iterator/src/Facade.phpNN&vendor/phpunit/php-timer/src/Timer.php݀¤7vendor/phpunit/php-timer/src/ResourceUsageFormatter.phpaa" ZXvendor/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php5vendor/phpunit/php-timer/src/exceptions/Exception.phpw`\xBvendor/phpunit/php-timer/src/exceptions/NoActiveTimerException.phpY _Y)vendor/phpunit/php-timer/src/Duration.php++̤(vendor/phpunit/phpunit/src/Exception.phpxx( l2vendor/phpunit/phpunit/src/Runner/PhptTestCase.php?=?=f:vendor/phpunit/phpunit/src/Runner/ResultCacheExtension.php ߤ5vendor/phpunit/phpunit/src/Runner/TestSuiteLoader.php`5vendor/phpunit/phpunit/src/Runner/TestResultCache.php=vendor/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php L=5vendor/phpunit/phpunit/src/Runner/TestSuiteSorter.php|ߤ-vendor/phpunit/phpunit/src/Runner/Version.phpHv/vendor/phpunit/phpunit/src/Runner/Exception.php Um@vendor/phpunit/phpunit/src/Runner/Extension/ExtensionHandler.php04:vendor/phpunit/phpunit/src/Runner/Extension/PharLoader.phpxx!{<vendor/phpunit/phpunit/src/Runner/DefaultTestResultCache.php 4S}Bvendor/phpunit/phpunit/src/Runner/Hook/AfterIncompleteTestHook.php l^3vendor/phpunit/phpunit/src/Runner/Hook/TestHook.phpooƤ>vendor/phpunit/phpunit/src/Runner/Hook/BeforeFirstTestHook.php{w?vendor/phpunit/phpunit/src/Runner/Hook/AfterTestFailureHook.phpR8vendor/phpunit/phpunit/src/Runner/Hook/AfterTestHook.phpi9vendor/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php%1=vendor/phpunit/phpunit/src/Runner/Hook/AfterTestErrorHook.phpIvʤBvendor/phpunit/phpunit/src/Runner/Hook/AfterSuccessfulTestHook.php5FҤ?vendor/phpunit/phpunit/src/Runner/Hook/AfterSkippedTestHook.phpB/vendor/phpunit/phpunit/src/Runner/Hook/Hook.php^^<vendor/phpunit/phpunit/src/Runner/Hook/AfterLastTestHook.php䇵A>vendor/phpunit/phpunit/src/Runner/Hook/TestListenerAdapter.php Ze=vendor/phpunit/phpunit/src/Runner/Hook/AfterRiskyTestHook.php#-ǿ?vendor/phpunit/phpunit/src/Runner/Hook/AfterTestWarningHook.php.4vendor/phpunit/phpunit/src/Runner/BaseTestRunner.php89vendor/phpunit/phpunit/src/Runner/NullTestResultCache.php4vendor/phpunit/phpunit/src/Runner/Filter/Factory.phphhȅ~Gvendor/phpunit/phpunit/src/Runner/Filter/IncludeGroupFilterIterator.phpmGvendor/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.phpJߤ?vendor/phpunit/phpunit/src/Runner/Filter/NameFilterIterator.php)a@vendor/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php5 8vendor/phpunit/phpunit/src/Framework/WarningTestCase.phpUU$Q7vendor/phpunit/phpunit/src/Framework/IncompleteTest.php_G6vendor/phpunit/phpunit/src/Framework/ErrorTestCase.phpMM\.3vendor/phpunit/phpunit/src/Framework/TestResult.phpQMQMʼn8vendor/phpunit/phpunit/src/Framework/SkippedTestCase.php>Dd>vendor/phpunit/phpunit/src/Framework/DataProviderTestSuite.phpWWJ r1vendor/phpunit/phpunit/src/Framework/TestCase.phpNN,;vendor/phpunit/phpunit/src/Framework/MockObject/Api/Api.php<<6w+>vendor/phpunit/phpunit/src/Framework/MockObject/Api/Method.phppp@mKvendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastOnce.phpv KKvendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtMostCount.php؃Gvendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtIndex.phpeCvendor/phpunit/phpunit/src/Framework/MockObject/Rule/MethodName.phpljHvendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvocationOrder.phpĤCvendor/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.phpL L JNvendor/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php--Evendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.phpE ǤHvendor/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php΂Fvendor/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.phpvv>xZLvendor/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastCount.phps XGvendor/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.phpr@>vendor/phpunit/phpunit/src/Framework/MockObject/MockMethod.phpۤ=vendor/phpunit/phpunit/src/Framework/MockObject/Generator.phpZZw8vendor/phpunit/phpunit/src/Framework/MockObject/Stub.phprrr* Hvendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnReference.phpHH.okˤCvendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnSelf.php䫣=vendor/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php  tv~Gvendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.phpǍ,bIvendor/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.phpy쇤Bvendor/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php]]\=Gvendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnArgument.phpUU8Gvendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnCallback.phpeCvendor/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php%%;缤>vendor/phpunit/phpunit/src/Framework/MockObject/Invocation.php Avendor/phpunit/phpunit/src/Framework/MockObject/MockMethodSet.phpBB+RYLvendor/phpunit/phpunit/src/Framework/MockObject/Generator/proxied_method.tpl}}@ėIvendor/phpunit/phpunit/src/Framework/MockObject/Generator/deprecation.tpl;;O5sYvendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_method_never_or_void.tplpJvendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_class.tplwZJvendor/phpunit/phpunit/src/Framework/MockObject/Generator/intersection.tplLL-XRvendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_static_method.tpl 4RIvendor/phpunit/phpunit/src/Framework/MockObject/Generator/wsdl_method.tpl<<iZvendor/phpunit/phpunit/src/Framework/MockObject/Generator/proxied_method_never_or_void.tplvvTHvendor/phpunit/phpunit/src/Framework/MockObject/Generator/wsdl_class.tplIvendor/phpunit/phpunit/src/Framework/MockObject/Generator/trait_class.tplQQ<ȤKvendor/phpunit/phpunit/src/Framework/MockObject/Generator/mocked_method.tplFFK=vendor/phpunit/phpunit/src/Framework/MockObject/MockTrait.php  +><vendor/phpunit/phpunit/src/Framework/MockObject/MockType.php"Hvendor/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php]]j?vendor/phpunit/phpunit/src/Framework/MockObject/MockBuilder.phpL|E=vendor/phpunit/phpunit/src/Framework/MockObject/MockClass.php0(դ>vendor/phpunit/phpunit/src/Framework/MockObject/Verifiable.php9>vendor/phpunit/phpunit/src/Framework/MockObject/MockObject.phpdU[vendor/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.phpnQvendor/phpunit/phpunit/src/Framework/MockObject/Exception/ReflectionException.php?%Xvendor/phpunit/phpunit/src/Framework/MockObject/Exception/InvalidMethodNameException.php[[ϤSvendor/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownClassException.phpJJBu^vendor/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.phpGG͘`vendor/phpunit/phpunit/src/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php@@{XVZVvendor/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsReadonlyException.phpkk].BSvendor/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsFinalException.phpeeN[vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.phpaa0Vvendor/phpunit/phpunit/src/Framework/MockObject/Exception/DuplicateMethodException.php44>6Svendor/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTraitException.phpJJ(^vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.phpGvendor/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.phpCERvendor/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTypeException.phpLLaCYvendor/phpunit/phpunit/src/Framework/MockObject/Exception/ClassAlreadyExistsException.phpPPQ?Nvendor/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.phpDͤ_vendor/phpunit/phpunit/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php^Fbvendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.phpdTvendor/phpunit/phpunit/src/Framework/MockObject/Exception/BadMethodCallException.php-Whvendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php''.Zvendor/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseAddMethodsException.phpS\_vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.phpJ`lvendor/phpunit/phpunit/src/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.phpNN^lvendor/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.phph_vendor/phpunit/phpunit/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.phpYY?8̤Lvendor/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.phpX@vendor/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.phpvlKvendor/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.phpgGKvendor/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.phpsDvendor/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php jMvendor/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php(&Fvendor/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.phpY;vendor/phpunit/phpunit/src/Framework/MockObject/Matcher.phpvvkEvendor/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php pUQGvendor/phpunit/phpunit/src/Framework/InvalidParameterGroupException.phpeOq4vendor/phpunit/phpunit/src/Framework/Reorderable.php w>vendor/phpunit/phpunit/src/Framework/Constraint/Constraint.php9/nSvendor/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php'^>vendor/phpunit/phpunit/src/Framework/Constraint/IsAnything.php$|$Ivendor/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsReadable.phpa2Nvendor/phpunit/phpunit/src/Framework/Constraint/Filesystem/DirectoryExists.phpvendor/phpunit/phpunit/src/Framework/Constraint/Math/IsNan.phpWTŤAvendor/phpunit/phpunit/src/Framework/Constraint/Math/IsFinite.php$$ȺXCvendor/phpunit/phpunit/src/Framework/Constraint/Math/IsInfinite.php,,)%)Gvendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalAnd.php9~)Evendor/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php}}4`Gvendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalNot.phpOkKvendor/phpunit/phpunit/src/Framework/Constraint/Operator/BinaryOperator.phpQqJvendor/phpunit/phpunit/src/Framework/Constraint/Operator/UnaryOperator.phppFvendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalOr.php:xzGvendor/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php-q7vendor/phpunit/phpunit/src/Framework/SelfDescribing.phpú]/vendor/phpunit/phpunit/src/Framework/Assert.php#9vendor/phpunit/phpunit/src/Framework/ExceptionWrapper.phpJ95vendor/phpunit/phpunit/src/Framework/TestListener.php  2hۤAvendor/phpunit/phpunit/src/Framework/ExecutionOrderDependency.php ZJvendor/phpunit/phpunit/src/Framework/TestListenerDefaultImplementation.phpJJۤ:vendor/phpunit/phpunit/src/Framework/TestSuiteIterator.php--( ;vendor/phpunit/phpunit/src/Framework/IncompleteTestCase.phpp&4vendor/phpunit/phpunit/src/Framework/TestFailure.php.>4vendor/phpunit/phpunit/src/Framework/SkippedTest.php(9vendor/phpunit/phpunit/src/Framework/Assert/Functions.php@p4vendor/phpunit/phpunit/src/Framework/TestBuilder.phpr14vendor/phpunit/phpunit/src/Framework/Error/Error.php``L[69vendor/phpunit/phpunit/src/Framework/Error/Deprecated.phpwwP5vendor/phpunit/phpunit/src/Framework/Error/Notice.phpssa7W6vendor/phpunit/phpunit/src/Framework/Error/Warning.phpttcP8vendor/phpunit/phpunit/src/Framework/Exception/Error.phpTOvendor/phpunit/phpunit/src/Framework/Exception/InvalidDataProviderException.phpŚ Avendor/phpunit/phpunit/src/Framework/Exception/SyntheticError.php?Vmvendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php?p;Rvendor/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.phpؤSvendor/phpunit/phpunit/src/Framework/Exception/MissingCoversAnnotationException.php[QMvendor/phpunit/phpunit/src/Framework/Exception/ExpectationFailedException.php44~KeChvendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php<vendor/phpunit/phpunit/src/Framework/Exception/Exception.phpðAvendor/phpunit/phpunit/src/Framework/Exception/RiskyTestError.php~~ VGvendor/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.phpfvendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.phpN>vendor/phpunit/phpunit/src/Framework/Exception/OutputError.php)äHvendor/phpunit/phpunit/src/Framework/Exception/SkippedTestSuiteError.phpXeƲKvendor/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php mfOvendor/phpunit/phpunit/src/Framework/Exception/InvalidCoversTargetException.phpTKvendor/phpunit/phpunit/src/Framework/Exception/InvalidArgumentException.phpyդLvendor/phpunit/phpunit/src/Framework/Exception/NoChildTestSuiteException.php :vendor/phpunit/phpunit/src/Framework/Exception/Warning.php7UޤRvendor/phpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.phpj OƤTvendor/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.phpTT,YHvendor/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.phpzzvgvendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.phpDFvendor/phpunit/phpunit/src/Framework/Exception/IncompleteTestError.php߀铤Cvendor/phpunit/phpunit/src/Framework/Exception/SkippedTestError.php,R76Hvendor/phpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.phpN^Xvendor/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotExistException.php 2vendor/phpunit/phpunit/src/Framework/TestSuite.php77|9-vendor/phpunit/phpunit/src/Framework/Test.phpp)vendor/phpunit/phpunit/src/Util/Color.php_ _ _% ?vendor/phpunit/phpunit/src/Util/PHP/Template/TestCaseMethod.tpl S.=vendor/phpunit/phpunit/src/Util/PHP/Template/PhptTestCase.tpl88h/>vendor/phpunit/phpunit/src/Util/PHP/Template/TestCaseClass.tplP P Npd9vendor/phpunit/phpunit/src/Util/PHP/DefaultPhpProcess.php {.:vendor/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php9vendor/phpunit/phpunit/src/Util/PHP/WindowsPhpProcess.php*E[/vendor/phpunit/phpunit/src/Util/GlobalState.php~~\ܤ5vendor/phpunit/phpunit/src/Util/RegularExpression.phpQQ?47vendor/phpunit/phpunit/src/Util/XmlTestListRenderer.php[[VH7vendor/phpunit/phpunit/src/Util/Annotation/DocBlock.php++Q7vendor/phpunit/phpunit/src/Util/Annotation/Registry.php??z +vendor/phpunit/phpunit/src/Util/Printer.php$4u-vendor/phpunit/phpunit/src/Util/Log/JUnit.php0vendor/phpunit/phpunit/src/Util/Log/TeamCity.php38vendor/phpunit/phpunit/src/Util/TextTestListRenderer.php=vendor/phpunit/phpunit/src/Util/TestDox/TextResultPrinter.phpPPGQ<vendor/phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.phpggsۆ:vendor/phpunit/phpunit/src/Util/TestDox/TestDoxPrinter.phpNN(zD=vendor/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.phpZɤ:vendor/phpunit/phpunit/src/Util/TestDox/NamePrettifier.phpVT9vendor/phpunit/phpunit/src/Util/TestDox/ResultPrinter.phplle;R=vendor/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php h6vendor/phpunit/phpunit/src/Util/Xml/SchemaDetector.php..֨08vendor/phpunit/phpunit/src/Util/Xml/SnapshotNodeList.phpSS~iH1vendor/phpunit/phpunit/src/Util/Xml/Exception.php ]1vendor/phpunit/phpunit/src/Util/Xml/Validator.phppp4vendor/phpunit/phpunit/src/Util/Xml/SchemaFinder.php̢8vendor/phpunit/phpunit/src/Util/Xml/ValidationResult.phpn{W=vendor/phpunit/phpunit/src/Util/Xml/SchemaDetectionResult.php  ?wfGvendor/phpunit/phpunit/src/Util/Xml/SuccessfulSchemaDetectionResult.phpss)tq.vendor/phpunit/phpunit/src/Util/Xml/Loader.php"3Cvendor/phpunit/phpunit/src/Util/Xml/FailedSchemaDetectionResult.phpCZ-vendor/phpunit/phpunit/src/Util/Exception.phpm /r(vendor/phpunit/phpunit/src/Util/Json.phpLҤ0vendor/phpunit/phpunit/src/Util/ErrorHandler.php0 0 [ .vendor/phpunit/phpunit/src/Util/Reflection.phpyy1H=vendor/phpunit/phpunit/src/Util/VersionComparisonOperator.php<*vendor/phpunit/phpunit/src/Util/Filter.phpy;vendor/phpunit/phpunit/src/Util/InvalidDataSetException.phpqշ?vendor/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php,Ӥ.vendor/phpunit/phpunit/src/Util/Filesystem.phpH/vendor/phpunit/phpunit/src/Util/ExcludeList.php !*vendor/phpunit/phpunit/src/Util/Cloner.phpgœ]'vendor/phpunit/phpunit/src/Util/Xml.php f>Ѥ.vendor/phpunit/phpunit/src/Util/FileLoader.php,3_-vendor/phpunit/phpunit/src/Util/Blacklist.php+(vendor/phpunit/phpunit/src/Util/Type.phpL?(vendor/phpunit/phpunit/src/Util/Test.php??>;0vendor/phpunit/phpunit/src/TextUI/TestRunner.php*vendor/phpunit/phpunit/src/TextUI/Help.phpU*U*ښQ>vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php5$Uvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php-&Evendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php hPCvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Constant.phpddJOvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollection.phpפUvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php( IEvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.phpuu؇Mvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollection.php]Mvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollection.phpQäWvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php))+ Cvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Variable.phpeIvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.phpll@ҤZvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php(Svendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationException.php(_vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.phpndvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.phprr3M꼤kvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php>Y`vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php뿤`vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php7ebvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.phpm7bvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.phpߗlvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php+}wvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.phpbbU]vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.phpgd[vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php?_vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php|)$fvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.phpYY/`vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.phpfZvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.phpFn"dvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.phpqKkvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php VԤUvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/Migration.phpSQvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilder.phpa7@vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Generator.phpZZBKvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/Directory.php[ Xvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php\Pvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollection.php`Uvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php"Fvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/File.phpME Ҥ]vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php&&ʳu:Pvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.phpQQ$g=Pvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php Nvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Php.phpAA I%jTvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.phpGGOvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Text.phpUQvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.phpDD`Qvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php,)iNvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.phpPPW٤Ovendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php!aTvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php||^vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.phpʐfvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php::$Dvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Junit.php77J3Kvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Text.php>>[Jvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Xml.php==r"Kvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Html.php>>d4Cvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Text.php667C3Gvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TeamCity.php::V̤Fvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Logging.phpFNvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.phpMIvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFile.php5`vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php22j4ɤTvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.phpAiSvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollection.phpL=\vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php  oXvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php/'[vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php 5Jvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuite.phprLvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollection.phpF^-FBvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Group.php]Cvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Groups.phpII9ΤTvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollectionIterator.phpw`@vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Exception.php nRvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php==fZvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php  DFvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php--:nݤHvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/Extension.phpDvendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Configuration.phpX=vendor/phpunit/phpunit/src/TextUI/XmlConfiguration/Loader.phprr؉W73vendor/phpunit/phpunit/src/TextUI/ResultPrinter.php""f<<vendor/phpunit/phpunit/src/TextUI/CliArguments/Exception.phpK@vendor/phpunit/phpunit/src/TextUI/CliArguments/Configuration.phprr6O9vendor/phpunit/phpunit/src/TextUI/CliArguments/Mapper.php!!A:vendor/phpunit/phpunit/src/TextUI/CliArguments/Builder.php55Ɛ:vendor/phpunit/phpunit/src/TextUI/DefaultResultPrinter.phpj)j) -vendor/phpunit/phpunit/src/TextUI/Command.phpOO[5vendor/phpunit/phpunit/src/TextUI/TestSuiteMapper.php}<(Cvendor/phpunit/phpunit/src/TextUI/Exception/ReflectionException.phpoNvendor/phpunit/phpunit/src/TextUI/Exception/TestDirectoryNotFoundException.phpIIh 9vendor/phpunit/phpunit/src/TextUI/Exception/Exception.phpK@vendor/phpunit/phpunit/src/TextUI/Exception/RuntimeException.php1eIvendor/phpunit/phpunit/src/TextUI/Exception/TestFileNotFoundException.php??5,=vendor/phpunit/php-text-template/src/exceptions/Exception.php? Dvendor/phpunit/php-text-template/src/exceptions/RuntimeException.phpoLvendor/phpunit/php-text-template/src/exceptions/InvalidArgumentException.phpj1vendor/phpunit/php-text-template/src/Template.php  +5vendor/phpunit/php-code-coverage/src/CodeCoverage.php//jBvendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.phpYQT0vendor/phpunit/php-code-coverage/src/Version.phpwwh<vendor/phpunit/php-code-coverage/src/RawCodeCoverageData.phpzz-=vendor/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.phpON:vendor/phpunit/php-code-coverage/src/Driver/PcovDriver.php>b=vendor/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.phpp p ệ8vendor/phpunit/php-code-coverage/src/Driver/Selector.phpII*ڤ<vendor/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.phpkw6vendor/phpunit/php-code-coverage/src/Driver/Driver.php -[IH;vendor/phpunit/php-code-coverage/src/Report/Html/Facade.php Ǥ=vendor/phpunit/php-code-coverage/src/Report/Html/Renderer.php]vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/dashboard_branch.html.distD]vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item_branch.html.distll-Rvendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/js/nv.d3.min.jsRRA.vendor/symfony/browser-kit/AbstractBrowser.php{3{3:*vendor/symfony/browser-kit/HttpBrowser.phpLLz&vendor/symfony/browser-kit/Request.phphh8?&vendor/symfony/browser-kit/History.php  /9+''vendor/symfony/browser-kit/Response.php7e`%vendor/symfony/browser-kit/Cookie.phpzz9\ؤ(vendor/symfony/browser-kit/CookieJar.php (?vendor/symfony/browser-kit/Exception/BadMethodCallException.phpPaFvendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php22sv3vendor/symfony/event-dispatcher-contracts/Event.phpt:vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.phpڔݤ<vendor/symfony/polyfill-php80/Resources/stubs/Stringable.phpkk+;vendor/symfony/polyfill-php80/Resources/stubs/Attribute.phpEvendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php]]g<vendor/symfony/polyfill-php80/Resources/stubs/ValueError.phpTTw'vendor/symfony/polyfill-php80/Php80.php [k +vendor/symfony/polyfill-php80/bootstrap.php*vendor/symfony/polyfill-php80/PhpToken.phpMI@vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php8[8[+R*Fvendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.phpy_Bvendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php@vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.phpTT+.vendor/symfony/polyfill-mbstring/bootstrap.php'w@00vendor/symfony/polyfill-mbstring/bootstrap80.php!! .Ѥ-vendor/symfony/polyfill-mbstring/Mbstring.phpSS3 f4vendor/symfony/css-selector/CssSelectorConverter.php)\9vendor/symfony/css-selector/XPath/TranslatorInterface.phpqqt_60vendor/symfony/css-selector/XPath/Translator.php群=vendor/symfony/css-selector/XPath/Extension/NodeExtension.php88k܃Dvendor/symfony/css-selector/XPath/Extension/CombinationExtension.php6$n=vendor/symfony/css-selector/XPath/Extension/HtmlExtension.phpAvendor/symfony/css-selector/XPath/Extension/AbstractExtension.php>:Dvendor/symfony/css-selector/XPath/Extension/PseudoClassExtension.phpzäBvendor/symfony/css-selector/XPath/Extension/ExtensionInterface.phpZƼJvendor/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.phpb b }Avendor/symfony/css-selector/XPath/Extension/FunctionExtension.php6 6 4\/vendor/symfony/css-selector/XPath/XPathExpr.phpȌAvendor/symfony/css-selector/Parser/Shortcut/EmptyStringParser.phpN=vendor/symfony/css-selector/Parser/Shortcut/ElementParser.php%+:vendor/symfony/css-selector/Parser/Shortcut/HashParser.phpss4a;vendor/symfony/css-selector/Parser/Shortcut/ClassParser.phpww)-vendor/symfony/css-selector/Parser/Parser.php@@յ6vendor/symfony/css-selector/Parser/ParserInterface.php{偤Bvendor/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.phpfH`/Bvendor/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php ,:vendor/symfony/css-selector/Parser/Tokenizer/Tokenizer.php_y2vendor/symfony/css-selector/Parser/TokenStream.phpPP:,vendor/symfony/css-selector/Parser/Token.php)d-vendor/symfony/css-selector/Parser/Reader.php{{wM@vendor/symfony/css-selector/Parser/Handler/WhitespaceHandler.phpUUۋ=vendor/symfony/css-selector/Parser/Handler/CommentHandler.phpՃ@vendor/symfony/css-selector/Parser/Handler/IdentifierHandler.phpo?vendor/symfony/css-selector/Parser/Handler/HandlerInterface.phpcn<vendor/symfony/css-selector/Parser/Handler/NumberHandler.php  ].R<vendor/symfony/css-selector/Parser/Handler/StringHandler.phpaaR9:vendor/symfony/css-selector/Parser/Handler/HashHandler.php:P2vendor/symfony/css-selector/Node/NodeInterface.phpo"F-vendor/symfony/css-selector/Node/HashNode.php$3ˤ1vendor/symfony/css-selector/Node/AbstractNode.php  ݰ̤9vendor/symfony/css-selector/Node/CombinedSelectorNode.phpq0vendor/symfony/css-selector/Node/ElementNode.php_Ҥ.vendor/symfony/css-selector/Node/ClassNode.phpKT0vendor/symfony/css-selector/Node/Specificity.phpg_1vendor/symfony/css-selector/Node/FunctionNode.php8j1vendor/symfony/css-selector/Node/NegationNode.phpғ1vendor/symfony/css-selector/Node/SelectorNode.php./vendor/symfony/css-selector/Node/PseudoNode.php/]K2vendor/symfony/css-selector/Node/AttributeNode.phpAAΘ>vendor/symfony/css-selector/Exception/SyntaxErrorException.php]d08vendor/symfony/css-selector/Exception/ParseException.php/X<vendor/symfony/css-selector/Exception/ExceptionInterface.phpB|Bvendor/symfony/css-selector/Exception/ExpressionErrorException.php6;@vendor/symfony/css-selector/Exception/InternalErrorException.php&O&vendor/symfony/process/InputStream.php'','vendor/symfony/process/ProcessUtils.phpI.vendor/symfony/process/Pipes/AbstractPipes.php* * &df-vendor/symfony/process/Pipes/WindowsPipes.php2 2 zژ*vendor/symfony/process/Pipes/UnixPipes.phpY</vendor/symfony/process/Pipes/PipesInterface.phpfQ .vendor/symfony/process/PhpExecutableFinder.phpm7%vendor/symfony/process/PhpProcess.php}0+vendor/symfony/process/ExecutableFinder.php&i"vendor/symfony/process/Process.phpfftߤ=vendor/symfony/process/Exception/ProcessSignaledException.phpYש3vendor/symfony/process/Exception/LogicException.php 7vendor/symfony/process/Exception/ExceptionInterface.phpyyqVXJ;vendor/symfony/process/Exception/ProcessFailedException.phpxxzy5vendor/symfony/process/Exception/RuntimeException.php:=vendor/symfony/process/Exception/InvalidArgumentException.php+_=vendor/symfony/process/Exception/ProcessTimedOutException.php11'Zvendor/symfony/finder/Glob.phpˤ3vendor/symfony/finder/Comparator/DateComparator.php%e/vendor/symfony/finder/Comparator/Comparator.phpdduE5vendor/symfony/finder/Comparator/NumberComparator.phpzz+h vendor/symfony/finder/Finder.php)) c;vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php"w:vendor/symfony/finder/Iterator/DateRangeFilterIterator.phpʀ=vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.phpF;vendor/symfony/finder/Iterator/VcsIgnoredFilterIterator.phpw w ŵ3Avendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php#Cߤ9vendor/symfony/finder/Iterator/FileTypeFilterIterator.phpww7m3vendor/symfony/finder/Iterator/SortableIterator.phpY Y By7vendor/symfony/finder/Iterator/CustomFilterIterator.phpTTQ=vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php o<vendor/symfony/finder/Iterator/FilecontentFilterIterator.phpHHҤ/vendor/symfony/finder/Iterator/LazyIterator.php/yJ:vendor/symfony/finder/Iterator/SizeRangeFilterIterator.phpmm=9vendor/symfony/finder/Iterator/FilenameFilterIterator.php)Nޤ5vendor/symfony/finder/Iterator/PathFilterIterator.php5$S#vendor/symfony/finder/Gitignore.phps 49vendor/symfony/finder/Exception/AccessDeniedException.phps>vendor/symfony/finder/Exception/DirectoryNotFoundException.phpa)%vendor/symfony/finder/SplFileInfo.phpvҘ:#vendor/symfony/console/Terminal.php o,w vendor/symfony/console/Color.php||\ք8vendor/symfony/console/SignalRegistry/SignalRegistry.php''$Y3vendor/symfony/console/SingleCommandApplication.phpJJ#YDvendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php~,vendor/symfony/console/Question/Question.php''t82vendor/symfony/console/Question/ChoiceQuestion.php# # eˤ8vendor/symfony/console/Question/ConfirmationQuestion.php  5(vendor/symfony/console/Helper/Helper.phpJ J 5+vendor/symfony/console/Helper/TableCell.phpZZtSФ,vendor/symfony/console/Helper/TableStyle.phpNݚe6vendor/symfony/console/Helper/DebugFormatterHelper.php؞2vendor/symfony/console/Helper/DescriptorHelper.php5+vendor/symfony/console/Helper/HelperSet.php̠{3vendor/symfony/console/Helper/ProgressIndicator.phpaP1vendor/symfony/console/Helper/HelperInterface.phpIC3-vendor/symfony/console/Helper/ProgressBar.php//?'vendor/symfony/console/Helper/Table.phpJJ0vendor/symfony/console/Helper/TableCellStyle.php˩l1vendor/symfony/console/Helper/FormatterHelper.phpj7vendor/symfony/console/Helper/SymfonyQuestionHelper.php  d(vendor/symfony/console/Helper/Dumper.php!!R+vendor/symfony/console/Helper/TableRows.php22?Ӥ0vendor/symfony/console/Helper/QuestionHelper.php(.(.92/vendor/symfony/console/Helper/ProcessHelper.php sQ2vendor/symfony/console/Helper/InputAwareHelper.phpcc0vendor/symfony/console/Helper/TableSeparator.php?vendor/symfony/console/CommandLoader/ContainerCommandLoader.php ە=vendor/symfony/console/CommandLoader/FactoryCommandLoader.php2Yդ?vendor/symfony/console/CommandLoader/CommandLoaderInterface.phpcc$>vendor/symfony/console/Formatter/OutputFormatterStyleStack.php?=vendor/symfony/console/Formatter/OutputFormatterInterface.phpJ4=vendor/symfony/console/Formatter/NullOutputFormatterStyle.php  %hBvendor/symfony/console/Formatter/OutputFormatterStyleInterface.php+Fvendor/symfony/console/Formatter/WrappableOutputFormatterInterface.phpZ8vendor/symfony/console/Formatter/NullOutputFormatter.php!& u4vendor/symfony/console/Formatter/OutputFormatter.phpL"9vendor/symfony/console/Formatter/OutputFormatterStyle.phpKKK2vendor/symfony/console/Event/ConsoleErrorEvent.php  V6vendor/symfony/console/Event/ConsoleTerminateEvent.phpJj3vendor/symfony/console/Event/ConsoleSignalEvent.phpKK -vendor/symfony/console/Event/ConsoleEvent.php]ܩ4vendor/symfony/console/Event/ConsoleCommandEvent.php"Xx26vendor/symfony/console/EventListener/ErrorListener.php''EФ(vendor/symfony/console/ConsoleEvents.phpgw]&vendor/symfony/console/Application.phpqq09vendor/symfony/console/Descriptor/DescriptorInterface.php@0vendor/symfony/console/Descriptor/Descriptor.php}}3^4vendor/symfony/console/Descriptor/TextDescriptor.php""Z 3vendor/symfony/console/Descriptor/XmlDescriptor.php8vendor/symfony/console/Descriptor/MarkdownDescriptor.phpQQtv4vendor/symfony/console/Descriptor/JsonDescriptor.php1%菤<vendor/symfony/console/Descriptor/ApplicationDescription.php w\0vendor/symfony/console/Command/LockableTrait.php]”t2vendor/symfony/console/Command/CompleteCommand.php .vendor/symfony/console/Command/ListCommand.php6 6 G.vendor/symfony/console/Command/LazyCommand.phpJYդ8vendor/symfony/console/Command/DumpCompletionCommand.phpSSV2i.vendor/symfony/console/Command/HelpCommand.php׆)*vendor/symfony/console/Command/Command.php((z=vendor/symfony/console/Command/SignalableCommandInterface.phpB`!vendor/symfony/console/Cursor.php Q.vendor/symfony/console/Attribute/AsCommand.php3/vendor/symfony/console/Tester/CommandTester.phpt19vendor/symfony/console/Tester/CommandCompletionTester.phpʛ;3vendor/symfony/console/Tester/ApplicationTester.phpg+r@vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.phpƃ-vendor/symfony/console/Tester/TesterTrait.php w//vendor/symfony/console/Style/StyleInterface.phpff3 ,vendor/symfony/console/Style/OutputStyle.phpS-vendor/symfony/console/Style/SymfonyStyle.php''*:vendor/symfony/console/Exception/MissingInputException.phpS 3vendor/symfony/console/Exception/LogicException.phpO\e;vendor/symfony/console/Exception/InvalidOptionException.phpH=vendor/symfony/console/Exception/CommandNotFoundException.php 7vendor/symfony/console/Exception/ExceptionInterface.phpyy9[&?vendor/symfony/console/Exception/NamespaceNotFoundException.phpn5vendor/symfony/console/Exception/RuntimeException.php,6=vendor/symfony/console/Exception/InvalidArgumentException.php̽Z0vendor/symfony/console/Completion/Suggestion.php::زŬ5vendor/symfony/console/Completion/CompletionInput.php$k;vendor/symfony/console/Completion/CompletionSuggestions.php## Avendor/symfony/console/Completion/Output/BashCompletionOutput.phpggWFvendor/symfony/console/Completion/Output/CompletionOutputInterface.phpFF2nM/vendor/symfony/console/Logger/ConsoleLogger.php  .0vendor/symfony/console/Input/InputDefinition.phpL/vendor/symfony/console/Input/InputInterface.phpddj:,vendor/symfony/console/Input/InputOption.phpU.vendor/symfony/console/Input/InputArgument.phpc6,vendor/symfony/console/Input/StringInput.php??ˤ9vendor/symfony/console/Input/StreamableInputInterface.phpB&vendor/symfony/console/Input/Input.php 64vendor/symfony/console/Input/InputAwareInterface.phpO+vendor/symfony/console/Input/ArrayInput.php~ ~ Y*vendor/symfony/console/Input/ArgvInput.php=2vendor/symfony/console/CI/GithubActionReporter.php 51vendor/symfony/console/Output/OutputInterface.php::5p%8vendor/symfony/console/Output/ConsoleOutputInterface.php!!C5j.vendor/symfony/console/Output/StreamOutput.php_'{(vendor/symfony/console/Output/Output.php( ( vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php~~9/ ?vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php Hvendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php<<ސ7vendor/symfony/var-dumper/Command/ServerDumpCommand.php 2Efb?vendor/symfony/var-dumper/Exception/ThrowingCasterException.phpm- +vendor/symfony/polyfill-ctype/bootstrap.php22a8-vendor/symfony/polyfill-ctype/bootstrap80.phphhKy'vendor/symfony/polyfill-ctype/Ctype.php ~d<vendor/symfony/service-contracts/Test/ServiceLocatorTest.php%$դ8vendor/symfony/service-contracts/ServiceLocatorTrait.php Jqb3vendor/symfony/service-contracts/ResetInterface.phpyyj=vendor/symfony/service-contracts/ServiceProviderInterface.phpRk?vendor/symfony/service-contracts/ServiceSubscriberInterface.phpx;vendor/symfony/service-contracts/ServiceSubscriberTrait.php@ä@vendor/symfony/service-contracts/Attribute/SubscribedService.php  1IT7vendor/symfony/service-contracts/Attribute/Required.phpxj네$vendor/symfony/dom-crawler/Image.php>]0vendor/symfony/dom-crawler/FormFieldRegistry.php7 7 &vendor/symfony/dom-crawler/Crawler.php R Ri`t*vendor/symfony/dom-crawler/UriResolver.phpXXݶC`Pvendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorAttributeValueSame.phpdFvendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextSame.phpppu?Dvendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorExists.php 4-Jvendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextContains.php<1vendor/symfony/dom-crawler/AbstractUriElement.phpPP'Ĥ#vendor/symfony/dom-crawler/Form.phpDD>ܤ#vendor/symfony/dom-crawler/Link.php.vendor/symfony/dom-crawler/Field/FormField.phptAj 2vendor/symfony/dom-crawler/Field/FileFormField.php**,#6vendor/symfony/dom-crawler/Field/TextareaFormField.php4^4vendor/symfony/dom-crawler/Field/ChoiceFormField.phpggsF~3vendor/symfony/dom-crawler/Field/InputFormField.php!Ovendor/symfony/yaml/Parser.phpVzVzävendor/symfony/yaml/Yaml.phpWW-vendor/symfony/yaml/Inline.php"M"Mø!vendor/symfony/yaml/Unescaper.php.K'vendor/symfony/yaml/Tag/TaggedValue.phpVVvX+vendor/symfony/yaml/Command/LintCommand.php[ [ =򲌤vendor/symfony/yaml/Dumper.phpVVIvendor/symfony/yaml/Escaper.phpGl0vendor/symfony/yaml/Exception/ParseException.php>4vendor/symfony/yaml/Exception/ExceptionInterface.phpvv0ʤ2vendor/symfony/yaml/Exception/RuntimeException.php$/vendor/symfony/yaml/Exception/DumpException.phpssƤ7vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.phpVV77vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php~~3vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php88LĜ2vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.phpcc騤2vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php22/6vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.phpnn>3vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.phpɹ52vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php ~on8vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.phpAASsB2vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.phpDb5vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php 2vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php11e2vendor/phpseclib/phpseclib/phpseclib/bootstrap.php+\Bvendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.phpo o 令9vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.phphhVg2vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php)d)dS2vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php""2F2vendor/phpseclib/phpseclib/phpseclib/File/X509.phpww!C:vendor/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php_*ё0vendor/phpseclib/phpseclib/phpseclib/Net/SCP.phpWWˬ?8vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php""b1vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.phpooE11vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php'^'^1vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.phpb8vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php8.w/vendor/mongodb/mongodb/src/InsertManyResult.php44x0'vendor/mongodb/mongodb/src/Database.php++UL2(vendor/mongodb/mongodb/src/functions.php!!J|3vendor/mongodb/mongodb/src/GridFS/StreamWrapper.php998y4vendor/mongodb/mongodb/src/GridFS/ReadableStream.phppp7vendor/mongodb/mongodb/src/GridFS/CollectionWrapper.phpkkeLZ,vendor/mongodb/mongodb/src/GridFS/Bucket.php"0"0J%)Evendor/mongodb/mongodb/src/GridFS/Exception/FileNotFoundException.phpdDvendor/mongodb/mongodb/src/GridFS/Exception/CorruptFileException.php@@-:?vendor/mongodb/mongodb/src/GridFS/Exception/StreamException.php22iH4vendor/mongodb/mongodb/src/GridFS/WritableStream.phpnɤBvendor/mongodb/mongodb/src/Model/CollectionInfoCommandIterator.phpu6vendor/mongodb/mongodb/src/Model/IndexInfoIterator.php>%0vendor/mongodb/mongodb/src/Model/CodecCursor.php y9vendor/mongodb/mongodb/src/Model/DatabaseInfoIterator.phpb1vendor/mongodb/mongodb/src/Model/BSONIterator.phpK5vendor/mongodb/mongodb/src/Model/SearchIndexInput.php||"1vendor/mongodb/mongodb/src/Model/DatabaseInfo.phpCC4$>vendor/mongodb/mongodb/src/Model/IndexInfoIteratorIterator.php==8];vendor/mongodb/mongodb/src/Model/CollectionInfoIterator.phpRV51vendor/mongodb/mongodb/src/Model/BSONDocument.phpǵ9vendor/mongodb/mongodb/src/Model/ChangeStreamIterator.php4vendor/mongodb/mongodb/src/Model/CachingIterator.phpBB_.vendor/mongodb/mongodb/src/Model/IndexInfo.phpl3vendor/mongodb/mongodb/src/Model/CollectionInfo.phpooT\5vendor/mongodb/mongodb/src/Model/CallbackIterator.php!.vendor/mongodb/mongodb/src/Model/BSONArray.phpu{/vendor/mongodb/mongodb/src/Model/IndexInput.php==@_?vendor/mongodb/mongodb/src/Model/DatabaseInfoLegacyIterator.php$z.vendor/mongodb/mongodb/src/MapReduceResult.php>dy)vendor/mongodb/mongodb/src/Collection.phpLLJq.vendor/mongodb/mongodb/src/BulkWriteResult.phpѨG6vendor/mongodb/mongodb/src/Codec/DecodeIfSupported.php__$6vendor/mongodb/mongodb/src/Codec/EncodeIfSupported.php__?_*vendor/mongodb/mongodb/src/Codec/Codec.phpff;Fn2vendor/mongodb/mongodb/src/Codec/DocumentCodec.php  ~ ,,vendor/mongodb/mongodb/src/Codec/Encoder.php  I},vendor/mongodb/mongodb/src/Codec/Decoder.php  l+vendor/mongodb/mongodb/src/DeleteResult.php5Τ,vendor/mongodb/mongodb/src/PsrLogAdapter.php 4+vendor/mongodb/mongodb/src/UpdateResult.php  ;56vendor/mongodb/mongodb/src/Command/ListCollections.php bHI4vendor/mongodb/mongodb/src/Command/ListDatabases.php  Ԥ%vendor/mongodb/mongodb/src/Client.phpooz.vendor/mongodb/mongodb/src/InsertOneResult.php%%@Bvendor/mongodb/mongodb/src/Exception/UnsupportedValueException.php7:l=vendor/mongodb/mongodb/src/Exception/ResumeTokenException.phpAm=vendor/mongodb/mongodb/src/Exception/UnsupportedException.phpKvendor/mongodb/mongodb/src/Exception/CreateEncryptedCollectionException.phpRR6ն2vendor/mongodb/mongodb/src/Exception/Exception.phpjܤ9vendor/mongodb/mongodb/src/Exception/RuntimeException.php $Avendor/mongodb/mongodb/src/Exception/InvalidArgumentException.phpaoAvendor/mongodb/mongodb/src/Exception/UnexpectedValueException.php㌿?vendor/mongodb/mongodb/src/Exception/BadMethodCallException.phpG+vendor/mongodb/mongodb/src/ChangeStream.php$$^:vendor/mongodb/mongodb/src/Operation/UpdateSearchIndex.phpBBĆ8vendor/mongodb/mongodb/src/Operation/DropSearchIndex.php`ä5vendor/mongodb/mongodb/src/Operation/DropDatabase.phpS0vendor/mongodb/mongodb/src/Operation/FindOne.php~~0_3vendor/mongodb/mongodb/src/Operation/InsertMany.php8vendor/mongodb/mongodb/src/Operation/ListCollections.phpvv.vendor/mongodb/mongodb/src/Operation/Watch.php##Q}פ2vendor/mongodb/mongodb/src/Operation/MapReduce.phpZ#Z#Q̤4vendor/mongodb/mongodb/src/Operation/DropIndexes.phpS S et/vendor/mongodb/mongodb/src/Operation/Update.php#-.Q9vendor/mongodb/mongodb/src/Operation/FindOneAndUpdate.php H8vendor/mongodb/mongodb/src/Operation/DatabaseCommand.phplV0vendor/mongodb/mongodb/src/Operation/Explain.php 06vendor/mongodb/mongodb/src/Operation/ListDatabases.php2vendor/mongodb/mongodb/src/Operation/InsertOne.php; ; 2vendor/mongodb/mongodb/src/Operation/UpdateOne.phpˤBvendor/mongodb/mongodb/src/Operation/CreateEncryptedCollection.phpEEg&8vendor/mongodb/mongodb/src/Operation/WithTransaction.php[3vendor/mongodb/mongodb/src/Operation/Executable.php왏-/vendor/mongodb/mongodb/src/Operation/Delete.php4vendor/mongodb/mongodb/src/Operation/ListIndexes.phpJ J !P9:vendor/mongodb/mongodb/src/Operation/ListDatabaseNames.php  tB9vendor/mongodb/mongodb/src/Operation/ModifyCollection.phpa a [Z4vendor/mongodb/mongodb/src/Operation/Explainable.php{^5<vendor/mongodb/mongodb/src/Operation/ListCollectionNames.php??7T?vendor/mongodb/mongodb/src/Operation/EstimatedDocumentCount.phpi6vendor/mongodb/mongodb/src/Operation/CreateIndexes.php!!prk:vendor/mongodb/mongodb/src/Operation/ListSearchIndexes.php11x)6vendor/mongodb/mongodb/src/Operation/FindAndModify.php[JӤ-vendor/mongodb/mongodb/src/Operation/Find.php&&XW2vendor/mongodb/mongodb/src/Operation/BulkWrite.php##K:vendor/mongodb/mongodb/src/Operation/FindOneAndReplace.php  !/m9vendor/mongodb/mongodb/src/Operation/FindOneAndDelete.phpנ2vendor/mongodb/mongodb/src/Operation/DeleteOne.php6c9vendor/mongodb/mongodb/src/Operation/CreateCollection.phpe)j3vendor/mongodb/mongodb/src/Operation/UpdateMany.php5%9vendor/mongodb/mongodb/src/Operation/RenameCollection.php  tˤ<vendor/mongodb/mongodb/src/Operation/CreateSearchIndexes.php  6\@vendor/mongodb/mongodb/src/Operation/DropEncryptedCollection.php!Z7vendor/mongodb/mongodb/src/Operation/CountDocuments.php4 4 dF2vendor/mongodb/mongodb/src/Operation/Aggregate.php.vendor/mongodb/mongodb/src/Operation/Count.php++,1vendor/mongodb/mongodb/src/Operation/Distinct.phpa!3vendor/mongodb/mongodb/src/Operation/ReplaceOne.php++ T7vendor/mongodb/mongodb/src/Operation/DropCollection.php C3vendor/mongodb/mongodb/src/Operation/DeleteMany.phpP9!vendor/mongodb/mongodb/rector.php4vendor/autoload.phpyyO vendor/psy/psysh/src/Context.php3ͤ"vendor/psy/psysh/src/SystemEnv.phpC)vendor/psy/psysh/src/Sudo/SudoVisitor.phpCC$)vendor/psy/psysh/src/ExecutionClosure.php  cƤ$vendor/psy/psysh/src/CodeCleaner.phpo;"vendor/psy/psysh/src/functions.php''W-vendor/psy/psysh/src/Readline/GNUReadline.php66$Z&*vendor/psy/psysh/src/Readline/Userland.php H'*vendor/psy/psysh/src/Readline/Readline.phpLL̒?)vendor/psy/psysh/src/Readline/Libedit.php44̛+vendor/psy/psysh/src/Readline/Transient.php??, .vendor/psy/psysh/src/Readline/Hoa/Protocol.php, , D^Ԥ?vendor/psy/psysh/src/Readline/Hoa/FileDoesNotExistException.phpɣ%@vendor/psy/psysh/src/Readline/Hoa/IteratorRecursiveDirectory.php63vendor/psy/psysh/src/Readline/Hoa/ExceptionIdle.php 3vendor/psy/psysh/src/Readline/Hoa/FileDirectory.php 93vendor/psy/psysh/src/Readline/Hoa/StreamContext.php-vendor/psy/psysh/src/Readline/Hoa/IStream.phpK,vendor/psy/psysh/src/Readline/Hoa/Stream.phpp>6vendor/psy/psysh/src/Readline/Hoa/ConsoleProcessus.php((Wr4vendor/psy/psysh/src/Readline/Hoa/StreamLockable.phpDDs@,7vendor/psy/psysh/src/Readline/Hoa/FileLinkReadWrite.php TT03vendor/psy/psysh/src/Readline/Hoa/FileException.phpwwBX7vendor/psy/psysh/src/Readline/Hoa/ProtocolException.phpww.2vendor/psy/psysh/src/Readline/Hoa/ConsoleInput.phpuY.vendor/psy/psysh/src/Readline/Hoa/Readline.php ; ;.vendor/psy/psysh/src/Readline/Hoa/FileLink.phpmN3vendor/psy/psysh/src/Readline/Hoa/ConsoleCursor.php-#-#c3vendor/psy/psysh/src/Readline/Hoa/ConsoleWindow.phpoo3vendor/psy/psysh/src/Readline/Hoa/Autocompleter.php@;7vendor/psy/psysh/src/Readline/Hoa/AutocompleterWord.php9 g0vendor/psy/psysh/src/Readline/Hoa/FileFinder.phpk9vendor/psy/psysh/src/Readline/Hoa/ProtocolNodeLibrary.php585vendor/psy/psysh/src/Readline/Hoa/EventListenable.phpY1'/vendor/psy/psysh/src/Readline/Hoa/Exception.php44pu-vendor/psy/psysh/src/Readline/Hoa/Ustring.phpI,ڤ1vendor/psy/psysh/src/Readline/Hoa/EventBucket.phpdä6vendor/psy/psysh/src/Readline/Hoa/StreamBufferable.php``.4vendor/psy/psysh/src/Readline/Hoa/StreamPathable.php'd1vendor/psy/psysh/src/Readline/Hoa/EventSource.phpcc4ѧ<vendor/psy/psysh/src/Readline/Hoa/AutocompleterAggregate.php7vendor/psy/psysh/src/Readline/Hoa/AutocompleterPath.phpk`2vendor/psy/psysh/src/Readline/Hoa/ProtocolNode.php'5vendor/psy/psysh/src/Readline/Hoa/StreamPointable.phpkk=4vendor/psy/psysh/src/Readline/Hoa/StreamStatable.phpo2vendor/psy/psysh/src/Readline/Hoa/EventListens.phpeդ6vendor/psy/psysh/src/Readline/Hoa/ConsoleException.phpxx;3vendor/psy/psysh/src/Readline/Hoa/FileReadWrite.php AB#.vendor/psy/psysh/src/Readline/Hoa/FileRead.phph(+vendor/psy/psysh/src/Readline/Hoa/Event.php /W5vendor/psy/psysh/src/Readline/Hoa/StreamTouchable.phpi4vendor/psy/psysh/src/Readline/Hoa/EventException.phpttݤ8vendor/psy/psysh/src/Readline/Hoa/IteratorFileSystem.php-2vendor/psy/psysh/src/Readline/Hoa/FileLinkRead.php^c1vendor/psy/psysh/src/Readline/Hoa/FileGeneric.php&[d3vendor/psy/psysh/src/Readline/Hoa/EventListener.phpW.vendor/psy/psysh/src/Readline/Hoa/StreamIn.php33g*vendor/psy/psysh/src/Readline/Hoa/File.php ܪ5vendor/psy/psysh/src/Readline/Hoa/ProtocolWrapper.php5/vendor/psy/psysh/src/Readline/Hoa/Xcallable.phpi i CԲ1vendor/psy/psysh/src/Readline/Hoa/ConsoleTput.phpL2L2( -vendor/psy/psysh/src/Readline/Hoa/Console.php 3vendor/psy/psysh/src/Readline/Hoa/ConsoleOutput.php|_֭/vendor/psy/psysh/src/Readline/Hoa/StreamOut.phpYYuP5vendor/psy/psysh/src/Readline/Hoa/StreamException.phpwwCV9vendor/psy/psysh/src/Readline/Hoa/IteratorSplFileInfo.php݌8,vendor/psy/psysh/src/VarDumper/Presenter.php9̤)vendor/psy/psysh/src/VarDumper/Dumper.phpJJtO'1vendor/psy/psysh/src/VarDumper/PresenterAware.phpZ})vendor/psy/psysh/src/VarDumper/Cloner.phpAA=p4vendor/psy/psysh/src/TabCompletion/AutoCompleter.php  s>2Cvendor/psy/psysh/src/TabCompletion/Matcher/MongoDatabaseMatcher.phpwQBvendor/psy/psysh/src/TabCompletion/Matcher/ClassMethodsMatcher.php118HTJvendor/psy/psysh/src/TabCompletion/Matcher/AbstractContextAwareMatcher.php[3>vendor/psy/psysh/src/TabCompletion/Matcher/CommandsMatcher.phpڝ?vendor/psy/psysh/src/TabCompletion/Matcher/FunctionsMatcher.phpM?vendor/psy/psysh/src/TabCompletion/Matcher/ConstantsMatcher.php:<Ovendor/psy/psysh/src/TabCompletion/Matcher/FunctionDefaultParametersMatcher.php""셤>vendor/psy/psysh/src/TabCompletion/Matcher/AbstractMatcher.php 8XFvendor/psy/psysh/src/TabCompletion/Matcher/ObjectAttributesMatcher.phpKK_Svendor/psy/psysh/src/TabCompletion/Matcher/ObjectMethodDefaultParametersMatcher.php+fOvendor/psy/psysh/src/TabCompletion/Matcher/AbstractDefaultParametersMatcher.php^(Ϥ>vendor/psy/psysh/src/TabCompletion/Matcher/KeywordsMatcher.phpyrAvendor/psy/psysh/src/TabCompletion/Matcher/MongoClientMatcher.phpRvendor/psy/psysh/src/TabCompletion/Matcher/ClassMethodDefaultParametersMatcher.php--`4Cvendor/psy/psysh/src/TabCompletion/Matcher/ObjectMethodsMatcher.php__-j?vendor/psy/psysh/src/TabCompletion/Matcher/VariablesMatcher.phpGEvendor/psy/psysh/src/TabCompletion/Matcher/ClassAttributesMatcher.phpRwU@vendor/psy/psysh/src/TabCompletion/Matcher/ClassNamesMatcher.phpO4vendor/psy/psysh/src/Formatter/DocblockFormatter.php pE1vendor/psy/psysh/src/Formatter/TraceFormatter.phpSX5vendor/psy/psysh/src/Formatter/SignatureFormatter.php%q5vendor/psy/psysh/src/Formatter/ReflectorFormatter.phplA0vendor/psy/psysh/src/Formatter/CodeFormatter.php``H4vendor/psy/psysh/src/ExecutionLoop/ProcessForker.phpv1٤5vendor/psy/psysh/src/ExecutionLoop/RunkitReloader.php%N/vendor/psy/psysh/src/ExecutionLoop/Listener.php#7vendor/psy/psysh/src/ExecutionLoop/AbstractListener.php 3vendor/psy/psysh/src/Sudo.php Yvendor/psy/psysh/src/Shell.php__!=1vendor/psy/psysh/src/VersionUpdater/Installer.php1w/vendor/psy/psysh/src/VersionUpdater/Checker.php  ;k}7vendor/psy/psysh/src/VersionUpdater/IntervalChecker.php:vendor/psy/psysh/src/VersionUpdater/Downloader/Factory.php$Avendor/psy/psysh/src/VersionUpdater/Downloader/FileDownloader.php66L5Avendor/psy/psysh/src/VersionUpdater/Downloader/CurlDownloader.phpbb#@2vendor/psy/psysh/src/VersionUpdater/Downloader.php2vendor/psy/psysh/src/VersionUpdater/SelfUpdate.phpR R io5vendor/psy/psysh/src/VersionUpdater/GitHubChecker.phpʽ3vendor/psy/psysh/src/VersionUpdater/NoopChecker.php, -vendor/psy/psysh/src/ExecutionLoopClosure.phpc&vendor/psy/psysh/src/ParserFactory.php%vendor/psy/psysh/src/EnvInterface.phpee(vendor/psy/psysh/src/SuperglobalsEnv.php?vendor/psy/psysh/src/Reflection/ReflectionLanguageConstruct.phpg:Hvendor/psy/psysh/src/Reflection/ReflectionLanguageConstructParameter.phpuu'16vendor/psy/psysh/src/Reflection/ReflectionConstant.phpOq7vendor/psy/psysh/src/Reflection/ReflectionNamespace.php $t;vendor/psy/psysh/src/CodeCleaner/EmptyArrayDimFetchPass.php\ 47vendor/psy/psysh/src/CodeCleaner/MagicConstantsPass.php`w<vendor/psy/psysh/src/CodeCleaner/PassableByReferencePass.php'~2vendor/psy/psysh/src/CodeCleaner/NamespacePass.php11 ?5vendor/psy/psysh/src/CodeCleaner/LabelContextPass.php=7vendor/psy/psysh/src/CodeCleaner/ImplicitReturnPass.phpR-Ƥ6vendor/psy/psysh/src/CodeCleaner/AbstractClassPass.php`2ߤ4vendor/psy/psysh/src/CodeCleaner/CalledClassPass.php''Evendor/psy/psysh/src/CodeCleaner/FunctionReturnInWriteContextPass.php7vendor/psy/psysh/src/CodeCleaner/NamespaceAwarePass.php  m3vendor/psy/psysh/src/CodeCleaner/ReturnTypePass.php@ @ g.vendor/psy/psysh/src/CodeCleaner/IssetPass.php<<(9vendor/psy/psysh/src/CodeCleaner/ValidConstructorPass.php//~̤2vendor/psy/psysh/src/CodeCleaner/NoReturnValue.php  8hs-vendor/psy/psysh/src/CodeCleaner/ExitPass.phpǤ0vendor/psy/psysh/src/CodeCleaner/RequirePass.phpԤ-vendor/psy/psysh/src/CodeCleaner/ListPass.phpUUv&18vendor/psy/psysh/src/CodeCleaner/LeavePsyshAlonePass.php^r=;vendor/psy/psysh/src/CodeCleaner/AssignThisVariablePass.php_h@vendor/psy/psysh/src/CodeCleaner/CallTimePassByReferencePass.php[24vendor/psy/psysh/src/CodeCleaner/CodeCleanerPass.phph7vendor/psy/psysh/src/CodeCleaner/ValidClassNamePass.php;:vendor/psy/psysh/src/CodeCleaner/ValidFunctionNamePass.php3vendor/psy/psysh/src/CodeCleaner/FinalClassPass.php4vendor/psy/psysh/src/CodeCleaner/LoopContextPass.phpNR4vendor/psy/psysh/src/CodeCleaner/StrictTypesPass.phpI*8vendor/psy/psysh/src/CodeCleaner/FunctionContextPass.phpY[5vendor/psy/psysh/src/CodeCleaner/UseStatementPass.phpK&vendor/psy/psysh/src/Configuration.phpkkW3vendor/psy/psysh/src/Command/CodeArgumentParser.phpX/-vendor/psy/psysh/src/Command/ClearCommand.phpddڤ,vendor/psy/psysh/src/Command/EditCommand.php0 0 4-vendor/psy/psysh/src/Command/TraceCommand.php00.vendor/psy/psysh/src/Command/BufferCommand.php|' ,vendor/psy/psysh/src/Command/SudoCommand.phpQr+vendor/psy/psysh/src/Command/WtfCommand.php' ' )+vendor/psy/psysh/src/Command/DocCommand.phpoݤ?vendor/psy/psysh/src/Command/ListCommand/VariableEnumerator.php++eR==vendor/psy/psysh/src/Command/ListCommand/MethodEnumerator.phpvx汤<vendor/psy/psysh/src/Command/ListCommand/ClassEnumerator.phpUDvendor/psy/psysh/src/Command/ListCommand/ClassConstantEnumerator.php||(FfŤ7vendor/psy/psysh/src/Command/ListCommand/Enumerator.php?pԤ?vendor/psy/psysh/src/Command/ListCommand/FunctionEnumerator.phptt{Evendor/psy/psysh/src/Command/ListCommand/GlobalVariableEnumerator.phpYP?vendor/psy/psysh/src/Command/ListCommand/ConstantEnumerator.php Z?vendor/psy/psysh/src/Command/ListCommand/PropertyEnumerator.php) ) d,vendor/psy/psysh/src/Command/ListCommand.php*此/vendor/psy/psysh/src/Command/ThrowUpCommand.phpRR,vendor/psy/psysh/src/Command/HelpCommand.php,,:0vendor/psy/psysh/src/Command/WhereamiCommand.phpa a ,vendor/psy/psysh/src/Command/ExitCommand.phpzz9t2vendor/psy/psysh/src/Command/PsyVersionCommand.phpr,vendor/psy/psysh/src/Command/ShowCommand.phpKخ-vendor/psy/psysh/src/Command/ParseCommand.php>2]2vendor/psy/psysh/src/Command/ReflectingCommand.php2Yw<vendor/psy/psysh/src/Command/TimeitCommand/TimeitVisitor.php&&j,vendor/psy/psysh/src/Command/DumpCommand.php1.vendor/psy/psysh/src/Command/TimeitCommand.php6 6 x/vendor/psy/psysh/src/Command/HistoryCommand.phpH%(vendor/psy/psysh/src/Command/Command.phpܤ$vendor/psy/psysh/src/Util/Mirror.phpM M *C!vendor/psy/psysh/src/Util/Str.phpdkC"vendor/psy/psysh/src/Util/Json.php0&vendor/psy/psysh/src/Util/Docblock.php  <vendor/psy/psysh/src/Exception/UnexpectedTargetException.phpnn+_6vendor/psy/psysh/src/Exception/FatalErrorException.phpqq]6vendor/psy/psysh/src/Exception/DeprecatedException.phpee[J,vendor/psy/psysh/src/Exception/Exception.phpmmϲ3vendor/psy/psysh/src/Exception/RuntimeException.phpPפ6vendor/psy/psysh/src/Exception/ParseErrorException.php%ڄ1vendor/psy/psysh/src/Exception/ErrorException.phpbb=탤1vendor/psy/psysh/src/Exception/BreakException.phpȤ3vendor/psy/psysh/src/Exception/ThrowUpException.php55ρ(%vendor/psy/psysh/src/ContextAware.phptt!i-$vendor/psy/psysh/src/ConfigPaths.php[[_mK*vendor/psy/psysh/src/Input/SilentInput.php,h,vendor/psy/psysh/src/Input/FilterOptions.phpDJ+vendor/psy/psysh/src/Input/CodeArgument.phpp)vendor/psy/psysh/src/Input/ShellInput.phpvY1/vendor/psy/psysh/src/Output/ProcOutputPager.php֑ݤ%vendor/psy/psysh/src/Output/Theme.php/ -vendor/psy/psysh/src/Output/PassthruPager.php**"ۤ+vendor/psy/psysh/src/Output/OutputPager.phpI+vendor/psy/psysh/src/Output/ShellOutput.phpQ Q m=5vendor/jean85/pretty-package-versions/src/Version.php{<vendor/jean85/pretty-package-versions/src/PrettyVersions.php--.KXvendor/jean85/pretty-package-versions/src/Exception/VersionMissingExceptionInterface.php*QPvendor/jean85/pretty-package-versions/src/Exception/ReplacedPackageException.phpssτPvendor/jean85/pretty-package-versions/src/Exception/ProvidedPackageException.phpuu8vendor/php-webdriver/webdriver/lib/WebDriverUpAction.php8vendor/php-webdriver/webdriver/lib/WebDriverTimeouts.phpD Dvendor/php-webdriver/webdriver/lib/Interactions/WebDriverActions.phpAWvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverDoubleClickAction.phpQvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverCoordinates.php``qQvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAction.php  [R_Wvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeysRelatedAction.php?ߤUvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSingleKeyAction.phphXvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverContextClickAction.php1ӌQvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseAction.phpRX}Xvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverClickAndHoldAction.phppAzYvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverButtonReleaseAction.phpeV$Svendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyDownAction.php4vTvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverSendKeysAction.phpWBQvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverKeyUpAction.phpDXvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMoveToOffsetAction.php.pQUvendor/php-webdriver/webdriver/lib/Interactions/Internal/WebDriverMouseMoveAction.phpT]-Ivendor/php-webdriver/webdriver/lib/Interactions/WebDriverTouchActions.php: : my8Lvendor/php-webdriver/webdriver/lib/Interactions/WebDriverCompositeAction.phpJrZvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollFromElementAction.php55˙Mvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverMoveAction.php'qMvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDownAction.php6Rvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverDoubleTapAction.phpQNvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickAction.php)쓤Ovendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverScrollAction.phpo}Nvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverTouchScreen.phpLrYvendor/php-webdriver/webdriver/lib/Interactions/Touch/WebDriverFlickFromElementAction.phpzzvendor/php-webdriver/webdriver/lib/Remote/RemoteWebElement.php&&pc<vendor/php-webdriver/webdriver/lib/Remote/RemoteKeyboard.phpygҤAvendor/php-webdriver/webdriver/lib/Remote/HttpCommandExecutor.php>>mA=vendor/php-webdriver/webdriver/lib/Remote/RemoteWebDriver.php7+7+Iw@Avendor/php-webdriver/webdriver/lib/Remote/UselessFileDetector.phpk&:vendor/php-webdriver/webdriver/lib/Remote/FileDetector.phpuuAvendor/php-webdriver/webdriver/lib/Remote/DesiredCapabilities.phpzBvendor/php-webdriver/webdriver/lib/Remote/WebDriverBrowserType.php^^3Evendor/php-webdriver/webdriver/lib/Remote/WebDriverCapabilityType.phpZ7¤:vendor/php-webdriver/webdriver/lib/Remote/RemoteStatus.php&X>vendor/php-webdriver/webdriver/lib/Remote/WebDriverCommand.php^^uzϤ;vendor/php-webdriver/webdriver/lib/Remote/ExecuteMethod.php.T?vendor/php-webdriver/webdriver/lib/Remote/LocalFileDetector.phpn.pAvendor/php-webdriver/webdriver/lib/Remote/RemoteTargetLocator.php @Xb?vendor/php-webdriver/webdriver/lib/Remote/RemoteTouchScreen.php//FԤDvendor/php-webdriver/webdriver/lib/Remote/CustomWebDriverCommand.phpY8vendor/php-webdriver/webdriver/lib/Remote/ShadowRoot.php11G-vendor/php-webdriver/webdriver/lib/Cookie.php ] äJvendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriver.phpݠKvendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebElement.phpssTvendor/php-webdriver/webdriver/lib/Support/Events/EventFiringWebDriverNavigation.phpEEQN?vendor/php-webdriver/webdriver/lib/Support/ScreenshotHelper.phpa;vendor/php-webdriver/webdriver/lib/Support/XPathEscaper.php dEvendor/php-webdriver/webdriver/lib/Support/IsElementDisplayedAtom.php6I7vendor/php-webdriver/webdriver/lib/WebDriverOptions.php~wΤ4vendor/php-webdriver/webdriver/lib/WebDriverKeys.php*Qͤ:vendor/php-webdriver/webdriver/lib/WebDriverNavigation.phpfCvendor/php-webdriver/webdriver/lib/WebDriverNavigationInterface.phpv ä6vendor/php-webdriver/webdriver/lib/WebDriverSelect.phpmk=vendor/php-webdriver/webdriver/lib/WebDriverSearchContext.php ?vendor/php-webdriver/webdriver/lib/WebDriverSelectInterface.php@@_?vendor/php-webdriver/webdriver/lib/WebDriverCommandExecutor.phpBvendor/php-webdriver/webdriver/lib/Internal/WebDriverLocatable.phpFz2vendor/php-webdriver/webdriver/lib/WebDriverBy.php[[ (Avendor/php-webdriver/webdriver/lib/Chrome/ChromeDriverService.phpu$Ť:vendor/php-webdriver/webdriver/lib/Chrome/ChromeDriver.phpKKFlp;vendor/php-webdriver/webdriver/lib/Chrome/ChromeOptions.phpȤBvendor/php-webdriver/webdriver/lib/Chrome/ChromeDevToolsDriver.phpGGeЅ5vendor/php-webdriver/webdriver/lib/Net/URLChecker.php||'Gvendor/php-webdriver/webdriver/lib/AbstractWebDriverCheckboxOrRadio.phpM5vendor/php-webdriver/webdriver/lib/WebDriverAlert.phpZ=A0vendor/php-webdriver/webdriver/lib/WebDriver.phpe7vendor/php-webdriver/webdriver/lib/WebDriverElement.php8E:vendor/php-webdriver/webdriver/lib/WebDriverCheckboxes.phpFp9vendor/php-webdriver/webdriver/lib/JavaScriptExecutor.php⌤9vendor/php-webdriver/webdriver/lib/WebDriverDimension.phpZ,=vendor/php-webdriver/webdriver/lib/WebDriverEventListener.phpHHLEvendor/php-webdriver/webdriver/lib/Exception/NoSuchFrameException.phpmmΚKvendor/php-webdriver/webdriver/lib/Exception/UnableToSetCookieException.phpssNvendor/php-webdriver/webdriver/lib/Exception/ElementNotSelectableException.php: Evendor/php-webdriver/webdriver/lib/Exception/XPathLookupException.phpmmaTWFvendor/php-webdriver/webdriver/lib/Exception/UnknownErrorException.phpnn8LBvendor/php-webdriver/webdriver/lib/Exception/NoStringException.phpjjuqCvendor/php-webdriver/webdriver/lib/Exception/WebDriverException.php Fvendor/php-webdriver/webdriver/lib/Exception/NoSuchCookieException.phpooe?hJvendor/php-webdriver/webdriver/lib/Exception/IndexOutOfBoundsException.phprr Jvendor/php-webdriver/webdriver/lib/Exception/NoSuchCollectionException.phprr_gHvendor/php-webdriver/webdriver/lib/Exception/NoSuchDocumentException.phpssMbФIvendor/php-webdriver/webdriver/lib/Exception/JavascriptErrorException.phpqqi.Gvendor/php-webdriver/webdriver/lib/Exception/UnknownServerException.phprr-Gvendor/php-webdriver/webdriver/lib/Exception/UnknownMethodException.phpooj8Hvendor/php-webdriver/webdriver/lib/Exception/UnknownCommandException.phpppJvendor/php-webdriver/webdriver/lib/Exception/InvalidSessionIdException.phpss>Bvendor/php-webdriver/webdriver/lib/Exception/ExpectedException.phpjjj驤Kvendor/php-webdriver/webdriver/lib/Exception/UnexpectedTagNameException.php22l̃Fvendor/php-webdriver/webdriver/lib/Exception/NoCollectionException.phpnn3Ovendor/php-webdriver/webdriver/lib/Exception/PhpWebDriverExceptionInterface.php``E⋤Fvendor/php-webdriver/webdriver/lib/Exception/NoSuchWindowException.phpnn>Ovendor/php-webdriver/webdriver/lib/Exception/StaleElementReferenceException.phpwwޢRHvendor/php-webdriver/webdriver/lib/Exception/NoScriptResultException.phpppIvendor/php-webdriver/webdriver/lib/Exception/IMENotAvailableException.phpqq(OeEvendor/php-webdriver/webdriver/lib/Exception/NoSuchAlertException.phpmmW Nvendor/php-webdriver/webdriver/lib/Exception/UnexpectedJavascriptException.php||ۤMvendor/php-webdriver/webdriver/lib/Exception/InsecureCertificateException.phpvv}Evendor/php-webdriver/webdriver/lib/Exception/NoAlertOpenException.phpooWGvendor/php-webdriver/webdriver/lib/Exception/NoSuchElementException.phpooHA\Ivendor/php-webdriver/webdriver/lib/Exception/NoStringWrapperException.phpqqdKwHvendor/php-webdriver/webdriver/lib/Exception/NoStringLengthException.phppp⬣Kvendor/php-webdriver/webdriver/lib/Exception/ElementNotVisibleException.phpss-mAvendor/php-webdriver/webdriver/lib/Exception/TimeoutException.phpiiIvendor/php-webdriver/webdriver/lib/Exception/InvalidArgumentException.phpqqZ Ovendor/php-webdriver/webdriver/lib/Exception/MoveTargetOutOfBoundsException.phpww( ~Pvendor/php-webdriver/webdriver/lib/Exception/ElementNotInteractableException.phpxxwHvendor/php-webdriver/webdriver/lib/Exception/Internal/LogicException.phpddtWEvendor/php-webdriver/webdriver/lib/Exception/Internal/IOException.phpss:w6Pvendor/php-webdriver/webdriver/lib/Exception/Internal/WebDriverCurlException.phpDZ3qJvendor/php-webdriver/webdriver/lib/Exception/Internal/RuntimeException.php99 Uvendor/php-webdriver/webdriver/lib/Exception/Internal/UnexpectedResponseException.phpKSvendor/php-webdriver/webdriver/lib/Exception/Internal/DriverServerDiedException.php~~ CMvendor/php-webdriver/webdriver/lib/Exception/InvalidCookieDomainException.phpuu@8sMvendor/php-webdriver/webdriver/lib/Exception/InvalidElementStateException.phpvvsHOvendor/php-webdriver/webdriver/lib/Exception/UnrecognizedExceptionException.phptttmWQvendor/php-webdriver/webdriver/lib/Exception/ElementClickInterceptedException.phpzzhZSLvendor/php-webdriver/webdriver/lib/Exception/InvalidCoordinatesException.phptt ܤEvendor/php-webdriver/webdriver/lib/Exception/NullPointerException.phpmm&#Ovendor/php-webdriver/webdriver/lib/Exception/UnableToCaptureScreenException.phpwwL}>Gvendor/php-webdriver/webdriver/lib/Exception/ScriptTimeoutException.phpoo3CIvendor/php-webdriver/webdriver/lib/Exception/InvalidSelectorException.phpqqvKrLvendor/php-webdriver/webdriver/lib/Exception/DetachedShadowRootException.phptt ۤNvendor/php-webdriver/webdriver/lib/Exception/UnsupportedOperationException.phpvv:Mvendor/php-webdriver/webdriver/lib/Exception/UnexpectedAlertOpenException.phpuuSvendor/php-webdriver/webdriver/lib/Exception/IMEEngineActivationFailedException.php{{FKvendor/php-webdriver/webdriver/lib/Exception/SessionNotCreatedException.phpss~jJvendor/php-webdriver/webdriver/lib/Exception/NoSuchShadowRootException.phprrz;Fvendor/php-webdriver/webdriver/lib/Exception/NoSuchDriverException.phpnn?vendor/php-webdriver/webdriver/lib/WebDriverHasInputDevices.phpDh5vendor/php-webdriver/webdriver/lib/WebDriverMouse.phpKY5vendor/php-webdriver/webdriver/lib/WebDriverPoint.php__<vendor/php-webdriver/webdriver/lib/Firefox/FirefoxDriver.php^aCvendor/php-webdriver/webdriver/lib/Firefox/FirefoxDriverService.phpXXzFAvendor/php-webdriver/webdriver/lib/Firefox/FirefoxPreferences.php=z=vendor/php-webdriver/webdriver/lib/Firefox/FirefoxOptions.phpgg鵫=vendor/php-webdriver/webdriver/lib/Firefox/FirefoxProfile.phpх0vendor/softcreatr/jsonpath/src/JSONPathToken.phpXXtVG4vendor/softcreatr/jsonpath/src/JSONPathException.phpee=~[V:vendor/softcreatr/jsonpath/src/Filters/RecursiveFilter.php==^9vendor/softcreatr/jsonpath/src/Filters/AbstractFilter.php8vendor/softcreatr/jsonpath/src/Filters/IndexesFilter.php6vendor/softcreatr/jsonpath/src/Filters/SliceFilter.phpOk;vendor/softcreatr/jsonpath/src/Filters/QueryMatchFilter.php, , ƖȤ6vendor/softcreatr/jsonpath/src/Filters/IndexFilter.phpII0<vendor/softcreatr/jsonpath/src/Filters/QueryResultFilter.php66y /vendor/softcreatr/jsonpath/src/AccessHelper.phpH H 2]+vendor/softcreatr/jsonpath/src/JSONPath.php rL0vendor/softcreatr/jsonpath/src/JSONPathLexer.php(]6vendor/laravel-zero/phar-updater/src/VersionParser.php Evendor/laravel-zero/phar-updater/src/Strategy/ShaStrategyAbstract.phpuϤ=vendor/laravel-zero/phar-updater/src/Strategy/ShaStrategy.phpSu`@vendor/laravel-zero/phar-updater/src/Strategy/GithubStrategy.php* * %4@vendor/laravel-zero/phar-updater/src/Strategy/Sha256Strategy.phpF Pvendor/laravel-zero/phar-updater/src/Strategy/DirectDownloadStrategyAbstract.php`]7Cvendor/laravel-zero/phar-updater/src/Strategy/StrategyInterface.php--8>@vendor/laravel-zero/phar-updater/src/Strategy/Sha512Strategy.phpؤ0vendor/laravel-zero/phar-updater/src/Updater.php0#0#.:Evendor/laravel-zero/phar-updater/src/Exception/ExceptionInterface.phpXXcu,Fvendor/laravel-zero/phar-updater/src/Exception/FilesystemException.phpnnGvendor/laravel-zero/phar-updater/src/Exception/NoSignatureException.phpoo"}<Cvendor/laravel-zero/phar-updater/src/Exception/RuntimeException.phpЋGvendor/laravel-zero/phar-updater/src/Exception/HttpRequestException.phpooDKvendor/laravel-zero/phar-updater/src/Exception/InvalidArgumentException.phpb"1Gvendor/laravel-zero/phar-updater/src/Exception/JsonParsingException.phpooZ?rawMessage = $message; $this->attributes = $attributes; $this->updateMessage(); } public function getRawMessage(): string { return $this->rawMessage; } public function getStartLine(): int { return $this->attributes['startLine'] ?? -1; } public function getEndLine(): int { return $this->attributes['endLine'] ?? -1; } public function getAttributes(): array { return $this->attributes; } public function setAttributes(array $attributes): void { $this->attributes = $attributes; $this->updateMessage(); } public function setRawMessage(string $message): void { $this->rawMessage = $message; $this->updateMessage(); } public function setStartLine(int $line): void { $this->attributes['startLine'] = $line; $this->updateMessage(); } public function hasColumnInfo(): bool { return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']); } public function getStartColumn(string $code): int { if (!$this->hasColumnInfo()) { throw new \RuntimeException('Error does not have column information'); } return $this->toColumn($code, $this->attributes['startFilePos']); } public function getEndColumn(string $code): int { if (!$this->hasColumnInfo()) { throw new \RuntimeException('Error does not have column information'); } return $this->toColumn($code, $this->attributes['endFilePos']); } public function getMessageWithColumnInfo(string $code): string { return sprintf( '%s from %d:%d to %d:%d', $this->getRawMessage(), $this->getStartLine(), $this->getStartColumn($code), $this->getEndLine(), $this->getEndColumn($code) ); } private function toColumn(string $code, int $pos): int { if ($pos > strlen($code)) { throw new \RuntimeException('Invalid position information'); } $lineStartPos = strrpos($code, "\n", $pos - strlen($code)); if (false === $lineStartPos) { $lineStartPos = -1; } return $pos - $lineStartPos; } protected function updateMessage(): void { $this->message = $this->rawMessage; if (-1 === $this->getStartLine()) { $this->message .= ' on unknown line'; } else { $this->message .= ' on line ' . $this->getStartLine(); } } } args($args) ); } public function namespace($name): Builder\Namespace_ { return new Builder\Namespace_($name); } public function class(string $name): Builder\Class_ { return new Builder\Class_($name); } public function interface(string $name): Builder\Interface_ { return new Builder\Interface_($name); } public function trait(string $name): Builder\Trait_ { return new Builder\Trait_($name); } public function enum(string $name): Builder\Enum_ { return new Builder\Enum_($name); } public function useTrait(...$traits): Builder\TraitUse { return new Builder\TraitUse(...$traits); } public function traitUseAdaptation($trait, $method = null): Builder\TraitUseAdaptation { if ($method === null) { $method = $trait; $trait = null; } return new Builder\TraitUseAdaptation($trait, $method); } public function method(string $name): Builder\Method { return new Builder\Method($name); } public function param(string $name): Builder\Param { return new Builder\Param($name); } public function property(string $name): Builder\Property { return new Builder\Property($name); } public function function(string $name): Builder\Function_ { return new Builder\Function_($name); } public function use($name): Builder\Use_ { return new Builder\Use_($name, Use_::TYPE_NORMAL); } public function useFunction($name): Builder\Use_ { return new Builder\Use_($name, Use_::TYPE_FUNCTION); } public function useConst($name): Builder\Use_ { return new Builder\Use_($name, Use_::TYPE_CONSTANT); } public function classConst($name, $value): Builder\ClassConst { return new Builder\ClassConst($name, $value); } public function enumCase($name): Builder\EnumCase { return new Builder\EnumCase($name); } public function val($value): Expr { return BuilderHelpers::normalizeValue($value); } public function var($name): Expr\Variable { if (!\is_string($name) && !$name instanceof Expr) { throw new \LogicException('Variable name must be string or Expr'); } return new Expr\Variable($name); } public function args(array $args): array { $normalizedArgs = []; foreach ($args as $key => $arg) { if (!($arg instanceof Arg)) { $arg = new Arg(BuilderHelpers::normalizeValue($arg)); } if (\is_string($key)) { $arg->name = BuilderHelpers::normalizeIdentifier($key); } $normalizedArgs[] = $arg; } return $normalizedArgs; } public function funcCall($name, array $args = []): Expr\FuncCall { return new Expr\FuncCall( BuilderHelpers::normalizeNameOrExpr($name), $this->args($args) ); } public function methodCall(Expr $var, $name, array $args = []): Expr\MethodCall { return new Expr\MethodCall( $var, BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args) ); } public function staticCall($class, $name, array $args = []): Expr\StaticCall { return new Expr\StaticCall( BuilderHelpers::normalizeNameOrExpr($class), BuilderHelpers::normalizeIdentifierOrExpr($name), $this->args($args) ); } public function new($class, array $args = []): Expr\New_ { return new Expr\New_( BuilderHelpers::normalizeNameOrExpr($class), $this->args($args) ); } public function constFetch($name): Expr\ConstFetch { return new Expr\ConstFetch(BuilderHelpers::normalizeName($name)); } public function propertyFetch(Expr $var, $name): Expr\PropertyFetch { return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name)); } public function classConstFetch($class, $name): Expr\ClassConstFetch { return new Expr\ClassConstFetch( BuilderHelpers::normalizeNameOrExpr($class), BuilderHelpers::normalizeIdentifierOrExpr($name) ); } public function concat(...$exprs): Concat { $numExprs = count($exprs); if ($numExprs < 2) { throw new \LogicException('Expected at least two expressions'); } $lastConcat = $this->normalizeStringExpr($exprs[0]); for ($i = 1; $i < $numExprs; $i++) { $lastConcat = new Concat($lastConcat, $this->normalizeStringExpr($exprs[$i])); } return $lastConcat; } private function normalizeStringExpr($expr): Expr { if ($expr instanceof Expr) { return $expr; } if (\is_string($expr)) { return new String_($expr); } throw new \LogicException('Expected string or Expr'); } } [-10, 0, 1], BinaryOp\Pow::class => [ 0, 0, 1], Expr\BitwiseNot::class => [ 10, -1, -1], Expr\UnaryPlus::class => [ 10, -1, -1], Expr\UnaryMinus::class => [ 10, -1, -1], Cast\Int_::class => [ 10, -1, -1], Cast\Double::class => [ 10, -1, -1], Cast\String_::class => [ 10, -1, -1], Cast\Array_::class => [ 10, -1, -1], Cast\Object_::class => [ 10, -1, -1], Cast\Bool_::class => [ 10, -1, -1], Cast\Unset_::class => [ 10, -1, -1], Expr\ErrorSuppress::class => [ 10, -1, -1], Expr\Instanceof_::class => [ 20, -1, -1], Expr\BooleanNot::class => [ 30, -1, -1], BinaryOp\Mul::class => [ 40, 41, 40], BinaryOp\Div::class => [ 40, 41, 40], BinaryOp\Mod::class => [ 40, 41, 40], BinaryOp\Plus::class => [ 50, 51, 50], BinaryOp\Minus::class => [ 50, 51, 50], BinaryOp\Concat::class => [ 50, 51, 50], BinaryOp\ShiftLeft::class => [ 60, 61, 60], BinaryOp\ShiftRight::class => [ 60, 61, 60], BinaryOp\Smaller::class => [ 70, 70, 70], BinaryOp\SmallerOrEqual::class => [ 70, 70, 70], BinaryOp\Greater::class => [ 70, 70, 70], BinaryOp\GreaterOrEqual::class => [ 70, 70, 70], BinaryOp\Equal::class => [ 80, 80, 80], BinaryOp\NotEqual::class => [ 80, 80, 80], BinaryOp\Identical::class => [ 80, 80, 80], BinaryOp\NotIdentical::class => [ 80, 80, 80], BinaryOp\Spaceship::class => [ 80, 80, 80], BinaryOp\BitwiseAnd::class => [ 90, 91, 90], BinaryOp\BitwiseXor::class => [100, 101, 100], BinaryOp\BitwiseOr::class => [110, 111, 110], BinaryOp\BooleanAnd::class => [120, 121, 120], BinaryOp\BooleanOr::class => [130, 131, 130], BinaryOp\Coalesce::class => [140, 140, 141], Expr\Ternary::class => [150, -1, -1], Expr\Assign::class => [160, -1, -1], Expr\AssignRef::class => [160, -1, -1], AssignOp\Plus::class => [160, -1, -1], AssignOp\Minus::class => [160, -1, -1], AssignOp\Mul::class => [160, -1, -1], AssignOp\Div::class => [160, -1, -1], AssignOp\Concat::class => [160, -1, -1], AssignOp\Mod::class => [160, -1, -1], AssignOp\BitwiseAnd::class => [160, -1, -1], AssignOp\BitwiseOr::class => [160, -1, -1], AssignOp\BitwiseXor::class => [160, -1, -1], AssignOp\ShiftLeft::class => [160, -1, -1], AssignOp\ShiftRight::class => [160, -1, -1], AssignOp\Pow::class => [160, -1, -1], AssignOp\Coalesce::class => [160, -1, -1], Expr\YieldFrom::class => [170, -1, -1], Expr\Yield_::class => [175, -1, -1], Expr\Print_::class => [180, -1, -1], BinaryOp\LogicalAnd::class => [190, 191, 190], BinaryOp\LogicalXor::class => [200, 201, 200], BinaryOp\LogicalOr::class => [210, 211, 210], Expr\Include_::class => [220, -1, -1], Expr\ArrowFunction::class => [230, -1, -1], Expr\Throw_::class => [240, -1, -1], ]; protected int $indentLevel; protected string $newline; protected string $nl; protected ?string $docStringEndToken; protected bool $canUseSemicolonNamespaces; protected bool $shortArraySyntax; protected PhpVersion $phpVersion; protected ?TokenStream $origTokens; protected Differ $nodeListDiffer; protected array $labelCharMap; protected array $fixupMap; protected array $removalMap; protected array $insertionMap; protected array $listInsertionMap; protected array $emptyListInsertionMap; protected array $modifierChangeMap; public function __construct(array $options = []) { $this->phpVersion = $options['phpVersion'] ?? PhpVersion::fromComponents(7, 4); $this->newline = $options['newline'] ?? "\n"; if ($this->newline !== "\n" && $this->newline != "\r\n") { throw new \LogicException('Option "newline" must be one of "\n" or "\r\n"'); } $this->shortArraySyntax = $options['shortArraySyntax'] ?? $this->phpVersion->supportsShortArraySyntax(); $this->docStringEndToken = $this->phpVersion->supportsFlexibleHeredoc() ? null : '_DOC_STRING_END_' . mt_rand(); } protected function resetState(): void { $this->indentLevel = 0; $this->nl = $this->newline; $this->origTokens = null; } protected function setIndentLevel(int $level): void { $this->indentLevel = $level; $this->nl = $this->newline . \str_repeat(' ', $level); } protected function indent(): void { $this->indentLevel += 4; $this->nl .= ' '; } protected function outdent(): void { assert($this->indentLevel >= 4); $this->indentLevel -= 4; $this->nl = $this->newline . str_repeat(' ', $this->indentLevel); } public function prettyPrint(array $stmts): string { $this->resetState(); $this->preprocessNodes($stmts); return ltrim($this->handleMagicTokens($this->pStmts($stmts, false))); } public function prettyPrintExpr(Expr $node): string { $this->resetState(); return $this->handleMagicTokens($this->p($node)); } public function prettyPrintFile(array $stmts): string { if (!$stmts) { return "newline . $this->newline; } $p = "newline . $this->newline . $this->prettyPrint($stmts); if ($stmts[0] instanceof Stmt\InlineHTML) { $p = preg_replace('/^<\?php\s+\?>\r?\n?/', '', $p); } if ($stmts[count($stmts) - 1] instanceof Stmt\InlineHTML) { $p = preg_replace('/<\?php$/', '', rtrim($p)); } return $p; } protected function preprocessNodes(array $nodes): void { $this->canUseSemicolonNamespaces = true; foreach ($nodes as $node) { if ($node instanceof Stmt\Namespace_ && null === $node->name) { $this->canUseSemicolonNamespaces = false; break; } } } protected function handleMagicTokens(string $str): string { if ($this->docStringEndToken !== null) { $str = str_replace( $this->docStringEndToken . ';' . $this->newline, ';' . $this->newline, $str); $str = str_replace($this->docStringEndToken, $this->newline, $str); } return $str; } protected function pStmts(array $nodes, bool $indent = true): string { if ($indent) { $this->indent(); } $result = ''; foreach ($nodes as $node) { $comments = $node->getComments(); if ($comments) { $result .= $this->nl . $this->pComments($comments); if ($node instanceof Stmt\Nop) { continue; } } $result .= $this->nl . $this->p($node); } if ($indent) { $this->outdent(); } return $result; } protected function pInfixOp( string $class, Node $leftNode, string $operatorString, Node $rightNode, int $precedence, int $lhsPrecedence ): string { list($opPrecedence, $newPrecedenceLHS, $newPrecedenceRHS) = $this->precedenceMap[$class]; $prefix = ''; $suffix = ''; if ($opPrecedence >= $precedence) { $prefix = '('; $suffix = ')'; $lhsPrecedence = self::MAX_PRECEDENCE; } return $prefix . $this->p($leftNode, $newPrecedenceLHS, $newPrecedenceLHS) . $operatorString . $this->p($rightNode, $newPrecedenceRHS, $lhsPrecedence) . $suffix; } protected function pPrefixOp(string $class, string $operatorString, Node $node, int $precedence, int $lhsPrecedence): string { $opPrecedence = $this->precedenceMap[$class][0]; $prefix = ''; $suffix = ''; if ($opPrecedence >= $lhsPrecedence) { $prefix = '('; $suffix = ')'; $lhsPrecedence = self::MAX_PRECEDENCE; } $printedArg = $this->p($node, $opPrecedence, $lhsPrecedence); if (($operatorString === '+' && $printedArg[0] === '+') || ($operatorString === '-' && $printedArg[0] === '-') ) { $printedArg = '(' . $printedArg . ')'; } return $prefix . $operatorString . $printedArg . $suffix; } protected function pPostfixOp(string $class, Node $node, string $operatorString, int $precedence, int $lhsPrecedence): string { $opPrecedence = $this->precedenceMap[$class][0]; $prefix = ''; $suffix = ''; if ($opPrecedence >= $precedence) { $prefix = '('; $suffix = ')'; $lhsPrecedence = self::MAX_PRECEDENCE; } if ($opPrecedence < $lhsPrecedence) { $lhsPrecedence = $opPrecedence; } return $prefix . $this->p($node, $opPrecedence, $lhsPrecedence) . $operatorString . $suffix; } protected function pImplode(array $nodes, string $glue = ''): string { $pNodes = []; foreach ($nodes as $node) { if (null === $node) { $pNodes[] = ''; } else { $pNodes[] = $this->p($node); } } return implode($glue, $pNodes); } protected function pCommaSeparated(array $nodes): string { return $this->pImplode($nodes, ', '); } protected function pCommaSeparatedMultiline(array $nodes, bool $trailingComma): string { $this->indent(); $result = ''; $lastIdx = count($nodes) - 1; foreach ($nodes as $idx => $node) { if ($node !== null) { $comments = $node->getComments(); if ($comments) { $result .= $this->nl . $this->pComments($comments); } $result .= $this->nl . $this->p($node); } else { $result .= $this->nl; } if ($trailingComma || $idx !== $lastIdx) { $result .= ','; } } $this->outdent(); return $result; } protected function pComments(array $comments): string { $formattedComments = []; foreach ($comments as $comment) { $formattedComments[] = str_replace("\n", $this->nl, $comment->getReformattedText()); } return implode($this->nl, $formattedComments); } public function printFormatPreserving(array $stmts, array $origStmts, array $origTokens): string { $this->initializeNodeListDiffer(); $this->initializeLabelCharMap(); $this->initializeFixupMap(); $this->initializeRemovalMap(); $this->initializeInsertionMap(); $this->initializeListInsertionMap(); $this->initializeEmptyListInsertionMap(); $this->initializeModifierChangeMap(); $this->resetState(); $this->origTokens = new TokenStream($origTokens); $this->preprocessNodes($stmts); $pos = 0; $result = $this->pArray($stmts, $origStmts, $pos, 0, 'File', 'stmts', null); if (null !== $result) { $result .= $this->origTokens->getTokenCode($pos, count($origTokens) - 1, 0); } else { $result = "newline . $this->pStmts($stmts, false); } return $this->handleMagicTokens($result); } protected function pFallback(Node $node, int $precedence, int $lhsPrecedence): string { return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence); } protected function p( Node $node, int $precedence = self::MAX_PRECEDENCE, int $lhsPrecedence = self::MAX_PRECEDENCE, bool $parentFormatPreserved = false ): string { if (!$this->origTokens) { return $this->{'p' . $node->getType()}($node, $precedence, $lhsPrecedence); } $origNode = $node->getAttribute('origNode'); if (null === $origNode) { return $this->pFallback($node, $precedence, $lhsPrecedence); } $class = \get_class($node); \assert($class === \get_class($origNode)); $startPos = $origNode->getStartTokenPos(); $endPos = $origNode->getEndTokenPos(); \assert($startPos >= 0 && $endPos >= 0); $fallbackNode = $node; if ($node instanceof Expr\New_ && $node->class instanceof Stmt\Class_) { assert($origNode instanceof Expr\New_); $node = PrintableNewAnonClassNode::fromNewNode($node); $origNode = PrintableNewAnonClassNode::fromNewNode($origNode); $class = PrintableNewAnonClassNode::class; } if ($node instanceof Stmt\InlineHTML && !$parentFormatPreserved) { return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } $indentAdjustment = $this->indentLevel - $this->origTokens->getIndentationBefore($startPos); $type = $node->getType(); $fixupInfo = $this->fixupMap[$class] ?? null; $result = ''; $pos = $startPos; foreach ($node->getSubNodeNames() as $subNodeName) { $subNode = $node->$subNodeName; $origSubNode = $origNode->$subNodeName; if ((!$subNode instanceof Node && $subNode !== null) || (!$origSubNode instanceof Node && $origSubNode !== null) ) { if ($subNode === $origSubNode) { continue; } if (is_array($subNode) && is_array($origSubNode)) { $listResult = $this->pArray( $subNode, $origSubNode, $pos, $indentAdjustment, $class, $subNodeName, $fixupInfo[$subNodeName] ?? null ); if (null === $listResult) { return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } $result .= $listResult; continue; } $key = $class . '->' . $subNodeName; if (!isset($this->modifierChangeMap[$key])) { return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } [$printFn, $findToken] = $this->modifierChangeMap[$key]; $result .= $this->$printFn($subNode); $pos = $this->origTokens->findRight($pos, $findToken); continue; } $extraLeft = ''; $extraRight = ''; if ($origSubNode !== null) { $subStartPos = $origSubNode->getStartTokenPos(); $subEndPos = $origSubNode->getEndTokenPos(); \assert($subStartPos >= 0 && $subEndPos >= 0); } else { if ($subNode === null) { continue; } $key = $type . '->' . $subNodeName; if (!isset($this->insertionMap[$key])) { return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } list($findToken, $beforeToken, $extraLeft, $extraRight) = $this->insertionMap[$key]; if (null !== $findToken) { $subStartPos = $this->origTokens->findRight($pos, $findToken) + (int) !$beforeToken; } else { $subStartPos = $pos; } if (null === $extraLeft && null !== $extraRight) { $subStartPos = $this->origTokens->skipRightWhitespace($subStartPos); } $subEndPos = $subStartPos - 1; } if (null === $subNode) { $key = $type . '->' . $subNodeName; if (!isset($this->removalMap[$key])) { return $this->pFallback($fallbackNode, $precedence, $lhsPrecedence); } $removalInfo = $this->removalMap[$key]; if (isset($removalInfo['left'])) { $subStartPos = $this->origTokens->skipLeft($subStartPos - 1, $removalInfo['left']) + 1; } if (isset($removalInfo['right'])) { $subEndPos = $this->origTokens->skipRight($subEndPos + 1, $removalInfo['right']) - 1; } } $result .= $this->origTokens->getTokenCode($pos, $subStartPos, $indentAdjustment); if (null !== $subNode) { $result .= $extraLeft; $origIndentLevel = $this->indentLevel; $this->setIndentLevel($this->origTokens->getIndentationBefore($subStartPos) + $indentAdjustment); if (isset($fixupInfo[$subNodeName]) && $subNode->getAttribute('origNode') !== $origSubNode ) { $fixup = $fixupInfo[$subNodeName]; $res = $this->pFixup($fixup, $subNode, $class, $subStartPos, $subEndPos); } else { $res = $this->p($subNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true); } $this->safeAppend($result, $res); $this->setIndentLevel($origIndentLevel); $result .= $extraRight; } $pos = $subEndPos + 1; } $result .= $this->origTokens->getTokenCode($pos, $endPos + 1, $indentAdjustment); return $result; } protected function pArray( array $nodes, array $origNodes, int &$pos, int $indentAdjustment, string $parentNodeClass, string $subNodeName, ?int $fixup ): ?string { $diff = $this->nodeListDiffer->diffWithReplacements($origNodes, $nodes); $mapKey = $parentNodeClass . '->' . $subNodeName; $insertStr = $this->listInsertionMap[$mapKey] ?? null; $isStmtList = $subNodeName === 'stmts'; $beforeFirstKeepOrReplace = true; $skipRemovedNode = false; $delayedAdd = []; $lastElemIndentLevel = $this->indentLevel; $insertNewline = false; if ($insertStr === "\n") { $insertStr = ''; $insertNewline = true; } if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) { $startPos = $origNodes[0]->getStartTokenPos(); $endPos = $origNodes[0]->getEndTokenPos(); \assert($startPos >= 0 && $endPos >= 0); if (!$this->origTokens->haveBraces($startPos, $endPos)) { return null; } } $result = ''; foreach ($diff as $i => $diffElem) { $diffType = $diffElem->type; $arrItem = $diffElem->new; $origArrItem = $diffElem->old; if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) { $beforeFirstKeepOrReplace = false; if ($origArrItem === null || $arrItem === null) { if ($origArrItem === $arrItem) { continue; } return null; } if (!$arrItem instanceof Node || !$origArrItem instanceof Node) { return null; } $itemStartPos = $origArrItem->getStartTokenPos(); $itemEndPos = $origArrItem->getEndTokenPos(); \assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos); $origIndentLevel = $this->indentLevel; $lastElemIndentLevel = $this->origTokens->getIndentationBefore($itemStartPos) + $indentAdjustment; $this->setIndentLevel($lastElemIndentLevel); $comments = $arrItem->getComments(); $origComments = $origArrItem->getComments(); $commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos; \assert($commentStartPos >= 0); if ($commentStartPos < $pos) { $commentStartPos = $itemStartPos; } if ($skipRemovedNode) { if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) { $this->setIndentLevel($origIndentLevel); return null; } } else { $result .= $this->origTokens->getTokenCode( $pos, $commentStartPos, $indentAdjustment); } if (!empty($delayedAdd)) { foreach ($delayedAdd as $delayedAddNode) { if ($insertNewline) { $delayedAddComments = $delayedAddNode->getComments(); if ($delayedAddComments) { $result .= $this->pComments($delayedAddComments) . $this->nl; } } $this->safeAppend($result, $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true)); if ($insertNewline) { $result .= $insertStr . $this->nl; } else { $result .= $insertStr; } } $delayedAdd = []; } if ($comments !== $origComments) { if ($comments) { $result .= $this->pComments($comments) . $this->nl; } } else { $result .= $this->origTokens->getTokenCode( $commentStartPos, $itemStartPos, $indentAdjustment); } $skipRemovedNode = false; } elseif ($diffType === DiffElem::TYPE_ADD) { if (null === $insertStr) { return null; } if (!$arrItem instanceof Node) { return null; } if ($insertStr === ', ' && ($this->isMultiline($origNodes) || $arrItem->getComments() || $parentNodeClass === Expr\Match_::class) ) { $insertStr = ','; $insertNewline = true; } if ($beforeFirstKeepOrReplace) { $delayedAdd[] = $arrItem; continue; } $itemStartPos = $pos; $itemEndPos = $pos - 1; $origIndentLevel = $this->indentLevel; $this->setIndentLevel($lastElemIndentLevel); if ($insertNewline) { $result .= $insertStr . $this->nl; $comments = $arrItem->getComments(); if ($comments) { $result .= $this->pComments($comments) . $this->nl; } } else { $result .= $insertStr; } } elseif ($diffType === DiffElem::TYPE_REMOVE) { if (!$origArrItem instanceof Node) { return null; } $itemStartPos = $origArrItem->getStartTokenPos(); $itemEndPos = $origArrItem->getEndTokenPos(); \assert($itemStartPos >= 0 && $itemEndPos >= 0); $origComments = $origArrItem->getComments(); if ($origComments) { $itemStartPos = $origComments[0]->getStartTokenPos(); } if ($i === 0) { $result .= $this->origTokens->getTokenCode( $pos, $itemStartPos, $indentAdjustment); $skipRemovedNode = true; } else { if ($isStmtList && $this->origTokens->haveTagInRange($pos, $itemStartPos)) { return null; } } $pos = $itemEndPos + 1; continue; } else { throw new \Exception("Shouldn't happen"); } if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) { $res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos); } else { $res = $this->p($arrItem, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true); } $this->safeAppend($result, $res); $this->setIndentLevel($origIndentLevel); $pos = $itemEndPos + 1; } if ($skipRemovedNode) { return null; } if (!empty($delayedAdd)) { if (!isset($this->emptyListInsertionMap[$mapKey])) { return null; } list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey]; if (null !== $findToken) { $insertPos = $this->origTokens->findRight($pos, $findToken) + 1; $result .= $this->origTokens->getTokenCode($pos, $insertPos, $indentAdjustment); $pos = $insertPos; } $first = true; $result .= $extraLeft; foreach ($delayedAdd as $delayedAddNode) { if (!$first) { $result .= $insertStr; if ($insertNewline) { $result .= $this->nl; } } $result .= $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true); $first = false; } $result .= $extraRight === "\n" ? $this->nl : $extraRight; } return $result; } protected function pFixup(int $fixup, Node $subNode, ?string $parentClass, int $subStartPos, int $subEndPos): string { switch ($fixup) { case self::FIXUP_PREC_LEFT: if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { $precedence = $this->precedenceMap[$parentClass][1]; return $this->p($subNode, $precedence, $precedence); } break; case self::FIXUP_PREC_RIGHT: if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { $precedence = $this->precedenceMap[$parentClass][2]; return $this->p($subNode, $precedence, $precedence); } break; case self::FIXUP_PREC_UNARY: if (!$this->origTokens->haveParens($subStartPos, $subEndPos)) { $precedence = $this->precedenceMap[$parentClass][0]; return $this->p($subNode, $precedence, $precedence); } break; case self::FIXUP_CALL_LHS: if ($this->callLhsRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos) ) { return '(' . $this->p($subNode) . ')'; } break; case self::FIXUP_DEREF_LHS: if ($this->dereferenceLhsRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos) ) { return '(' . $this->p($subNode) . ')'; } break; case self::FIXUP_STATIC_DEREF_LHS: if ($this->staticDereferenceLhsRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos) ) { return '(' . $this->p($subNode) . ')'; } break; case self::FIXUP_NEW: if ($this->newOperandRequiresParens($subNode) && !$this->origTokens->haveParens($subStartPos, $subEndPos)) { return '(' . $this->p($subNode) . ')'; } break; case self::FIXUP_BRACED_NAME: case self::FIXUP_VAR_BRACED_NAME: if ($subNode instanceof Expr && !$this->origTokens->haveBraces($subStartPos, $subEndPos) ) { return ($fixup === self::FIXUP_VAR_BRACED_NAME ? '$' : '') . '{' . $this->p($subNode) . '}'; } break; case self::FIXUP_ENCAPSED: if (!$subNode instanceof Node\InterpolatedStringPart && !$this->origTokens->haveBraces($subStartPos, $subEndPos) ) { return '{' . $this->p($subNode) . '}'; } break; default: throw new \Exception('Cannot happen'); } return $this->p($subNode); } protected function safeAppend(string &$str, string $append): void { if ($str === "") { $str = $append; return; } if ($append === "") { return; } if (!$this->labelCharMap[$append[0]] || !$this->labelCharMap[$str[\strlen($str) - 1]]) { $str .= $append; } else { $str .= " " . $append; } } protected function callLhsRequiresParens(Node $node): bool { return !($node instanceof Node\Name || $node instanceof Expr\Variable || $node instanceof Expr\ArrayDimFetch || $node instanceof Expr\FuncCall || $node instanceof Expr\MethodCall || $node instanceof Expr\NullsafeMethodCall || $node instanceof Expr\StaticCall || $node instanceof Expr\Array_); } protected function dereferenceLhsRequiresParens(Node $node): bool { return $this->staticDereferenceLhsRequiresParens($node) && !$node instanceof Expr\ConstFetch; } protected function staticDereferenceLhsRequiresParens(Node $node): bool { return !($node instanceof Expr\Variable || $node instanceof Node\Name || $node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch || $node instanceof Expr\NullsafePropertyFetch || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\FuncCall || $node instanceof Expr\MethodCall || $node instanceof Expr\NullsafeMethodCall || $node instanceof Expr\StaticCall || $node instanceof Expr\Array_ || $node instanceof Scalar\String_ || $node instanceof Expr\ClassConstFetch); } protected function newOperandRequiresParens(Node $node): bool { if ($node instanceof Node\Name || $node instanceof Expr\Variable) { return false; } if ($node instanceof Expr\ArrayDimFetch || $node instanceof Expr\PropertyFetch || $node instanceof Expr\NullsafePropertyFetch ) { return $this->newOperandRequiresParens($node->var); } if ($node instanceof Expr\StaticPropertyFetch) { return $this->newOperandRequiresParens($node->class); } return true; } protected function pModifiers(int $modifiers): string { return ($modifiers & Modifiers::FINAL ? 'final ' : '') . ($modifiers & Modifiers::ABSTRACT ? 'abstract ' : '') . ($modifiers & Modifiers::PUBLIC ? 'public ' : '') . ($modifiers & Modifiers::PROTECTED ? 'protected ' : '') . ($modifiers & Modifiers::PRIVATE ? 'private ' : '') . ($modifiers & Modifiers::STATIC ? 'static ' : '') . ($modifiers & Modifiers::READONLY ? 'readonly ' : ''); } protected function pStatic(bool $static): string { return $static ? 'static ' : ''; } protected function isMultiline(array $nodes): bool { if (\count($nodes) < 2) { return false; } $pos = -1; foreach ($nodes as $node) { if (null === $node) { continue; } $endPos = $node->getEndTokenPos() + 1; if ($pos >= 0) { $text = $this->origTokens->getTokenCode($pos, $endPos, 0); if (false === strpos($text, "\n")) { return false; } } $pos = $endPos; } return true; } protected function initializeLabelCharMap(): void { if (isset($this->labelCharMap)) { return; } $this->labelCharMap = []; for ($i = 0; $i < 256; $i++) { $chr = chr($i); $this->labelCharMap[$chr] = $i >= 0x80 || ctype_alnum($chr); } if ($this->phpVersion->allowsDelInIdentifiers()) { $this->labelCharMap["\x7f"] = true; } } protected function initializeNodeListDiffer(): void { if (isset($this->nodeListDiffer)) { return; } $this->nodeListDiffer = new Internal\Differ(function ($a, $b) { if ($a instanceof Node && $b instanceof Node) { return $a === $b->getAttribute('origNode'); } return $a === null && $b === null; }); } protected function initializeFixupMap(): void { if (isset($this->fixupMap)) { return; } $this->fixupMap = [ Expr\Instanceof_::class => [ 'expr' => self::FIXUP_PREC_UNARY, 'class' => self::FIXUP_NEW, ], Expr\Ternary::class => [ 'cond' => self::FIXUP_PREC_LEFT, 'else' => self::FIXUP_PREC_RIGHT, ], Expr\Yield_::class => ['value' => self::FIXUP_PREC_UNARY], Expr\FuncCall::class => ['name' => self::FIXUP_CALL_LHS], Expr\StaticCall::class => ['class' => self::FIXUP_STATIC_DEREF_LHS], Expr\ArrayDimFetch::class => ['var' => self::FIXUP_DEREF_LHS], Expr\ClassConstFetch::class => [ 'class' => self::FIXUP_STATIC_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME, ], Expr\New_::class => ['class' => self::FIXUP_NEW], Expr\MethodCall::class => [ 'var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME, ], Expr\NullsafeMethodCall::class => [ 'var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME, ], Expr\StaticPropertyFetch::class => [ 'class' => self::FIXUP_STATIC_DEREF_LHS, 'name' => self::FIXUP_VAR_BRACED_NAME, ], Expr\PropertyFetch::class => [ 'var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME, ], Expr\NullsafePropertyFetch::class => [ 'var' => self::FIXUP_DEREF_LHS, 'name' => self::FIXUP_BRACED_NAME, ], Scalar\InterpolatedString::class => [ 'parts' => self::FIXUP_ENCAPSED, ], ]; $binaryOps = [ BinaryOp\Pow::class, BinaryOp\Mul::class, BinaryOp\Div::class, BinaryOp\Mod::class, BinaryOp\Plus::class, BinaryOp\Minus::class, BinaryOp\Concat::class, BinaryOp\ShiftLeft::class, BinaryOp\ShiftRight::class, BinaryOp\Smaller::class, BinaryOp\SmallerOrEqual::class, BinaryOp\Greater::class, BinaryOp\GreaterOrEqual::class, BinaryOp\Equal::class, BinaryOp\NotEqual::class, BinaryOp\Identical::class, BinaryOp\NotIdentical::class, BinaryOp\Spaceship::class, BinaryOp\BitwiseAnd::class, BinaryOp\BitwiseXor::class, BinaryOp\BitwiseOr::class, BinaryOp\BooleanAnd::class, BinaryOp\BooleanOr::class, BinaryOp\Coalesce::class, BinaryOp\LogicalAnd::class, BinaryOp\LogicalXor::class, BinaryOp\LogicalOr::class, ]; foreach ($binaryOps as $binaryOp) { $this->fixupMap[$binaryOp] = [ 'left' => self::FIXUP_PREC_LEFT, 'right' => self::FIXUP_PREC_RIGHT ]; } $prefixOps = [ Expr\Clone_::class, Expr\BitwiseNot::class, Expr\BooleanNot::class, Expr\UnaryPlus::class, Expr\UnaryMinus::class, Cast\Int_::class, Cast\Double::class, Cast\String_::class, Cast\Array_::class, Cast\Object_::class, Cast\Bool_::class, Cast\Unset_::class, Expr\ErrorSuppress::class, Expr\YieldFrom::class, Expr\Print_::class, Expr\Include_::class, Expr\Assign::class, Expr\AssignRef::class, AssignOp\Plus::class, AssignOp\Minus::class, AssignOp\Mul::class, AssignOp\Div::class, AssignOp\Concat::class, AssignOp\Mod::class, AssignOp\BitwiseAnd::class, AssignOp\BitwiseOr::class, AssignOp\BitwiseXor::class, AssignOp\ShiftLeft::class, AssignOp\ShiftRight::class, AssignOp\Pow::class, AssignOp\Coalesce::class, Expr\ArrowFunction::class, Expr\Throw_::class, ]; foreach ($prefixOps as $prefixOp) { $this->fixupMap[$prefixOp] = ['expr' => self::FIXUP_PREC_UNARY]; } } protected function initializeRemovalMap(): void { if (isset($this->removalMap)) { return; } $stripBoth = ['left' => \T_WHITESPACE, 'right' => \T_WHITESPACE]; $stripLeft = ['left' => \T_WHITESPACE]; $stripRight = ['right' => \T_WHITESPACE]; $stripDoubleArrow = ['right' => \T_DOUBLE_ARROW]; $stripColon = ['left' => ':']; $stripEquals = ['left' => '=']; $this->removalMap = [ 'Expr_ArrayDimFetch->dim' => $stripBoth, 'ArrayItem->key' => $stripDoubleArrow, 'Expr_ArrowFunction->returnType' => $stripColon, 'Expr_Closure->returnType' => $stripColon, 'Expr_Exit->expr' => $stripBoth, 'Expr_Ternary->if' => $stripBoth, 'Expr_Yield->key' => $stripDoubleArrow, 'Expr_Yield->value' => $stripBoth, 'Param->type' => $stripRight, 'Param->default' => $stripEquals, 'Stmt_Break->num' => $stripBoth, 'Stmt_Catch->var' => $stripLeft, 'Stmt_ClassConst->type' => $stripRight, 'Stmt_ClassMethod->returnType' => $stripColon, 'Stmt_Class->extends' => ['left' => \T_EXTENDS], 'Stmt_Enum->scalarType' => $stripColon, 'Stmt_EnumCase->expr' => $stripEquals, 'Expr_PrintableNewAnonClass->extends' => ['left' => \T_EXTENDS], 'Stmt_Continue->num' => $stripBoth, 'Stmt_Foreach->keyVar' => $stripDoubleArrow, 'Stmt_Function->returnType' => $stripColon, 'Stmt_If->else' => $stripLeft, 'Stmt_Namespace->name' => $stripLeft, 'Stmt_Property->type' => $stripRight, 'PropertyItem->default' => $stripEquals, 'Stmt_Return->expr' => $stripBoth, 'Stmt_StaticVar->default' => $stripEquals, 'Stmt_TraitUseAdaptation_Alias->newName' => $stripLeft, 'Stmt_TryCatch->finally' => $stripLeft, ]; } protected function initializeInsertionMap(): void { if (isset($this->insertionMap)) { return; } $this->insertionMap = [ 'Expr_ArrayDimFetch->dim' => ['[', false, null, null], 'ArrayItem->key' => [null, false, null, ' => '], 'Expr_ArrowFunction->returnType' => [')', false, ': ', null], 'Expr_Closure->returnType' => [')', false, ': ', null], 'Expr_Ternary->if' => ['?', false, ' ', ' '], 'Expr_Yield->key' => [\T_YIELD, false, null, ' => '], 'Expr_Yield->value' => [\T_YIELD, false, ' ', null], 'Param->type' => [null, false, null, ' '], 'Param->default' => [null, false, ' = ', null], 'Stmt_Break->num' => [\T_BREAK, false, ' ', null], 'Stmt_Catch->var' => [null, false, ' ', null], 'Stmt_ClassMethod->returnType' => [')', false, ': ', null], 'Stmt_ClassConst->type' => [\T_CONST, false, ' ', null], 'Stmt_Class->extends' => [null, false, ' extends ', null], 'Stmt_Enum->scalarType' => [null, false, ' : ', null], 'Stmt_EnumCase->expr' => [null, false, ' = ', null], 'Expr_PrintableNewAnonClass->extends' => [null, false, ' extends ', null], 'Stmt_Continue->num' => [\T_CONTINUE, false, ' ', null], 'Stmt_Foreach->keyVar' => [\T_AS, false, null, ' => '], 'Stmt_Function->returnType' => [')', false, ': ', null], 'Stmt_If->else' => [null, false, ' ', null], 'Stmt_Namespace->name' => [\T_NAMESPACE, false, ' ', null], 'Stmt_Property->type' => [\T_VARIABLE, true, null, ' '], 'PropertyItem->default' => [null, false, ' = ', null], 'Stmt_Return->expr' => [\T_RETURN, false, ' ', null], 'Stmt_StaticVar->default' => [null, false, ' = ', null], 'Stmt_TryCatch->finally' => [null, false, ' ', null], ]; } protected function initializeListInsertionMap(): void { if (isset($this->listInsertionMap)) { return; } $this->listInsertionMap = [ Stmt\Catch_::class . '->types' => '|', UnionType::class . '->types' => '|', IntersectionType::class . '->types' => '&', Stmt\If_::class . '->elseifs' => ' ', Stmt\TryCatch::class . '->catches' => ' ', Expr\Array_::class . '->items' => ', ', Expr\ArrowFunction::class . '->params' => ', ', Expr\Closure::class . '->params' => ', ', Expr\Closure::class . '->uses' => ', ', Expr\FuncCall::class . '->args' => ', ', Expr\Isset_::class . '->vars' => ', ', Expr\List_::class . '->items' => ', ', Expr\MethodCall::class . '->args' => ', ', Expr\NullsafeMethodCall::class . '->args' => ', ', Expr\New_::class . '->args' => ', ', PrintableNewAnonClassNode::class . '->args' => ', ', Expr\StaticCall::class . '->args' => ', ', Stmt\ClassConst::class . '->consts' => ', ', Stmt\ClassMethod::class . '->params' => ', ', Stmt\Class_::class . '->implements' => ', ', Stmt\Enum_::class . '->implements' => ', ', PrintableNewAnonClassNode::class . '->implements' => ', ', Stmt\Const_::class . '->consts' => ', ', Stmt\Declare_::class . '->declares' => ', ', Stmt\Echo_::class . '->exprs' => ', ', Stmt\For_::class . '->init' => ', ', Stmt\For_::class . '->cond' => ', ', Stmt\For_::class . '->loop' => ', ', Stmt\Function_::class . '->params' => ', ', Stmt\Global_::class . '->vars' => ', ', Stmt\GroupUse::class . '->uses' => ', ', Stmt\Interface_::class . '->extends' => ', ', Expr\Match_::class . '->arms' => ', ', Stmt\Property::class . '->props' => ', ', Stmt\StaticVar::class . '->vars' => ', ', Stmt\TraitUse::class . '->traits' => ', ', Stmt\TraitUseAdaptation\Precedence::class . '->insteadof' => ', ', Stmt\Unset_::class . '->vars' => ', ', Stmt\UseUse::class . '->uses' => ', ', MatchArm::class . '->conds' => ', ', AttributeGroup::class . '->attrs' => ', ', Expr\Closure::class . '->stmts' => "\n", Stmt\Case_::class . '->stmts' => "\n", Stmt\Catch_::class . '->stmts' => "\n", Stmt\Class_::class . '->stmts' => "\n", Stmt\Enum_::class . '->stmts' => "\n", PrintableNewAnonClassNode::class . '->stmts' => "\n", Stmt\Interface_::class . '->stmts' => "\n", Stmt\Trait_::class . '->stmts' => "\n", Stmt\ClassMethod::class . '->stmts' => "\n", Stmt\Declare_::class . '->stmts' => "\n", Stmt\Do_::class . '->stmts' => "\n", Stmt\ElseIf_::class . '->stmts' => "\n", Stmt\Else_::class . '->stmts' => "\n", Stmt\Finally_::class . '->stmts' => "\n", Stmt\Foreach_::class . '->stmts' => "\n", Stmt\For_::class . '->stmts' => "\n", Stmt\Function_::class . '->stmts' => "\n", Stmt\If_::class . '->stmts' => "\n", Stmt\Namespace_::class . '->stmts' => "\n", Stmt\Block::class . '->stmts' => "\n", Stmt\Class_::class . '->attrGroups' => "\n", Stmt\Enum_::class . '->attrGroups' => "\n", Stmt\EnumCase::class . '->attrGroups' => "\n", Stmt\Interface_::class . '->attrGroups' => "\n", Stmt\Trait_::class . '->attrGroups' => "\n", Stmt\Function_::class . '->attrGroups' => "\n", Stmt\ClassMethod::class . '->attrGroups' => "\n", Stmt\ClassConst::class . '->attrGroups' => "\n", Stmt\Property::class . '->attrGroups' => "\n", PrintableNewAnonClassNode::class . '->attrGroups' => ' ', Expr\Closure::class . '->attrGroups' => ' ', Expr\ArrowFunction::class . '->attrGroups' => ' ', Param::class . '->attrGroups' => ' ', Stmt\Switch_::class . '->cases' => "\n", Stmt\TraitUse::class . '->adaptations' => "\n", Stmt\TryCatch::class . '->stmts' => "\n", Stmt\While_::class . '->stmts' => "\n", 'File->stmts' => "\n", ]; } protected function initializeEmptyListInsertionMap(): void { if (isset($this->emptyListInsertionMap)) { return; } $this->emptyListInsertionMap = [ Expr\ArrowFunction::class . '->params' => ['(', '', ''], Expr\Closure::class . '->uses' => [')', ' use (', ')'], Expr\Closure::class . '->params' => ['(', '', ''], Expr\FuncCall::class . '->args' => ['(', '', ''], Expr\MethodCall::class . '->args' => ['(', '', ''], Expr\NullsafeMethodCall::class . '->args' => ['(', '', ''], Expr\New_::class . '->args' => ['(', '', ''], PrintableNewAnonClassNode::class . '->args' => ['(', '', ''], PrintableNewAnonClassNode::class . '->implements' => [null, ' implements ', ''], Expr\StaticCall::class . '->args' => ['(', '', ''], Stmt\Class_::class . '->implements' => [null, ' implements ', ''], Stmt\Enum_::class . '->implements' => [null, ' implements ', ''], Stmt\ClassMethod::class . '->params' => ['(', '', ''], Stmt\Interface_::class . '->extends' => [null, ' extends ', ''], Stmt\Function_::class . '->params' => ['(', '', ''], Stmt\Interface_::class . '->attrGroups' => [null, '', "\n"], Stmt\Class_::class . '->attrGroups' => [null, '', "\n"], Stmt\ClassConst::class . '->attrGroups' => [null, '', "\n"], Stmt\ClassMethod::class . '->attrGroups' => [null, '', "\n"], Stmt\Function_::class . '->attrGroups' => [null, '', "\n"], Stmt\Property::class . '->attrGroups' => [null, '', "\n"], Stmt\Trait_::class . '->attrGroups' => [null, '', "\n"], Expr\ArrowFunction::class . '->attrGroups' => [null, '', ' '], Expr\Closure::class . '->attrGroups' => [null, '', ' '], PrintableNewAnonClassNode::class . '->attrGroups' => [\T_NEW, ' ', ''], ]; } protected function initializeModifierChangeMap(): void { if (isset($this->modifierChangeMap)) { return; } $this->modifierChangeMap = [ Stmt\ClassConst::class . '->flags' => ['pModifiers', \T_CONST], Stmt\ClassMethod::class . '->flags' => ['pModifiers', \T_FUNCTION], Stmt\Class_::class . '->flags' => ['pModifiers', \T_CLASS], Stmt\Property::class . '->flags' => ['pModifiers', \T_VARIABLE], PrintableNewAnonClassNode::class . '->flags' => ['pModifiers', \T_CLASS], Param::class . '->flags' => ['pModifiers', \T_VARIABLE], Expr\Closure::class . '->static' => ['pStatic', \T_FUNCTION], Expr\ArrowFunction::class . '->static' => ['pStatic', \T_FN], ]; } } pAttrGroups($node->attrGroups, true) . $this->pModifiers($node->flags) . ($node->type ? $this->p($node->type) . ' ' : '') . ($node->byRef ? '&' : '') . ($node->variadic ? '...' : '') . $this->p($node->var) . ($node->default ? ' = ' . $this->p($node->default) : ''); } protected function pArg(Node\Arg $node): string { return ($node->name ? $node->name->toString() . ': ' : '') . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); } protected function pVariadicPlaceholder(Node\VariadicPlaceholder $node): string { return '...'; } protected function pConst(Node\Const_ $node): string { return $node->name . ' = ' . $this->p($node->value); } protected function pNullableType(Node\NullableType $node): string { return '?' . $this->p($node->type); } protected function pUnionType(Node\UnionType $node): string { $types = []; foreach ($node->types as $typeNode) { if ($typeNode instanceof Node\IntersectionType) { $types[] = '('. $this->p($typeNode) . ')'; continue; } $types[] = $this->p($typeNode); } return implode('|', $types); } protected function pIntersectionType(Node\IntersectionType $node): string { return $this->pImplode($node->types, '&'); } protected function pIdentifier(Node\Identifier $node): string { return $node->name; } protected function pVarLikeIdentifier(Node\VarLikeIdentifier $node): string { return '$' . $node->name; } protected function pAttribute(Node\Attribute $node): string { return $this->p($node->name) . ($node->args ? '(' . $this->pCommaSeparated($node->args) . ')' : ''); } protected function pAttributeGroup(Node\AttributeGroup $node): string { return '#[' . $this->pCommaSeparated($node->attrs) . ']'; } protected function pName(Name $node): string { return $node->name; } protected function pName_FullyQualified(Name\FullyQualified $node): string { return '\\' . $node->name; } protected function pName_Relative(Name\Relative $node): string { return 'namespace\\' . $node->name; } protected function pScalar_MagicConst_Class(MagicConst\Class_ $node): string { return '__CLASS__'; } protected function pScalar_MagicConst_Dir(MagicConst\Dir $node): string { return '__DIR__'; } protected function pScalar_MagicConst_File(MagicConst\File $node): string { return '__FILE__'; } protected function pScalar_MagicConst_Function(MagicConst\Function_ $node): string { return '__FUNCTION__'; } protected function pScalar_MagicConst_Line(MagicConst\Line $node): string { return '__LINE__'; } protected function pScalar_MagicConst_Method(MagicConst\Method $node): string { return '__METHOD__'; } protected function pScalar_MagicConst_Namespace(MagicConst\Namespace_ $node): string { return '__NAMESPACE__'; } protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node): string { return '__TRAIT__'; } private function indentString(string $str): string { return str_replace("\n", $this->nl, $str); } protected function pScalar_String(Scalar\String_ $node): string { $kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED); switch ($kind) { case Scalar\String_::KIND_NOWDOC: $label = $node->getAttribute('docLabel'); if ($label && !$this->containsEndLabel($node->value, $label)) { $shouldIdent = $this->phpVersion->supportsFlexibleHeredoc(); $nl = $shouldIdent ? $this->nl : $this->newline; if ($node->value === '') { return "<<<'$label'$nl$label{$this->docStringEndToken}"; } if ($node->value[strlen($node->value) - 1] !== "\r") { $value = $shouldIdent ? $this->indentString($node->value) : $node->value; return "<<<'$label'$nl$value$nl$label{$this->docStringEndToken}"; } } case Scalar\String_::KIND_SINGLE_QUOTED: return $this->pSingleQuotedString($node->value); case Scalar\String_::KIND_HEREDOC: $label = $node->getAttribute('docLabel'); $escaped = $this->escapeString($node->value, null); if ($label && !$this->containsEndLabel($escaped, $label)) { $nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline; if ($escaped === '') { return "<<<$label$nl$label{$this->docStringEndToken}"; } return "<<<$label$nl$escaped$nl$label{$this->docStringEndToken}"; } case Scalar\String_::KIND_DOUBLE_QUOTED: return '"' . $this->escapeString($node->value, '"') . '"'; } throw new \Exception('Invalid string kind'); } protected function pScalar_InterpolatedString(Scalar\InterpolatedString $node): string { if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) { $label = $node->getAttribute('docLabel'); if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) { $nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline; if (count($node->parts) === 1 && $node->parts[0] instanceof Node\InterpolatedStringPart && $node->parts[0]->value === '' ) { return "<<<$label$nl$label{$this->docStringEndToken}"; } return "<<<$label$nl" . $this->pEncapsList($node->parts, null) . "$nl$label{$this->docStringEndToken}"; } } return '"' . $this->pEncapsList($node->parts, '"') . '"'; } protected function pScalar_Int(Scalar\Int_ $node): string { if ($node->value === -\PHP_INT_MAX - 1) { return '(-' . \PHP_INT_MAX . '-1)'; } $kind = $node->getAttribute('kind', Scalar\Int_::KIND_DEC); if (Scalar\Int_::KIND_DEC === $kind) { return (string) $node->value; } if ($node->value < 0) { $sign = '-'; $str = (string) -$node->value; } else { $sign = ''; $str = (string) $node->value; } switch ($kind) { case Scalar\Int_::KIND_BIN: return $sign . '0b' . base_convert($str, 10, 2); case Scalar\Int_::KIND_OCT: return $sign . '0' . base_convert($str, 10, 8); case Scalar\Int_::KIND_HEX: return $sign . '0x' . base_convert($str, 10, 16); } throw new \Exception('Invalid number kind'); } protected function pScalar_Float(Scalar\Float_ $node): string { if (!is_finite($node->value)) { if ($node->value === \INF) { return '1.0E+1000'; } if ($node->value === -\INF) { return '-1.0E+1000'; } else { return '\NAN'; } } $stringValue = sprintf('%.16G', $node->value); if ($node->value !== (float) $stringValue) { $stringValue = sprintf('%.17G', $node->value); } $stringValue = str_replace(',', '.', $stringValue); return preg_match('/^-?[0-9]+$/', $stringValue) ? $stringValue . '.0' : $stringValue; } protected function pExpr_Assign(Expr\Assign $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\Assign::class, $this->p($node->var) . ' = ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignRef(Expr\AssignRef $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\AssignRef::class, $this->p($node->var) . ' =& ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Plus(AssignOp\Plus $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Plus::class, $this->p($node->var) . ' += ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Minus(AssignOp\Minus $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Minus::class, $this->p($node->var) . ' -= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Mul(AssignOp\Mul $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Mul::class, $this->p($node->var) . ' *= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Div(AssignOp\Div $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Div::class, $this->p($node->var) . ' /= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Concat(AssignOp\Concat $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Concat::class, $this->p($node->var) . ' .= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Mod(AssignOp\Mod $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Mod::class, $this->p($node->var) . ' %= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_BitwiseAnd(AssignOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\BitwiseAnd::class, $this->p($node->var) . ' &= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_BitwiseOr(AssignOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\BitwiseOr::class, $this->p($node->var) . ' |= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_BitwiseXor(AssignOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\BitwiseXor::class, $this->p($node->var) . ' ^= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_ShiftLeft(AssignOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\ShiftLeft::class, $this->p($node->var) . ' <<= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_ShiftRight(AssignOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\ShiftRight::class, $this->p($node->var) . ' >>= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Pow(AssignOp\Pow $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Pow::class, $this->p($node->var) . ' **= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_AssignOp_Coalesce(AssignOp\Coalesce $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(AssignOp\Coalesce::class, $this->p($node->var) . ' ??= ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Plus(BinaryOp\Plus $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Plus::class, $node->left, ' + ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Minus(BinaryOp\Minus $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Minus::class, $node->left, ' - ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Mul(BinaryOp\Mul $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Mul::class, $node->left, ' * ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Div(BinaryOp\Div $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Div::class, $node->left, ' / ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Concat(BinaryOp\Concat $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Concat::class, $node->left, ' . ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Mod(BinaryOp\Mod $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Mod::class, $node->left, ' % ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_BooleanAnd(BinaryOp\BooleanAnd $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\BooleanAnd::class, $node->left, ' && ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_BooleanOr(BinaryOp\BooleanOr $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\BooleanOr::class, $node->left, ' || ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_BitwiseAnd(BinaryOp\BitwiseAnd $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\BitwiseAnd::class, $node->left, ' & ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_BitwiseOr(BinaryOp\BitwiseOr $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\BitwiseOr::class, $node->left, ' | ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_BitwiseXor(BinaryOp\BitwiseXor $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\BitwiseXor::class, $node->left, ' ^ ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_ShiftLeft(BinaryOp\ShiftLeft $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\ShiftLeft::class, $node->left, ' << ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_ShiftRight(BinaryOp\ShiftRight $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\ShiftRight::class, $node->left, ' >> ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Pow(BinaryOp\Pow $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Pow::class, $node->left, ' ** ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_LogicalAnd(BinaryOp\LogicalAnd $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\LogicalAnd::class, $node->left, ' and ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_LogicalOr(BinaryOp\LogicalOr $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\LogicalOr::class, $node->left, ' or ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_LogicalXor(BinaryOp\LogicalXor $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\LogicalXor::class, $node->left, ' xor ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Equal(BinaryOp\Equal $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Equal::class, $node->left, ' == ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_NotEqual(BinaryOp\NotEqual $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\NotEqual::class, $node->left, ' != ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Identical(BinaryOp\Identical $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Identical::class, $node->left, ' === ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_NotIdentical(BinaryOp\NotIdentical $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\NotIdentical::class, $node->left, ' !== ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Spaceship(BinaryOp\Spaceship $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Spaceship::class, $node->left, ' <=> ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Greater(BinaryOp\Greater $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Greater::class, $node->left, ' > ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_GreaterOrEqual(BinaryOp\GreaterOrEqual $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\GreaterOrEqual::class, $node->left, ' >= ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Smaller(BinaryOp\Smaller $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Smaller::class, $node->left, ' < ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_SmallerOrEqual(BinaryOp\SmallerOrEqual $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\SmallerOrEqual::class, $node->left, ' <= ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_BinaryOp_Coalesce(BinaryOp\Coalesce $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(BinaryOp\Coalesce::class, $node->left, ' ?? ', $node->right, $precedence, $lhsPrecedence); } protected function pExpr_Instanceof(Expr\Instanceof_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPostfixOp( Expr\Instanceof_::class, $node->expr, ' instanceof ' . $this->pNewOperand($node->class), $precedence, $lhsPrecedence); } protected function pExpr_BooleanNot(Expr\BooleanNot $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\BooleanNot::class, '!', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_BitwiseNot(Expr\BitwiseNot $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\BitwiseNot::class, '~', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_UnaryMinus(Expr\UnaryMinus $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\UnaryMinus::class, '-', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_UnaryPlus(Expr\UnaryPlus $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\UnaryPlus::class, '+', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_PreInc(Expr\PreInc $node): string { return '++' . $this->p($node->var); } protected function pExpr_PreDec(Expr\PreDec $node): string { return '--' . $this->p($node->var); } protected function pExpr_PostInc(Expr\PostInc $node): string { return $this->p($node->var) . '++'; } protected function pExpr_PostDec(Expr\PostDec $node): string { return $this->p($node->var) . '--'; } protected function pExpr_ErrorSuppress(Expr\ErrorSuppress $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\ErrorSuppress::class, '@', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_YieldFrom(Expr\YieldFrom $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\YieldFrom::class, 'yield from ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Print(Expr\Print_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\Print_::class, 'print ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Cast_Int(Cast\Int_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Cast\Int_::class, '(int) ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Cast_Double(Cast\Double $node, int $precedence, int $lhsPrecedence): string { $kind = $node->getAttribute('kind', Cast\Double::KIND_DOUBLE); if ($kind === Cast\Double::KIND_DOUBLE) { $cast = '(double)'; } elseif ($kind === Cast\Double::KIND_FLOAT) { $cast = '(float)'; } else { assert($kind === Cast\Double::KIND_REAL); $cast = '(real)'; } return $this->pPrefixOp(Cast\Double::class, $cast . ' ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Cast_String(Cast\String_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Cast\String_::class, '(string) ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Cast_Array(Cast\Array_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Cast\Array_::class, '(array) ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Cast_Object(Cast\Object_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Cast\Object_::class, '(object) ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Cast_Bool(Cast\Bool_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Cast\Bool_::class, '(bool) ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Cast_Unset(Cast\Unset_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Cast\Unset_::class, '(unset) ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_FuncCall(Expr\FuncCall $node): string { return $this->pCallLhs($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } protected function pExpr_MethodCall(Expr\MethodCall $node): string { return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } protected function pExpr_NullsafeMethodCall(Expr\NullsafeMethodCall $node): string { return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } protected function pExpr_StaticCall(Expr\StaticCall $node): string { return $this->pStaticDereferenceLhs($node->class) . '::' . ($node->name instanceof Expr ? ($node->name instanceof Expr\Variable ? $this->p($node->name) : '{' . $this->p($node->name) . '}') : $node->name) . '(' . $this->pMaybeMultiline($node->args) . ')'; } protected function pExpr_Empty(Expr\Empty_ $node): string { return 'empty(' . $this->p($node->expr) . ')'; } protected function pExpr_Isset(Expr\Isset_ $node): string { return 'isset(' . $this->pCommaSeparated($node->vars) . ')'; } protected function pExpr_Eval(Expr\Eval_ $node): string { return 'eval(' . $this->p($node->expr) . ')'; } protected function pExpr_Include(Expr\Include_ $node, int $precedence, int $lhsPrecedence): string { static $map = [ Expr\Include_::TYPE_INCLUDE => 'include', Expr\Include_::TYPE_INCLUDE_ONCE => 'include_once', Expr\Include_::TYPE_REQUIRE => 'require', Expr\Include_::TYPE_REQUIRE_ONCE => 'require_once', ]; return $this->pPrefixOp(Expr\Include_::class, $map[$node->type] . ' ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_List(Expr\List_ $node): string { $syntax = $node->getAttribute('kind', $this->phpVersion->supportsShortArrayDestructuring() ? Expr\List_::KIND_ARRAY : Expr\List_::KIND_LIST); if ($syntax === Expr\List_::KIND_ARRAY) { return '[' . $this->pMaybeMultiline($node->items, true) . ']'; } else { return 'list(' . $this->pMaybeMultiline($node->items, true) . ')'; } } protected function pExpr_Error(Expr\Error $node): string { throw new \LogicException('Cannot pretty-print AST with Error nodes'); } protected function pExpr_Variable(Expr\Variable $node): string { if ($node->name instanceof Expr) { return '${' . $this->p($node->name) . '}'; } else { return '$' . $node->name; } } protected function pExpr_Array(Expr\Array_ $node): string { $syntax = $node->getAttribute('kind', $this->shortArraySyntax ? Expr\Array_::KIND_SHORT : Expr\Array_::KIND_LONG); if ($syntax === Expr\Array_::KIND_SHORT) { return '[' . $this->pMaybeMultiline($node->items, true) . ']'; } else { return 'array(' . $this->pMaybeMultiline($node->items, true) . ')'; } } protected function pKey(?Node $node): string { if ($node === null) { return ''; } $yieldPrecedence = $this->precedenceMap[Expr\Yield_::class][0]; return $this->p($node, self::MAX_PRECEDENCE, $yieldPrecedence) . ' => '; } protected function pArrayItem(Node\ArrayItem $node): string { return $this->pKey($node->key) . ($node->byRef ? '&' : '') . ($node->unpack ? '...' : '') . $this->p($node->value); } protected function pExpr_ArrayDimFetch(Expr\ArrayDimFetch $node): string { return $this->pDereferenceLhs($node->var) . '[' . (null !== $node->dim ? $this->p($node->dim) : '') . ']'; } protected function pExpr_ConstFetch(Expr\ConstFetch $node): string { return $this->p($node->name); } protected function pExpr_ClassConstFetch(Expr\ClassConstFetch $node): string { return $this->pStaticDereferenceLhs($node->class) . '::' . $this->pObjectProperty($node->name); } protected function pExpr_PropertyFetch(Expr\PropertyFetch $node): string { return $this->pDereferenceLhs($node->var) . '->' . $this->pObjectProperty($node->name); } protected function pExpr_NullsafePropertyFetch(Expr\NullsafePropertyFetch $node): string { return $this->pDereferenceLhs($node->var) . '?->' . $this->pObjectProperty($node->name); } protected function pExpr_StaticPropertyFetch(Expr\StaticPropertyFetch $node): string { return $this->pStaticDereferenceLhs($node->class) . '::$' . $this->pObjectProperty($node->name); } protected function pExpr_ShellExec(Expr\ShellExec $node): string { return '`' . $this->pEncapsList($node->parts, '`') . '`'; } protected function pExpr_Closure(Expr\Closure $node): string { return $this->pAttrGroups($node->attrGroups, true) . $this->pStatic($node->static) . 'function ' . ($node->byRef ? '&' : '') . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . (!empty($node->uses) ? ' use (' . $this->pCommaSeparated($node->uses) . ')' : '') . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pExpr_Match(Expr\Match_ $node): string { return 'match (' . $this->p($node->cond) . ') {' . $this->pCommaSeparatedMultiline($node->arms, true) . $this->nl . '}'; } protected function pMatchArm(Node\MatchArm $node): string { $result = ''; if ($node->conds) { for ($i = 0, $c = \count($node->conds); $i + 1 < $c; $i++) { $result .= $this->p($node->conds[$i]) . ', '; } $result .= $this->pKey($node->conds[$i]); } else { $result = 'default => '; } return $result . $this->p($node->body); } protected function pExpr_ArrowFunction(Expr\ArrowFunction $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp( Expr\ArrowFunction::class, $this->pAttrGroups($node->attrGroups, true) . $this->pStatic($node->static) . 'fn' . ($node->byRef ? '&' : '') . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . ' => ', $node->expr, $precedence, $lhsPrecedence); } protected function pClosureUse(Node\ClosureUse $node): string { return ($node->byRef ? '&' : '') . $this->p($node->var); } protected function pExpr_New(Expr\New_ $node): string { if ($node->class instanceof Stmt\Class_) { $args = $node->args ? '(' . $this->pMaybeMultiline($node->args) . ')' : ''; return 'new ' . $this->pClassCommon($node->class, $args); } return 'new ' . $this->pNewOperand($node->class) . '(' . $this->pMaybeMultiline($node->args) . ')'; } protected function pExpr_Clone(Expr\Clone_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\Clone_::class, 'clone ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Ternary(Expr\Ternary $node, int $precedence, int $lhsPrecedence): string { return $this->pInfixOp(Expr\Ternary::class, $node->cond, ' ?' . (null !== $node->if ? ' ' . $this->p($node->if) . ' ' : '') . ': ', $node->else, $precedence, $lhsPrecedence ); } protected function pExpr_Exit(Expr\Exit_ $node): string { $kind = $node->getAttribute('kind', Expr\Exit_::KIND_DIE); return ($kind === Expr\Exit_::KIND_EXIT ? 'exit' : 'die') . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : ''); } protected function pExpr_Throw(Expr\Throw_ $node, int $precedence, int $lhsPrecedence): string { return $this->pPrefixOp(Expr\Throw_::class, 'throw ', $node->expr, $precedence, $lhsPrecedence); } protected function pExpr_Yield(Expr\Yield_ $node, int $precedence, int $lhsPrecedence): string { if ($node->value === null) { $opPrecedence = $this->precedenceMap[Expr\Yield_::class][0]; return $opPrecedence >= $lhsPrecedence ? '(yield)' : 'yield'; } else { if (!$this->phpVersion->supportsYieldWithoutParentheses()) { return '(yield ' . $this->pKey($node->key) . $this->p($node->value) . ')'; } return $this->pPrefixOp( Expr\Yield_::class, 'yield ' . $this->pKey($node->key), $node->value, $precedence, $lhsPrecedence); } } protected function pStmt_Namespace(Stmt\Namespace_ $node): string { if ($this->canUseSemicolonNamespaces) { return 'namespace ' . $this->p($node->name) . ';' . $this->nl . $this->pStmts($node->stmts, false); } else { return 'namespace' . (null !== $node->name ? ' ' . $this->p($node->name) : '') . ' {' . $this->pStmts($node->stmts) . $this->nl . '}'; } } protected function pStmt_Use(Stmt\Use_ $node): string { return 'use ' . $this->pUseType($node->type) . $this->pCommaSeparated($node->uses) . ';'; } protected function pStmt_GroupUse(Stmt\GroupUse $node): string { return 'use ' . $this->pUseType($node->type) . $this->pName($node->prefix) . '\{' . $this->pCommaSeparated($node->uses) . '};'; } protected function pUseItem(Node\UseItem $node): string { return $this->pUseType($node->type) . $this->p($node->name) . (null !== $node->alias ? ' as ' . $node->alias : ''); } protected function pUseType(int $type): string { return $type === Stmt\Use_::TYPE_FUNCTION ? 'function ' : ($type === Stmt\Use_::TYPE_CONSTANT ? 'const ' : ''); } protected function pStmt_Interface(Stmt\Interface_ $node): string { return $this->pAttrGroups($node->attrGroups) . 'interface ' . $node->name . (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Enum(Stmt\Enum_ $node): string { return $this->pAttrGroups($node->attrGroups) . 'enum ' . $node->name . ($node->scalarType ? ' : ' . $this->p($node->scalarType) : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Class(Stmt\Class_ $node): string { return $this->pClassCommon($node, ' ' . $node->name); } protected function pStmt_Trait(Stmt\Trait_ $node): string { return $this->pAttrGroups($node->attrGroups) . 'trait ' . $node->name . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_EnumCase(Stmt\EnumCase $node): string { return $this->pAttrGroups($node->attrGroups) . 'case ' . $node->name . ($node->expr ? ' = ' . $this->p($node->expr) : '') . ';'; } protected function pStmt_TraitUse(Stmt\TraitUse $node): string { return 'use ' . $this->pCommaSeparated($node->traits) . (empty($node->adaptations) ? ';' : ' {' . $this->pStmts($node->adaptations) . $this->nl . '}'); } protected function pStmt_TraitUseAdaptation_Precedence(Stmt\TraitUseAdaptation\Precedence $node): string { return $this->p($node->trait) . '::' . $node->method . ' insteadof ' . $this->pCommaSeparated($node->insteadof) . ';'; } protected function pStmt_TraitUseAdaptation_Alias(Stmt\TraitUseAdaptation\Alias $node): string { return (null !== $node->trait ? $this->p($node->trait) . '::' : '') . $node->method . ' as' . (null !== $node->newModifier ? ' ' . rtrim($this->pModifiers($node->newModifier), ' ') : '') . (null !== $node->newName ? ' ' . $node->newName : '') . ';'; } protected function pStmt_Property(Stmt\Property $node): string { return $this->pAttrGroups($node->attrGroups) . (0 === $node->flags ? 'var ' : $this->pModifiers($node->flags)) . ($node->type ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->props) . ';'; } protected function pPropertyItem(Node\PropertyItem $node): string { return '$' . $node->name . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); } protected function pStmt_ClassMethod(Stmt\ClassMethod $node): string { return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . (null !== $node->stmts ? $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); } protected function pStmt_ClassConst(Stmt\ClassConst $node): string { return $this->pAttrGroups($node->attrGroups) . $this->pModifiers($node->flags) . 'const ' . (null !== $node->type ? $this->p($node->type) . ' ' : '') . $this->pCommaSeparated($node->consts) . ';'; } protected function pStmt_Function(Stmt\Function_ $node): string { return $this->pAttrGroups($node->attrGroups) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $this->pMaybeMultiline($node->params, $this->phpVersion->supportsTrailingCommaInParamList()) . ')' . (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Const(Stmt\Const_ $node): string { return 'const ' . $this->pCommaSeparated($node->consts) . ';'; } protected function pStmt_Declare(Stmt\Declare_ $node): string { return 'declare (' . $this->pCommaSeparated($node->declares) . ')' . (null !== $node->stmts ? ' {' . $this->pStmts($node->stmts) . $this->nl . '}' : ';'); } protected function pDeclareItem(Node\DeclareItem $node): string { return $node->key . '=' . $this->p($node->value); } protected function pStmt_If(Stmt\If_ $node): string { return 'if (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->elseifs ? ' ' . $this->pImplode($node->elseifs, ' ') : '') . (null !== $node->else ? ' ' . $this->p($node->else) : ''); } protected function pStmt_ElseIf(Stmt\ElseIf_ $node): string { return 'elseif (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Else(Stmt\Else_ $node): string { if (\count($node->stmts) === 1 && $node->stmts[0] instanceof Stmt\If_) { return 'else ' . $this->p($node->stmts[0]); } return 'else {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_For(Stmt\For_ $node): string { return 'for (' . $this->pCommaSeparated($node->init) . ';' . (!empty($node->cond) ? ' ' : '') . $this->pCommaSeparated($node->cond) . ';' . (!empty($node->loop) ? ' ' : '') . $this->pCommaSeparated($node->loop) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Foreach(Stmt\Foreach_ $node): string { return 'foreach (' . $this->p($node->expr) . ' as ' . (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '') . ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_While(Stmt\While_ $node): string { return 'while (' . $this->p($node->cond) . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Do(Stmt\Do_ $node): string { return 'do {' . $this->pStmts($node->stmts) . $this->nl . '} while (' . $this->p($node->cond) . ');'; } protected function pStmt_Switch(Stmt\Switch_ $node): string { return 'switch (' . $this->p($node->cond) . ') {' . $this->pStmts($node->cases) . $this->nl . '}'; } protected function pStmt_Case(Stmt\Case_ $node): string { return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':' . $this->pStmts($node->stmts); } protected function pStmt_TryCatch(Stmt\TryCatch $node): string { return 'try {' . $this->pStmts($node->stmts) . $this->nl . '}' . ($node->catches ? ' ' . $this->pImplode($node->catches, ' ') : '') . ($node->finally !== null ? ' ' . $this->p($node->finally) : ''); } protected function pStmt_Catch(Stmt\Catch_ $node): string { return 'catch (' . $this->pImplode($node->types, '|') . ($node->var !== null ? ' ' . $this->p($node->var) : '') . ') {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Finally(Stmt\Finally_ $node): string { return 'finally {' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pStmt_Break(Stmt\Break_ $node): string { return 'break' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; } protected function pStmt_Continue(Stmt\Continue_ $node): string { return 'continue' . ($node->num !== null ? ' ' . $this->p($node->num) : '') . ';'; } protected function pStmt_Return(Stmt\Return_ $node): string { return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '') . ';'; } protected function pStmt_Label(Stmt\Label $node): string { return $node->name . ':'; } protected function pStmt_Goto(Stmt\Goto_ $node): string { return 'goto ' . $node->name . ';'; } protected function pStmt_Expression(Stmt\Expression $node): string { return $this->p($node->expr) . ';'; } protected function pStmt_Echo(Stmt\Echo_ $node): string { return 'echo ' . $this->pCommaSeparated($node->exprs) . ';'; } protected function pStmt_Static(Stmt\Static_ $node): string { return 'static ' . $this->pCommaSeparated($node->vars) . ';'; } protected function pStmt_Global(Stmt\Global_ $node): string { return 'global ' . $this->pCommaSeparated($node->vars) . ';'; } protected function pStaticVar(Node\StaticVar $node): string { return $this->p($node->var) . (null !== $node->default ? ' = ' . $this->p($node->default) : ''); } protected function pStmt_Unset(Stmt\Unset_ $node): string { return 'unset(' . $this->pCommaSeparated($node->vars) . ');'; } protected function pStmt_InlineHTML(Stmt\InlineHTML $node): string { $newline = $node->getAttribute('hasLeadingNewline', true) ? $this->newline : ''; return '?>' . $newline . $node->value . 'remaining; } protected function pStmt_Nop(Stmt\Nop $node): string { return ''; } protected function pStmt_Block(Stmt\Block $node): string { return '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pClassCommon(Stmt\Class_ $node, string $afterClassToken): string { return $this->pAttrGroups($node->attrGroups, $node->name === null) . $this->pModifiers($node->flags) . 'class' . $afterClassToken . (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '') . (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '') . $this->nl . '{' . $this->pStmts($node->stmts) . $this->nl . '}'; } protected function pObjectProperty(Node $node): string { if ($node instanceof Expr) { return '{' . $this->p($node) . '}'; } else { assert($node instanceof Node\Identifier); return $node->name; } } protected function pEncapsList(array $encapsList, ?string $quote): string { $return = ''; foreach ($encapsList as $element) { if ($element instanceof Node\InterpolatedStringPart) { $return .= $this->escapeString($element->value, $quote); } else { $return .= '{' . $this->p($element) . '}'; } } return $return; } protected function pSingleQuotedString(string $string): string { $regex = '/\'|\\\\(?=[\'\\\\]|$)|(?<=\\\\)\\\\/'; return '\'' . preg_replace($regex, '\\\\$0', $string) . '\''; } protected function escapeString(string $string, ?string $quote): string { if (null === $quote) { $escaped = addcslashes($string, "\t\f\v$\\"); $escaped = preg_replace('/\r(?!\n)/', '\\r', $escaped); if ($this->phpVersion->supportsFlexibleHeredoc()) { $escaped = $this->indentString($escaped); } } else { $escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\"); } $regex = '/( [\x00-\x08\x0E-\x1F] # Control characters | [\xC0-\xC1] # Invalid UTF-8 Bytes | [\xF5-\xFF] # Invalid UTF-8 Bytes | \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point | \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle | (? $part) { if ($part instanceof Node\InterpolatedStringPart && $this->containsEndLabel($this->escapeString($part->value, null), $label, $i === 0) ) { return true; } } return false; } protected function pDereferenceLhs(Node $node): string { if (!$this->dereferenceLhsRequiresParens($node)) { return $this->p($node); } else { return '(' . $this->p($node) . ')'; } } protected function pStaticDereferenceLhs(Node $node): string { if (!$this->staticDereferenceLhsRequiresParens($node)) { return $this->p($node); } else { return '(' . $this->p($node) . ')'; } } protected function pCallLhs(Node $node): string { if (!$this->callLhsRequiresParens($node)) { return $this->p($node); } else { return '(' . $this->p($node) . ')'; } } protected function pNewOperand(Node $node): string { if (!$this->newOperandRequiresParens($node)) { return $this->p($node); } else { return '(' . $this->p($node) . ')'; } } protected function hasNodeWithComments(array $nodes): bool { foreach ($nodes as $node) { if ($node && $node->getComments()) { return true; } } return false; } protected function pMaybeMultiline(array $nodes, bool $trailingComma = false): string { if (!$this->hasNodeWithComments($nodes)) { return $this->pCommaSeparated($nodes); } else { return $this->pCommaSeparatedMultiline($nodes, $trailingComma) . $this->nl; } } protected function pAttrGroups(array $nodes, bool $inline = false): string { $result = ''; $sep = $inline ? ' ' : $this->nl; foreach ($nodes as $node) { $result .= $this->p($node) . $sep; } return $result; } } visitors = $visitors; } public function addVisitor(NodeVisitor $visitor): void { $this->visitors[] = $visitor; } public function removeVisitor(NodeVisitor $visitor): void { $index = array_search($visitor, $this->visitors); if ($index !== false) { array_splice($this->visitors, $index, 1, []); } } public function traverse(array $nodes): array { $this->stopTraversal = false; foreach ($this->visitors as $visitor) { if (null !== $return = $visitor->beforeTraverse($nodes)) { $nodes = $return; } } $nodes = $this->traverseArray($nodes); for ($i = \count($this->visitors) - 1; $i >= 0; --$i) { $visitor = $this->visitors[$i]; if (null !== $return = $visitor->afterTraverse($nodes)) { $nodes = $return; } } return $nodes; } protected function traverseNode(Node $node): void { foreach ($node->getSubNodeNames() as $name) { $subNode = $node->$name; if (\is_array($subNode)) { $node->$name = $this->traverseArray($subNode); if ($this->stopTraversal) { break; } } elseif ($subNode instanceof Node) { $traverseChildren = true; $visitorIndex = -1; foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($subNode); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($subNode, $return); $subNode = $node->$name = $return; } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) { $traverseChildren = false; } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { $traverseChildren = false; break; } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = true; break 2; } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { $node->$name = null; continue 2; } else { throw new \LogicException( 'enterNode() returned invalid value of type ' . gettype($return) ); } } } if ($traverseChildren) { $this->traverseNode($subNode); if ($this->stopTraversal) { break; } } for (; $visitorIndex >= 0; --$visitorIndex) { $visitor = $this->visitors[$visitorIndex]; $return = $visitor->leaveNode($subNode); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($subNode, $return); $subNode = $node->$name = $return; } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = true; break 2; } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { $node->$name = null; break; } elseif (\is_array($return)) { throw new \LogicException( 'leaveNode() may only return an array ' . 'if the parent structure is an array' ); } else { throw new \LogicException( 'leaveNode() returned invalid value of type ' . gettype($return) ); } } } } } } protected function traverseArray(array $nodes): array { $doNodes = []; foreach ($nodes as $i => $node) { if ($node instanceof Node) { $traverseChildren = true; $visitorIndex = -1; foreach ($this->visitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($node); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($node, $return); $nodes[$i] = $node = $return; } elseif (\is_array($return)) { $doNodes[] = [$i, $return]; continue 2; } elseif (NodeVisitor::REMOVE_NODE === $return) { $doNodes[] = [$i, []]; continue 2; } elseif (NodeVisitor::DONT_TRAVERSE_CHILDREN === $return) { $traverseChildren = false; } elseif (NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN === $return) { $traverseChildren = false; break; } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = true; break 2; } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { throw new \LogicException( 'REPLACE_WITH_NULL can not be used if the parent structure is an array'); } else { throw new \LogicException( 'enterNode() returned invalid value of type ' . gettype($return) ); } } } if ($traverseChildren) { $this->traverseNode($node); if ($this->stopTraversal) { break; } } for (; $visitorIndex >= 0; --$visitorIndex) { $visitor = $this->visitors[$visitorIndex]; $return = $visitor->leaveNode($node); if (null !== $return) { if ($return instanceof Node) { $this->ensureReplacementReasonable($node, $return); $nodes[$i] = $node = $return; } elseif (\is_array($return)) { $doNodes[] = [$i, $return]; break; } elseif (NodeVisitor::REMOVE_NODE === $return) { $doNodes[] = [$i, []]; break; } elseif (NodeVisitor::STOP_TRAVERSAL === $return) { $this->stopTraversal = true; break 2; } elseif (NodeVisitor::REPLACE_WITH_NULL === $return) { throw new \LogicException( 'REPLACE_WITH_NULL can not be used if the parent structure is an array'); } else { throw new \LogicException( 'leaveNode() returned invalid value of type ' . gettype($return) ); } } } } elseif (\is_array($node)) { throw new \LogicException('Invalid node structure: Contains nested arrays'); } } if (!empty($doNodes)) { while (list($i, $replace) = array_pop($doNodes)) { array_splice($nodes, $i, 1, $replace); } } return $nodes; } private function ensureReplacementReasonable(Node $old, Node $new): void { if ($old instanceof Node\Stmt && $new instanceof Node\Expr) { throw new \LogicException( "Trying to replace statement ({$old->getType()}) " . "with expression ({$new->getType()}). Are you missing a " . "Stmt_Expression wrapper?" ); } if ($old instanceof Node\Expr && $new instanceof Node\Stmt) { throw new \LogicException( "Trying to replace expression ({$old->getType()}) " . "with statement ({$new->getType()})" ); } } } true, 'startLine' => true, 'endLine' => true, 'startFilePos' => true, 'endFilePos' => true, 'startTokenPos' => true, 'endTokenPos' => true, ]; public function __construct(array $options = []) { $this->dumpComments = !empty($options['dumpComments']); $this->dumpPositions = !empty($options['dumpPositions']); $this->dumpOtherAttributes = !empty($options['dumpOtherAttributes']); } public function dump($node, ?string $code = null): string { $this->code = $code; $this->res = ''; $this->nl = "\n"; $this->dumpRecursive($node, false); return $this->res; } protected function dumpRecursive($node, bool $indent = true): void { if ($indent) { $this->nl .= " "; } if ($node instanceof Node) { $this->res .= $node->getType(); if ($this->dumpPositions && null !== $p = $this->dumpPosition($node)) { $this->res .= $p; } $this->res .= '('; foreach ($node->getSubNodeNames() as $key) { $this->res .= "$this->nl " . $key . ': '; $value = $node->$key; if (\is_int($value)) { if ('flags' === $key || 'newModifier' === $key) { $this->res .= $this->dumpFlags($value); continue; } if ('type' === $key && $node instanceof Include_) { $this->res .= $this->dumpIncludeType($value); continue; } if ('type' === $key && ($node instanceof Use_ || $node instanceof UseItem || $node instanceof GroupUse)) { $this->res .= $this->dumpUseType($value); continue; } } $this->dumpRecursive($value); } if ($this->dumpComments && $comments = $node->getComments()) { $this->res .= "$this->nl comments: "; $this->dumpRecursive($comments); } if ($this->dumpOtherAttributes) { foreach ($node->getAttributes() as $key => $value) { if (isset(self::IGNORE_ATTRIBUTES[$key])) { continue; } $this->res .= "$this->nl $key: "; if (\is_int($value)) { if ('kind' === $key) { if ($node instanceof Int_) { $this->res .= $this->dumpIntKind($value); continue; } if ($node instanceof String_ || $node instanceof InterpolatedString) { $this->res .= $this->dumpStringKind($value); continue; } if ($node instanceof Array_) { $this->res .= $this->dumpArrayKind($value); continue; } if ($node instanceof List_) { $this->res .= $this->dumpListKind($value); continue; } } } $this->dumpRecursive($value); } } $this->res .= "$this->nl)"; } elseif (\is_array($node)) { $this->res .= 'array('; foreach ($node as $key => $value) { $this->res .= "$this->nl " . $key . ': '; $this->dumpRecursive($value); } $this->res .= "$this->nl)"; } elseif ($node instanceof Comment) { $this->res .= \str_replace("\n", $this->nl, $node->getReformattedText()); } elseif (\is_string($node)) { $this->res .= \str_replace("\n", $this->nl, (string)$node); } elseif (\is_int($node) || \is_float($node)) { $this->res .= $node; } elseif (null === $node) { $this->res .= 'null'; } elseif (false === $node) { $this->res .= 'false'; } elseif (true === $node) { $this->res .= 'true'; } else { throw new \InvalidArgumentException('Can only dump nodes and arrays.'); } if ($indent) { $this->nl = \substr($this->nl, 0, -4); } } protected function dumpFlags(int $flags): string { $strs = []; if ($flags & Modifiers::PUBLIC) { $strs[] = 'PUBLIC'; } if ($flags & Modifiers::PROTECTED) { $strs[] = 'PROTECTED'; } if ($flags & Modifiers::PRIVATE) { $strs[] = 'PRIVATE'; } if ($flags & Modifiers::ABSTRACT) { $strs[] = 'ABSTRACT'; } if ($flags & Modifiers::STATIC) { $strs[] = 'STATIC'; } if ($flags & Modifiers::FINAL) { $strs[] = 'FINAL'; } if ($flags & Modifiers::READONLY) { $strs[] = 'READONLY'; } if ($strs) { return implode(' | ', $strs) . ' (' . $flags . ')'; } else { return (string) $flags; } } private function dumpEnum(int $value, array $map): string { if (!isset($map[$value])) { return (string) $value; } return $map[$value] . ' (' . $value . ')'; } private function dumpIncludeType(int $type): string { return $this->dumpEnum($type, [ Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE', ]); } private function dumpUseType(int $type): string { return $this->dumpEnum($type, [ Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', Use_::TYPE_NORMAL => 'TYPE_NORMAL', Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', Use_::TYPE_CONSTANT => 'TYPE_CONSTANT', ]); } private function dumpIntKind(int $kind): string { return $this->dumpEnum($kind, [ Int_::KIND_BIN => 'KIND_BIN', Int_::KIND_OCT => 'KIND_OCT', Int_::KIND_DEC => 'KIND_DEC', Int_::KIND_HEX => 'KIND_HEX', ]); } private function dumpStringKind(int $kind): string { return $this->dumpEnum($kind, [ String_::KIND_SINGLE_QUOTED => 'KIND_SINGLE_QUOTED', String_::KIND_DOUBLE_QUOTED => 'KIND_DOUBLE_QUOTED', String_::KIND_HEREDOC => 'KIND_HEREDOC', String_::KIND_NOWDOC => 'KIND_NOWDOC', ]); } private function dumpArrayKind(int $kind): string { return $this->dumpEnum($kind, [ Array_::KIND_LONG => 'KIND_LONG', Array_::KIND_SHORT => 'KIND_SHORT', ]); } private function dumpListKind(int $kind): string { return $this->dumpEnum($kind, [ List_::KIND_LIST => 'KIND_LIST', List_::KIND_ARRAY => 'KIND_ARRAY', ]); } protected function dumpPosition(Node $node): ?string { if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) { return null; } $start = $node->getStartLine(); $end = $node->getEndLine(); if ($node->hasAttribute('startFilePos') && $node->hasAttribute('endFilePos') && null !== $this->code ) { $start .= ':' . $this->toColumn($this->code, $node->getStartFilePos()); $end .= ':' . $this->toColumn($this->code, $node->getEndFilePos()); } return "[$start - $end]"; } private function toColumn(string $code, int $pos): int { if ($pos > strlen($code)) { throw new \RuntimeException('Invalid position information'); } $lineStartPos = strrpos($code, "\n", $pos - strlen($code)); if (false === $lineStartPos) { $lineStartPos = -1; } return $pos - $lineStartPos; } } errors[] = $error; } public function getErrors(): array { return $this->errors; } public function hasErrors(): bool { return !empty($this->errors); } public function clearErrors(): void { $this->errors = []; } } 50100, 'callable' => 50400, 'bool' => 70000, 'int' => 70000, 'float' => 70000, 'string' => 70000, 'iterable' => 70100, 'void' => 70100, 'object' => 70200, 'null' => 80000, 'false' => 80000, 'mixed' => 80000, 'never' => 80100, 'true' => 80200, ]; private function __construct(int $id) { $this->id = $id; } public static function fromComponents(int $major, int $minor): self { return new self($major * 10000 + $minor * 100); } public static function getNewestSupported(): self { return self::fromComponents(8, 3); } public static function getHostVersion(): self { return self::fromComponents(\PHP_MAJOR_VERSION, \PHP_MINOR_VERSION); } public static function fromString(string $version): self { if (!preg_match('/^(\d+)\.(\d+)/', $version, $matches)) { throw new \LogicException("Invalid PHP version \"$version\""); } return self::fromComponents((int) $matches[1], (int) $matches[2]); } public function equals(PhpVersion $other): bool { return $this->id === $other->id; } public function newerOrEqual(PhpVersion $other): bool { return $this->id >= $other->id; } public function older(PhpVersion $other): bool { return $this->id < $other->id; } public function isHostVersion(): bool { return $this->equals(self::getHostVersion()); } public function supportsBuiltinType(string $type): bool { $minVersion = self::BUILTIN_TYPE_VERSIONS[$type] ?? null; return $minVersion !== null && $this->id >= $minVersion; } public function supportsShortArraySyntax(): bool { return $this->id >= 50400; } public function supportsShortArrayDestructuring(): bool { return $this->id >= 70100; } public function supportsFlexibleHeredoc(): bool { return $this->id >= 70300; } public function supportsTrailingCommaInParamList(): bool { return $this->id >= 80000; } public function allowsAssignNewByReference(): bool { return $this->id < 70000; } public function allowsInvalidOctals(): bool { return $this->id < 70000; } public function allowsDelInIdentifiers(): bool { return $this->id < 70100; } public function supportsYieldWithoutParentheses(): bool { return $this->id >= 70000; } public function supportsUnicodeEscapes(): bool { return $this->id >= 70000; } } decodeRecursive($value); } private function decodeRecursive($value) { if (\is_array($value)) { if (isset($value['nodeType'])) { if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') { return $this->decodeComment($value); } return $this->decodeNode($value); } return $this->decodeArray($value); } return $value; } private function decodeArray(array $array): array { $decodedArray = []; foreach ($array as $key => $value) { $decodedArray[$key] = $this->decodeRecursive($value); } return $decodedArray; } private function decodeNode(array $value): Node { $nodeType = $value['nodeType']; if (!\is_string($nodeType)) { throw new \RuntimeException('Node type must be a string'); } $reflectionClass = $this->reflectionClassFromNodeType($nodeType); $node = $reflectionClass->newInstanceWithoutConstructor(); if (isset($value['attributes'])) { if (!\is_array($value['attributes'])) { throw new \RuntimeException('Attributes must be an array'); } $node->setAttributes($this->decodeArray($value['attributes'])); } foreach ($value as $name => $subNode) { if ($name === 'nodeType' || $name === 'attributes') { continue; } $node->$name = $this->decodeRecursive($subNode); } return $node; } private function decodeComment(array $value): Comment { $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; if (!isset($value['text'])) { throw new \RuntimeException('Comment must have text'); } return new $className( $value['text'], $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1, $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1 ); } private function reflectionClassFromNodeType(string $nodeType): \ReflectionClass { if (!isset($this->reflectionClassCache[$nodeType])) { $className = $this->classNameFromNodeType($nodeType); $this->reflectionClassCache[$nodeType] = new \ReflectionClass($className); } return $this->reflectionClassCache[$nodeType]; } private function classNameFromNodeType(string $nodeType): string { $className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\'); if (class_exists($className)) { return $className; } $className .= '_'; if (class_exists($className)) { return $className; } throw new \RuntimeException("Unknown node type \"$nodeType\""); } } text = $text; $this->startLine = $startLine; $this->startFilePos = $startFilePos; $this->startTokenPos = $startTokenPos; $this->endLine = $endLine; $this->endFilePos = $endFilePos; $this->endTokenPos = $endTokenPos; } public function getText(): string { return $this->text; } public function getStartLine(): int { return $this->startLine; } public function getStartFilePos(): int { return $this->startFilePos; } public function getStartTokenPos(): int { return $this->startTokenPos; } public function getEndLine(): int { return $this->endLine; } public function getEndFilePos(): int { return $this->endFilePos; } public function getEndTokenPos(): int { return $this->endTokenPos; } public function __toString(): string { return $this->text; } public function getReformattedText(): string { $text = str_replace("\r\n", "\n", $this->text); $newlinePos = strpos($text, "\n"); if (false === $newlinePos) { return $text; } if (preg_match('(^.*(?:\n\s+\*.*)+$)', $text)) { return preg_replace('(^\s+\*)m', ' *', $text); } if (preg_match('(^/\*\*?\s*\n)', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) { return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text); } if (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) { $prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1)); $removeLen = $prefixLen - strlen($matches[0]); return preg_replace('(^\s{' . $removeLen . '})m', '', $text); } return $text; } private function getShortestWhitespacePrefixLen(string $str): int { $lines = explode("\n", $str); $shortestPrefixLen = \PHP_INT_MAX; foreach ($lines as $line) { preg_match('(^\s*)', $line, $matches); $prefixLen = strlen($matches[0]); if ($prefixLen < $shortestPrefixLen) { $shortestPrefixLen = $prefixLen; } } return $shortestPrefixLen; } public function jsonSerialize(): array { $type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment'; return [ 'nodeType' => $type, 'text' => $this->text, 'line' => $this->startLine, 'filePos' => $this->startFilePos, 'tokenPos' => $this->startTokenPos, 'endLine' => $this->endLine, 'endFilePos' => $this->endFilePos, 'endTokenPos' => $this->endTokenPos, ]; } } postprocessTokens($tokens, $errorHandler); if (false !== $scream) { ini_set('xdebug.scream', $scream); } return $tokens; } private function handleInvalidCharacter(Token $token, ErrorHandler $errorHandler): void { $chr = $token->text; if ($chr === "\0") { $errorMsg = 'Unexpected null byte'; } else { $errorMsg = sprintf( 'Unexpected character "%s" (ASCII %d)', $chr, ord($chr) ); } $errorHandler->handleError(new Error($errorMsg, [ 'startLine' => $token->line, 'endLine' => $token->line, 'startFilePos' => $token->pos, 'endFilePos' => $token->pos, ])); } private function isUnterminatedComment(Token $token): bool { return $token->is([\T_COMMENT, \T_DOC_COMMENT]) && substr($token->text, 0, 2) === '/*' && substr($token->text, -2) !== '*/'; } protected function postprocessTokens(array &$tokens, ErrorHandler $errorHandler): void { $numTokens = \count($tokens); if ($numTokens === 0) { $tokens[] = new Token(0, "\0", 1, 0); return; } for ($i = 0; $i < $numTokens; $i++) { $token = $tokens[$i]; if ($token->id === \T_BAD_CHARACTER) { $this->handleInvalidCharacter($token, $errorHandler); } if ($token->id === \ord('&')) { $next = $i + 1; while (isset($tokens[$next]) && $tokens[$next]->id === \T_WHITESPACE) { $next++; } $followedByVarOrVarArg = isset($tokens[$next]) && $tokens[$next]->is([\T_VARIABLE, \T_ELLIPSIS]); $token->id = $followedByVarOrVarArg ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; } } $lastToken = $tokens[$numTokens - 1]; if ($this->isUnterminatedComment($lastToken)) { $errorHandler->handleError(new Error('Unterminated comment', [ 'startLine' => $lastToken->line, 'endLine' => $lastToken->getEndLine(), 'startFilePos' => $lastToken->pos, 'endFilePos' => $lastToken->getEndPos(), ])); } $tokens[] = new Token(0, "\0", $lastToken->getEndLine(), $lastToken->getEndPos()); } } fallbackEvaluator = $fallbackEvaluator ?? function (Expr $expr) { throw new ConstExprEvaluationException( "Expression of type {$expr->getType()} cannot be evaluated" ); }; } public function evaluateSilently(Expr $expr) { set_error_handler(function ($num, $str, $file, $line) { throw new \ErrorException($str, 0, $num, $file, $line); }); try { return $this->evaluate($expr); } catch (\Throwable $e) { if (!$e instanceof ConstExprEvaluationException) { $e = new ConstExprEvaluationException( "An error occurred during constant expression evaluation", 0, $e); } throw $e; } finally { restore_error_handler(); } } public function evaluateDirectly(Expr $expr) { return $this->evaluate($expr); } private function evaluate(Expr $expr) { if ($expr instanceof Scalar\Int_ || $expr instanceof Scalar\Float_ || $expr instanceof Scalar\String_ ) { return $expr->value; } if ($expr instanceof Expr\Array_) { return $this->evaluateArray($expr); } if ($expr instanceof Expr\UnaryPlus) { return +$this->evaluate($expr->expr); } if ($expr instanceof Expr\UnaryMinus) { return -$this->evaluate($expr->expr); } if ($expr instanceof Expr\BooleanNot) { return !$this->evaluate($expr->expr); } if ($expr instanceof Expr\BitwiseNot) { return ~$this->evaluate($expr->expr); } if ($expr instanceof Expr\BinaryOp) { return $this->evaluateBinaryOp($expr); } if ($expr instanceof Expr\Ternary) { return $this->evaluateTernary($expr); } if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) { return $this->evaluate($expr->var)[$this->evaluate($expr->dim)]; } if ($expr instanceof Expr\ConstFetch) { return $this->evaluateConstFetch($expr); } return ($this->fallbackEvaluator)($expr); } private function evaluateArray(Expr\Array_ $expr): array { $array = []; foreach ($expr->items as $item) { if (null !== $item->key) { $array[$this->evaluate($item->key)] = $this->evaluate($item->value); } elseif ($item->unpack) { $array = array_merge($array, $this->evaluate($item->value)); } else { $array[] = $this->evaluate($item->value); } } return $array; } private function evaluateTernary(Expr\Ternary $expr) { if (null === $expr->if) { return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else); } return $this->evaluate($expr->cond) ? $this->evaluate($expr->if) : $this->evaluate($expr->else); } private function evaluateBinaryOp(Expr\BinaryOp $expr) { if ($expr instanceof Expr\BinaryOp\Coalesce && $expr->left instanceof Expr\ArrayDimFetch ) { return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)] ?? $this->evaluate($expr->right); } $l = $expr->left; $r = $expr->right; switch ($expr->getOperatorSigil()) { case '&': return $this->evaluate($l) & $this->evaluate($r); case '|': return $this->evaluate($l) | $this->evaluate($r); case '^': return $this->evaluate($l) ^ $this->evaluate($r); case '&&': return $this->evaluate($l) && $this->evaluate($r); case '||': return $this->evaluate($l) || $this->evaluate($r); case '??': return $this->evaluate($l) ?? $this->evaluate($r); case '.': return $this->evaluate($l) . $this->evaluate($r); case '/': return $this->evaluate($l) / $this->evaluate($r); case '==': return $this->evaluate($l) == $this->evaluate($r); case '>': return $this->evaluate($l) > $this->evaluate($r); case '>=': return $this->evaluate($l) >= $this->evaluate($r); case '===': return $this->evaluate($l) === $this->evaluate($r); case 'and': return $this->evaluate($l) and $this->evaluate($r); case 'or': return $this->evaluate($l) or $this->evaluate($r); case 'xor': return $this->evaluate($l) xor $this->evaluate($r); case '-': return $this->evaluate($l) - $this->evaluate($r); case '%': return $this->evaluate($l) % $this->evaluate($r); case '*': return $this->evaluate($l) * $this->evaluate($r); case '!=': return $this->evaluate($l) != $this->evaluate($r); case '!==': return $this->evaluate($l) !== $this->evaluate($r); case '+': return $this->evaluate($l) + $this->evaluate($r); case '**': return $this->evaluate($l) ** $this->evaluate($r); case '<<': return $this->evaluate($l) << $this->evaluate($r); case '>>': return $this->evaluate($l) >> $this->evaluate($r); case '<': return $this->evaluate($l) < $this->evaluate($r); case '<=': return $this->evaluate($l) <= $this->evaluate($r); case '<=>': return $this->evaluate($l) <=> $this->evaluate($r); } throw new \Exception('Should not happen'); } private function evaluateConstFetch(Expr\ConstFetch $expr) { $name = $expr->name->toLowerString(); switch ($name) { case 'null': return null; case 'false': return false; case 'true': return true; } return ($this->fallbackEvaluator)($expr); } } isHostVersion()) { $lexer = new Lexer(); } else { $lexer = new Lexer\Emulative($version); } if ($version->id >= 80000) { return new Php8($lexer, $version); } return new Php7($lexer, $version); } public function createForNewestSupportedVersion(): Parser { return $this->createForVersion(PhpVersion::getNewestSupported()); } public function createForHostVersion(): Parser { return $this->createForVersion(PhpVersion::getHostVersion()); } } getNode(); } if ($node instanceof Node) { return $node; } throw new \LogicException('Expected node or builder object'); } public static function normalizeStmt($node): Stmt { $node = self::normalizeNode($node); if ($node instanceof Stmt) { return $node; } if ($node instanceof Expr) { return new Stmt\Expression($node); } throw new \LogicException('Expected statement or expression node'); } public static function normalizeIdentifier($name): Identifier { if ($name instanceof Identifier) { return $name; } if (\is_string($name)) { return new Identifier($name); } throw new \LogicException('Expected string or instance of Node\Identifier'); } public static function normalizeIdentifierOrExpr($name) { if ($name instanceof Identifier || $name instanceof Expr) { return $name; } if (\is_string($name)) { return new Identifier($name); } throw new \LogicException('Expected string or instance of Node\Identifier or Node\Expr'); } public static function normalizeName($name): Name { if ($name instanceof Name) { return $name; } if (is_string($name)) { if (!$name) { throw new \LogicException('Name cannot be empty'); } if ($name[0] === '\\') { return new Name\FullyQualified(substr($name, 1)); } if (0 === strpos($name, 'namespace\\')) { return new Name\Relative(substr($name, strlen('namespace\\'))); } return new Name($name); } throw new \LogicException('Name must be a string or an instance of Node\Name'); } public static function normalizeNameOrExpr($name) { if ($name instanceof Expr) { return $name; } if (!is_string($name) && !($name instanceof Name)) { throw new \LogicException( 'Name must be a string or an instance of Node\Name or Node\Expr' ); } return self::normalizeName($name); } public static function normalizeType($type) { if (!is_string($type)) { if ( !$type instanceof Name && !$type instanceof Identifier && !$type instanceof ComplexType ) { throw new \LogicException( 'Type must be a string, or an instance of Name, Identifier or ComplexType' ); } return $type; } $nullable = false; if (strlen($type) > 0 && $type[0] === '?') { $nullable = true; $type = substr($type, 1); } $builtinTypes = [ 'array', 'callable', 'bool', 'int', 'float', 'string', 'iterable', 'void', 'object', 'null', 'false', 'mixed', 'never', 'true', ]; $lowerType = strtolower($type); if (in_array($lowerType, $builtinTypes)) { $type = new Identifier($lowerType); } else { $type = self::normalizeName($type); } $notNullableTypes = [ 'void', 'mixed', 'never', ]; if ($nullable && in_array((string) $type, $notNullableTypes)) { throw new \LogicException(sprintf('%s type cannot be nullable', $type)); } return $nullable ? new NullableType($type) : $type; } public static function normalizeValue($value): Expr { if ($value instanceof Node\Expr) { return $value; } if (is_null($value)) { return new Expr\ConstFetch( new Name('null') ); } if (is_bool($value)) { return new Expr\ConstFetch( new Name($value ? 'true' : 'false') ); } if (is_int($value)) { return new Scalar\Int_($value); } if (is_float($value)) { return new Scalar\Float_($value); } if (is_string($value)) { return new Scalar\String_($value); } if (is_array($value)) { $items = []; $lastKey = -1; foreach ($value as $itemKey => $itemValue) { if (null !== $lastKey && ++$lastKey === $itemKey) { $items[] = new Node\ArrayItem( self::normalizeValue($itemValue) ); } else { $lastKey = null; $items[] = new Node\ArrayItem( self::normalizeValue($itemValue), self::normalizeValue($itemKey) ); } } return new Expr\Array_($items); } throw new \LogicException('Invalid value'); } public static function normalizeDocComment($docComment): Comment\Doc { if ($docComment instanceof Comment\Doc) { return $docComment; } if (is_string($docComment)) { return new Comment\Doc($docComment); } throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc'); } public static function normalizeAttribute($attribute): Node\AttributeGroup { if ($attribute instanceof Node\AttributeGroup) { return $attribute; } if (!($attribute instanceof Node\Attribute)) { throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup'); } return new Node\AttributeGroup([$attribute]); } public static function addModifier(int $modifiers, int $modifier): int { Modifiers::verifyModifier($modifiers, $modifier); return $modifiers | $modifier; } public static function addClassModifier(int $existingModifiers, int $modifierToSet): int { Modifiers::verifyClassModifier($existingModifiers, $modifierToSet); return $existingModifiers | $modifierToSet; } } nameContext = new NameContext($errorHandler ?? new ErrorHandler\Throwing()); $this->preserveOriginalNames = $options['preserveOriginalNames'] ?? false; $this->replaceNodes = $options['replaceNodes'] ?? true; } public function getNameContext(): NameContext { return $this->nameContext; } public function beforeTraverse(array $nodes): ?array { $this->nameContext->startNamespace(); return null; } public function enterNode(Node $node) { if ($node instanceof Stmt\Namespace_) { $this->nameContext->startNamespace($node->name); } elseif ($node instanceof Stmt\Use_) { foreach ($node->uses as $use) { $this->addAlias($use, $node->type, null); } } elseif ($node instanceof Stmt\GroupUse) { foreach ($node->uses as $use) { $this->addAlias($use, $node->type, $node->prefix); } } elseif ($node instanceof Stmt\Class_) { if (null !== $node->extends) { $node->extends = $this->resolveClassName($node->extends); } foreach ($node->implements as &$interface) { $interface = $this->resolveClassName($interface); } $this->resolveAttrGroups($node); if (null !== $node->name) { $this->addNamespacedName($node); } else { $node->namespacedName = null; } } elseif ($node instanceof Stmt\Interface_) { foreach ($node->extends as &$interface) { $interface = $this->resolveClassName($interface); } $this->resolveAttrGroups($node); $this->addNamespacedName($node); } elseif ($node instanceof Stmt\Enum_) { foreach ($node->implements as &$interface) { $interface = $this->resolveClassName($interface); } $this->resolveAttrGroups($node); $this->addNamespacedName($node); } elseif ($node instanceof Stmt\Trait_) { $this->resolveAttrGroups($node); $this->addNamespacedName($node); } elseif ($node instanceof Stmt\Function_) { $this->resolveSignature($node); $this->resolveAttrGroups($node); $this->addNamespacedName($node); } elseif ($node instanceof Stmt\ClassMethod || $node instanceof Expr\Closure || $node instanceof Expr\ArrowFunction ) { $this->resolveSignature($node); $this->resolveAttrGroups($node); } elseif ($node instanceof Stmt\Property) { if (null !== $node->type) { $node->type = $this->resolveType($node->type); } $this->resolveAttrGroups($node); } elseif ($node instanceof Stmt\Const_) { foreach ($node->consts as $const) { $this->addNamespacedName($const); } } elseif ($node instanceof Stmt\ClassConst) { if (null !== $node->type) { $node->type = $this->resolveType($node->type); } $this->resolveAttrGroups($node); } elseif ($node instanceof Stmt\EnumCase) { $this->resolveAttrGroups($node); } elseif ($node instanceof Expr\StaticCall || $node instanceof Expr\StaticPropertyFetch || $node instanceof Expr\ClassConstFetch || $node instanceof Expr\New_ || $node instanceof Expr\Instanceof_ ) { if ($node->class instanceof Name) { $node->class = $this->resolveClassName($node->class); } } elseif ($node instanceof Stmt\Catch_) { foreach ($node->types as &$type) { $type = $this->resolveClassName($type); } } elseif ($node instanceof Expr\FuncCall) { if ($node->name instanceof Name) { $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION); } } elseif ($node instanceof Expr\ConstFetch) { $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT); } elseif ($node instanceof Stmt\TraitUse) { foreach ($node->traits as &$trait) { $trait = $this->resolveClassName($trait); } foreach ($node->adaptations as $adaptation) { if (null !== $adaptation->trait) { $adaptation->trait = $this->resolveClassName($adaptation->trait); } if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) { foreach ($adaptation->insteadof as &$insteadof) { $insteadof = $this->resolveClassName($insteadof); } } } } return null; } private function addAlias(Node\UseItem $use, int $type, ?Name $prefix = null): void { $name = $prefix ? Name::concat($prefix, $use->name) : $use->name; $type |= $use->type; $this->nameContext->addAlias( $name, (string) $use->getAlias(), $type, $use->getAttributes() ); } private function resolveSignature($node): void { foreach ($node->params as $param) { $param->type = $this->resolveType($param->type); $this->resolveAttrGroups($param); } $node->returnType = $this->resolveType($node->returnType); } private function resolveType(?Node $node): ?Node { if ($node instanceof Name) { return $this->resolveClassName($node); } if ($node instanceof Node\NullableType) { $node->type = $this->resolveType($node->type); return $node; } if ($node instanceof Node\UnionType || $node instanceof Node\IntersectionType) { foreach ($node->types as &$type) { $type = $this->resolveType($type); } return $node; } return $node; } protected function resolveName(Name $name, int $type): Name { if (!$this->replaceNodes) { $resolvedName = $this->nameContext->getResolvedName($name, $type); if (null !== $resolvedName) { $name->setAttribute('resolvedName', $resolvedName); } else { $name->setAttribute('namespacedName', FullyQualified::concat( $this->nameContext->getNamespace(), $name, $name->getAttributes())); } return $name; } if ($this->preserveOriginalNames) { $originalName = $name; $name = clone $originalName; $name->setAttribute('originalName', $originalName); } $resolvedName = $this->nameContext->getResolvedName($name, $type); if (null !== $resolvedName) { return $resolvedName; } $name->setAttribute('namespacedName', FullyQualified::concat( $this->nameContext->getNamespace(), $name, $name->getAttributes())); return $name; } protected function resolveClassName(Name $name): Name { return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL); } protected function addNamespacedName(Node $node): void { $node->namespacedName = Name::concat( $this->nameContext->getNamespace(), (string) $node->name); } protected function resolveAttrGroups(Node $node): void { foreach ($node->attrGroups as $attrGroup) { foreach ($attrGroup->attrs as $attr) { $attr->name = $this->resolveClassName($attr->name); } } } } filterCallback = $filterCallback; } public function getFoundNode(): ?Node { return $this->foundNode; } public function beforeTraverse(array $nodes): ?array { $this->foundNode = null; return null; } public function enterNode(Node $node) { $filterCallback = $this->filterCallback; if ($filterCallback($node)) { $this->foundNode = $node; return NodeVisitor::STOP_TRAVERSAL; } return null; } } setAttribute('origNode', $origNode); return $node; } } tokens = $tokens; foreach ($tokens as $i => $token) { if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) { $this->commentPositions[] = $i; } } } public function enterNode(Node $node) { $nextCommentPos = current($this->commentPositions); if ($nextCommentPos === false) { return self::STOP_TRAVERSAL; } $oldPos = $this->pos; $this->pos = $pos = $node->getStartTokenPos(); if ($nextCommentPos > $oldPos && $nextCommentPos < $pos) { $comments = []; while (--$pos >= $oldPos) { $token = $this->tokens[$pos]; if ($token->id === \T_DOC_COMMENT) { $comments[] = new Comment\Doc( $token->text, $token->line, $token->pos, $pos, $token->getEndLine(), $token->getEndPos() - 1, $pos); continue; } if ($token->id === \T_COMMENT) { $comments[] = new Comment( $token->text, $token->line, $token->pos, $pos, $token->getEndLine(), $token->getEndPos() - 1, $pos); continue; } if ($token->id !== \T_WHITESPACE) { break; } } if (!empty($comments)) { $node->setAttribute('comments', array_reverse($comments)); } do { $nextCommentPos = next($this->commentPositions); } while ($nextCommentPos !== false && $nextCommentPos < $this->pos); } $endPos = $node->getEndTokenPos(); if ($nextCommentPos > $endPos) { $this->pos = $endPos; return self::DONT_TRAVERSE_CHILDREN; } return null; } } stack = []; $this->previous = null; } public function enterNode(Node $node) { if (!empty($this->stack)) { $node->setAttribute('parent', $this->stack[count($this->stack) - 1]); } if ($this->previous !== null && $this->previous->getAttribute('parent') === $node->getAttribute('parent')) { $node->setAttribute('previous', $this->previous); $this->previous->setAttribute('next', $node); } $this->stack[] = $node; } public function leaveNode(Node $node) { $this->previous = $node; array_pop($this->stack); } } stack = []; } public function enterNode(Node $node) { if (!empty($this->stack)) { $node->setAttribute('parent', $this->stack[count($this->stack) - 1]); } $this->stack[] = $node; } public function leaveNode(Node $node) { array_pop($this->stack); } } filterCallback = $filterCallback; } public function getFoundNodes(): array { return $this->foundNodes; } public function beforeTraverse(array $nodes): ?array { $this->foundNodes = []; return null; } public function enterNode(Node $node) { $filterCallback = $this->filterCallback; if ($filterCallback($node)) { $this->foundNodes[] = $node; } return null; } } traverse($nodes); return $visitor->getFoundNodes(); } public function findInstanceOf($nodes, string $class): array { return $this->find($nodes, function ($node) use ($class) { return $node instanceof $class; }); } public function findFirst($nodes, callable $filter): ?Node { if ($nodes === []) { return null; } if (!is_array($nodes)) { $nodes = [$nodes]; } $visitor = new FirstFindingVisitor($filter); $traverser = new NodeTraverser($visitor); $traverser->traverse($nodes); return $visitor->getFoundNode(); } public function findFirstInstanceOf($nodes, string $class): ?Node { return $this->findFirst($nodes, function ($node) use ($class) { return $node instanceof $class; }); } } '", "T_IS_GREATER_OR_EQUAL", "T_SL", "T_SR", "'+'", "'-'", "'.'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_READONLY", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_ENUM", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_NULLSAFE_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "T_ATTRIBUTE", "';'", "']'", "'('", "')'", "'{'", "'}'", "'`'", "'\"'", "'$'" ); protected array $tokenToSymbol = array( 0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 166, 168, 167, 55, 168, 168, 161, 162, 53, 50, 8, 51, 52, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 159, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 160, 36, 168, 165, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 163, 35, 164, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 48, 49, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158 ); protected array $action = array( 133, 134, 135, 582, 136, 137, 0, 751, 752, 753, 138, 38,-32766,-32766,-32766, 151,-32766,-32766,-32766,-32767, -32767,-32767,-32767, 102, 103, 104, 105, 106, 1112, 1113, 1114, 1111, 1110, 1109, 1115, 745, 744,-32766,-32766,-32766, -32766,-32766,-32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767, -32767, 1245, 837,-32766, 1322, 754,-32766,-32766,-32766,-32766, -594,-32766,-32766,-32766, 104, 105, 106, -594, 1306, 265, 139, 404, 758, 759, 760, 761, 990,-32766, 429,-32766, -32766, -16,-32766, 242, 1027, 815, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 791, 583, 792, 793, 794, 795, 783, 784, 345, 346, 786, 787, 772, 773, 774, 776, 777, 778, 356, 818, 819, 820, 821, 822, 584, 779, 780, 585, 586,-32766, 803, 801, 802, 814, 798, 799, 835, 826, 587, 588, 797, 589, 590, 591, 592, 593, 594, 826, 459, 460, 461, 1036, 800, 595, 596, 941, 140, 2, 133, 134, 135, 582, 136, 137, 1060, 751, 752, 753, 138, 38, -328, -110, -110, 1326, 290, 23, -110,-32766,-32766,-32766, 1325, 35, -110, 1112, 1113, 1114, 1111, 1110, 1109, 1115, 612,-32766, 129, 745, 744, 107, 108, 109,-32766, 274,-32766,-32766,-32766,-32766, -32766,-32766,-32766, 828, 991, -194, 145, 110, 298, 754, 836, 75,-32766,-32766,-32766, 1351, 142, 326, 1352, -594, 326, -594, 254, 265, 139, 404, 758, 759, 760, 761, 82, -272, 429,-32766, 326,-32766,-32766,-32766,-32766, 815, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 791, 583, 792, 793, 794, 795, 783, 784, 345, 346, 786, 787, 772, 773, 774, 776, 777, 778, 356, 818, 819, 820, 821, 822, 584, 779, 780, 585, 586, 830, 803, 801, 802, 814, 798, 799, 712, 309, 587, 588, 797, 589, 590, 591, 592, 593, 594, -78, 83, 84, 85, -85, 800, 595, 596, 311, 149, 775, 746, 747, 748, 749, 750, 725, 751, 752, 753, 788, 789, 37, -328, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 323, 274, 482,-32766,-32766, -32766, -58,-32766,-32766,-32766, 959, 960, 127, 110, -194, 961, 339, 754,-32766,-32766,-32766, 955, -85, 291,-32766, 1088,-32766,-32766,-32766,-32766,-32766, 755, 756, 757, 758, 759, 760, 761, -193,-32766, 824,-32766,-32766,-32766, -367, 429, -367, 815, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 791, 813, 792, 793, 794, 795, 783, 784, 785, 812, 786, 787, 772, 773, 774, 776, 777, 778, 817, 818, 819, 820, 821, 822, 823, 779, 780, 781, 782, -548, 803, 801, 802, 814, 798, 799, 340, 327, 790, 796, 797, 804, 805, 807, 806, 808, 809, 1033, 391, 606, 7,-32766, 800, 811, 810, 50, 51, 52, 513, 53, 54, 831, 1240, 1239, 1241, 55, 56, -110, 57, 1036, 920, 1090, -110, 1036, -110, 291, 483, 745, 744, 305, 382, 381, -110, -110, -110, -110, -110, -110, -110, -110, 423, 920, 283, -548, -548, 152, 290, 380, 381, 1245, 715, 467, 468, 58, 59, 370, 21, 423, -545, 60, 556, 61, 248, 249, 62, 63, 64, 65, 66, 67, 68, 69, -548, 28, 267, 70, 445, 514, 1104, 374, -342, 1272, 1273, 515, -193, 835, 154, 832, -544, 1270, 42, 25, 516, 389, 517, 241, 518, 920, 519, 298, 1238, 520, 521, 910, 920, 441, 44, 45, 446, 377, 376,-32766, 46, 522, 1023, 1022, 1021, 1024, 368, 338, 442, 1278, -545, -545, 910, 1231, 443, 524, 525, 526, 835, 1245, 835, 1036, 716, 1341, 1236, -545, 155, 528, 529,-32766, 1259, 1260, 1261, 1262, 1256, 1257, 297, -551, 943, -545, -544, -544, 1263, 1258, 290, 1035, 1240, 1239, 1241, 298, 444, 1036, 71, 1266, 841, -544, 321, 322, 326, -153, -153, -153, 920, 1240, 1239, 1241, 922, -550, 910, -544, 710, 943, -591,-32766, -153, 910, -153, 357, -153, -591, -153, 862, 1033, 863, 1089, 36, 251, 922, 737, 156, 375, 710, 717, 862, -585, 863, -585, 75, 158, -546, 835, 959, 960, 326, 1036, -57, 523, 920,-32766,-32766, 362, 896, 955, -110, -110, -110, 32, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 745, 744, 656, 26, 835, -110, -110, 720, 745, 744, -110, 33, 834, 922, 124, 910, -110, 710, -153, 125, 922, 675, 676, 130, 710, -32766, 150, 407, 131, 1150, 1152, 48, 144, -546, -546, 378, 379,-32766, 383, 384, -543, 28, 159, 1238, 920, 160, 298, 1059, -546, 75,-32766,-32766,-32766, 835,-32766, 326,-32766, 1270,-32766, -87, 910,-32766, -546, 647, 648, 161,-32766,-32766,-32766, -4, 920, -84,-32766,-32766, 727, 162, 287, 163,-32766, 420, -302, -78, -73, -72, -71, 141, 287,-32766, -70, 326, 976, 745, 744, 1231, 710, 299, 300, -69, -68, -67, -298, -591, -66, -591, -543, -543, -65, 528, 529, -46, 1259, 1260, 1261, 1262, 1256, 1257, -18, 74, 148, -543, 273, 284, 1263, 1258, 126, -543, 726, 910,-32766, 729, 919, 147, 73, -543, 1238, 922, 690, 322, 326, 710, 279,-32766,-32766,-32766, 280, -32766, 285,-32766, 286,-32766, 332, 288,-32766, 910, 289, 292, 49,-32766,-32766,-32766, 293, 274, 1033,-32766,-32766, 937, 110, -50, 685,-32766, 420, 146, 691, 826, 701, 375, 703, 436,-32766, 1353, 20, 561, 296, 645, 1036, 835, 959, 960, 1119, -543, -543, 523,-32766, 692, 693, 306, 527, 955, -110, -110, -110, 132, 922, 834, -543, 464, 710, 283, 662, 657,-32766, 1240, 1239, 1241, 678, 304, 1238, 283, -543, 10, 301, 302, 493,-32766,-32766, -32766, 663,-32766, 922,-32766, 679,-32766, 710, -4,-32766, 373, 40, -508, 956,-32766,-32766,-32766, -275, 731,-32766, -32766,-32766, 920, 303, 128, 1238,-32766, 420, 310, 0, 567, 0,-32766,-32766,-32766,-32766,-32766, 0,-32766, 0, -32766,-32766, 0,-32766, 0, 1277, -498, 0,-32766,-32766, -32766,-32766, 1279, 0,-32766,-32766, 8, 1238, 24, 372, -32766, 420, 920, 1267,-32766,-32766,-32766, 610,-32766,-32766, -32766, 939,-32766, 298, -579,-32766, 846, 41, 734, 488, -32766,-32766,-32766,-32766, 735, 854,-32766,-32766, 901, 1238, 574, 1000,-32766, 420, 977, 984,-32766,-32766,-32766, 974, -32766,-32766,-32766, 985,-32766, 910, 899,-32766, 972, 1093, 1096, 1097,-32766,-32766,-32766, 1094, 1095, 1101,-32766,-32766, 1292, -250, -250, -250,-32766, 420, 1310, 375, 1344, 650, 28, 267, -578,-32766, -577, -551, -550, -549, 959, 960, -492, 1, 835, 523, 29, 910, 1270, 30, 896, 955, -110, -110, -110, 39, 43, 47, 72, 76, 77, 78, 79, -249, -249, -249, 80, 81, 143, 375, 153, 157, 897, 247, 328, 357, 358, 359, 360, 361, 959, 960, 922, 362, 1231, 523, 710, -250, 363, 364, 896, 955, -110, -110, -110, 365, 366, 367, 369, 529, 28, 1259, 1260, 1261, 1262, 1256, 1257, 437, 555, 1348, -273, -272, 835, 1263, 1258, 13, 1270, 14,-32766, 15, 16, 18, 922, 73, 1238, 1350, 710, -249, 322, 326, 406,-32766, -32766,-32766, 484,-32766, 485,-32766, 492,-32766, 495, 496, -32766, 497, 498, 502, 503,-32766,-32766,-32766, 504, 511, 1231,-32766,-32766, 572, 696, 1249, 1190,-32766, 420, 1268, 1062, 1061, 1042, 1226, 1038, 529,-32766, 1259, 1260, 1261, 1262, 1256, 1257, -277, -102, 12, 17, 27, 295, 1263, 1258, 405, 603, 607, 636, 702, 1194, 1244, 1191, 73, 34, 1323, 0, 320, 322, 326, 371, 711, 714, 718, 719, 721, 722, 723, 724, 0, 728, 713, 0, 857, 856, 865, 949, 992, 864, 1349, 948, 946, 947, 950, 1222, 930, 940, 928, 982, 983, 634, 1347, 1304, 1293, 1311, 1320, 0, 1207, 0, 1271, 0, 326 ); protected array $actionCheck = array( 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 9, 10, 11, 14, 9, 10, 11, 44, 45, 46, 47, 48, 49, 50, 51, 52, 116, 117, 118, 119, 120, 121, 122, 37, 38, 30, 116, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 1, 1, 9, 1, 57, 9, 10, 11, 137, 1, 9, 10, 11, 50, 51, 52, 8, 1, 71, 72, 73, 74, 75, 76, 77, 31, 30, 80, 32, 33, 31, 30, 14, 1, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 116, 128, 129, 130, 131, 132, 133, 82, 80, 136, 137, 138, 139, 140, 141, 142, 143, 144, 80, 129, 130, 131, 138, 150, 151, 152, 1, 154, 8, 2, 3, 4, 5, 6, 7, 162, 9, 10, 11, 12, 13, 8, 117, 118, 1, 161, 8, 122, 9, 10, 11, 8, 8, 128, 116, 117, 118, 119, 120, 121, 122, 51, 137, 8, 37, 38, 53, 54, 55, 30, 57, 32, 33, 34, 35, 36, 37, 38, 80, 159, 8, 8, 69, 158, 57, 159, 161, 9, 10, 11, 80, 163, 167, 83, 160, 167, 162, 8, 71, 72, 73, 74, 75, 76, 77, 163, 162, 80, 30, 167, 32, 33, 34, 35, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 156, 128, 129, 130, 131, 132, 133, 163, 8, 136, 137, 138, 139, 140, 141, 142, 143, 144, 16, 9, 10, 11, 31, 150, 151, 152, 8, 154, 2, 3, 4, 5, 6, 7, 163, 9, 10, 11, 12, 13, 30, 162, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 8, 57, 31, 9, 10, 11, 16, 9, 10, 11, 117, 118, 14, 69, 162, 122, 8, 57, 9, 10, 11, 128, 97, 30, 30, 1, 32, 33, 34, 35, 36, 71, 72, 73, 74, 75, 76, 77, 8, 30, 80, 32, 33, 34, 106, 80, 108, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 70, 128, 129, 130, 131, 132, 133, 8, 70, 136, 137, 138, 139, 140, 141, 142, 143, 144, 116, 106, 1, 108, 116, 150, 151, 152, 2, 3, 4, 5, 6, 7, 80, 155, 156, 157, 12, 13, 101, 15, 138, 1, 164, 106, 138, 108, 30, 163, 37, 38, 113, 106, 107, 116, 117, 118, 119, 120, 121, 122, 123, 116, 1, 161, 134, 135, 14, 161, 106, 107, 1, 31, 134, 135, 50, 51, 8, 101, 116, 70, 56, 85, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 163, 70, 71, 72, 73, 74, 123, 8, 164, 78, 79, 80, 162, 82, 14, 156, 70, 86, 87, 88, 89, 8, 91, 97, 93, 1, 95, 158, 80, 98, 99, 84, 1, 8, 103, 104, 105, 106, 107, 116, 109, 110, 119, 120, 121, 122, 115, 116, 8, 146, 134, 135, 84, 122, 8, 124, 125, 126, 82, 1, 82, 138, 31, 85, 116, 149, 14, 136, 137, 116, 139, 140, 141, 142, 143, 144, 145, 161, 122, 163, 134, 135, 151, 152, 161, 137, 155, 156, 157, 158, 8, 138, 161, 1, 8, 149, 165, 166, 167, 75, 76, 77, 1, 155, 156, 157, 159, 161, 84, 163, 163, 122, 1, 137, 90, 84, 92, 161, 94, 8, 96, 106, 116, 108, 159, 147, 148, 159, 163, 14, 106, 163, 31, 106, 160, 108, 162, 161, 14, 70, 82, 117, 118, 167, 138, 16, 122, 1, 9, 10, 161, 127, 128, 129, 130, 131, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 37, 38, 75, 76, 82, 117, 118, 31, 37, 38, 122, 14, 155, 159, 16, 84, 128, 163, 164, 16, 159, 75, 76, 16, 163, 137, 101, 102, 16, 59, 60, 70, 16, 134, 135, 106, 107, 74, 106, 107, 70, 70, 16, 80, 1, 16, 158, 1, 149, 161, 87, 88, 89, 82, 91, 167, 93, 86, 95, 31, 84, 98, 163, 111, 112, 16, 103, 104, 105, 0, 1, 31, 109, 110, 31, 16, 30, 16, 115, 116, 35, 31, 31, 31, 31, 163, 30, 124, 31, 167, 159, 37, 38, 122, 163, 134, 135, 31, 31, 31, 35, 160, 31, 162, 134, 135, 31, 136, 137, 31, 139, 140, 141, 142, 143, 144, 31, 154, 31, 149, 31, 31, 151, 152, 163, 70, 31, 84, 74, 31, 31, 31, 161, 163, 80, 159, 80, 166, 167, 163, 35, 87, 88, 89, 35, 91, 35, 93, 35, 95, 35, 37, 98, 84, 37, 37, 70, 103, 104, 105, 37, 57, 116, 109, 110, 38, 69, 31, 77, 115, 116, 70, 116, 80, 80, 106, 92, 108, 124, 83, 97, 89, 113, 113, 138, 82, 117, 118, 82, 134, 135, 122, 85, 137, 138, 114, 127, 128, 129, 130, 131, 31, 159, 155, 149, 97, 163, 161, 96, 90, 74, 155, 156, 157, 94, 133, 80, 161, 163, 150, 134, 135, 97, 87, 88, 89, 100, 91, 159, 93, 100, 95, 163, 164, 98, 149, 159, 149, 128, 103, 104, 105, 162, 164, 74, 109, 110, 1, 132, 163, 80, 115, 116, 132, -1, 153, -1, 87, 88, 89, 124, 91, -1, 93, -1, 95, 137, -1, 98, -1, 146, 149, -1, 103, 104, 105, 74, 146, -1, 109, 110, 149, 80, 149, 149, 115, 116, 1, 160, 87, 88, 89, 153, 91, 124, 93, 154, 95, 158, 161, 98, 160, 159, 159, 102, 103, 104, 105, 74, 159, 159, 109, 110, 159, 80, 81, 159, 115, 116, 159, 159, 87, 88, 89, 159, 91, 124, 93, 159, 95, 84, 159, 98, 159, 159, 159, 159, 103, 104, 105, 159, 159, 159, 109, 110, 160, 100, 101, 102, 115, 116, 160, 106, 160, 160, 70, 71, 161, 124, 161, 161, 161, 161, 117, 118, 161, 161, 82, 122, 161, 84, 86, 161, 127, 128, 129, 130, 131, 161, 161, 161, 161, 161, 161, 161, 161, 100, 101, 102, 161, 161, 161, 106, 161, 161, 164, 161, 161, 161, 161, 161, 161, 161, 117, 118, 159, 161, 122, 122, 163, 164, 161, 161, 127, 128, 129, 130, 131, 161, 161, 161, 161, 137, 70, 139, 140, 141, 142, 143, 144, 161, 161, 164, 162, 162, 82, 151, 152, 162, 86, 162, 74, 162, 162, 162, 159, 161, 80, 164, 163, 164, 166, 167, 162, 87, 88, 89, 162, 91, 162, 93, 162, 95, 162, 162, 98, 162, 162, 162, 162, 103, 104, 105, 162, 162, 122, 109, 110, 162, 162, 162, 162, 115, 116, 162, 162, 162, 162, 162, 162, 137, 124, 139, 140, 141, 142, 143, 144, 162, 162, 162, 162, 162, 162, 151, 152, 162, 162, 162, 162, 162, 162, 162, 162, 161, 163, 162, -1, 163, 166, 167, 163, 163, 163, 163, 163, 163, 163, 163, 163, -1, 163, 163, -1, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -1, 165, -1, 166, -1, 167 ); protected array $actionBase = array( 0, -2, 152, 549, 764, 941, 981, 751, 617, 310, 123, 877, 556, 671, 671, 738, 671, 472, 626, 789, 63, 305, 305, 789, 305, 493, 493, 493, 658, 658, 658, 658, 749, 749, 897, 897, 929, 865, 831, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 51, 45, 451, 692, 1036, 1044, 1040, 1045, 1034, 1033, 1039, 1041, 1046, 1083, 1084, 795, 1085, 1086, 1082, 1087, 1042, 889, 1035, 1043, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 44, 343, 664, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 52, 52, 52, 666, 666, 47, 354, 980, 203, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 665, 339, 164, 164, 7, 7, 7, 7, 7, 50, 369, 583, -25, -25, -25, -25, 448, 741, 501, 408, 283, 338, 394, 334, 334, 14, 14, 531, 531, 9, 9, 531, 531, 531, 478, 478, 478, 478, 441, 471, 552, 428, 824, 53, 53, 53, 53, 824, 824, 824, 824, 826, 1089, 824, 824, 824, 594, 750, 750, 781, 138, 138, 138, 750, 540, 503, 503, 540, 238, 503, 67, 135, -78, 805, 377, 499, -78, 362, 656, 636, 59, 743, 624, 743, 1032, 481, 802, 802, 514, 773, 746, 878, 1064, 1049, 821, 1080, 825, 1081, 15, 370, 745, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1090, 443, 1032, 384, 1090, 1090, 1090, 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, 647, 384, 622, 641, 384, 810, 443, 51, 817, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 780, 316, 51, 45, 150, 150, 490, 83, 150, 150, 150, 150, 51, 51, 51, 51, 624, 799, 797, 627, 834, 375, 799, 799, 799, 270, 158, 69, 197, 740, 760, 345, 788, 788, 801, 900, 900, 788, 798, 788, 801, 914, 788, 788, 900, 900, 835, 180, 550, 353, 524, 565, 900, 279, 788, 788, 788, 788, 816, 571, 788, 214, 198, 788, 788, 816, 811, 785, 145, 777, 900, 900, 900, 816, 500, 777, 777, 777, 839, 845, 765, 784, 337, 297, 611, 169, 822, 784, 784, 788, 538, 765, 784, 765, 784, 837, 784, 784, 784, 765, 784, 798, 431, 784, 721, 607, 163, 784, 6, 915, 916, 723, 917, 912, 918, 964, 919, 923, 1054, 899, 930, 913, 924, 965, 906, 903, 794, 693, 698, 827, 783, 896, 792, 792, 792, 894, 792, 792, 792, 792, 792, 792, 792, 792, 693, 823, 830, 787, 933, 702, 707, 1011, 819, 926, 1088, 932, 1013, 925, 772, 711, 977, 934, 774, 1050, 935, 936, 986, 1014, 846, 1017, 963, 796, 979, 1065, 836, 945, 1055, 792, 915, 923, 735, 913, 924, 906, 903, 770, 766, 762, 763, 761, 752, 747, 748, 782, 1018, 893, 833, 880, 940, 895, 693, 886, 971, 1047, 990, 992, 1053, 803, 791, 888, 1066, 946, 952, 953, 1056, 1019, 1057, 838, 973, 775, 994, 820, 1067, 996, 997, 999, 1000, 1058, 1068, 1059, 891, 1060, 849, 814, 966, 807, 1069, 1, 806, 808, 818, 955, 484, 931, 1061, 1070, 1071, 1001, 1002, 1006, 1072, 1073, 927, 852, 975, 815, 976, 967, 855, 856, 525, 813, 1020, 800, 804, 812, 577, 640, 1074, 1075, 1076, 928, 790, 786, 860, 864, 1021, 809, 1022, 1077, 649, 867, 724, 1078, 1012, 744, 754, 281, 654, 335, 756, 779, 1063, 829, 776, 778, 954, 754, 793, 869, 1079, 870, 871, 872, 1007, 876, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 456, 456, 456, 456, 456, 456, 305, 305, 305, 305, 305, 456, 456, 456, 456, 456, 456, 456, 305, 305, 0, 0, 305, 0, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 473, 473, 289, 289, 473, 289, 473, 473, 473, 473, 473, 473, 473, 473, 473, 0, 289, 289, 289, 289, 289, 289, 289, 289, 473, 835, 473, 138, 138, 138, 138, 473, 473, 473, -88, -88, 473, 238, 473, 473, 138, 138, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 0, 0, 384, 503, 473, 798, 798, 798, 798, 473, 473, 473, 473, 503, 503, 473, 473, 473, 0, 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, 384, 0, 0, 798, 798, 473, 238, 835, 168, 473, 0, 0, 0, 0, 384, 798, 384, 443, 788, 503, 503, 788, 443, 443, 150, 51, 168, 620, 620, 620, 620, 0, 0, 624, 835, 835, 835, 835, 835, 835, 835, 835, 835, 835, 835, 798, 0, 835, 0, 798, 798, 798, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 798, 0, 0, 900, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 914, 0, 0, 0, 0, 0, 0, 798, 0, 0, 0, 0, 0, 0, 0, 0, 0, 792, 803, 0, 803, 0, 792, 792, 792, 0, 0, 0, 0, 813, 809 ); protected array $actionDefault = array( 3,32767, 102,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767, 100,32767,32767,32767,32767, 597, 597, 597, 597,32767,32767, 254, 102,32767,32767, 470, 387, 387, 387,32767,32767, 541, 541, 541, 541, 541, 541, 32767,32767,32767,32767,32767,32767, 470,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767, 100, 32767,32767,32767, 36, 7, 8, 10, 11, 49, 17, 324,32767,32767,32767,32767, 102,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767, 590,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767, 474, 453, 454, 456, 457, 386, 542, 596, 327, 593, 385, 145, 339, 329, 242, 330, 258, 475, 259, 476, 479, 480, 215, 287, 382, 149, 150, 417, 471, 419, 469, 473, 418, 392, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 390, 391, 472, 450, 449, 448,32767,32767, 415, 416,32767, 420,32767,32767,32767, 32767,32767,32767,32767, 102,32767, 389, 423, 421, 422, 439, 440, 437, 438, 441,32767,32767,32767, 442, 443, 444, 445, 316,32767,32767, 366, 364, 316, 111,32767, 32767, 430, 431,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767, 535, 447,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767, 102, 32767, 100, 537, 412, 414, 504, 425, 426, 424, 393, 32767, 511,32767, 102,32767, 513,32767,32767,32767,32767, 32767,32767,32767, 536,32767, 543, 543,32767, 497, 100, 195,32767,32767, 512,32767, 195, 195,32767,32767,32767, 32767,32767,32767,32767,32767, 604, 497, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,32767, 195, 110,32767,32767,32767, 100, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 190,32767, 268, 270, 102, 558, 195,32767, 516,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767, 509,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 497, 435, 138,32767, 138, 543, 427, 428, 429, 499, 543, 543, 543, 312, 289,32767,32767,32767,32767, 514, 514, 100, 100, 100, 100, 509,32767,32767,32767,32767, 111, 99, 99, 99, 99, 99, 103, 101,32767,32767, 32767,32767, 223, 99,32767, 101, 101,32767,32767, 223, 225, 212, 101, 227,32767, 562, 563, 223, 101, 227, 227, 227, 247, 247, 486, 318, 101, 99, 101, 101, 197, 318, 318,32767, 101, 486, 318, 486, 318, 199, 318, 318, 318, 486, 318,32767, 101, 318, 214, 99, 99, 318,32767,32767,32767, 499,32767,32767,32767,32767, 32767,32767,32767, 222,32767,32767,32767,32767,32767,32767, 32767,32767, 530,32767, 547, 560, 433, 434, 436, 545, 458, 459, 460, 461, 462, 463, 464, 466, 592,32767, 503,32767,32767,32767, 338,32767, 602,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767, 603,32767, 543,32767,32767,32767, 32767, 432, 9, 74, 492, 42, 43, 51, 57, 520, 521, 522, 523, 517, 518, 524, 519,32767,32767, 525, 568,32767,32767, 544, 595,32767,32767,32767,32767,32767, 32767, 138,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767, 530,32767, 136,32767,32767,32767,32767, 32767,32767,32767,32767, 526,32767,32767,32767, 543,32767, 32767,32767,32767, 314, 311,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767, 543,32767,32767,32767,32767,32767, 291,32767, 308, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767, 286,32767,32767, 381, 499, 294, 296, 297,32767,32767,32767,32767, 360,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 152, 152, 3, 3, 341, 152, 152, 152, 341, 341, 152, 341, 341, 341, 152, 152, 152, 152, 152, 152, 280, 185, 262, 265, 247, 247, 152, 352, 152 ); protected array $goto = array( 196, 196, 1034, 1065, 697, 431, 661, 621, 658, 319, 706, 425, 313, 314, 335, 576, 430, 336, 432, 638, 654, 655, 852, 672, 673, 674, 853, 167, 167, 167, 167, 221, 197, 193, 193, 177, 179, 216, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 188, 189, 190, 191, 192, 218, 216, 219, 536, 537, 421, 538, 540, 541, 542, 543, 544, 545, 546, 547, 1136, 168, 169, 170, 195, 171, 172, 173, 166, 174, 175, 176, 178, 215, 217, 220, 238, 243, 244, 246, 257, 258, 259, 260, 261, 262, 263, 264, 268, 269, 270, 271, 281, 282, 316, 317, 318, 426, 427, 428, 581, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 180, 237, 181, 198, 199, 200, 239, 188, 189, 190, 191, 192, 218, 1136, 201, 182, 183, 184, 202, 198, 185, 240, 203, 201, 165, 204, 205, 186, 206, 207, 208, 187, 209, 210, 211, 212, 213, 214, 855, 466, 466, 278, 278, 278, 278, 623, 623, 351, 466, 1269, 600, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1287, 1287, 599, 1100, 1287, 709, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 508, 700, 458, 1098, 975, 559, 552, 860, 419, 909, 904, 905, 918, 861, 906, 858, 907, 908, 859, 848, 827, 912, 354, 354, 354, 354, 396, 399, 560, 601, 605, 1087, 1082, 1083, 1084, 341, 552, 559, 568, 569, 344, 579, 602, 616, 617, 408, 409, 1232, 440, 479, 670, 22, 671, 886, 412, 413, 414, 481, 684, 349, 1237, 415, 1237, 1107, 1108, 347, 833, 1034, 1034, 1237, 573, 848, 1034, 1327, 1034, 1034, 1040, 1039, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1319, 1319, 1319, 1319, 1237, 893, 851, 893, 893, 1237, 1237, 1237, 1237, 1233, 1234, 1237, 1237, 1237, 833, 355, 833, 843, 996, 252, 252, 1043, 1044, 1037, 1037, 355, 355, 681, 952, 394, 926, 1029, 1045, 1046, 927, 1235, 1295, 1296, 942, 355, 355, 942, 913, 355, 914, 1354, 250, 250, 250, 250, 245, 253, 548, 548, 548, 548, 554, 604, 1285, 1285, 355, 355, 1285, 571, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 539, 539, 342, 424, 539, 611, 539, 539, 539, 539, 539, 539, 539, 539, 539, 566, 476, 1312, 1313, 733, 637, 639, 325, 308, 659, 848, 343, 342, 683, 687, 1010, 695, 704, 1006, 660, 1298, 609, 624, 627, 628, 629, 630, 651, 652, 653, 708, 1216, 944, 1314, 1315, 1217, 1220, 945, 1221, 1337, 1337, 686, 352, 353, 868, 553, 563, 450, 450, 450, 553, 1309, 563, 1309, 1133, 397, 462, 1337, 1058, 880, 1309, 1185, 867, 500, 5, 501, 6, 469, 580, 470, 471, 507, 554, 878, 1340, 1340, 1345, 1346, 433, 438, 550, 666, 550, 433, 682, 1321, 1321, 1321, 1321, 550, 337, 1041, 1041, 931, 1123, 873, 665, 1052, 1048, 1049, 619, 845, 876, 324, 275, 324, 1015, 967, 410, 705, 577, 614, 1305, 456, 872, 403, 664, 994, 969, 969, 969, 969, 866, 870, 456, 963, 970, 881, 869, 1070, 1074, 631, 633, 635, 1227, 1230, 958, 615, 978, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 999, 1018, 450, 971, 1073, 732, 477, 1228, 1307, 1307, 1073, 736, 968, 551, 1008, 1003, 882, 694, 1075, 1071, 829, 255, 255, 980, 0, 1118, 0, 1013, 1013, 694, 0, 0, 0, 694, 1116, 885 ); protected array $gotoCheck = array( 42, 42, 73, 127, 73, 66, 66, 56, 56, 66, 9, 66, 66, 66, 66, 66, 66, 66, 66, 66, 86, 86, 26, 86, 86, 86, 27, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 15, 149, 149, 23, 23, 23, 23, 108, 108, 97, 149, 108, 130, 108, 108, 108, 108, 108, 108, 108, 108, 108, 170, 170, 8, 8, 170, 8, 170, 170, 170, 170, 170, 170, 170, 170, 170, 8, 8, 83, 8, 49, 76, 76, 15, 43, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 22, 6, 15, 24, 24, 24, 24, 59, 59, 59, 59, 59, 15, 15, 15, 15, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 82, 82, 20, 83, 84, 82, 76, 82, 45, 82, 82, 82, 84, 82, 179, 73, 82, 73, 144, 144, 82, 12, 73, 73, 73, 172, 22, 73, 181, 73, 73, 118, 118, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 9, 9, 9, 9, 73, 25, 25, 25, 25, 73, 73, 73, 73, 20, 20, 73, 73, 73, 12, 14, 12, 20, 103, 5, 5, 119, 119, 89, 89, 14, 14, 89, 89, 62, 73, 89, 89, 89, 73, 20, 20, 20, 9, 14, 14, 9, 65, 14, 65, 14, 5, 5, 5, 5, 5, 5, 107, 107, 107, 107, 14, 107, 171, 171, 14, 14, 171, 104, 171, 171, 171, 171, 171, 171, 171, 171, 171, 173, 173, 168, 13, 173, 13, 173, 173, 173, 173, 173, 173, 173, 173, 173, 48, 176, 176, 176, 48, 48, 48, 169, 169, 48, 22, 168, 168, 48, 48, 48, 48, 48, 48, 64, 14, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 79, 79, 178, 178, 79, 79, 79, 79, 182, 182, 14, 97, 97, 35, 9, 9, 23, 23, 23, 9, 130, 9, 130, 150, 9, 9, 182, 114, 35, 130, 151, 35, 155, 46, 155, 46, 9, 9, 9, 9, 155, 14, 9, 182, 182, 9, 9, 117, 113, 19, 120, 19, 117, 116, 130, 130, 130, 130, 19, 29, 117, 117, 17, 17, 39, 117, 117, 117, 117, 17, 18, 9, 24, 24, 24, 17, 93, 93, 93, 2, 2, 130, 19, 17, 28, 17, 17, 19, 19, 19, 19, 17, 37, 19, 19, 19, 16, 16, 16, 16, 85, 85, 85, 17, 14, 92, 80, 16, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 50, 110, 23, 50, 130, 50, 157, 160, 130, 130, 130, 99, 16, 50, 50, 50, 41, 7, 132, 129, 7, 5, 5, 96, -1, 147, -1, 107, 107, 7, -1, -1, -1, 7, 16, 16 ); protected array $gotoBase = array( 0, 0, -221, 0, 0, 311, 200, 541, 179, -10, 0, 0, -30, 32, 11, -185, 56, 9, 173, 196, -146, 0, -59, 163, 219, 291, 18, 22, 159, 175, 0, 0, 0, 0, 0, 54, 0, 165, 0, 153, 0, 106, -1, 189, 0, 230, -291, 0, -330, 186, 519, 0, 0, 0, 0, 0, -33, 0, 0, 181, 0, 0, 280, 0, 158, 321, -236, 0, 0, 0, 0, 0, 0, -5, 0, 0, -140, 0, 0, 4, 174, 44, -246, -76, -220, 33, -698, 0, 0, 37, 0, 0, 188, 184, 0, 0, 111, -311, 0, 135, 0, 0, 0, 276, 313, 0, 0, 317, -71, 0, 162, 0, 0, 183, 166, 0, 182, 187, -3, 29, 172, 0, 0, 0, 0, 0, 0, 1, 0, 176, 167, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -12, 0, 0, 112, 0, 130, 190, 168, 0, 0, 0, -51, 0, 97, 0, 0, 169, 0, 0, 0, 0, 0, 0, 0, 71, 67, -56, 110, 241, 125, 0, 0, 82, 0, 42, 229, 0, 242, 113, 0, 0 ); protected array $gotoDefault = array( -32768, 512, 740, 4, 741, 935, 816, 825, 597, 530, 707, 348, 625, 422, 1303, 911, 1122, 578, 844, 1246, 1254, 457, 847, 330, 730, 923, 894, 895, 400, 386, 392, 398, 649, 626, 494, 879, 453, 871, 486, 874, 452, 883, 164, 418, 510, 887, 3, 890, 557, 921, 973, 387, 898, 388, 677, 900, 562, 902, 903, 395, 401, 402, 1127, 570, 622, 915, 256, 564, 916, 385, 917, 925, 390, 393, 688, 465, 505, 499, 411, 1102, 565, 608, 646, 447, 473, 620, 632, 618, 480, 434, 416, 329, 957, 965, 487, 463, 979, 350, 987, 738, 1135, 640, 489, 995, 641, 1002, 1005, 531, 532, 478, 1017, 272, 1020, 490, 19, 667, 1031, 1032, 668, 642, 1054, 643, 669, 644, 1056, 472, 598, 1064, 454, 1072, 1291, 455, 1076, 266, 1079, 277, 417, 435, 1085, 1086, 9, 1092, 698, 699, 11, 276, 509, 1117, 689, 451, 1134, 439, 1204, 1206, 558, 491, 1224, 1223, 680, 506, 1229, 448, 1294, 449, 533, 474, 315, 534, 1338, 307, 333, 312, 549, 294, 334, 535, 475, 1300, 1308, 331, 31, 1328, 1339, 575, 613 ); protected array $ruleToNonTerminal = array( 0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, 21, 21, 22, 23, 23, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 30, 30, 32, 34, 34, 28, 36, 36, 33, 38, 38, 35, 35, 37, 37, 39, 39, 31, 40, 40, 41, 43, 44, 44, 45, 45, 46, 46, 48, 47, 47, 47, 47, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 25, 25, 50, 69, 69, 72, 72, 71, 70, 70, 63, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 26, 26, 27, 27, 27, 27, 27, 88, 88, 90, 90, 83, 83, 91, 91, 92, 92, 92, 84, 84, 87, 87, 85, 85, 93, 94, 94, 57, 57, 65, 65, 68, 68, 68, 67, 95, 95, 96, 58, 58, 58, 58, 97, 97, 98, 98, 99, 99, 100, 101, 101, 102, 102, 103, 103, 55, 55, 51, 51, 105, 53, 53, 106, 52, 52, 54, 54, 64, 64, 64, 64, 81, 81, 109, 109, 111, 111, 112, 112, 112, 112, 110, 110, 110, 114, 114, 114, 114, 89, 89, 117, 117, 117, 118, 118, 115, 115, 119, 119, 121, 121, 122, 122, 116, 123, 123, 120, 124, 124, 124, 124, 113, 113, 82, 82, 82, 20, 20, 20, 126, 125, 125, 127, 127, 127, 127, 60, 128, 128, 129, 61, 131, 131, 132, 132, 133, 133, 86, 134, 134, 134, 134, 134, 134, 134, 139, 139, 140, 140, 141, 141, 141, 141, 141, 142, 143, 143, 138, 138, 135, 135, 137, 137, 145, 145, 144, 144, 144, 144, 144, 144, 144, 136, 146, 146, 148, 147, 147, 62, 104, 149, 149, 56, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 156, 150, 150, 155, 155, 158, 159, 159, 160, 161, 162, 162, 162, 162, 19, 19, 73, 73, 73, 73, 151, 151, 151, 151, 164, 164, 152, 152, 154, 154, 154, 157, 157, 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 171, 171, 108, 173, 173, 173, 173, 153, 153, 153, 153, 153, 153, 153, 153, 59, 59, 167, 167, 167, 167, 174, 174, 163, 163, 163, 175, 175, 175, 175, 175, 175, 74, 74, 66, 66, 66, 66, 130, 130, 130, 130, 178, 177, 166, 166, 166, 166, 166, 166, 166, 165, 165, 165, 176, 176, 176, 176, 107, 172, 180, 180, 179, 179, 181, 181, 181, 181, 181, 181, 181, 181, 169, 169, 169, 169, 168, 183, 182, 182, 182, 182, 182, 182, 182, 182, 184, 184, 184, 184 ); protected array $ruleToLength = array( 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 3, 4, 1, 2, 0, 1, 1, 1, 1, 4, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 3, 1, 3, 1, 2, 2, 3, 1, 3, 2, 3, 1, 3, 3, 2, 0, 1, 1, 1, 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, 6, 5, 6, 3, 2, 1, 1, 1, 1, 0, 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, 1, 0, 1, 0, 1, 3, 1, 1, 1, 8, 9, 7, 8, 7, 6, 8, 0, 2, 0, 2, 1, 2, 1, 2, 1, 1, 1, 0, 2, 0, 2, 0, 2, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, 1, 3, 0, 2, 1, 1, 1, 1, 6, 8, 6, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 1, 1, 2, 1, 1, 0, 1, 0, 2, 2, 2, 4, 3, 1, 1, 3, 1, 2, 2, 3, 2, 3, 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, 5, 6, 10, 3, 5, 1, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, 0, 4, 2, 1, 3, 2, 1, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 4, 4, 1, 4, 4, 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 3, 6, 3, 1, 1, 2, 1 ); protected function initReduceCallbacks(): void { $this->reduceCallbacks = [ 0 => null, 1 => static function ($self, $stackPos) { $self->semValue = $self->handleNamespaces($self->semStack[$stackPos-(1-1)]); }, 2 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];; }, 3 => static function ($self, $stackPos) { $self->semValue = array(); }, 4 => static function ($self, $stackPos) { $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);; if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 5 => null, 6 => null, 7 => null, 8 => null, 9 => null, 10 => null, 11 => null, 12 => null, 13 => null, 14 => null, 15 => null, 16 => null, 17 => null, 18 => null, 19 => null, 20 => null, 21 => null, 22 => null, 23 => null, 24 => null, 25 => null, 26 => null, 27 => null, 28 => null, 29 => null, 30 => null, 31 => null, 32 => null, 33 => null, 34 => null, 35 => null, 36 => null, 37 => null, 38 => null, 39 => null, 40 => null, 41 => null, 42 => null, 43 => null, 44 => null, 45 => null, 46 => null, 47 => null, 48 => null, 49 => null, 50 => null, 51 => null, 52 => null, 53 => null, 54 => null, 55 => null, 56 => null, 57 => null, 58 => null, 59 => null, 60 => null, 61 => null, 62 => null, 63 => null, 64 => null, 65 => null, 66 => null, 67 => null, 68 => null, 69 => null, 70 => null, 71 => null, 72 => null, 73 => null, 74 => null, 75 => null, 76 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; if ($self->semValue === "emitError(new Error('Cannot use "getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]))); }, 77 => null, 78 => null, 79 => null, 80 => null, 81 => null, 82 => null, 83 => null, 84 => null, 85 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 86 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 87 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 88 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 89 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 90 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 91 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 92 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 93 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 94 => null, 95 => static function ($self, $stackPos) { $self->semValue = new Name(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 96 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 97 => static function ($self, $stackPos) { }, 98 => static function ($self, $stackPos) { }, 99 => static function ($self, $stackPos) { }, 100 => static function ($self, $stackPos) { $self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]))); }, 101 => null, 102 => null, 103 => static function ($self, $stackPos) { $self->semValue = new Node\Attribute($self->semStack[$stackPos-(1-1)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 104 => static function ($self, $stackPos) { $self->semValue = new Node\Attribute($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 105 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 106 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 107 => static function ($self, $stackPos) { $self->semValue = new Node\AttributeGroup($self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 108 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 109 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 110 => static function ($self, $stackPos) { $self->semValue = []; }, 111 => null, 112 => null, 113 => null, 114 => null, 115 => static function ($self, $stackPos) { $self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 116 => static function ($self, $stackPos) { $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(3-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); $self->checkNamespace($self->semValue); }, 117 => static function ($self, $stackPos) { $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); $self->checkNamespace($self->semValue); }, 118 => static function ($self, $stackPos) { $self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); $self->checkNamespace($self->semValue); }, 119 => static function ($self, $stackPos) { $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 120 => static function ($self, $stackPos) { $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 121 => null, 122 => static function ($self, $stackPos) { $self->semValue = new Stmt\Const_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 123 => static function ($self, $stackPos) { $self->semValue = Stmt\Use_::TYPE_FUNCTION; }, 124 => static function ($self, $stackPos) { $self->semValue = Stmt\Use_::TYPE_CONSTANT; }, 125 => static function ($self, $stackPos) { $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 126 => static function ($self, $stackPos) { $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 127 => null, 128 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 129 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 130 => null, 131 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 132 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 133 => null, 134 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 135 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 136 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1)); }, 137 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3)); }, 138 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1)); }, 139 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3)); }, 140 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->semValue->type = Stmt\Use_::TYPE_NORMAL; }, 141 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; $self->semValue->type = $self->semStack[$stackPos-(2-1)]; }, 142 => null, 143 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 144 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 145 => static function ($self, $stackPos) { $self->semValue = new Node\Const_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 146 => null, 147 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 148 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 149 => static function ($self, $stackPos) { $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 150 => static function ($self, $stackPos) { $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 151 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];; }, 152 => static function ($self, $stackPos) { $self->semValue = array(); }, 153 => static function ($self, $stackPos) { $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);; if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 154 => null, 155 => null, 156 => null, 157 => static function ($self, $stackPos) { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 158 => static function ($self, $stackPos) { $self->semValue = new Stmt\Block($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 159 => static function ($self, $stackPos) { $self->semValue = new Stmt\If_($self->semStack[$stackPos-(7-3)], ['stmts' => $self->semStack[$stackPos-(7-5)], 'elseifs' => $self->semStack[$stackPos-(7-6)], 'else' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 160 => static function ($self, $stackPos) { $self->semValue = new Stmt\If_($self->semStack[$stackPos-(10-3)], ['stmts' => $self->semStack[$stackPos-(10-6)], 'elseifs' => $self->semStack[$stackPos-(10-7)], 'else' => $self->semStack[$stackPos-(10-8)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); }, 161 => static function ($self, $stackPos) { $self->semValue = new Stmt\While_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 162 => static function ($self, $stackPos) { $self->semValue = new Stmt\Do_($self->semStack[$stackPos-(7-5)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 163 => static function ($self, $stackPos) { $self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos-(9-3)], 'cond' => $self->semStack[$stackPos-(9-5)], 'loop' => $self->semStack[$stackPos-(9-7)], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 164 => static function ($self, $stackPos) { $self->semValue = new Stmt\Switch_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 165 => static function ($self, $stackPos) { $self->semValue = new Stmt\Break_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 166 => static function ($self, $stackPos) { $self->semValue = new Stmt\Continue_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 167 => static function ($self, $stackPos) { $self->semValue = new Stmt\Return_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 168 => static function ($self, $stackPos) { $self->semValue = new Stmt\Global_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 169 => static function ($self, $stackPos) { $self->semValue = new Stmt\Static_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 170 => static function ($self, $stackPos) { $self->semValue = new Stmt\Echo_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 171 => static function ($self, $stackPos) { $self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos-(1-1))); }, 172 => static function ($self, $stackPos) { $self->semValue = new Stmt\Expression($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 173 => static function ($self, $stackPos) { $self->semValue = new Stmt\Unset_($self->semStack[$stackPos-(5-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 174 => static function ($self, $stackPos) { $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos-(7-5)][1], 'stmts' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 175 => static function ($self, $stackPos) { $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-7)][0], ['keyVar' => $self->semStack[$stackPos-(9-5)], 'byRef' => $self->semStack[$stackPos-(9-7)][1], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 176 => static function ($self, $stackPos) { $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(6-3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-4)], $self->tokenEndStack[$stackPos-(6-4)])), ['stmts' => $self->semStack[$stackPos-(6-6)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 177 => static function ($self, $stackPos) { $self->semValue = new Stmt\Declare_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 178 => static function ($self, $stackPos) { $self->semValue = new Stmt\TryCatch($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->checkTryCatch($self->semValue); }, 179 => static function ($self, $stackPos) { $self->semValue = new Stmt\Goto_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 180 => static function ($self, $stackPos) { $self->semValue = new Stmt\Label($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 181 => static function ($self, $stackPos) { $self->semValue = null; }, 182 => null, 183 => static function ($self, $stackPos) { $self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); }, 184 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; }; }, 185 => static function ($self, $stackPos) { $self->semValue = array(); }, 186 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 187 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 188 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 189 => static function ($self, $stackPos) { $self->semValue = new Stmt\Catch_($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-7)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 190 => static function ($self, $stackPos) { $self->semValue = null; }, 191 => static function ($self, $stackPos) { $self->semValue = new Stmt\Finally_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 192 => null, 193 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 194 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 195 => static function ($self, $stackPos) { $self->semValue = false; }, 196 => static function ($self, $stackPos) { $self->semValue = true; }, 197 => static function ($self, $stackPos) { $self->semValue = false; }, 198 => static function ($self, $stackPos) { $self->semValue = true; }, 199 => static function ($self, $stackPos) { $self->semValue = false; }, 200 => static function ($self, $stackPos) { $self->semValue = true; }, 201 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 202 => static function ($self, $stackPos) { $self->semValue = []; }, 203 => null, 204 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 205 => static function ($self, $stackPos) { $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(8-3)], ['byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-5)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 206 => static function ($self, $stackPos) { $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(9-4)], ['byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-6)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 207 => static function ($self, $stackPos) { $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(7-2)], ['type' => $self->semStack[$stackPos-(7-1)], 'extends' => $self->semStack[$stackPos-(7-3)], 'implements' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); $self->checkClass($self->semValue, $stackPos-(7-2)); }, 208 => static function ($self, $stackPos) { $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(8-3)], ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); $self->checkClass($self->semValue, $stackPos-(8-3)); }, 209 => static function ($self, $stackPos) { $self->semValue = new Stmt\Interface_($self->semStack[$stackPos-(7-3)], ['extends' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => $self->semStack[$stackPos-(7-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); $self->checkInterface($self->semValue, $stackPos-(7-3)); }, 210 => static function ($self, $stackPos) { $self->semValue = new Stmt\Trait_($self->semStack[$stackPos-(6-3)], ['stmts' => $self->semStack[$stackPos-(6-5)], 'attrGroups' => $self->semStack[$stackPos-(6-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 211 => static function ($self, $stackPos) { $self->semValue = new Stmt\Enum_($self->semStack[$stackPos-(8-3)], ['scalarType' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); $self->checkEnum($self->semValue, $stackPos-(8-3)); }, 212 => static function ($self, $stackPos) { $self->semValue = null; }, 213 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 214 => static function ($self, $stackPos) { $self->semValue = null; }, 215 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 216 => static function ($self, $stackPos) { $self->semValue = 0; }, 217 => null, 218 => null, 219 => static function ($self, $stackPos) { $self->checkClassModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)]; }, 220 => static function ($self, $stackPos) { $self->semValue = Modifiers::ABSTRACT; }, 221 => static function ($self, $stackPos) { $self->semValue = Modifiers::FINAL; }, 222 => static function ($self, $stackPos) { $self->semValue = Modifiers::READONLY; }, 223 => static function ($self, $stackPos) { $self->semValue = null; }, 224 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 225 => static function ($self, $stackPos) { $self->semValue = array(); }, 226 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 227 => static function ($self, $stackPos) { $self->semValue = array(); }, 228 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 229 => null, 230 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 231 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 232 => null, 233 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 234 => null, 235 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 236 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; }; }, 237 => static function ($self, $stackPos) { $self->semValue = null; }, 238 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 239 => null, 240 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 241 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 242 => static function ($self, $stackPos) { $self->semValue = new Node\DeclareItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 243 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 244 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-3)]; }, 245 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 246 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(5-3)]; }, 247 => static function ($self, $stackPos) { $self->semValue = array(); }, 248 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 249 => static function ($self, $stackPos) { $self->semValue = new Stmt\Case_($self->semStack[$stackPos-(4-2)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 250 => static function ($self, $stackPos) { $self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 251 => null, 252 => null, 253 => static function ($self, $stackPos) { $self->semValue = new Expr\Match_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 254 => static function ($self, $stackPos) { $self->semValue = []; }, 255 => null, 256 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 257 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 258 => static function ($self, $stackPos) { $self->semValue = new Node\MatchArm($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 259 => static function ($self, $stackPos) { $self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 260 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 261 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 262 => static function ($self, $stackPos) { $self->semValue = array(); }, 263 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 264 => static function ($self, $stackPos) { $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 265 => static function ($self, $stackPos) { $self->semValue = array(); }, 266 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 267 => static function ($self, $stackPos) { $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue); }, 268 => static function ($self, $stackPos) { $self->semValue = null; }, 269 => static function ($self, $stackPos) { $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 270 => static function ($self, $stackPos) { $self->semValue = null; }, 271 => static function ($self, $stackPos) { $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue); }, 272 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)], false); }, 273 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(2-2)], true); }, 274 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)], false); }, 275 => static function ($self, $stackPos) { $self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos-(1-1)]), false); }, 276 => null, 277 => static function ($self, $stackPos) { $self->semValue = array(); }, 278 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 279 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 280 => static function ($self, $stackPos) { $self->semValue = 0; }, 281 => static function ($self, $stackPos) { $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)]; }, 282 => static function ($self, $stackPos) { $self->semValue = Modifiers::PUBLIC; }, 283 => static function ($self, $stackPos) { $self->semValue = Modifiers::PROTECTED; }, 284 => static function ($self, $stackPos) { $self->semValue = Modifiers::PRIVATE; }, 285 => static function ($self, $stackPos) { $self->semValue = Modifiers::READONLY; }, 286 => static function ($self, $stackPos) { $self->semValue = new Node\Param($self->semStack[$stackPos-(6-6)], null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]); $self->checkParam($self->semValue); }, 287 => static function ($self, $stackPos) { $self->semValue = new Node\Param($self->semStack[$stackPos-(8-6)], $self->semStack[$stackPos-(8-8)], $self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(8-2)], $self->semStack[$stackPos-(8-1)]); $self->checkParam($self->semValue); }, 288 => static function ($self, $stackPos) { $self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]); }, 289 => null, 290 => static function ($self, $stackPos) { $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 291 => static function ($self, $stackPos) { $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 292 => null, 293 => null, 294 => static function ($self, $stackPos) { $self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 295 => static function ($self, $stackPos) { $self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos-(1-1)]); }, 296 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 297 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 298 => null, 299 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 300 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 301 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 302 => null, 303 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 304 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 305 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 306 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 307 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 308 => static function ($self, $stackPos) { $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 309 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 310 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 311 => static function ($self, $stackPos) { $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 312 => null, 313 => static function ($self, $stackPos) { $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 314 => static function ($self, $stackPos) { $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 315 => null, 316 => static function ($self, $stackPos) { $self->semValue = null; }, 317 => null, 318 => static function ($self, $stackPos) { $self->semValue = null; }, 319 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 320 => static function ($self, $stackPos) { $self->semValue = null; }, 321 => static function ($self, $stackPos) { $self->semValue = array(); }, 322 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 323 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-2)]); }, 324 => static function ($self, $stackPos) { $self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 325 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 326 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 327 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(1-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 328 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], true, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 329 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], false, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 330 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(3-3)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(3-1)]); }, 331 => null, 332 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 333 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 334 => null, 335 => null, 336 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 337 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 338 => static function ($self, $stackPos) { $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 339 => static function ($self, $stackPos) { $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 340 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; } else { $self->semValue = $self->semStack[$stackPos-(2-1)]; } }, 341 => static function ($self, $stackPos) { $self->semValue = array(); }, 342 => static function ($self, $stackPos) { $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);; if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 343 => static function ($self, $stackPos) { $self->semValue = new Stmt\Property($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-1)]); $self->checkProperty($self->semValue, $stackPos-(5-2)); }, 344 => static function ($self, $stackPos) { $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]); $self->checkClassConst($self->semValue, $stackPos-(5-2)); }, 345 => static function ($self, $stackPos) { $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-1)], $self->semStack[$stackPos-(6-4)]); $self->checkClassConst($self->semValue, $stackPos-(6-2)); }, 346 => static function ($self, $stackPos) { $self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos-(10-5)], ['type' => $self->semStack[$stackPos-(10-2)], 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-7)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); $self->checkClassMethod($self->semValue, $stackPos-(10-2)); }, 347 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUse($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 348 => static function ($self, $stackPos) { $self->semValue = new Stmt\EnumCase($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 349 => static function ($self, $stackPos) { $self->semValue = null; }, 350 => static function ($self, $stackPos) { $self->semValue = array(); }, 351 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 352 => static function ($self, $stackPos) { $self->semValue = array(); }, 353 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 354 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 355 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(5-1)][0], $self->semStack[$stackPos-(5-1)][1], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 356 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 357 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 358 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 359 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 360 => null, 361 => static function ($self, $stackPos) { $self->semValue = array(null, $self->semStack[$stackPos-(1-1)]); }, 362 => static function ($self, $stackPos) { $self->semValue = null; }, 363 => null, 364 => null, 365 => static function ($self, $stackPos) { $self->semValue = 0; }, 366 => static function ($self, $stackPos) { $self->semValue = 0; }, 367 => null, 368 => null, 369 => static function ($self, $stackPos) { $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)]; }, 370 => static function ($self, $stackPos) { $self->semValue = Modifiers::PUBLIC; }, 371 => static function ($self, $stackPos) { $self->semValue = Modifiers::PROTECTED; }, 372 => static function ($self, $stackPos) { $self->semValue = Modifiers::PRIVATE; }, 373 => static function ($self, $stackPos) { $self->semValue = Modifiers::STATIC; }, 374 => static function ($self, $stackPos) { $self->semValue = Modifiers::ABSTRACT; }, 375 => static function ($self, $stackPos) { $self->semValue = Modifiers::FINAL; }, 376 => static function ($self, $stackPos) { $self->semValue = Modifiers::READONLY; }, 377 => null, 378 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 379 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 380 => static function ($self, $stackPos) { $self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 381 => static function ($self, $stackPos) { $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 382 => static function ($self, $stackPos) { $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 383 => null, 384 => null, 385 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 386 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 387 => static function ($self, $stackPos) { $self->semValue = array(); }, 388 => null, 389 => null, 390 => static function ($self, $stackPos) { $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 391 => static function ($self, $stackPos) { $self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos-(3-1)]), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 392 => static function ($self, $stackPos) { $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 393 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 394 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); if (!$self->phpVersion->allowsAssignNewByReference()) { $self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]))); } }, 395 => null, 396 => null, 397 => static function ($self, $stackPos) { $self->semValue = new Expr\Clone_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 398 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 399 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 400 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 401 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 402 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 403 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 404 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 405 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 406 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 407 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 408 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 409 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 410 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 411 => static function ($self, $stackPos) { $self->semValue = new Expr\PostInc($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 412 => static function ($self, $stackPos) { $self->semValue = new Expr\PreInc($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 413 => static function ($self, $stackPos) { $self->semValue = new Expr\PostDec($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 414 => static function ($self, $stackPos) { $self->semValue = new Expr\PreDec($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 415 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 416 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 417 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 418 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 419 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 420 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 421 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 422 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 423 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 424 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 425 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 426 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 427 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 428 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 429 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 430 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 431 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 432 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 433 => static function ($self, $stackPos) { $self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 434 => static function ($self, $stackPos) { $self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 435 => static function ($self, $stackPos) { $self->semValue = new Expr\BooleanNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 436 => static function ($self, $stackPos) { $self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 437 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 438 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 439 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 440 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 441 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 442 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 443 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 444 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 445 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 446 => static function ($self, $stackPos) { $self->semValue = new Expr\Instanceof_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 447 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 448 => static function ($self, $stackPos) { $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 449 => static function ($self, $stackPos) { $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(4-1)], null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 450 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 451 => static function ($self, $stackPos) { $self->semValue = new Expr\Isset_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 452 => static function ($self, $stackPos) { $self->semValue = new Expr\Empty_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 453 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 454 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 455 => static function ($self, $stackPos) { $self->semValue = new Expr\Eval_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 456 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 457 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 458 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 459 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos-(2-1)]); $self->semValue = new Expr\Cast\Double($self->semStack[$stackPos-(2-2)], $attrs); }, 460 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\String_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 461 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 462 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 463 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 464 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 465 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = strtolower($self->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; $self->semValue = new Expr\Exit_($self->semStack[$stackPos-(2-2)], $attrs); }, 466 => static function ($self, $stackPos) { $self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 467 => null, 468 => static function ($self, $stackPos) { $self->semValue = new Expr\ShellExec($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 469 => static function ($self, $stackPos) { $self->semValue = new Expr\Print_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 470 => static function ($self, $stackPos) { $self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 471 => static function ($self, $stackPos) { $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(2-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 472 => static function ($self, $stackPos) { $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 473 => static function ($self, $stackPos) { $self->semValue = new Expr\YieldFrom($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 474 => static function ($self, $stackPos) { $self->semValue = new Expr\Throw_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 475 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'returnType' => $self->semStack[$stackPos-(8-6)], 'expr' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 476 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 477 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'uses' => $self->semStack[$stackPos-(8-6)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 478 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 479 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 480 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'returnType' => $self->semStack[$stackPos-(10-8)], 'expr' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); }, 481 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 482 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'uses' => $self->semStack[$stackPos-(10-8)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); }, 483 => static function ($self, $stackPos) { $self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos-(8-3)]); $self->checkClass($self->semValue[0], -1); }, 484 => static function ($self, $stackPos) { $self->semValue = new Expr\New_($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 485 => static function ($self, $stackPos) { list($class, $ctorArgs) = $self->semStack[$stackPos-(2-2)]; $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 486 => static function ($self, $stackPos) { $self->semValue = array(); }, 487 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-3)]; }, 488 => null, 489 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 490 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 491 => static function ($self, $stackPos) { $self->semValue = new Node\ClosureUse($self->semStack[$stackPos-(2-2)], $self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 492 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 493 => static function ($self, $stackPos) { $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 494 => static function ($self, $stackPos) { $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 495 => static function ($self, $stackPos) { $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 496 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 497 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 498 => null, 499 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 500 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 501 => static function ($self, $stackPos) { $self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 502 => static function ($self, $stackPos) { $self->semValue = new Name\Relative(substr($self->semStack[$stackPos-(1-1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 503 => null, 504 => null, 505 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 506 => static function ($self, $stackPos) { $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 507 => null, 508 => null, 509 => static function ($self, $stackPos) { $self->semValue = null; }, 510 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 511 => static function ($self, $stackPos) { $self->semValue = array(); }, 512 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); foreach ($self->semValue as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; }, 513 => static function ($self, $stackPos) { foreach ($self->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 514 => static function ($self, $stackPos) { $self->semValue = array(); }, 515 => null, 516 => static function ($self, $stackPos) { $self->semValue = new Expr\ConstFetch($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 517 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 518 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 519 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 520 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 521 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 522 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 523 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 524 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 525 => static function ($self, $stackPos) { $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 526 => static function ($self, $stackPos) { $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 527 => static function ($self, $stackPos) { $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)])), $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 528 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT; $self->semValue = new Expr\Array_($self->semStack[$stackPos-(3-2)], $attrs); }, 529 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_LONG; $self->semValue = new Expr\Array_($self->semStack[$stackPos-(4-3)], $attrs); $self->createdArrays->attach($self->semValue); }, 530 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->createdArrays->attach($self->semValue); }, 531 => static function ($self, $stackPos) { $self->semValue = Scalar\String_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes()); }, 532 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; foreach ($self->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos-(3-2)], $attrs); }, 533 => static function ($self, $stackPos) { $self->semValue = $self->parseLNumber($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals()); }, 534 => static function ($self, $stackPos) { $self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 535 => null, 536 => null, 537 => null, 538 => static function ($self, $stackPos) { $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true); }, 539 => static function ($self, $stackPos) { $self->semValue = $self->parseDocString($self->semStack[$stackPos-(2-1)], '', $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(2-2)], $self->tokenEndStack[$stackPos-(2-2)]), true); }, 540 => static function ($self, $stackPos) { $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true); }, 541 => static function ($self, $stackPos) { $self->semValue = null; }, 542 => null, 543 => null, 544 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 545 => null, 546 => null, 547 => null, 548 => null, 549 => null, 550 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 551 => null, 552 => null, 553 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 554 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 555 => null, 556 => static function ($self, $stackPos) { $self->semValue = new Expr\MethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 557 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 558 => static function ($self, $stackPos) { $self->semValue = null; }, 559 => null, 560 => null, 561 => null, 562 => static function ($self, $stackPos) { $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 563 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 564 => null, 565 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 566 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 567 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 568 => static function ($self, $stackPos) { $var = $self->semStack[$stackPos-(1-1)]->name; $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])) : $var; }, 569 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 570 => null, 571 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 572 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 573 => static function ($self, $stackPos) { $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 574 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 575 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 576 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 577 => null, 578 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 579 => null, 580 => null, 581 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 582 => null, 583 => static function ($self, $stackPos) { $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 584 => static function ($self, $stackPos) { $self->semValue = new Expr\List_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST); $self->postprocessList($self->semValue); }, 585 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; $end = count($self->semValue)-1; if ($self->semValue[$end]->value instanceof Expr\Error) array_pop($self->semValue); }, 586 => null, 587 => static function ($self, $stackPos) { }, 588 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 589 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 590 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 591 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 592 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 593 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 594 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-1)], true, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 595 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 596 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), true); }, 597 => static function ($self, $stackPos) { $attrs = $self->createEmptyElemAttributes($self->tokenPos); $self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, false, $attrs); }, 598 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 599 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 600 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 601 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)]); }, 602 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); $attrs['rawValue'] = $self->semStack[$stackPos-(1-1)]; $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos-(1-1)], $attrs); }, 603 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 604 => null, 605 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 606 => static function ($self, $stackPos) { $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 607 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 608 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 609 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 610 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 611 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 612 => static function ($self, $stackPos) { $self->semValue = new Scalar\String_($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 613 => static function ($self, $stackPos) { $self->semValue = $self->parseNumString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 614 => static function ($self, $stackPos) { $self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 615 => null, ]; } } '", "T_IS_GREATER_OR_EQUAL", "'.'", "T_SL", "T_SR", "'+'", "'-'", "'*'", "'/'", "'%'", "'!'", "T_INSTANCEOF", "'~'", "T_INC", "T_DEC", "T_INT_CAST", "T_DOUBLE_CAST", "T_STRING_CAST", "T_ARRAY_CAST", "T_OBJECT_CAST", "T_BOOL_CAST", "T_UNSET_CAST", "'@'", "T_POW", "'['", "T_NEW", "T_CLONE", "T_EXIT", "T_IF", "T_ELSEIF", "T_ELSE", "T_ENDIF", "T_LNUMBER", "T_DNUMBER", "T_STRING", "T_STRING_VARNAME", "T_VARIABLE", "T_NUM_STRING", "T_INLINE_HTML", "T_ENCAPSED_AND_WHITESPACE", "T_CONSTANT_ENCAPSED_STRING", "T_ECHO", "T_DO", "T_WHILE", "T_ENDWHILE", "T_FOR", "T_ENDFOR", "T_FOREACH", "T_ENDFOREACH", "T_DECLARE", "T_ENDDECLARE", "T_AS", "T_SWITCH", "T_MATCH", "T_ENDSWITCH", "T_CASE", "T_DEFAULT", "T_BREAK", "T_CONTINUE", "T_GOTO", "T_FUNCTION", "T_FN", "T_CONST", "T_RETURN", "T_TRY", "T_CATCH", "T_FINALLY", "T_USE", "T_INSTEADOF", "T_GLOBAL", "T_STATIC", "T_ABSTRACT", "T_FINAL", "T_PRIVATE", "T_PROTECTED", "T_PUBLIC", "T_READONLY", "T_VAR", "T_UNSET", "T_ISSET", "T_EMPTY", "T_HALT_COMPILER", "T_CLASS", "T_TRAIT", "T_INTERFACE", "T_ENUM", "T_EXTENDS", "T_IMPLEMENTS", "T_OBJECT_OPERATOR", "T_NULLSAFE_OBJECT_OPERATOR", "T_LIST", "T_ARRAY", "T_CALLABLE", "T_CLASS_C", "T_TRAIT_C", "T_METHOD_C", "T_FUNC_C", "T_LINE", "T_FILE", "T_START_HEREDOC", "T_END_HEREDOC", "T_DOLLAR_OPEN_CURLY_BRACES", "T_CURLY_OPEN", "T_PAAMAYIM_NEKUDOTAYIM", "T_NAMESPACE", "T_NS_C", "T_DIR", "T_NS_SEPARATOR", "T_ELLIPSIS", "T_NAME_FULLY_QUALIFIED", "T_NAME_QUALIFIED", "T_NAME_RELATIVE", "T_ATTRIBUTE", "';'", "']'", "'('", "')'", "'{'", "'}'", "'`'", "'\"'", "'$'" ); protected array $tokenToSymbol = array( 0, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 56, 166, 168, 167, 55, 168, 168, 161, 162, 53, 51, 8, 52, 48, 54, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 31, 159, 44, 16, 46, 30, 68, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 70, 168, 160, 36, 168, 165, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 163, 35, 164, 58, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43, 45, 47, 49, 50, 57, 59, 60, 61, 62, 63, 64, 65, 66, 67, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158 ); protected array $action = array( 133, 134, 135, 582, 136, 137, 0, 751, 752, 753, 138, 38, 327,-32766,-32766,-32766,-32766,-32766,-32766, 837, 826,-32767,-32767,-32767,-32767, 102, 103, 104, 1112, 1113, 1114, 1111, 1110, 1109, 1115, 745, 744,-32766, 1027,-32766, -32766,-32766,-32766,-32766,-32766,-32766,-32767,-32767,-32767,-32767, -32767, 1245,-32766,-32766, 1322, 754, 1112, 1113, 1114, 1111, 1110, 1109, 1115, 459, 460, 461, 2, 990, 1306, 265, 139, 404, 758, 759, 760, 761, 467, 468, 429, 835, 606, -16, 1341, 23, 292, 815, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 791, 583, 792, 793, 794, 795, 783, 784, 345, 346, 786, 787, 772, 773, 774, 776, 777, 778, 356, 818, 819, 820, 821, 822, 584, 779, 780, 585, 586, 941, 803, 801, 802, 814, 798, 799, 835, 826, 587, 588, 797, 589, 590, 591, 592, 593, 594, -328, 36, 251, 35, -194, 800, 595, 596, -193, 140, -85, 133, 134, 135, 582, 136, 137, 1060, 751, 752, 753, 138, 38, 129, -110, -110, -585, -32766, -585, -110,-32766,-32766,-32766, 241, 836, -110, 145, 959, 960,-32766,-32766,-32766, 961, -594,-32766, 482, 745, 744, 955, 1036, -594,-32766, 991,-32766,-32766,-32766,-32766, -32766,-32766,-32766,-32766,-32766,-32766,-32766,-32766, 299, 754, 831, 75,-32766,-32766,-32766, 291, 142, 326, 242, -85, 326, 382, 381, 265, 139, 404, 758, 759, 760, 761, 82, 423, 429,-32766, 326,-32766,-32766,-32766,-32766, 815, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 791, 583, 792, 793, 794, 795, 783, 784, 345, 346, 786, 787, 772, 773, 774, 776, 777, 778, 356, 818, 819, 820, 821, 822, 584, 779, 780, 585, 586, 254, 803, 801, 802, 814, 798, 799, 832, 725, 587, 588, 797, 589, 590, 591, 592, 593, 594, -328, 83, 84, 85, -194, 800, 595, 596, -193, 149, 775, 746, 747, 748, 749, 750, 151, 751, 752, 753, 788, 789, 37, 483, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, -594, 274, -594,-32766,-32766, -32766,-32766,-32766,-32766, 310, 1089, 127, 312, 110, 737, 1326, 21, 754,-32766,-32766,-32766, -272, 1325,-32766,-32766, 1088,-32766,-32766,-32766,-32766,-32766, 755, 756, 757, 758, 759, 760, 761, 1104,-32766, 824,-32766,-32766, -545, 429, 1036, 323, 815, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 791, 813, 792, 793, 794, 795, 783, 784, 785, 812, 786, 787, 772, 773, 774, 776, 777, 778, 817, 818, 819, 820, 821, 822, 823, 779, 780, 781, 782, 1033, 803, 801, 802, 814, 798, 799, 745, 744, 790, 796, 797, 804, 805, 807, 806, 808, 809, 152,-32766, -545, -545, 1036, 800, 811, 810, 50, 51, 52, 513, 53, 54, 1240, 1239, 1241, -545, 55, 56, -110, 57,-32766, 1090, 920, -110, 556, -110, 292, -551, 339, -545, 306, 103, 104, -110, -110, -110, -110, -110, -110, -110, -110, 105, 106, 107, 108, 109, 1245, 274, 380, 381, -591, -367, 715, -367, 340, 58, 59, -591, 423, 110, 60, 370, 61, 248, 249, 62, 63, 64, 65, 66, 67, 68, 69, -544, 28, 267, 70, 445, 514,-32766, 374, -342, 1272, 1273, 515, 1278, 835, 862, 389, 863, 1270, 42, 25, 516, 943, 517, 943, 518, 920, 519, 299, 1036, 520, 521, 1266, 910, 441, 44, 45, 446, 377, 376,-32766, 46, 522, 1023, 1022, 1021, 1024, 368, 338, 391, 1238, 7, 291, 442, 1231, 835, 524, 525, 526, 443, 1245, 357, 1036, 362, 834, -544, -544, 154, 528, 529, 444, 1259, 1260, 1261, 1262, 1256, 1257, 298,-32766,-32766, -544, -548, 1059, 1263, 1258, 291, 1236, 1240, 1239, 1241, 299, 841, -550, 71, -544, 656, 26, 321, 322, 326, -153, -153, -153, 920, 612, 675, 676, 1035, 922, 910,-32766, 286, 710, 835, 155, -153, 828, -153, 862, -153, 863, -153, 150, 407, 156, 1240, 1239, 1241,-32766,-32766,-32766, 375, 1351, 716, 75, 1352, 158, -591, 33, -591, 326, 835, 959, 960, -78, -548, -548, 523, 920,-32766, 378, 379, 896, 955, -110, -110, -110, 32, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 745, 744, -58, -548, -57, -110, -110, 717, 745, 744, -110, 383, 384, 922, 1033, 910, -110, 710, -153, 647, 648, 830, 124, 141, 125, -32766, 1033, 326, 712, 1150, 1152, 48, 130, 131, 144, 159, 1036,-32766, 160, 161, -543, 28, 162, 1238, 920, 163, 299, 920, 1036, 75,-32766,-32766,-32766, 835,-32766, 326,-32766, 1270,-32766, 282, 910,-32766, -87, -84, -78, -73,-32766,-32766,-32766, -4, 920, 282,-32766,-32766, 720, -72, -71, 727,-32766, 420, -70, -69, -68, -67, -66, 287, 286,-32766, -65, -46, 922, 745, 744, 1231, 710, 300, 301, -546, -18, 148, -302, 273, 283, 726, -543, -543, 729, 528, 529, 920, 1259, 1260, 1261, 1262, 1256, 1257, 919, 74, 147, -543, 288, 293, 1263, 1258, 126, -298, 280, 910,-32766, 281, 910, 284, 73, -543, 1238, 976, 690, 322, 326, 710, 285,-32766,-32766,-32766, 332, -32766, 274,-32766, 294,-32766, 937, 110,-32766, 910, 685, 835, -543,-32766,-32766,-32766, 826, -546, -546,-32766,-32766, 146,-32766, -50, 701,-32766, 420, 703, 691, 20, 1119, 375, -546, 436,-32766, 645, 1353, 1277, 297, 657,-32766, 1279, 959, 960, 561, 956, -546, 523, 910, 692, 693, 678, 527, 955, -110, -110, -110, 132, 922, 662, 663, 922, 710, 464, -508, 710,-32766, 1240, 1239, 1241, 493, 679, 1238, 282, 939, 10, -543, -543, 40,-32766,-32766, -32766, 731,-32766, 922,-32766, 307,-32766, 710, -4,-32766, -543, 305, 41, 304,-32766,-32766,-32766, 0, 0,-32766, -32766,-32766, 920, 0, -543, 1238,-32766, 420, 311, 0, 567, 299,-32766,-32766,-32766,-32766,-32766, -498,-32766, 897, -32766, 0, 922,-32766, 8, 0, 710, 24,-32766,-32766, -32766,-32766, 372, 610,-32766,-32766, 834, 1238, 734, -275, -32766, 420, 920, 735,-32766,-32766,-32766, 854,-32766,-32766, -32766, 901,-32766, 1000, 977,-32766, 49, 984, 974, 488, -32766,-32766,-32766,-32766, 985, 899,-32766,-32766, 972, 1238, 574, 1093,-32766, 420, 1096, 1097,-32766,-32766,-32766, 1094, -32766,-32766,-32766, 1095,-32766, 910, 1101,-32766, 1267, 846, 1292, 1310,-32766,-32766,-32766, 1344, 650, 34,-32766,-32766, -579, -250, -250, -250,-32766, 420, -578, 375, -577, -551, 28, 267, -550,-32766, -549, -492, 1, 29, 959, 960, 302, 303, 835, 523, 30, 910, 1270, 39, 896, 955, -110, -110, -110, 43, 47, 373, 72, 76, 77, 78, 79, -249, -249, -249, 80, 81, 143, 375, 153, 128, -273, 157, 247, 328, 357, 358, 359, 360, 959, 960, 922, 361, 1231, 523, 710, -250, 362, 363, 896, 955, -110, -110, -110, 364, 365, 366, 367, 529, 28, 1259, 1260, 1261, 1262, 1256, 1257, 369, 437, 555, 1207, -272, 835, 1263, 1258, 13, 1270, 14,-32766, 15, 16, 18, 922, 73, 1238, 1348, 710, -249, 322, 326, 406,-32766, -32766,-32766, 484,-32766, 485,-32766, 492,-32766, 495, 496, -32766, 497, 498, 502, 503,-32766,-32766,-32766, 504, 511, 1231,-32766,-32766, 572, 696, 1249, 1190,-32766, 420, 1268, 1062, 1061, 1042, 1226, 1038, 529,-32766, 1259, 1260, 1261, 1262, 1256, 1257, -277, -102, 12, 17, 27, 296, 1263, 1258, 405, 603, 607, 636, 702, 1194, 1244, 1191, 73, 320, 1323, 0, 371, 322, 326, 711, 714, 718, 719, 721, 722, 723, 724, 728, 0, 713, 0, 1350, 857, 856, 865, 949, 992, 864, 1349, 948, 946, 947, 950, 1222, 930, 940, 928, 982, 983, 634, 1347, 1304, 1293, 1311, 1320, 0, 0, 1271, 0, 326 ); protected array $actionCheck = array( 2, 3, 4, 5, 6, 7, 0, 9, 10, 11, 12, 13, 70, 9, 10, 11, 9, 10, 11, 1, 80, 44, 45, 46, 47, 48, 49, 50, 116, 117, 118, 119, 120, 121, 122, 37, 38, 30, 1, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 1, 9, 10, 1, 57, 116, 117, 118, 119, 120, 121, 122, 129, 130, 131, 8, 31, 1, 71, 72, 73, 74, 75, 76, 77, 134, 135, 80, 82, 1, 31, 85, 8, 30, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 1, 128, 129, 130, 131, 132, 133, 82, 80, 136, 137, 138, 139, 140, 141, 142, 143, 144, 8, 147, 148, 8, 8, 150, 151, 152, 8, 154, 31, 2, 3, 4, 5, 6, 7, 162, 9, 10, 11, 12, 13, 8, 117, 118, 160, 116, 162, 122, 9, 10, 11, 97, 159, 128, 8, 117, 118, 9, 10, 11, 122, 1, 137, 31, 37, 38, 128, 138, 8, 30, 159, 32, 33, 34, 35, 36, 37, 38, 30, 9, 32, 33, 34, 158, 57, 80, 161, 9, 10, 11, 161, 163, 167, 14, 97, 167, 106, 107, 71, 72, 73, 74, 75, 76, 77, 163, 116, 80, 30, 167, 32, 33, 34, 35, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 8, 128, 129, 130, 131, 132, 133, 156, 163, 136, 137, 138, 139, 140, 141, 142, 143, 144, 162, 9, 10, 11, 162, 150, 151, 152, 162, 154, 2, 3, 4, 5, 6, 7, 14, 9, 10, 11, 12, 13, 30, 163, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 160, 57, 162, 9, 10, 11, 9, 10, 11, 8, 159, 14, 8, 69, 163, 1, 101, 57, 9, 10, 11, 162, 8, 116, 30, 1, 32, 33, 34, 35, 36, 71, 72, 73, 74, 75, 76, 77, 123, 30, 80, 32, 33, 70, 80, 138, 8, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 116, 128, 129, 130, 131, 132, 133, 37, 38, 136, 137, 138, 139, 140, 141, 142, 143, 144, 14, 116, 134, 135, 138, 150, 151, 152, 2, 3, 4, 5, 6, 7, 155, 156, 157, 149, 12, 13, 101, 15, 137, 164, 1, 106, 85, 108, 30, 161, 8, 163, 113, 49, 50, 116, 117, 118, 119, 120, 121, 122, 123, 51, 52, 53, 54, 55, 1, 57, 106, 107, 1, 106, 31, 108, 8, 51, 52, 8, 116, 69, 56, 8, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 70, 71, 72, 73, 74, 116, 8, 164, 78, 79, 80, 146, 82, 106, 8, 108, 86, 87, 88, 89, 122, 91, 122, 93, 1, 95, 158, 138, 98, 99, 1, 84, 8, 103, 104, 105, 106, 107, 116, 109, 110, 119, 120, 121, 122, 115, 116, 106, 80, 108, 161, 8, 122, 82, 124, 125, 126, 8, 1, 161, 138, 161, 155, 134, 135, 14, 136, 137, 8, 139, 140, 141, 142, 143, 144, 145, 51, 52, 149, 70, 1, 151, 152, 161, 116, 155, 156, 157, 158, 8, 161, 161, 163, 75, 76, 165, 166, 167, 75, 76, 77, 1, 52, 75, 76, 137, 159, 84, 137, 30, 163, 82, 14, 90, 80, 92, 106, 94, 108, 96, 101, 102, 14, 155, 156, 157, 9, 10, 11, 106, 80, 31, 161, 83, 14, 160, 14, 162, 167, 82, 117, 118, 16, 134, 135, 122, 1, 30, 106, 107, 127, 128, 129, 130, 131, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 37, 38, 16, 163, 16, 117, 118, 31, 37, 38, 122, 106, 107, 159, 116, 84, 128, 163, 164, 111, 112, 156, 16, 163, 16, 137, 116, 167, 163, 59, 60, 70, 16, 16, 16, 16, 138, 74, 16, 16, 70, 70, 16, 80, 1, 16, 158, 1, 138, 161, 87, 88, 89, 82, 91, 167, 93, 86, 95, 161, 84, 98, 31, 31, 31, 31, 103, 104, 105, 0, 1, 161, 109, 110, 31, 31, 31, 31, 115, 116, 31, 31, 31, 31, 31, 37, 30, 124, 31, 31, 159, 37, 38, 122, 163, 134, 135, 70, 31, 31, 35, 31, 31, 31, 134, 135, 31, 136, 137, 1, 139, 140, 141, 142, 143, 144, 31, 154, 31, 149, 37, 37, 151, 152, 163, 35, 35, 84, 74, 35, 84, 35, 161, 163, 80, 159, 80, 166, 167, 163, 35, 87, 88, 89, 35, 91, 57, 93, 37, 95, 38, 69, 98, 84, 77, 82, 70, 103, 104, 105, 80, 134, 135, 109, 110, 70, 85, 31, 80, 115, 116, 92, 116, 97, 82, 106, 149, 108, 124, 113, 83, 146, 113, 90, 137, 146, 117, 118, 89, 128, 163, 122, 84, 137, 138, 94, 127, 128, 129, 130, 131, 31, 159, 96, 100, 159, 163, 97, 149, 163, 74, 155, 156, 157, 97, 100, 80, 161, 154, 150, 134, 135, 159, 87, 88, 89, 164, 91, 159, 93, 114, 95, 163, 164, 98, 149, 133, 159, 132, 103, 104, 105, -1, -1, 74, 109, 110, 1, -1, 163, 80, 115, 116, 132, -1, 153, 158, 87, 88, 89, 124, 91, 149, 93, 164, 95, -1, 159, 98, 149, -1, 163, 149, 103, 104, 105, 74, 149, 153, 109, 110, 155, 80, 159, 162, 115, 116, 1, 159, 87, 88, 89, 159, 91, 124, 93, 159, 95, 159, 159, 98, 70, 159, 159, 102, 103, 104, 105, 74, 159, 159, 109, 110, 159, 80, 81, 159, 115, 116, 159, 159, 87, 88, 89, 159, 91, 124, 93, 159, 95, 84, 159, 98, 160, 160, 160, 160, 103, 104, 105, 160, 160, 163, 109, 110, 161, 100, 101, 102, 115, 116, 161, 106, 161, 161, 70, 71, 161, 124, 161, 161, 161, 161, 117, 118, 134, 135, 82, 122, 161, 84, 86, 161, 127, 128, 129, 130, 131, 161, 161, 149, 161, 161, 161, 161, 161, 100, 101, 102, 161, 161, 161, 106, 161, 163, 162, 161, 161, 161, 161, 161, 161, 161, 117, 118, 159, 161, 122, 122, 163, 164, 161, 161, 127, 128, 129, 130, 131, 161, 161, 161, 161, 137, 70, 139, 140, 141, 142, 143, 144, 161, 161, 161, 165, 162, 82, 151, 152, 162, 86, 162, 74, 162, 162, 162, 159, 161, 80, 164, 163, 164, 166, 167, 162, 87, 88, 89, 162, 91, 162, 93, 162, 95, 162, 162, 98, 162, 162, 162, 162, 103, 104, 105, 162, 162, 122, 109, 110, 162, 162, 162, 162, 115, 116, 162, 162, 162, 162, 162, 162, 137, 124, 139, 140, 141, 142, 143, 144, 162, 162, 162, 162, 162, 162, 151, 152, 162, 162, 162, 162, 162, 162, 162, 162, 161, 163, 162, -1, 163, 166, 167, 163, 163, 163, 163, 163, 163, 163, 163, 163, -1, 163, -1, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, -1, -1, 166, -1, 167 ); protected array $actionBase = array( 0, -2, 152, 549, 764, 941, 981, 751, 555, 309, 560, 864, 626, 738, 738, 741, 738, 473, 671, 783, -60, 305, 305, 783, 305, 803, 803, 803, 658, 658, 658, 658, 749, 749, 897, 897, 929, 865, 831, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 18, 36, 79, 648, 1036, 1044, 1040, 1045, 1034, 1033, 1039, 1041, 1046, 1083, 1084, 782, 1085, 1086, 1082, 1087, 1042, 876, 1035, 1043, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 195, 342, 43, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 643, 643, 643, 666, 666, 354, 173, 980, 203, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 1048, 665, 339, 164, 164, 7, 7, 7, 7, 7, 50, 369, 583, -23, -23, -23, -23, 448, 605, 497, 260, 397, 434, 54, 394, 593, 593, 316, 316, 415, 415, 316, 316, 316, 442, 442, 252, 252, 252, 252, 318, 455, 433, 391, 742, 53, 53, 53, 53, 742, 742, 742, 742, 734, 1088, 742, 742, 742, 722, 781, 781, 926, 551, 551, 781, 536, -3, -3, 536, 63, -3, 67, 576, 335, 756, 115, 9, 335, 535, 656, 501, 185, 821, 568, 821, 1032, 424, 776, 776, 426, 753, 729, 867, 1063, 1049, 799, 1080, 810, 1081, -66, -58, 728, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1031, 1089, 402, 1032, 130, 1089, 1089, 1089, 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, 603, 130, 544, 554, 130, 804, 402, 18, 808, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 762, 157, 18, 36, 124, 124, 196, 37, 124, 124, 124, 124, 18, 18, 18, 18, 568, 784, 797, 600, 820, 143, 784, 784, 784, 122, 135, 204, 139, 760, 785, 467, 775, 775, 787, 895, 895, 775, 768, 775, 787, 913, 775, 775, 895, 895, 793, 158, 550, 472, 524, 569, 895, 346, 775, 775, 775, 775, 816, 575, 775, 271, 171, 775, 775, 816, 801, 766, 58, 798, 895, 895, 895, 816, 505, 798, 798, 798, 819, 824, 761, 765, 383, 349, 607, 138, 807, 765, 765, 775, 532, 761, 765, 761, 765, 759, 765, 765, 765, 761, 765, 768, 498, 765, 714, 586, 75, 765, 6, 915, 916, 726, 917, 906, 918, 965, 919, 923, 1053, 894, 931, 912, 924, 966, 903, 896, 780, 701, 703, 815, 754, 893, 777, 777, 777, 888, 777, 777, 777, 777, 777, 777, 777, 777, 701, 868, 823, 794, 934, 711, 712, 1011, 730, 795, 963, 933, 1013, 925, 758, 713, 977, 935, 757, 1047, 936, 940, 986, 1014, 828, 1017, 979, 790, 1064, 1065, 869, 946, 1054, 777, 915, 923, 727, 912, 924, 903, 896, 752, 748, 746, 747, 745, 744, 739, 740, 763, 1018, 887, 879, 870, 945, 891, 701, 871, 971, 874, 990, 992, 1050, 805, 792, 875, 1066, 952, 953, 954, 1055, 1019, 1056, 773, 973, 817, 994, 812, 1067, 996, 997, 999, 1000, 1057, 1068, 1058, 885, 1059, 832, 788, 928, 802, 1069, 299, 791, 800, 806, 964, 436, 932, 1060, 1070, 1071, 1001, 1002, 1006, 1072, 1073, 927, 834, 975, 796, 976, 967, 835, 838, 577, 779, 1020, 786, 789, 778, 624, 634, 1074, 1075, 1076, 930, 767, 772, 839, 845, 1021, 743, 1022, 1077, 646, 846, 717, 1078, 1012, 718, 721, 652, 683, 681, 724, 774, 1061, 818, 811, 771, 955, 721, 770, 849, 1079, 852, 855, 856, 1007, 860, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 456, 456, 456, 456, 456, 456, 305, 305, 305, 305, 305, 456, 456, 456, 456, 456, 456, 456, 305, 305, 0, 0, 305, 0, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 494, 494, 289, 289, 494, 289, 494, 494, 494, 494, 494, 494, 494, 494, 494, 0, 289, 289, 289, 289, 289, 289, 289, 289, 494, 793, 494, 442, 442, 442, 442, 494, 494, 494, -88, -88, 442, 494, 63, 494, 494, 494, 494, 494, 494, 494, 494, 494, 0, 0, 494, 494, 494, 494, 0, 0, 130, -3, 494, 768, 768, 768, 768, 494, 494, 494, 494, -3, -3, 494, 494, 494, 0, 0, 0, 0, 442, 442, 0, 130, 0, 0, 130, 0, 0, 768, 768, 494, 63, 793, 359, 494, 0, 0, 0, 0, 130, 768, 130, 402, 775, -3, -3, 775, 402, 402, 124, 18, 359, 545, 545, 545, 545, 0, 0, 568, 793, 793, 793, 793, 793, 793, 793, 793, 793, 793, 793, 768, 0, 793, 0, 768, 768, 768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 768, 0, 0, 895, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 913, 0, 0, 0, 0, 0, 0, 768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 777, 805, 0, 805, 0, 777, 777, 777, 0, 0, 0, 0, 779, 743 ); protected array $actionDefault = array( 3,32767, 102,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767, 100,32767,32767,32767,32767, 597, 597, 597, 597,32767,32767, 254, 102,32767,32767, 470, 387, 387, 387,32767,32767, 541, 541, 541, 541, 541, 541, 32767,32767,32767,32767,32767,32767, 470,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767, 100, 32767,32767,32767, 36, 7, 8, 10, 11, 49, 17, 324,32767,32767,32767,32767, 102,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767, 590,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767, 474, 453, 454, 456, 457, 386, 542, 596, 327, 593, 385, 145, 339, 329, 242, 330, 258, 475, 259, 476, 479, 480, 215, 287, 382, 149, 150, 417, 471, 419, 469, 473, 418, 392, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 390, 391, 472, 450, 449, 448,32767,32767, 415, 416,32767, 420,32767,32767,32767, 32767,32767,32767,32767, 102,32767, 389, 423, 421, 422, 439, 440, 437, 438, 441,32767,32767,32767, 442, 443, 444, 445, 316,32767,32767, 366, 364, 424, 316, 111, 32767,32767,32767,32767,32767,32767,32767,32767,32767, 430, 431,32767,32767,32767,32767, 535, 447,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 102,32767, 100, 537, 412, 414, 504, 425, 426, 393, 32767, 511,32767, 102,32767, 513,32767,32767,32767,32767, 32767,32767,32767, 536,32767, 543, 543,32767, 497, 100, 195,32767,32767, 512,32767, 195, 195,32767,32767,32767, 32767,32767,32767,32767,32767, 604, 497, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,32767, 195, 110,32767,32767,32767, 100, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 190,32767, 268, 270, 102, 558, 195,32767, 516,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767, 509,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 497, 435, 138,32767, 138, 543, 427, 428, 429, 499, 543, 543, 543, 312, 289,32767,32767,32767,32767, 514, 514, 100, 100, 100, 100, 509,32767,32767,32767,32767, 111, 99, 99, 99, 99, 99, 103, 101,32767,32767, 32767,32767, 223, 99,32767, 101, 101,32767,32767, 223, 225, 212, 101, 227,32767, 562, 563, 223, 101, 227, 227, 227, 247, 247, 486, 318, 101, 99, 101, 101, 197, 318, 318,32767, 101, 486, 318, 486, 318, 199, 318, 318, 318, 486, 318,32767, 101, 318, 214, 99, 99, 318,32767,32767,32767, 499,32767,32767,32767,32767, 32767,32767,32767, 222,32767,32767,32767,32767,32767,32767, 32767,32767, 530,32767, 547, 560, 433, 434, 436, 545, 458, 459, 460, 461, 462, 463, 464, 466, 592,32767, 503,32767,32767,32767, 338,32767, 602,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767, 603,32767, 543,32767,32767,32767, 32767, 432, 9, 74, 492, 42, 43, 51, 57, 520, 521, 522, 523, 517, 518, 524, 519,32767,32767, 525, 568,32767,32767, 544, 595,32767,32767,32767,32767,32767, 32767, 138,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767, 530,32767, 136,32767,32767,32767,32767, 32767,32767,32767,32767, 526,32767,32767,32767, 543,32767, 32767,32767,32767, 314, 311,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767, 543,32767,32767,32767,32767,32767, 291,32767, 308, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 32767,32767,32767,32767,32767,32767, 286,32767,32767, 381, 499, 294, 296, 297,32767,32767,32767,32767, 360,32767, 32767,32767,32767,32767,32767,32767,32767,32767,32767,32767, 152, 152, 3, 3, 341, 152, 152, 152, 341, 341, 152, 341, 341, 341, 152, 152, 152, 152, 152, 152, 280, 185, 262, 265, 247, 247, 152, 352, 152 ); protected array $goto = array( 196, 196, 1034, 1065, 697, 431, 661, 621, 658, 319, 706, 425, 314, 315, 335, 576, 430, 336, 432, 638, 654, 655, 852, 672, 673, 674, 853, 167, 167, 167, 167, 221, 197, 193, 193, 177, 179, 216, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 188, 189, 190, 191, 192, 218, 216, 219, 536, 537, 421, 538, 540, 541, 542, 543, 544, 545, 546, 547, 1136, 168, 169, 170, 195, 171, 172, 173, 166, 174, 175, 176, 178, 215, 217, 220, 238, 243, 244, 246, 257, 258, 259, 260, 261, 262, 263, 264, 268, 269, 270, 271, 277, 289, 290, 317, 318, 426, 427, 428, 581, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 180, 237, 181, 198, 199, 200, 239, 188, 189, 190, 191, 192, 218, 1136, 201, 182, 183, 184, 202, 198, 185, 240, 203, 201, 165, 204, 205, 186, 206, 207, 208, 187, 209, 210, 211, 212, 213, 214, 855, 1232, 975, 279, 279, 279, 279, 623, 623, 419, 351, 1269, 600, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1269, 1287, 1287, 599, 1100, 1287, 709, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 508, 700, 827, 1098, 458, 559, 552, 860, 833, 909, 904, 905, 918, 861, 906, 858, 907, 908, 859, 1233, 1234, 912, 500, 886, 501, 252, 252, 843, 1107, 1108, 507, 1087, 1082, 1083, 1084, 341, 552, 559, 568, 569, 344, 579, 602, 616, 617, 1235, 1295, 1296, 833, 440, 833, 22, 250, 250, 250, 250, 245, 253, 694, 573, 1237, 829, 1237, 893, 851, 893, 893, 1034, 1034, 1237, 694, 349, 342, 1034, 694, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 848, 1327, 1034, 1034, 1034, 1034, 1319, 1319, 1319, 1319, 1237, 343, 342, 1040, 1039, 1237, 1237, 1237, 1237, 868, 996, 1237, 1237, 1237, 913, 355, 914, 354, 354, 354, 354, 466, 466, 479, 880, 355, 355, 867, 394, 926, 466, 481, 571, 927, 967, 410, 705, 942, 355, 355, 942, 848, 355, 660, 1354, 609, 624, 627, 628, 629, 630, 651, 652, 653, 708, 554, 1133, 1285, 1285, 355, 355, 1285, 1058, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 539, 539, 1185, 424, 539, 611, 539, 539, 539, 539, 539, 539, 539, 539, 539, 566, 682, 1337, 1337, 733, 637, 639, 1043, 1044, 659, 476, 1312, 1313, 683, 687, 1010, 695, 704, 1006, 1337, 1298, 438, 408, 409, 631, 633, 635, 670, 5, 671, 6, 412, 413, 414, 337, 684, 1340, 1340, 415, 325, 309, 686, 347, 352, 353, 553, 563, 450, 450, 450, 553, 1309, 563, 1309, 666, 397, 462, 845, 1314, 1315, 1309, 548, 548, 548, 548, 873, 604, 469, 580, 470, 471, 403, 554, 878, 848, 958, 1345, 1346, 577, 614, 870, 550, 615, 550, 255, 255, 1321, 1321, 1321, 1321, 550, 999, 1018, 477, 971, 1228, 732, 736, 881, 869, 1070, 1074, 876, 882, 551, 1008, 1003, 1071, 1075, 978, 980, 0, 1305, 1118, 0, 456, 0, 0, 0, 0, 969, 969, 969, 969, 0, 0, 456, 963, 970, 0, 0, 0, 0, 968, 0, 1230, 0, 0, 0, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 931, 1123, 450, 0, 1073, 1116, 885, 619, 1307, 1307, 1073, 1216, 944, 1015, 433, 1217, 1220, 945, 1221, 0, 433, 872, 0, 664, 994, 0, 1041, 1041, 0, 866, 0, 0, 0, 665, 1052, 1048, 1049, 0, 0, 0, 0, 1227, 324, 275, 324, 1037, 1037, 681, 952, 0, 0, 1029, 1045, 1046, 396, 399, 560, 601, 605, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1013, 1013 ); protected array $gotoCheck = array( 42, 42, 73, 127, 73, 66, 66, 56, 56, 66, 9, 66, 66, 66, 66, 66, 66, 66, 66, 66, 86, 86, 26, 86, 86, 86, 27, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 15, 20, 49, 23, 23, 23, 23, 108, 108, 43, 97, 108, 130, 108, 108, 108, 108, 108, 108, 108, 108, 108, 170, 170, 8, 8, 170, 8, 170, 170, 170, 170, 170, 170, 170, 170, 170, 8, 8, 6, 8, 83, 76, 76, 15, 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 20, 20, 15, 155, 45, 155, 5, 5, 20, 144, 144, 155, 15, 15, 15, 15, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 20, 20, 20, 12, 83, 12, 76, 5, 5, 5, 5, 5, 5, 7, 172, 73, 7, 73, 25, 25, 25, 25, 73, 73, 73, 7, 179, 168, 73, 7, 73, 73, 73, 73, 73, 73, 73, 73, 73, 22, 181, 73, 73, 73, 73, 9, 9, 9, 9, 73, 168, 168, 118, 118, 73, 73, 73, 73, 35, 103, 73, 73, 73, 65, 14, 65, 24, 24, 24, 24, 149, 149, 84, 35, 14, 14, 35, 62, 73, 149, 84, 104, 73, 93, 93, 93, 9, 14, 14, 9, 22, 14, 64, 14, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 14, 150, 171, 171, 14, 14, 171, 114, 171, 171, 171, 171, 171, 171, 171, 171, 171, 173, 173, 151, 13, 173, 13, 173, 173, 173, 173, 173, 173, 173, 173, 173, 48, 116, 182, 182, 48, 48, 48, 119, 119, 48, 176, 176, 176, 48, 48, 48, 48, 48, 48, 182, 14, 113, 82, 82, 85, 85, 85, 82, 46, 82, 46, 82, 82, 82, 29, 82, 182, 182, 82, 169, 169, 14, 82, 97, 97, 9, 9, 23, 23, 23, 9, 130, 9, 130, 120, 9, 9, 18, 178, 178, 130, 107, 107, 107, 107, 39, 107, 9, 9, 9, 9, 28, 14, 9, 22, 92, 9, 9, 2, 2, 37, 19, 80, 19, 5, 5, 130, 130, 130, 130, 19, 50, 110, 157, 50, 160, 50, 99, 16, 16, 16, 16, 9, 41, 50, 50, 50, 129, 132, 16, 96, -1, 130, 147, -1, 19, -1, -1, -1, -1, 19, 19, 19, 19, -1, -1, 19, 19, 19, -1, -1, -1, -1, 16, -1, 14, -1, -1, -1, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, 17, 23, -1, 130, 16, 16, 17, 130, 130, 130, 79, 79, 17, 117, 79, 79, 79, 79, -1, 117, 17, -1, 17, 17, -1, 117, 117, -1, 17, -1, -1, -1, 117, 117, 117, 117, -1, -1, -1, -1, 17, 24, 24, 24, 89, 89, 89, 89, -1, -1, 89, 89, 89, 59, 59, 59, 59, 59, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 107, 107 ); protected array $gotoBase = array( 0, 0, -253, 0, 0, 224, 182, 251, 179, -10, 0, 0, -89, 32, 11, -185, 27, 66, 128, 197, -229, 0, 5, 163, 308, 260, 18, 22, 115, 118, 0, 0, 0, 0, 0, -68, 0, 122, 0, 123, 0, 43, -1, 153, 0, 200, -327, 0, -330, 147, 460, 0, 0, 0, 0, 0, -33, 0, 0, 540, 0, 0, 280, 0, 95, 294, -236, 0, 0, 0, 0, 0, 0, -5, 0, 0, -140, 0, 0, 134, 119, -19, -88, -75, -152, -74, -698, 0, 0, 296, 0, 0, 127, 23, 0, 0, 48, -310, 0, 71, 0, 0, 0, 269, 283, 0, 0, 414, -71, 0, 103, 0, 0, 124, 83, 0, 100, 273, 17, 104, 144, 0, 0, 0, 0, 0, 0, 1, 0, 114, 167, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -47, 0, 0, 50, 0, 281, 105, 94, 0, 0, 0, -273, 0, 34, 0, 0, 107, 0, 0, 0, 0, 0, 0, 0, -26, 99, -56, 110, 230, 125, 0, 0, 90, 0, 67, 241, 0, 254, 75, 0, 0 ); protected array $gotoDefault = array( -32768, 512, 740, 4, 741, 935, 816, 825, 597, 530, 707, 348, 625, 422, 1303, 911, 1122, 578, 844, 1246, 1254, 457, 847, 330, 730, 923, 894, 895, 400, 386, 392, 398, 649, 626, 494, 879, 453, 871, 486, 874, 452, 883, 164, 418, 510, 887, 3, 890, 557, 921, 973, 387, 898, 388, 677, 900, 562, 902, 903, 395, 401, 402, 1127, 570, 622, 915, 256, 564, 916, 385, 917, 925, 390, 393, 688, 465, 505, 499, 411, 1102, 565, 608, 646, 447, 473, 620, 632, 618, 480, 434, 416, 329, 957, 965, 487, 463, 979, 350, 987, 738, 1135, 640, 489, 995, 641, 1002, 1005, 531, 532, 478, 1017, 272, 1020, 490, 19, 667, 1031, 1032, 668, 642, 1054, 643, 669, 644, 1056, 472, 598, 1064, 454, 1072, 1291, 455, 1076, 266, 1079, 278, 417, 435, 1085, 1086, 9, 1092, 698, 699, 11, 276, 509, 1117, 689, 451, 1134, 439, 1204, 1206, 558, 491, 1224, 1223, 680, 506, 1229, 448, 1294, 449, 533, 474, 316, 534, 1338, 308, 333, 313, 549, 295, 334, 535, 475, 1300, 1308, 331, 31, 1328, 1339, 575, 613 ); protected array $ruleToNonTerminal = array( 0, 1, 3, 3, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 9, 10, 11, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, 21, 21, 22, 23, 23, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29, 29, 30, 30, 32, 34, 34, 28, 36, 36, 33, 38, 38, 35, 35, 37, 37, 39, 39, 31, 40, 40, 41, 43, 44, 44, 45, 45, 46, 46, 48, 47, 47, 47, 47, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 25, 25, 50, 69, 69, 72, 72, 71, 70, 70, 63, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 26, 26, 27, 27, 27, 27, 27, 88, 88, 90, 90, 83, 83, 91, 91, 92, 92, 92, 84, 84, 87, 87, 85, 85, 93, 94, 94, 57, 57, 65, 65, 68, 68, 68, 67, 95, 95, 96, 58, 58, 58, 58, 97, 97, 98, 98, 99, 99, 100, 101, 101, 102, 102, 103, 103, 55, 55, 51, 51, 105, 53, 53, 106, 52, 52, 54, 54, 64, 64, 64, 64, 81, 81, 109, 109, 111, 111, 112, 112, 112, 112, 110, 110, 110, 114, 114, 114, 114, 89, 89, 117, 117, 117, 118, 118, 115, 115, 119, 119, 121, 121, 122, 122, 116, 123, 123, 120, 124, 124, 124, 124, 113, 113, 82, 82, 82, 20, 20, 20, 126, 125, 125, 127, 127, 127, 127, 60, 128, 128, 129, 61, 131, 131, 132, 132, 133, 133, 86, 134, 134, 134, 134, 134, 134, 134, 139, 139, 140, 140, 141, 141, 141, 141, 141, 142, 143, 143, 138, 138, 135, 135, 137, 137, 145, 145, 144, 144, 144, 144, 144, 144, 144, 136, 146, 146, 148, 147, 147, 62, 104, 149, 149, 56, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 156, 150, 150, 155, 155, 158, 159, 159, 160, 161, 162, 162, 162, 162, 19, 19, 73, 73, 73, 73, 151, 151, 151, 151, 164, 164, 152, 152, 154, 154, 154, 157, 157, 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 171, 171, 108, 173, 173, 173, 173, 153, 153, 153, 153, 153, 153, 153, 153, 59, 59, 167, 167, 167, 167, 174, 174, 163, 163, 163, 175, 175, 175, 175, 175, 175, 74, 74, 66, 66, 66, 66, 130, 130, 130, 130, 178, 177, 166, 166, 166, 166, 166, 166, 166, 165, 165, 165, 176, 176, 176, 176, 107, 172, 180, 180, 179, 179, 181, 181, 181, 181, 181, 181, 181, 181, 169, 169, 169, 169, 168, 183, 182, 182, 182, 182, 182, 182, 182, 182, 184, 184, 184, 184 ); protected array $ruleToLength = array( 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 3, 4, 1, 2, 0, 1, 1, 1, 1, 4, 3, 5, 4, 3, 4, 2, 3, 1, 1, 7, 6, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 3, 1, 3, 1, 2, 2, 3, 1, 3, 2, 3, 1, 3, 3, 2, 0, 1, 1, 1, 1, 1, 3, 7, 10, 5, 7, 9, 5, 3, 3, 3, 3, 3, 3, 1, 2, 5, 7, 9, 6, 5, 6, 3, 2, 1, 1, 1, 1, 0, 2, 1, 3, 8, 0, 4, 2, 1, 3, 0, 1, 0, 1, 0, 1, 3, 1, 1, 1, 8, 9, 7, 8, 7, 6, 8, 0, 2, 0, 2, 1, 2, 1, 2, 1, 1, 1, 0, 2, 0, 2, 0, 2, 2, 1, 3, 1, 4, 1, 4, 1, 1, 4, 2, 1, 3, 3, 3, 4, 4, 5, 0, 2, 4, 3, 1, 1, 7, 0, 2, 1, 3, 3, 4, 1, 4, 0, 2, 5, 0, 2, 6, 0, 2, 0, 3, 1, 2, 1, 1, 2, 0, 1, 3, 0, 2, 1, 1, 1, 1, 6, 8, 6, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 1, 3, 3, 1, 1, 2, 1, 1, 0, 1, 0, 2, 2, 2, 4, 3, 1, 1, 3, 1, 2, 2, 3, 2, 3, 1, 1, 2, 3, 1, 1, 3, 2, 0, 1, 5, 5, 6, 10, 3, 5, 1, 1, 3, 0, 2, 4, 5, 4, 4, 4, 3, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 3, 2, 2, 3, 1, 0, 1, 1, 3, 3, 3, 4, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 3, 4, 4, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 2, 1, 2, 4, 2, 2, 8, 9, 8, 9, 9, 10, 9, 10, 8, 3, 2, 0, 4, 2, 1, 3, 2, 1, 2, 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 0, 3, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 3, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 2, 3, 0, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 4, 4, 1, 4, 4, 0, 1, 1, 1, 3, 3, 1, 4, 2, 2, 1, 3, 1, 4, 4, 3, 3, 3, 3, 1, 3, 1, 1, 3, 1, 1, 4, 1, 1, 1, 3, 1, 1, 2, 1, 3, 4, 3, 2, 0, 2, 2, 1, 2, 1, 1, 1, 4, 3, 3, 3, 3, 6, 3, 1, 1, 2, 1 ); protected function initReduceCallbacks(): void { $this->reduceCallbacks = [ 0 => null, 1 => static function ($self, $stackPos) { $self->semValue = $self->handleNamespaces($self->semStack[$stackPos-(1-1)]); }, 2 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];; }, 3 => static function ($self, $stackPos) { $self->semValue = array(); }, 4 => static function ($self, $stackPos) { $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);; if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 5 => null, 6 => null, 7 => null, 8 => null, 9 => null, 10 => null, 11 => null, 12 => null, 13 => null, 14 => null, 15 => null, 16 => null, 17 => null, 18 => null, 19 => null, 20 => null, 21 => null, 22 => null, 23 => null, 24 => null, 25 => null, 26 => null, 27 => null, 28 => null, 29 => null, 30 => null, 31 => null, 32 => null, 33 => null, 34 => null, 35 => null, 36 => null, 37 => null, 38 => null, 39 => null, 40 => null, 41 => null, 42 => null, 43 => null, 44 => null, 45 => null, 46 => null, 47 => null, 48 => null, 49 => null, 50 => null, 51 => null, 52 => null, 53 => null, 54 => null, 55 => null, 56 => null, 57 => null, 58 => null, 59 => null, 60 => null, 61 => null, 62 => null, 63 => null, 64 => null, 65 => null, 66 => null, 67 => null, 68 => null, 69 => null, 70 => null, 71 => null, 72 => null, 73 => null, 74 => null, 75 => null, 76 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; if ($self->semValue === "emitError(new Error('Cannot use "getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]))); }, 77 => null, 78 => null, 79 => null, 80 => null, 81 => null, 82 => null, 83 => null, 84 => null, 85 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 86 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 87 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 88 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 89 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 90 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 91 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 92 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 93 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 94 => null, 95 => static function ($self, $stackPos) { $self->semValue = new Name(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 96 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 97 => static function ($self, $stackPos) { }, 98 => static function ($self, $stackPos) { }, 99 => static function ($self, $stackPos) { }, 100 => static function ($self, $stackPos) { $self->emitError(new Error('A trailing comma is not allowed here', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]))); }, 101 => null, 102 => null, 103 => static function ($self, $stackPos) { $self->semValue = new Node\Attribute($self->semStack[$stackPos-(1-1)], [], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 104 => static function ($self, $stackPos) { $self->semValue = new Node\Attribute($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 105 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 106 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 107 => static function ($self, $stackPos) { $self->semValue = new Node\AttributeGroup($self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 108 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 109 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 110 => static function ($self, $stackPos) { $self->semValue = []; }, 111 => null, 112 => null, 113 => null, 114 => null, 115 => static function ($self, $stackPos) { $self->semValue = new Stmt\HaltCompiler($self->handleHaltCompiler(), $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 116 => static function ($self, $stackPos) { $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(3-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_SEMICOLON); $self->checkNamespace($self->semValue); }, 117 => static function ($self, $stackPos) { $self->semValue = new Stmt\Namespace_($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); $self->checkNamespace($self->semValue); }, 118 => static function ($self, $stackPos) { $self->semValue = new Stmt\Namespace_(null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Stmt\Namespace_::KIND_BRACED); $self->checkNamespace($self->semValue); }, 119 => static function ($self, $stackPos) { $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(3-2)], Stmt\Use_::TYPE_NORMAL, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 120 => static function ($self, $stackPos) { $self->semValue = new Stmt\Use_($self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 121 => null, 122 => static function ($self, $stackPos) { $self->semValue = new Stmt\Const_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 123 => static function ($self, $stackPos) { $self->semValue = Stmt\Use_::TYPE_FUNCTION; }, 124 => static function ($self, $stackPos) { $self->semValue = Stmt\Use_::TYPE_CONSTANT; }, 125 => static function ($self, $stackPos) { $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 126 => static function ($self, $stackPos) { $self->semValue = new Stmt\GroupUse($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 127 => null, 128 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 129 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 130 => null, 131 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 132 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 133 => null, 134 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 135 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 136 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1)); }, 137 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3)); }, 138 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(1-1)], null, Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(1-1)); }, 139 => static function ($self, $stackPos) { $self->semValue = new Node\UseItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], Stmt\Use_::TYPE_UNKNOWN, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->checkUseUse($self->semValue, $stackPos-(3-3)); }, 140 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->semValue->type = Stmt\Use_::TYPE_NORMAL; }, 141 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; $self->semValue->type = $self->semStack[$stackPos-(2-1)]; }, 142 => null, 143 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 144 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 145 => static function ($self, $stackPos) { $self->semValue = new Node\Const_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 146 => null, 147 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 148 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 149 => static function ($self, $stackPos) { $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 150 => static function ($self, $stackPos) { $self->semValue = new Node\Const_(new Node\Identifier($self->semStack[$stackPos-(3-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos-(3-1)])), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 151 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; } $self->semValue = $self->semStack[$stackPos-(2-1)];; }, 152 => static function ($self, $stackPos) { $self->semValue = array(); }, 153 => static function ($self, $stackPos) { $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);; if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 154 => null, 155 => null, 156 => null, 157 => static function ($self, $stackPos) { throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 158 => static function ($self, $stackPos) { $self->semValue = new Stmt\Block($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 159 => static function ($self, $stackPos) { $self->semValue = new Stmt\If_($self->semStack[$stackPos-(7-3)], ['stmts' => $self->semStack[$stackPos-(7-5)], 'elseifs' => $self->semStack[$stackPos-(7-6)], 'else' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 160 => static function ($self, $stackPos) { $self->semValue = new Stmt\If_($self->semStack[$stackPos-(10-3)], ['stmts' => $self->semStack[$stackPos-(10-6)], 'elseifs' => $self->semStack[$stackPos-(10-7)], 'else' => $self->semStack[$stackPos-(10-8)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); }, 161 => static function ($self, $stackPos) { $self->semValue = new Stmt\While_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 162 => static function ($self, $stackPos) { $self->semValue = new Stmt\Do_($self->semStack[$stackPos-(7-5)], $self->semStack[$stackPos-(7-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 163 => static function ($self, $stackPos) { $self->semValue = new Stmt\For_(['init' => $self->semStack[$stackPos-(9-3)], 'cond' => $self->semStack[$stackPos-(9-5)], 'loop' => $self->semStack[$stackPos-(9-7)], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 164 => static function ($self, $stackPos) { $self->semValue = new Stmt\Switch_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 165 => static function ($self, $stackPos) { $self->semValue = new Stmt\Break_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 166 => static function ($self, $stackPos) { $self->semValue = new Stmt\Continue_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 167 => static function ($self, $stackPos) { $self->semValue = new Stmt\Return_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 168 => static function ($self, $stackPos) { $self->semValue = new Stmt\Global_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 169 => static function ($self, $stackPos) { $self->semValue = new Stmt\Static_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 170 => static function ($self, $stackPos) { $self->semValue = new Stmt\Echo_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 171 => static function ($self, $stackPos) { $self->semValue = new Stmt\InlineHTML($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('hasLeadingNewline', $self->inlineHtmlHasLeadingNewline($stackPos-(1-1))); }, 172 => static function ($self, $stackPos) { $self->semValue = new Stmt\Expression($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 173 => static function ($self, $stackPos) { $self->semValue = new Stmt\Unset_($self->semStack[$stackPos-(5-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 174 => static function ($self, $stackPos) { $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-5)][0], ['keyVar' => null, 'byRef' => $self->semStack[$stackPos-(7-5)][1], 'stmts' => $self->semStack[$stackPos-(7-7)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 175 => static function ($self, $stackPos) { $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(9-3)], $self->semStack[$stackPos-(9-7)][0], ['keyVar' => $self->semStack[$stackPos-(9-5)], 'byRef' => $self->semStack[$stackPos-(9-7)][1], 'stmts' => $self->semStack[$stackPos-(9-9)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 176 => static function ($self, $stackPos) { $self->semValue = new Stmt\Foreach_($self->semStack[$stackPos-(6-3)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-4)], $self->tokenEndStack[$stackPos-(6-4)])), ['stmts' => $self->semStack[$stackPos-(6-6)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 177 => static function ($self, $stackPos) { $self->semValue = new Stmt\Declare_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 178 => static function ($self, $stackPos) { $self->semValue = new Stmt\TryCatch($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->checkTryCatch($self->semValue); }, 179 => static function ($self, $stackPos) { $self->semValue = new Stmt\Goto_($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 180 => static function ($self, $stackPos) { $self->semValue = new Stmt\Label($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 181 => static function ($self, $stackPos) { $self->semValue = null; }, 182 => null, 183 => static function ($self, $stackPos) { $self->semValue = $self->maybeCreateNop($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); }, 184 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; }; }, 185 => static function ($self, $stackPos) { $self->semValue = array(); }, 186 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 187 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 188 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 189 => static function ($self, $stackPos) { $self->semValue = new Stmt\Catch_($self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-7)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 190 => static function ($self, $stackPos) { $self->semValue = null; }, 191 => static function ($self, $stackPos) { $self->semValue = new Stmt\Finally_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 192 => null, 193 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 194 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 195 => static function ($self, $stackPos) { $self->semValue = false; }, 196 => static function ($self, $stackPos) { $self->semValue = true; }, 197 => static function ($self, $stackPos) { $self->semValue = false; }, 198 => static function ($self, $stackPos) { $self->semValue = true; }, 199 => static function ($self, $stackPos) { $self->semValue = false; }, 200 => static function ($self, $stackPos) { $self->semValue = true; }, 201 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 202 => static function ($self, $stackPos) { $self->semValue = []; }, 203 => null, 204 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 205 => static function ($self, $stackPos) { $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(8-3)], ['byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-5)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 206 => static function ($self, $stackPos) { $self->semValue = new Stmt\Function_($self->semStack[$stackPos-(9-4)], ['byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-6)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 207 => static function ($self, $stackPos) { $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(7-2)], ['type' => $self->semStack[$stackPos-(7-1)], 'extends' => $self->semStack[$stackPos-(7-3)], 'implements' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); $self->checkClass($self->semValue, $stackPos-(7-2)); }, 208 => static function ($self, $stackPos) { $self->semValue = new Stmt\Class_($self->semStack[$stackPos-(8-3)], ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); $self->checkClass($self->semValue, $stackPos-(8-3)); }, 209 => static function ($self, $stackPos) { $self->semValue = new Stmt\Interface_($self->semStack[$stackPos-(7-3)], ['extends' => $self->semStack[$stackPos-(7-4)], 'stmts' => $self->semStack[$stackPos-(7-6)], 'attrGroups' => $self->semStack[$stackPos-(7-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); $self->checkInterface($self->semValue, $stackPos-(7-3)); }, 210 => static function ($self, $stackPos) { $self->semValue = new Stmt\Trait_($self->semStack[$stackPos-(6-3)], ['stmts' => $self->semStack[$stackPos-(6-5)], 'attrGroups' => $self->semStack[$stackPos-(6-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 211 => static function ($self, $stackPos) { $self->semValue = new Stmt\Enum_($self->semStack[$stackPos-(8-3)], ['scalarType' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); $self->checkEnum($self->semValue, $stackPos-(8-3)); }, 212 => static function ($self, $stackPos) { $self->semValue = null; }, 213 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 214 => static function ($self, $stackPos) { $self->semValue = null; }, 215 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 216 => static function ($self, $stackPos) { $self->semValue = 0; }, 217 => null, 218 => null, 219 => static function ($self, $stackPos) { $self->checkClassModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)]; }, 220 => static function ($self, $stackPos) { $self->semValue = Modifiers::ABSTRACT; }, 221 => static function ($self, $stackPos) { $self->semValue = Modifiers::FINAL; }, 222 => static function ($self, $stackPos) { $self->semValue = Modifiers::READONLY; }, 223 => static function ($self, $stackPos) { $self->semValue = null; }, 224 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 225 => static function ($self, $stackPos) { $self->semValue = array(); }, 226 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 227 => static function ($self, $stackPos) { $self->semValue = array(); }, 228 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 229 => null, 230 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 231 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 232 => null, 233 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 234 => null, 235 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 236 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(1-1)] instanceof Stmt\Block) { $self->semValue = $self->semStack[$stackPos-(1-1)]->stmts; } else if ($self->semStack[$stackPos-(1-1)] === null) { $self->semValue = []; } else { $self->semValue = [$self->semStack[$stackPos-(1-1)]]; }; }, 237 => static function ($self, $stackPos) { $self->semValue = null; }, 238 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 239 => null, 240 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 241 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 242 => static function ($self, $stackPos) { $self->semValue = new Node\DeclareItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 243 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 244 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-3)]; }, 245 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 246 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(5-3)]; }, 247 => static function ($self, $stackPos) { $self->semValue = array(); }, 248 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 249 => static function ($self, $stackPos) { $self->semValue = new Stmt\Case_($self->semStack[$stackPos-(4-2)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 250 => static function ($self, $stackPos) { $self->semValue = new Stmt\Case_(null, $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 251 => null, 252 => null, 253 => static function ($self, $stackPos) { $self->semValue = new Expr\Match_($self->semStack[$stackPos-(7-3)], $self->semStack[$stackPos-(7-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(7-1)], $self->tokenEndStack[$stackPos])); }, 254 => static function ($self, $stackPos) { $self->semValue = []; }, 255 => null, 256 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 257 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 258 => static function ($self, $stackPos) { $self->semValue = new Node\MatchArm($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 259 => static function ($self, $stackPos) { $self->semValue = new Node\MatchArm(null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 260 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 261 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 262 => static function ($self, $stackPos) { $self->semValue = array(); }, 263 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 264 => static function ($self, $stackPos) { $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 265 => static function ($self, $stackPos) { $self->semValue = array(); }, 266 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 267 => static function ($self, $stackPos) { $self->semValue = new Stmt\ElseIf_($self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-6)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue); }, 268 => static function ($self, $stackPos) { $self->semValue = null; }, 269 => static function ($self, $stackPos) { $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 270 => static function ($self, $stackPos) { $self->semValue = null; }, 271 => static function ($self, $stackPos) { $self->semValue = new Stmt\Else_($self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->fixupAlternativeElse($self->semValue); }, 272 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)], false); }, 273 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(2-2)], true); }, 274 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)], false); }, 275 => static function ($self, $stackPos) { $self->semValue = array($self->fixupArrayDestructuring($self->semStack[$stackPos-(1-1)]), false); }, 276 => null, 277 => static function ($self, $stackPos) { $self->semValue = array(); }, 278 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 279 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 280 => static function ($self, $stackPos) { $self->semValue = 0; }, 281 => static function ($self, $stackPos) { $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)]; }, 282 => static function ($self, $stackPos) { $self->semValue = Modifiers::PUBLIC; }, 283 => static function ($self, $stackPos) { $self->semValue = Modifiers::PROTECTED; }, 284 => static function ($self, $stackPos) { $self->semValue = Modifiers::PRIVATE; }, 285 => static function ($self, $stackPos) { $self->semValue = Modifiers::READONLY; }, 286 => static function ($self, $stackPos) { $self->semValue = new Node\Param($self->semStack[$stackPos-(6-6)], null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]); $self->checkParam($self->semValue); }, 287 => static function ($self, $stackPos) { $self->semValue = new Node\Param($self->semStack[$stackPos-(8-6)], $self->semStack[$stackPos-(8-8)], $self->semStack[$stackPos-(8-3)], $self->semStack[$stackPos-(8-4)], $self->semStack[$stackPos-(8-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(8-2)], $self->semStack[$stackPos-(8-1)]); $self->checkParam($self->semValue); }, 288 => static function ($self, $stackPos) { $self->semValue = new Node\Param(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])), null, $self->semStack[$stackPos-(6-3)], $self->semStack[$stackPos-(6-4)], $self->semStack[$stackPos-(6-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-1)]); }, 289 => null, 290 => static function ($self, $stackPos) { $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 291 => static function ($self, $stackPos) { $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 292 => null, 293 => null, 294 => static function ($self, $stackPos) { $self->semValue = new Node\Name('static', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 295 => static function ($self, $stackPos) { $self->semValue = $self->handleBuiltinTypes($self->semStack[$stackPos-(1-1)]); }, 296 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier('array', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 297 => static function ($self, $stackPos) { $self->semValue = new Node\Identifier('callable', $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 298 => null, 299 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 300 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 301 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 302 => null, 303 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 304 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 305 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 306 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 307 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 308 => static function ($self, $stackPos) { $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 309 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 310 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 311 => static function ($self, $stackPos) { $self->semValue = new Node\IntersectionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 312 => null, 313 => static function ($self, $stackPos) { $self->semValue = new Node\NullableType($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 314 => static function ($self, $stackPos) { $self->semValue = new Node\UnionType($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 315 => null, 316 => static function ($self, $stackPos) { $self->semValue = null; }, 317 => null, 318 => static function ($self, $stackPos) { $self->semValue = null; }, 319 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(2-2)]; }, 320 => static function ($self, $stackPos) { $self->semValue = null; }, 321 => static function ($self, $stackPos) { $self->semValue = array(); }, 322 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-2)]; }, 323 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-2)]); }, 324 => static function ($self, $stackPos) { $self->semValue = new Node\VariadicPlaceholder($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 325 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 326 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 327 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(1-1)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 328 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], true, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 329 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(2-2)], false, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 330 => static function ($self, $stackPos) { $self->semValue = new Node\Arg($self->semStack[$stackPos-(3-3)], false, false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(3-1)]); }, 331 => null, 332 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 333 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 334 => null, 335 => null, 336 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 337 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 338 => static function ($self, $stackPos) { $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 339 => static function ($self, $stackPos) { $self->semValue = new Node\StaticVar($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 340 => static function ($self, $stackPos) { if ($self->semStack[$stackPos-(2-2)] !== null) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; } else { $self->semValue = $self->semStack[$stackPos-(2-1)]; } }, 341 => static function ($self, $stackPos) { $self->semValue = array(); }, 342 => static function ($self, $stackPos) { $nop = $self->maybeCreateZeroLengthNop($self->tokenPos);; if ($nop !== null) { $self->semStack[$stackPos-(1-1)][] = $nop; } $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 343 => static function ($self, $stackPos) { $self->semValue = new Stmt\Property($self->semStack[$stackPos-(5-2)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-1)]); $self->checkProperty($self->semValue, $stackPos-(5-2)); }, 344 => static function ($self, $stackPos) { $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(5-1)]); $self->checkClassConst($self->semValue, $stackPos-(5-2)); }, 345 => static function ($self, $stackPos) { $self->semValue = new Stmt\ClassConst($self->semStack[$stackPos-(6-5)], $self->semStack[$stackPos-(6-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos]), $self->semStack[$stackPos-(6-1)], $self->semStack[$stackPos-(6-4)]); $self->checkClassConst($self->semValue, $stackPos-(6-2)); }, 346 => static function ($self, $stackPos) { $self->semValue = new Stmt\ClassMethod($self->semStack[$stackPos-(10-5)], ['type' => $self->semStack[$stackPos-(10-2)], 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-7)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); $self->checkClassMethod($self->semValue, $stackPos-(10-2)); }, 347 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUse($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 348 => static function ($self, $stackPos) { $self->semValue = new Stmt\EnumCase($self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->semStack[$stackPos-(5-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 349 => static function ($self, $stackPos) { $self->semValue = null; }, 350 => static function ($self, $stackPos) { $self->semValue = array(); }, 351 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 352 => static function ($self, $stackPos) { $self->semValue = array(); }, 353 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 354 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Precedence($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 355 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(5-1)][0], $self->semStack[$stackPos-(5-1)][1], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 356 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], $self->semStack[$stackPos-(4-3)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 357 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 358 => static function ($self, $stackPos) { $self->semValue = new Stmt\TraitUseAdaptation\Alias($self->semStack[$stackPos-(4-1)][0], $self->semStack[$stackPos-(4-1)][1], null, $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 359 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)]); }, 360 => null, 361 => static function ($self, $stackPos) { $self->semValue = array(null, $self->semStack[$stackPos-(1-1)]); }, 362 => static function ($self, $stackPos) { $self->semValue = null; }, 363 => null, 364 => null, 365 => static function ($self, $stackPos) { $self->semValue = 0; }, 366 => static function ($self, $stackPos) { $self->semValue = 0; }, 367 => null, 368 => null, 369 => static function ($self, $stackPos) { $self->checkModifier($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $stackPos-(2-2)); $self->semValue = $self->semStack[$stackPos-(2-1)] | $self->semStack[$stackPos-(2-2)]; }, 370 => static function ($self, $stackPos) { $self->semValue = Modifiers::PUBLIC; }, 371 => static function ($self, $stackPos) { $self->semValue = Modifiers::PROTECTED; }, 372 => static function ($self, $stackPos) { $self->semValue = Modifiers::PRIVATE; }, 373 => static function ($self, $stackPos) { $self->semValue = Modifiers::STATIC; }, 374 => static function ($self, $stackPos) { $self->semValue = Modifiers::ABSTRACT; }, 375 => static function ($self, $stackPos) { $self->semValue = Modifiers::FINAL; }, 376 => static function ($self, $stackPos) { $self->semValue = Modifiers::READONLY; }, 377 => null, 378 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 379 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 380 => static function ($self, $stackPos) { $self->semValue = new Node\VarLikeIdentifier(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 381 => static function ($self, $stackPos) { $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(1-1)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 382 => static function ($self, $stackPos) { $self->semValue = new Node\PropertyItem($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 383 => null, 384 => null, 385 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 386 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 387 => static function ($self, $stackPos) { $self->semValue = array(); }, 388 => null, 389 => null, 390 => static function ($self, $stackPos) { $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 391 => static function ($self, $stackPos) { $self->semValue = new Expr\Assign($self->fixupArrayDestructuring($self->semStack[$stackPos-(3-1)]), $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 392 => static function ($self, $stackPos) { $self->semValue = new Expr\Assign($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 393 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 394 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignRef($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); if (!$self->phpVersion->allowsAssignNewByReference()) { $self->emitError(new Error('Cannot assign new by reference', $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]))); } }, 395 => null, 396 => null, 397 => static function ($self, $stackPos) { $self->semValue = new Expr\Clone_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 398 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 399 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 400 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 401 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 402 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 403 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 404 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 405 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 406 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 407 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 408 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 409 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 410 => static function ($self, $stackPos) { $self->semValue = new Expr\AssignOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 411 => static function ($self, $stackPos) { $self->semValue = new Expr\PostInc($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 412 => static function ($self, $stackPos) { $self->semValue = new Expr\PreInc($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 413 => static function ($self, $stackPos) { $self->semValue = new Expr\PostDec($self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 414 => static function ($self, $stackPos) { $self->semValue = new Expr\PreDec($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 415 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BooleanOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 416 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BooleanAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 417 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\LogicalOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 418 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\LogicalAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 419 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\LogicalXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 420 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseOr($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 421 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 422 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseAnd($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 423 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\BitwiseXor($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 424 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Concat($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 425 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Plus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 426 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Minus($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 427 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Mul($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 428 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Div($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 429 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Mod($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 430 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\ShiftLeft($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 431 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\ShiftRight($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 432 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Pow($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 433 => static function ($self, $stackPos) { $self->semValue = new Expr\UnaryPlus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 434 => static function ($self, $stackPos) { $self->semValue = new Expr\UnaryMinus($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 435 => static function ($self, $stackPos) { $self->semValue = new Expr\BooleanNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 436 => static function ($self, $stackPos) { $self->semValue = new Expr\BitwiseNot($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 437 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Identical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 438 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\NotIdentical($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 439 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Equal($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 440 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\NotEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 441 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Spaceship($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 442 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Smaller($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 443 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\SmallerOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 444 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Greater($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 445 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\GreaterOrEqual($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 446 => static function ($self, $stackPos) { $self->semValue = new Expr\Instanceof_($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 447 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 448 => static function ($self, $stackPos) { $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-3)], $self->semStack[$stackPos-(5-5)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 449 => static function ($self, $stackPos) { $self->semValue = new Expr\Ternary($self->semStack[$stackPos-(4-1)], null, $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 450 => static function ($self, $stackPos) { $self->semValue = new Expr\BinaryOp\Coalesce($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 451 => static function ($self, $stackPos) { $self->semValue = new Expr\Isset_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 452 => static function ($self, $stackPos) { $self->semValue = new Expr\Empty_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 453 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 454 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_INCLUDE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 455 => static function ($self, $stackPos) { $self->semValue = new Expr\Eval_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 456 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 457 => static function ($self, $stackPos) { $self->semValue = new Expr\Include_($self->semStack[$stackPos-(2-2)], Expr\Include_::TYPE_REQUIRE_ONCE, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 458 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Int_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 459 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = $self->getFloatCastKind($self->semStack[$stackPos-(2-1)]); $self->semValue = new Expr\Cast\Double($self->semStack[$stackPos-(2-2)], $attrs); }, 460 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\String_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 461 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Array_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 462 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Object_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 463 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Bool_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 464 => static function ($self, $stackPos) { $self->semValue = new Expr\Cast\Unset_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 465 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = strtolower($self->semStack[$stackPos-(2-1)]) === 'exit' ? Expr\Exit_::KIND_EXIT : Expr\Exit_::KIND_DIE; $self->semValue = new Expr\Exit_($self->semStack[$stackPos-(2-2)], $attrs); }, 466 => static function ($self, $stackPos) { $self->semValue = new Expr\ErrorSuppress($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 467 => null, 468 => static function ($self, $stackPos) { $self->semValue = new Expr\ShellExec($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 469 => static function ($self, $stackPos) { $self->semValue = new Expr\Print_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 470 => static function ($self, $stackPos) { $self->semValue = new Expr\Yield_(null, null, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 471 => static function ($self, $stackPos) { $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(2-2)], null, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 472 => static function ($self, $stackPos) { $self->semValue = new Expr\Yield_($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 473 => static function ($self, $stackPos) { $self->semValue = new Expr\YieldFrom($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 474 => static function ($self, $stackPos) { $self->semValue = new Expr\Throw_($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 475 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'returnType' => $self->semStack[$stackPos-(8-6)], 'expr' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 476 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 477 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(8-2)], 'params' => $self->semStack[$stackPos-(8-4)], 'uses' => $self->semStack[$stackPos-(8-6)], 'returnType' => $self->semStack[$stackPos-(8-7)], 'stmts' => $self->semStack[$stackPos-(8-8)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])); }, 478 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => []], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 479 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'returnType' => $self->semStack[$stackPos-(9-7)], 'expr' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 480 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrowFunction(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'returnType' => $self->semStack[$stackPos-(10-8)], 'expr' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); }, 481 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => false, 'byRef' => $self->semStack[$stackPos-(9-3)], 'params' => $self->semStack[$stackPos-(9-5)], 'uses' => $self->semStack[$stackPos-(9-7)], 'returnType' => $self->semStack[$stackPos-(9-8)], 'stmts' => $self->semStack[$stackPos-(9-9)], 'attrGroups' => $self->semStack[$stackPos-(9-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(9-1)], $self->tokenEndStack[$stackPos])); }, 482 => static function ($self, $stackPos) { $self->semValue = new Expr\Closure(['static' => true, 'byRef' => $self->semStack[$stackPos-(10-4)], 'params' => $self->semStack[$stackPos-(10-6)], 'uses' => $self->semStack[$stackPos-(10-8)], 'returnType' => $self->semStack[$stackPos-(10-9)], 'stmts' => $self->semStack[$stackPos-(10-10)], 'attrGroups' => $self->semStack[$stackPos-(10-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(10-1)], $self->tokenEndStack[$stackPos])); }, 483 => static function ($self, $stackPos) { $self->semValue = array(new Stmt\Class_(null, ['type' => $self->semStack[$stackPos-(8-2)], 'extends' => $self->semStack[$stackPos-(8-4)], 'implements' => $self->semStack[$stackPos-(8-5)], 'stmts' => $self->semStack[$stackPos-(8-7)], 'attrGroups' => $self->semStack[$stackPos-(8-1)]], $self->getAttributes($self->tokenStartStack[$stackPos-(8-1)], $self->tokenEndStack[$stackPos])), $self->semStack[$stackPos-(8-3)]); $self->checkClass($self->semValue[0], -1); }, 484 => static function ($self, $stackPos) { $self->semValue = new Expr\New_($self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 485 => static function ($self, $stackPos) { list($class, $ctorArgs) = $self->semStack[$stackPos-(2-2)]; $self->semValue = new Expr\New_($class, $ctorArgs, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 486 => static function ($self, $stackPos) { $self->semValue = array(); }, 487 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(4-3)]; }, 488 => null, 489 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 490 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 491 => static function ($self, $stackPos) { $self->semValue = new Node\ClosureUse($self->semStack[$stackPos-(2-2)], $self->semStack[$stackPos-(2-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 492 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 493 => static function ($self, $stackPos) { $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 494 => static function ($self, $stackPos) { $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 495 => static function ($self, $stackPos) { $self->semValue = new Expr\FuncCall($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 496 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 497 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 498 => null, 499 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 500 => static function ($self, $stackPos) { $self->semValue = new Name($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 501 => static function ($self, $stackPos) { $self->semValue = new Name\FullyQualified(substr($self->semStack[$stackPos-(1-1)], 1), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 502 => static function ($self, $stackPos) { $self->semValue = new Name\Relative(substr($self->semStack[$stackPos-(1-1)], 10), $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 503 => null, 504 => null, 505 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 506 => static function ($self, $stackPos) { $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 507 => null, 508 => null, 509 => static function ($self, $stackPos) { $self->semValue = null; }, 510 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 511 => static function ($self, $stackPos) { $self->semValue = array(); }, 512 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); foreach ($self->semValue as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; }, 513 => static function ($self, $stackPos) { foreach ($self->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = $self->semStack[$stackPos-(1-1)]; }, 514 => static function ($self, $stackPos) { $self->semValue = array(); }, 515 => null, 516 => static function ($self, $stackPos) { $self->semValue = new Expr\ConstFetch($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 517 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Line($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 518 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\File($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 519 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Dir($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 520 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Class_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 521 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Trait_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 522 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Method($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 523 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Function_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 524 => static function ($self, $stackPos) { $self->semValue = new Scalar\MagicConst\Namespace_($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 525 => static function ($self, $stackPos) { $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 526 => static function ($self, $stackPos) { $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(5-1)], $self->semStack[$stackPos-(5-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(5-1)], $self->tokenEndStack[$stackPos])); }, 527 => static function ($self, $stackPos) { $self->semValue = new Expr\ClassConstFetch($self->semStack[$stackPos-(3-1)], new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)])), $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 528 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_SHORT; $self->semValue = new Expr\Array_($self->semStack[$stackPos-(3-2)], $attrs); }, 529 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Expr\Array_::KIND_LONG; $self->semValue = new Expr\Array_($self->semStack[$stackPos-(4-3)], $attrs); $self->createdArrays->attach($self->semValue); }, 530 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; $self->createdArrays->attach($self->semValue); }, 531 => static function ($self, $stackPos) { $self->semValue = Scalar\String_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->supportsUnicodeEscapes()); }, 532 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; foreach ($self->semStack[$stackPos-(3-2)] as $s) { if ($s instanceof Node\InterpolatedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', $self->phpVersion->supportsUnicodeEscapes()); } }; $self->semValue = new Scalar\InterpolatedString($self->semStack[$stackPos-(3-2)], $attrs); }, 533 => static function ($self, $stackPos) { $self->semValue = $self->parseLNumber($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]), $self->phpVersion->allowsInvalidOctals()); }, 534 => static function ($self, $stackPos) { $self->semValue = Scalar\Float_::fromString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 535 => null, 536 => null, 537 => null, 538 => static function ($self, $stackPos) { $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true); }, 539 => static function ($self, $stackPos) { $self->semValue = $self->parseDocString($self->semStack[$stackPos-(2-1)], '', $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(2-2)], $self->tokenEndStack[$stackPos-(2-2)]), true); }, 540 => static function ($self, $stackPos) { $self->semValue = $self->parseDocString($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-2)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos]), $self->getAttributes($self->tokenStartStack[$stackPos-(3-3)], $self->tokenEndStack[$stackPos-(3-3)]), true); }, 541 => static function ($self, $stackPos) { $self->semValue = null; }, 542 => null, 543 => null, 544 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 545 => null, 546 => null, 547 => null, 548 => null, 549 => null, 550 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 551 => null, 552 => null, 553 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 554 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 555 => null, 556 => static function ($self, $stackPos) { $self->semValue = new Expr\MethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 557 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafeMethodCall($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->semStack[$stackPos-(4-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 558 => static function ($self, $stackPos) { $self->semValue = null; }, 559 => null, 560 => null, 561 => null, 562 => static function ($self, $stackPos) { $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 563 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 564 => null, 565 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 566 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 567 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable(new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])), $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 568 => static function ($self, $stackPos) { $var = $self->semStack[$stackPos-(1-1)]->name; $self->semValue = \is_string($var) ? new Node\VarLikeIdentifier($var, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])) : $var; }, 569 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 570 => null, 571 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 572 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 573 => static function ($self, $stackPos) { $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 574 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 575 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 576 => static function ($self, $stackPos) { $self->semValue = new Expr\StaticPropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 577 => null, 578 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 579 => null, 580 => null, 581 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 582 => null, 583 => static function ($self, $stackPos) { $self->semValue = new Expr\Error($self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); $self->errorState = 2; }, 584 => static function ($self, $stackPos) { $self->semValue = new Expr\List_($self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); $self->semValue->setAttribute('kind', Expr\List_::KIND_LIST); $self->postprocessList($self->semValue); }, 585 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(1-1)]; $end = count($self->semValue)-1; if ($self->semValue[$end]->value instanceof Expr\Error) array_pop($self->semValue); }, 586 => null, 587 => static function ($self, $stackPos) { }, 588 => static function ($self, $stackPos) { $self->semStack[$stackPos-(3-1)][] = $self->semStack[$stackPos-(3-3)]; $self->semValue = $self->semStack[$stackPos-(3-1)]; }, 589 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 590 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 591 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, true, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 592 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(1-1)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 593 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 594 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(4-4)], $self->semStack[$stackPos-(4-1)], true, $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 595 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(3-3)], $self->semStack[$stackPos-(3-1)], false, $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 596 => static function ($self, $stackPos) { $self->semValue = new Node\ArrayItem($self->semStack[$stackPos-(2-2)], null, false, $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos]), true); }, 597 => static function ($self, $stackPos) { $attrs = $self->createEmptyElemAttributes($self->tokenPos); $self->semValue = new Node\ArrayItem(new Expr\Error($attrs), null, false, $attrs); }, 598 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 599 => static function ($self, $stackPos) { $self->semStack[$stackPos-(2-1)][] = $self->semStack[$stackPos-(2-2)]; $self->semValue = $self->semStack[$stackPos-(2-1)]; }, 600 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(1-1)]); }, 601 => static function ($self, $stackPos) { $self->semValue = array($self->semStack[$stackPos-(2-1)], $self->semStack[$stackPos-(2-2)]); }, 602 => static function ($self, $stackPos) { $attrs = $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos]); $attrs['rawValue'] = $self->semStack[$stackPos-(1-1)]; $self->semValue = new Node\InterpolatedStringPart($self->semStack[$stackPos-(1-1)], $attrs); }, 603 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 604 => null, 605 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(4-1)], $self->semStack[$stackPos-(4-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(4-1)], $self->tokenEndStack[$stackPos])); }, 606 => static function ($self, $stackPos) { $self->semValue = new Expr\PropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 607 => static function ($self, $stackPos) { $self->semValue = new Expr\NullsafePropertyFetch($self->semStack[$stackPos-(3-1)], $self->semStack[$stackPos-(3-3)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 608 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 609 => static function ($self, $stackPos) { $self->semValue = new Expr\Variable($self->semStack[$stackPos-(3-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(3-1)], $self->tokenEndStack[$stackPos])); }, 610 => static function ($self, $stackPos) { $self->semValue = new Expr\ArrayDimFetch($self->semStack[$stackPos-(6-2)], $self->semStack[$stackPos-(6-4)], $self->getAttributes($self->tokenStartStack[$stackPos-(6-1)], $self->tokenEndStack[$stackPos])); }, 611 => static function ($self, $stackPos) { $self->semValue = $self->semStack[$stackPos-(3-2)]; }, 612 => static function ($self, $stackPos) { $self->semValue = new Scalar\String_($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 613 => static function ($self, $stackPos) { $self->semValue = $self->parseNumString($self->semStack[$stackPos-(1-1)], $self->getAttributes($self->tokenStartStack[$stackPos-(1-1)], $self->tokenEndStack[$stackPos])); }, 614 => static function ($self, $stackPos) { $self->semValue = $self->parseNumString('-' . $self->semStack[$stackPos-(2-2)], $self->getAttributes($self->tokenStartStack[$stackPos-(2-1)], $self->tokenEndStack[$stackPos])); }, 615 => null, ]; } } type = $type; $this->old = $old; $this->new = $new; } } tokens = $tokens; $this->indentMap = $this->calcIndentMap(); } public function haveParens(int $startPos, int $endPos): bool { return $this->haveTokenImmediatelyBefore($startPos, '(') && $this->haveTokenImmediatelyAfter($endPos, ')'); } public function haveBraces(int $startPos, int $endPos): bool { return ($this->haveTokenImmediatelyBefore($startPos, '{') || $this->haveTokenImmediatelyBefore($startPos, T_CURLY_OPEN)) && $this->haveTokenImmediatelyAfter($endPos, '}'); } public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType): bool { $tokens = $this->tokens; $pos--; for (; $pos >= 0; $pos--) { $token = $tokens[$pos]; if ($token->is($expectedTokenType)) { return true; } if (!$token->isIgnorable()) { break; } } return false; } public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType): bool { $tokens = $this->tokens; $pos++; for ($c = \count($tokens); $pos < $c; $pos++) { $token = $tokens[$pos]; if ($token->is($expectedTokenType)) { return true; } if (!$token->isIgnorable()) { break; } } return false; } public function skipLeft(int $pos, $skipTokenType): int { $tokens = $this->tokens; $pos = $this->skipLeftWhitespace($pos); if ($skipTokenType === \T_WHITESPACE) { return $pos; } if (!$tokens[$pos]->is($skipTokenType)) { throw new \Exception('Encountered unexpected token'); } $pos--; return $this->skipLeftWhitespace($pos); } public function skipRight(int $pos, $skipTokenType): int { $tokens = $this->tokens; $pos = $this->skipRightWhitespace($pos); if ($skipTokenType === \T_WHITESPACE) { return $pos; } if (!$tokens[$pos]->is($skipTokenType)) { throw new \Exception('Encountered unexpected token'); } $pos++; return $this->skipRightWhitespace($pos); } public function skipLeftWhitespace(int $pos): int { $tokens = $this->tokens; for (; $pos >= 0; $pos--) { if (!$tokens[$pos]->isIgnorable()) { break; } } return $pos; } public function skipRightWhitespace(int $pos): int { $tokens = $this->tokens; for ($count = \count($tokens); $pos < $count; $pos++) { if (!$tokens[$pos]->isIgnorable()) { break; } } return $pos; } public function findRight(int $pos, $findTokenType): int { $tokens = $this->tokens; for ($count = \count($tokens); $pos < $count; $pos++) { if ($tokens[$pos]->is($findTokenType)) { return $pos; } } return -1; } public function haveTokenInRange(int $startPos, int $endPos, $tokenType): bool { $tokens = $this->tokens; for ($pos = $startPos; $pos < $endPos; $pos++) { if ($tokens[$pos]->is($tokenType)) { return true; } } return false; } public function haveTagInRange(int $startPos, int $endPos): bool { return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG) || $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG); } public function getIndentationBefore(int $pos): int { return $this->indentMap[$pos]; } public function getTokenCode(int $from, int $to, int $indent): string { $tokens = $this->tokens; $result = ''; for ($pos = $from; $pos < $to; $pos++) { $token = $tokens[$pos]; $id = $token->id; $text = $token->text; if ($id === \T_CONSTANT_ENCAPSED_STRING || $id === \T_ENCAPSED_AND_WHITESPACE) { $result .= $text; } else { if ($indent < 0) { $result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $text); } elseif ($indent > 0) { $result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $text); } else { $result .= $text; } } } return $result; } private function calcIndentMap(): array { $indentMap = []; $indent = 0; foreach ($this->tokens as $i => $token) { $indentMap[] = $indent; if ($token->id === \T_WHITESPACE) { $content = $token->text; $newlinePos = \strrpos($content, "\n"); if (false !== $newlinePos) { $indent = \strlen($content) - $newlinePos - 1; } elseif ($i === 1 && $this->tokens[0]->id === \T_OPEN_TAG && $this->tokens[0]->text[\strlen($this->tokens[0]->text) - 1] === "\n") { $indent = \strlen($content); } } } $indentMap[] = $indent; return $indentMap; } } attrGroups = $attrGroups; $this->flags = $flags; $this->args = $args; $this->extends = $extends; $this->implements = $implements; $this->stmts = $stmts; } public static function fromNewNode(Expr\New_ $newNode): self { $class = $newNode->class; assert($class instanceof Node\Stmt\Class_); return new self( $class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements, $class->stmts, $newNode->getAttributes() ); } public function getType(): string { return 'Expr_PrintableNewAnonClass'; } public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts']; } } isEqual = $isEqual; } public function diff(array $old, array $new): array { $old = \array_values($old); $new = \array_values($new); list($trace, $x, $y) = $this->calculateTrace($old, $new); return $this->extractDiff($trace, $x, $y, $old, $new); } public function diffWithReplacements(array $old, array $new): array { return $this->coalesceReplacements($this->diff($old, $new)); } private function calculateTrace(array $old, array $new): array { $n = \count($old); $m = \count($new); $max = $n + $m; $v = [1 => 0]; $trace = []; for ($d = 0; $d <= $max; $d++) { $trace[] = $v; for ($k = -$d; $k <= $d; $k += 2) { if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) { $x = $v[$k + 1]; } else { $x = $v[$k - 1] + 1; } $y = $x - $k; while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) { $x++; $y++; } $v[$k] = $x; if ($x >= $n && $y >= $m) { return [$trace, $x, $y]; } } } throw new \Exception('Should not happen'); } private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array { $result = []; for ($d = \count($trace) - 1; $d >= 0; $d--) { $v = $trace[$d]; $k = $x - $y; if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) { $prevK = $k + 1; } else { $prevK = $k - 1; } $prevX = $v[$prevK]; $prevY = $prevX - $prevK; while ($x > $prevX && $y > $prevY) { $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]); $x--; $y--; } if ($d === 0) { break; } while ($x > $prevX) { $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null); $x--; } while ($y > $prevY) { $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]); $y--; } } return array_reverse($result); } private function coalesceReplacements(array $diff): array { $newDiff = []; $c = \count($diff); for ($i = 0; $i < $c; $i++) { $diffType = $diff[$i]->type; if ($diffType !== DiffElem::TYPE_REMOVE) { $newDiff[] = $diff[$i]; continue; } $j = $i; while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) { $j++; } $k = $j; while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) { $k++; } if ($j - $i === $k - $j) { $len = $j - $i; for ($n = 0; $n < $len; $n++) { $newDiff[] = new DiffElem( DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new ); } } else { for (; $i < $k; $i++) { $newDiff[] = $diff[$i]; } } $i = $k - 1; } return $newDiff; } } = 80000) { class TokenPolyfill extends \PhpToken { } return; } class TokenPolyfill { public int $id; public string $text; public int $line; public int $pos; private const IGNORABLE_TOKENS = [ \T_WHITESPACE => true, \T_COMMENT => true, \T_DOC_COMMENT => true, \T_OPEN_TAG => true, ]; private static array $identifierTokens; final public function __construct(int $id, string $text, int $line = -1, int $pos = -1) { $this->id = $id; $this->text = $text; $this->line = $line; $this->pos = $pos; } public function getTokenName(): ?string { if ($this->id < 256) { return \chr($this->id); } $name = token_name($this->id); return $name === 'UNKNOWN' ? null : $name; } public function is($kind): bool { if (\is_int($kind)) { return $this->id === $kind; } if (\is_string($kind)) { return $this->text === $kind; } if (\is_array($kind)) { foreach ($kind as $entry) { if (\is_int($entry)) { if ($this->id === $entry) { return true; } } elseif (\is_string($entry)) { if ($this->text === $entry) { return true; } } else { throw new \TypeError( 'Argument #1 ($kind) must only have elements of type string|int, ' . gettype($entry) . ' given'); } } return false; } throw new \TypeError( 'Argument #1 ($kind) must be of type string|int|array, ' .gettype($kind) . ' given'); } public function isIgnorable(): bool { return isset(self::IGNORABLE_TOKENS[$this->id]); } public function __toString(): string { return $this->text; } public static function tokenize(string $code, int $flags = 0): array { self::init(); $tokens = []; $line = 1; $pos = 0; $origTokens = \token_get_all($code, $flags); $numTokens = \count($origTokens); for ($i = 0; $i < $numTokens; $i++) { $token = $origTokens[$i]; if (\is_string($token)) { if (\strlen($token) === 2) { $tokens[] = new static(\ord('"'), $token, $line, $pos); $pos += 2; } else { $tokens[] = new static(\ord($token), $token, $line, $pos); $pos++; } } else { $id = $token[0]; $text = $token[1]; if ($id === \T_COMMENT && \substr($text, 0, 2) !== '/*' && \preg_match('/(\r\n|\n|\r)$/D', $text, $matches) ) { $trailingNewline = $matches[0]; $text = \substr($text, 0, -\strlen($trailingNewline)); $tokens[] = new static($id, $text, $line, $pos); $pos += \strlen($text); if ($i + 1 < $numTokens && $origTokens[$i + 1][0] === \T_WHITESPACE) { $origTokens[$i + 1][1] = $trailingNewline . $origTokens[$i + 1][1]; $origTokens[$i + 1][2]--; } else { $tokens[] = new static(\T_WHITESPACE, $trailingNewline, $line, $pos); $line++; $pos += \strlen($trailingNewline); } continue; } if (($id === \T_NS_SEPARATOR || isset(self::$identifierTokens[$id]))) { $newText = $text; $lastWasSeparator = $id === \T_NS_SEPARATOR; for ($j = $i + 1; $j < $numTokens; $j++) { if ($lastWasSeparator) { if (!isset(self::$identifierTokens[$origTokens[$j][0]])) { break; } $lastWasSeparator = false; } else { if ($origTokens[$j][0] !== \T_NS_SEPARATOR) { break; } $lastWasSeparator = true; } $newText .= $origTokens[$j][1]; } if ($lastWasSeparator) { $j--; $newText = \substr($newText, 0, -1); } if ($j > $i + 1) { if ($id === \T_NS_SEPARATOR) { $id = \T_NAME_FULLY_QUALIFIED; } elseif ($id === \T_NAMESPACE) { $id = \T_NAME_RELATIVE; } else { $id = \T_NAME_QUALIFIED; } $tokens[] = new static($id, $newText, $line, $pos); $pos += \strlen($newText); $i = $j - 1; continue; } } $tokens[] = new static($id, $text, $line, $pos); $line += \substr_count($text, "\n"); $pos += \strlen($text); } } return $tokens; } private static function init(): void { if (isset(self::$identifierTokens)) { return; } self::$identifierTokens = \array_fill_keys([ \T_STRING, \T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY, \T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND, \T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE, \T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH, \T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO, \T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT, \T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS, \T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN, \T_MATCH, ], true); } } targetPhpVersion = $phpVersion ?? PhpVersion::getNewestSupported(); $this->hostPhpVersion = PhpVersion::getHostVersion(); $emulators = [ new MatchTokenEmulator(), new NullsafeTokenEmulator(), new AttributeEmulator(), new EnumTokenEmulator(), new ReadonlyTokenEmulator(), new ExplicitOctalEmulator(), new ReadonlyFunctionTokenEmulator(), ]; foreach ($emulators as $emulator) { $emulatorPhpVersion = $emulator->getPhpVersion(); if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) { $this->emulators[] = $emulator; } elseif ($this->isReverseEmulationNeeded($emulatorPhpVersion)) { $this->emulators[] = new ReverseEmulator($emulator); } } } public function tokenize(string $code, ?ErrorHandler $errorHandler = null): array { $emulators = array_filter($this->emulators, function ($emulator) use ($code) { return $emulator->isEmulationNeeded($code); }); if (empty($emulators)) { return parent::tokenize($code, $errorHandler); } if ($errorHandler === null) { $errorHandler = new ErrorHandler\Throwing(); } $this->patches = []; foreach ($emulators as $emulator) { $code = $emulator->preprocessCode($code, $this->patches); } $collector = new ErrorHandler\Collecting(); $tokens = parent::tokenize($code, $collector); $this->sortPatches(); $tokens = $this->fixupTokens($tokens); $errors = $collector->getErrors(); if (!empty($errors)) { $this->fixupErrors($errors); foreach ($errors as $error) { $errorHandler->handleError($error); } } foreach ($emulators as $emulator) { $tokens = $emulator->emulate($code, $tokens); } return $tokens; } private function isForwardEmulationNeeded(PhpVersion $emulatorPhpVersion): bool { return $this->hostPhpVersion->older($emulatorPhpVersion) && $this->targetPhpVersion->newerOrEqual($emulatorPhpVersion); } private function isReverseEmulationNeeded(PhpVersion $emulatorPhpVersion): bool { return $this->hostPhpVersion->newerOrEqual($emulatorPhpVersion) && $this->targetPhpVersion->older($emulatorPhpVersion); } private function sortPatches(): void { usort($this->patches, function ($p1, $p2) { return $p1[0] <=> $p2[0]; }); } private function fixupTokens(array $tokens): array { if (\count($this->patches) === 0) { return $tokens; } $patchIdx = 0; list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; $posDelta = 0; $lineDelta = 0; for ($i = 0, $c = \count($tokens); $i < $c; $i++) { $token = $tokens[$i]; $pos = $token->pos; $token->pos += $posDelta; $token->line += $lineDelta; $localPosDelta = 0; $len = \strlen($token->text); while ($patchPos >= $pos && $patchPos < $pos + $len) { $patchTextLen = \strlen($patchText); if ($patchType === 'remove') { if ($patchPos === $pos && $patchTextLen === $len) { array_splice($tokens, $i, 1, []); $i--; $c--; } else { $token->text = substr_replace( $token->text, '', $patchPos - $pos + $localPosDelta, $patchTextLen ); $localPosDelta -= $patchTextLen; } $lineDelta -= \substr_count($patchText, "\n"); } elseif ($patchType === 'add') { $token->text = substr_replace( $token->text, $patchText, $patchPos - $pos + $localPosDelta, 0 ); $localPosDelta += $patchTextLen; $lineDelta += \substr_count($patchText, "\n"); } elseif ($patchType === 'replace') { $token->text = substr_replace( $token->text, $patchText, $patchPos - $pos + $localPosDelta, $patchTextLen ); } else { assert(false); } $patchIdx++; if ($patchIdx >= \count($this->patches)) { $patchPos = \PHP_INT_MAX; break; } list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx]; } $posDelta += $localPosDelta; } return $tokens; } private function fixupErrors(array $errors): void { foreach ($errors as $error) { $attrs = $error->getAttributes(); $posDelta = 0; $lineDelta = 0; foreach ($this->patches as $patch) { list($patchPos, $patchType, $patchText) = $patch; if ($patchPos >= $attrs['startFilePos']) { break; } if ($patchType === 'add') { $posDelta += strlen($patchText); $lineDelta += substr_count($patchText, "\n"); } elseif ($patchType === 'remove') { $posDelta -= strlen($patchText); $lineDelta -= substr_count($patchText, "\n"); } } $attrs['startFilePos'] += $posDelta; $attrs['endFilePos'] += $posDelta; $attrs['startLine'] += $lineDelta; $attrs['endLine'] += $lineDelta; $error->setAttributes($attrs); } } } ') !== false; } public function emulate(string $code, array $tokens): array { for ($i = 0, $c = count($tokens); $i < $c; ++$i) { $token = $tokens[$i]; if ($token->text === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1]->id === \T_OBJECT_OPERATOR) { array_splice($tokens, $i, 2, [ new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos), ]); $c--; continue; } if ($token->id === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1]) && $tokens[$i - 1]->id === \T_VARIABLE && preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $token->text, $matches) ) { $replacement = [ new Token(\T_NULLSAFE_OBJECT_OPERATOR, '?->', $token->line, $token->pos), new Token(\T_STRING, $matches[1], $token->line, $token->pos + 3), ]; $matchLen = \strlen($matches[0]); if ($matchLen !== \strlen($token->text)) { $replacement[] = new Token( \T_ENCAPSED_AND_WHITESPACE, \substr($token->text, $matchLen), $token->line, $token->pos + $matchLen ); } array_splice($tokens, $i, 1, $replacement); $c += \count($replacement) - 1; continue; } } return $tokens; } public function reverseEmulate(string $code, array $tokens): array { return $tokens; } } text === '(' || ($tokens[$pos + 1]->id === \T_WHITESPACE && isset($tokens[$pos + 2]) && $tokens[$pos + 2]->text === '('))); } } id == \T_LNUMBER && $token->text === '0' && isset($tokens[$i + 1]) && $tokens[$i + 1]->id == \T_STRING && preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1]->text) ) { $tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1]->text); array_splice($tokens, $i, 2, [ new Token($tokenKind, '0' . $tokens[$i + 1]->text, $token->line, $token->pos), ]); $c--; } } return $tokens; } private function resolveIntegerOrFloatToken(string $str): int { $str = substr($str, 1); $str = str_replace('_', '', $str); $num = octdec($str); return is_float($num) ? \T_DNUMBER : \T_LNUMBER; } public function reverseEmulate(string $code, array $tokens): array { return $tokens; } } text === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1]->text === '[') { array_splice($tokens, $i, 2, [ new Token(\T_ATTRIBUTE, '#[', $token->line, $token->pos), ]); $c--; continue; } } return $tokens; } public function reverseEmulate(string $code, array $tokens): array { return $tokens; } public function preprocessCode(string $code, array &$patches): string { $pos = 0; while (false !== $pos = strpos($code, '#[', $pos)) { $code[$pos] = '%'; $patches[] = [$pos, 'replace', '#']; $pos += 2; } return $code; } } id === \T_WHITESPACE && $tokens[$pos + 2]->id === \T_STRING; } } getKeywordString()) !== false; } protected function isKeywordContext(array $tokens, int $pos): bool { $previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos); return $previousNonSpaceToken === null || $previousNonSpaceToken->id !== \T_OBJECT_OPERATOR; } public function emulate(string $code, array $tokens): array { $keywordString = $this->getKeywordString(); foreach ($tokens as $i => $token) { if ($token->id === T_STRING && strtolower($token->text) === $keywordString && $this->isKeywordContext($tokens, $i)) { $token->id = $this->getKeywordToken(); } } return $tokens; } private function getPreviousNonSpaceToken(array $tokens, int $start): ?Token { for ($i = $start - 1; $i >= 0; --$i) { if ($tokens[$i]->id === T_WHITESPACE) { continue; } return $tokens[$i]; } return null; } public function reverseEmulate(string $code, array $tokens): array { $keywordToken = $this->getKeywordToken(); foreach ($tokens as $token) { if ($token->id === $keywordToken) { $token->id = \T_STRING; } } return $tokens; } } emulator = $emulator; } public function getPhpVersion(): PhpVersion { return $this->emulator->getPhpVersion(); } public function isEmulationNeeded(string $code): bool { return $this->emulator->isEmulationNeeded($code); } public function emulate(string $code, array $tokens): array { return $this->emulator->reverseEmulate($code, $tokens); } public function reverseEmulate(string $code, array $tokens): array { return $this->emulator->emulate($code, $tokens); } public function preprocessCode(string $code, array &$patches): string { return $code; } } errorHandler = $errorHandler; } public function startNamespace(?Name $namespace = null): void { $this->namespace = $namespace; $this->origAliases = $this->aliases = [ Stmt\Use_::TYPE_NORMAL => [], Stmt\Use_::TYPE_FUNCTION => [], Stmt\Use_::TYPE_CONSTANT => [], ]; } public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []): void { if ($type === Stmt\Use_::TYPE_CONSTANT) { $aliasLookupName = $aliasName; } else { $aliasLookupName = strtolower($aliasName); } if (isset($this->aliases[$type][$aliasLookupName])) { $typeStringMap = [ Stmt\Use_::TYPE_NORMAL => '', Stmt\Use_::TYPE_FUNCTION => 'function ', Stmt\Use_::TYPE_CONSTANT => 'const ', ]; $this->errorHandler->handleError(new Error( sprintf( 'Cannot use %s%s as %s because the name is already in use', $typeStringMap[$type], $name, $aliasName ), $errorAttrs )); return; } $this->aliases[$type][$aliasLookupName] = $name; $this->origAliases[$type][$aliasName] = $name; } public function getNamespace(): ?Name { return $this->namespace; } public function getResolvedName(Name $name, int $type): ?Name { if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) { if (!$name->isUnqualified()) { $this->errorHandler->handleError(new Error( sprintf("'\\%s' is an invalid class name", $name->toString()), $name->getAttributes() )); } return $name; } if ($name->isFullyQualified()) { return $name; } if (null !== $resolvedName = $this->resolveAlias($name, $type)) { return $resolvedName; } if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) { if (null === $this->namespace) { return new FullyQualified($name, $name->getAttributes()); } return null; } return FullyQualified::concat($this->namespace, $name, $name->getAttributes()); } public function getResolvedClassName(Name $name): Name { return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL); } public function getPossibleNames(string $name, int $type): array { $lcName = strtolower($name); if ($type === Stmt\Use_::TYPE_NORMAL) { if ($lcName === "self" || $lcName === "parent" || $lcName === "static") { return [new Name($name)]; } } $possibleNames = [new FullyQualified($name)]; if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) { if (null === $this->resolveAlias($nsRelativeName, $type)) { $possibleNames[] = $nsRelativeName; } } foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) { $lcOrig = $orig->toLowerString(); if (0 === strpos($lcName, $lcOrig . '\\')) { $possibleNames[] = new Name($alias . substr($name, strlen($lcOrig))); } } foreach ($this->origAliases[$type] as $alias => $orig) { if ($type === Stmt\Use_::TYPE_CONSTANT) { $normalizedOrig = $this->normalizeConstName($orig->toString()); if ($normalizedOrig === $this->normalizeConstName($name)) { $possibleNames[] = new Name($alias); } } else { if ($orig->toLowerString() === $lcName) { $possibleNames[] = new Name($alias); } } } return $possibleNames; } public function getShortName(string $name, int $type): Name { $possibleNames = $this->getPossibleNames($name, $type); $shortestName = null; $shortestLength = \INF; foreach ($possibleNames as $possibleName) { $length = strlen($possibleName->toCodeString()); if ($length < $shortestLength) { $shortestName = $possibleName; $shortestLength = $length; } } return $shortestName; } private function resolveAlias(Name $name, int $type): ?FullyQualified { $firstPart = $name->getFirst(); if ($name->isQualified()) { $checkName = strtolower($firstPart); if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) { $alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName]; return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes()); } } elseif ($name->isUnqualified()) { $checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : strtolower($firstPart); if (isset($this->aliases[$type][$checkName])) { return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes()); } } return null; } private function getNamespaceRelativeName(string $name, string $lcName, int $type): ?Name { if (null === $this->namespace) { return new Name($name); } if ($type === Stmt\Use_::TYPE_CONSTANT) { if ($lcName === "true" || $lcName === "false" || $lcName === "null") { return new Name($name); } } $namespacePrefix = strtolower($this->namespace . '\\'); if (0 === strpos($lcName, $namespacePrefix)) { return new Name(substr($name, strlen($namespacePrefix))); } return null; } private function normalizeConstName(string $name): string { $nsSep = strrpos($name, '\\'); if (false === $nsSep) { return $name; } $ns = substr($name, 0, $nsSep); $shortName = substr($name, $nsSep + 1); return strtolower($ns) . '\\' . $shortName; } } pos + \strlen($this->text); } public function getEndLine(): int { return $this->line + \substr_count($this->text, "\n"); } } attributes = $attributes; $this->type = $type; } public function getSubNodeNames(): array { return ['type']; } public function getType(): string { return 'NullableType'; } } conds = $conds; $this->body = $body; $this->attributes = $attributes; } public function getSubNodeNames(): array { return ['conds', 'body']; } public function getType(): string { return 'MatchArm'; } } true, 'parent' => true, 'static' => true, ]; final public function __construct($name, array $attributes = []) { $this->attributes = $attributes; $this->name = self::prepareName($name); } public function getSubNodeNames(): array { return ['name']; } public function getParts(): array { return \explode('\\', $this->name); } public function getFirst(): string { if (false !== $pos = \strpos($this->name, '\\')) { return \substr($this->name, 0, $pos); } return $this->name; } public function getLast(): string { if (false !== $pos = \strrpos($this->name, '\\')) { return \substr($this->name, $pos + 1); } return $this->name; } public function isUnqualified(): bool { return false === \strpos($this->name, '\\'); } public function isQualified(): bool { return false !== \strpos($this->name, '\\'); } public function isFullyQualified(): bool { return false; } public function isRelative(): bool { return false; } public function toString(): string { return $this->name; } public function toCodeString(): string { return $this->toString(); } public function toLowerString(): string { return strtolower($this->name); } public function isSpecialClassName(): bool { return isset(self::$specialClassNames[strtolower($this->name)]); } public function __toString(): string { return $this->name; } public function slice(int $offset, ?int $length = null) { if ($offset === 1 && $length === null) { if (false !== $pos = \strpos($this->name, '\\')) { return new static(\substr($this->name, $pos + 1)); } return null; } $parts = \explode('\\', $this->name); $numParts = \count($parts); $realOffset = $offset < 0 ? $offset + $numParts : $offset; if ($realOffset < 0 || $realOffset > $numParts) { throw new \OutOfBoundsException(sprintf('Offset %d is out of bounds', $offset)); } if (null === $length) { $realLength = $numParts - $realOffset; } else { $realLength = $length < 0 ? $length + $numParts - $realOffset : $length; if ($realLength < 0 || $realLength > $numParts - $realOffset) { throw new \OutOfBoundsException(sprintf('Length %d is out of bounds', $length)); } } if ($realLength === 0) { return null; } return new static(array_slice($parts, $realOffset, $realLength), $this->attributes); } public static function concat($name1, $name2, array $attributes = []) { if (null === $name1 && null === $name2) { return null; } if (null === $name1) { return new static($name2, $attributes); } if (null === $name2) { return new static($name1, $attributes); } else { return new static( self::prepareName($name1) . '\\' . self::prepareName($name2), $attributes ); } } private static function prepareName($name): string { if (\is_string($name)) { if ('' === $name) { throw new \InvalidArgumentException('Name cannot be empty'); } return $name; } if (\is_array($name)) { if (empty($name)) { throw new \InvalidArgumentException('Name cannot be empty'); } return implode('\\', $name); } if ($name instanceof self) { return $name->name; } throw new \InvalidArgumentException( 'Expected string, array of parts or Name instance' ); } public function getType(): string { return 'Name'; } } attributes = $attributes; $this->attrs = $attrs; } public function getSubNodeNames(): array { return ['attrs']; } public function getType(): string { return 'AttributeGroup'; } } attributes = $attributes; $this->type = $type; $this->byRef = $byRef; $this->variadic = $variadic; $this->var = $var; $this->default = $default; $this->flags = $flags; $this->attrGroups = $attrGroups; } public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } public function getType(): string { return 'Param'; } public function isPromoted(): bool { return $this->flags !== 0; } public function isPublic(): bool { return (bool) ($this->flags & Modifiers::PUBLIC); } public function isProtected(): bool { return (bool) ($this->flags & Modifiers::PROTECTED); } public function isPrivate(): bool { return (bool) ($this->flags & Modifiers::PRIVATE); } public function isReadonly(): bool { return (bool) ($this->flags & Modifiers::READONLY); } } attributes = $attributes; $this->types = $types; } public function getSubNodeNames(): array { return ['types']; } public function getType(): string { return 'IntersectionType'; } } attributes = $attributes; $this->var = $var; $this->default = $default; } public function getSubNodeNames(): array { return ['var', 'default']; } public function getType(): string { return 'StaticVar'; } } class_alias(StaticVar::class, Stmt\StaticVar::class); attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['stmts', 'cond']; } public function getType(): string { return 'Stmt_Do'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Stmt_Return'; } } name = \is_string($name) ? new Node\Identifier($name) : $name; $this->expr = $expr; $this->attrGroups = $attrGroups; } public function getSubNodeNames(): array { return ['attrGroups', 'name', 'expr']; } public function getType(): string { return 'Stmt_EnumCase'; } } attributes = $attributes; $this->stmts = $stmts; } public function getType(): string { return 'Stmt_Block'; } public function getSubNodeNames(): array { return ['stmts']; } } attributes = $attributes; $this->declares = $declares; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['declares', 'stmts']; } public function getType(): string { return 'Stmt_Declare'; } } attributes = $attributes; $this->init = $subNodes['init'] ?? []; $this->cond = $subNodes['cond'] ?? []; $this->loop = $subNodes['loop'] ?? []; $this->stmts = $subNodes['stmts'] ?? []; } public function getSubNodeNames(): array { return ['init', 'cond', 'loop', 'stmts']; } public function getType(): string { return 'Stmt_For'; } } attributes = $attributes; $this->vars = $vars; } public function getSubNodeNames(): array { return ['vars']; } public function getType(): string { return 'Stmt_Global'; } } attributes = $attributes; $this->expr = $expr; $this->keyVar = $subNodes['keyVar'] ?? null; $this->byRef = $subNodes['byRef'] ?? false; $this->valueVar = $valueVar; $this->stmts = $subNodes['stmts'] ?? []; } public function getSubNodeNames(): array { return ['expr', 'keyVar', 'byRef', 'valueVar', 'stmts']; } public function getType(): string { return 'Stmt_Foreach'; } } attributes = $attributes; $this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } public function getSubNodeNames(): array { return ['attrGroups', 'name', 'stmts']; } public function getType(): string { return 'Stmt_Trait'; } } attributes = $attributes; $this->name = \is_string($name) ? new Identifier($name) : $name; } public function getSubNodeNames(): array { return ['name']; } public function getType(): string { return 'Stmt_Goto'; } } attributes = $attributes; $this->cond = $cond; $this->cases = $cases; } public function getSubNodeNames(): array { return ['cond', 'cases']; } public function getType(): string { return 'Stmt_Switch'; } } attributes = $attributes; $this->vars = $vars; } public function getSubNodeNames(): array { return ['vars']; } public function getType(): string { return 'Stmt_Static'; } } attributes = $attributes; $this->stmts = $stmts; $this->catches = $catches; $this->finally = $finally; } public function getSubNodeNames(): array { return ['stmts', 'catches', 'finally']; } public function getType(): string { return 'Stmt_TryCatch'; } } attributes = $attributes; $this->value = $value; } public function getSubNodeNames(): array { return ['value']; } public function getType(): string { return 'Stmt_InlineHTML'; } } attributes = $attributes; $this->flags = $flags; $this->consts = $consts; $this->attrGroups = $attrGroups; $this->type = $type; } public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'type', 'consts']; } public function isPublic(): bool { return ($this->flags & Modifiers::PUBLIC) !== 0 || ($this->flags & Modifiers::VISIBILITY_MASK) === 0; } public function isProtected(): bool { return (bool) ($this->flags & Modifiers::PROTECTED); } public function isPrivate(): bool { return (bool) ($this->flags & Modifiers::PRIVATE); } public function isFinal(): bool { return (bool) ($this->flags & Modifiers::FINAL); } public function getType(): string { return 'Stmt_ClassConst'; } } attributes = $attributes; $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; $this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->extends = $subNodes['extends'] ?? null; $this->implements = $subNodes['implements'] ?? []; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'name', 'extends', 'implements', 'stmts']; } public function isAbstract(): bool { return (bool) ($this->flags & Modifiers::ABSTRACT); } public function isFinal(): bool { return (bool) ($this->flags & Modifiers::FINAL); } public function isReadonly(): bool { return (bool) ($this->flags & Modifiers::READONLY); } public function isAnonymous(): bool { return null === $this->name; } public function getType(): string { return 'Stmt_Class'; } } attributes = $attributes; $this->remaining = $remaining; } public function getSubNodeNames(): array { return ['remaining']; } public function getType(): string { return 'Stmt_HaltCompiler'; } } true, '__destruct' => true, '__call' => true, '__callstatic' => true, '__get' => true, '__set' => true, '__isset' => true, '__unset' => true, '__sleep' => true, '__wakeup' => true, '__tostring' => true, '__set_state' => true, '__clone' => true, '__invoke' => true, '__debuginfo' => true, '__serialize' => true, '__unserialize' => true, ]; public function __construct($name, array $subNodes = [], array $attributes = []) { $this->attributes = $attributes; $this->flags = $subNodes['flags'] ?? $subNodes['type'] ?? 0; $this->byRef = $subNodes['byRef'] ?? false; $this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->params = $subNodes['params'] ?? []; $this->returnType = $subNodes['returnType'] ?? null; $this->stmts = array_key_exists('stmts', $subNodes) ? $subNodes['stmts'] : []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'byRef', 'name', 'params', 'returnType', 'stmts']; } public function returnsByRef(): bool { return $this->byRef; } public function getParams(): array { return $this->params; } public function getReturnType() { return $this->returnType; } public function getStmts(): ?array { return $this->stmts; } public function getAttrGroups(): array { return $this->attrGroups; } public function isPublic(): bool { return ($this->flags & Modifiers::PUBLIC) !== 0 || ($this->flags & Modifiers::VISIBILITY_MASK) === 0; } public function isProtected(): bool { return (bool) ($this->flags & Modifiers::PROTECTED); } public function isPrivate(): bool { return (bool) ($this->flags & Modifiers::PRIVATE); } public function isAbstract(): bool { return (bool) ($this->flags & Modifiers::ABSTRACT); } public function isFinal(): bool { return (bool) ($this->flags & Modifiers::FINAL); } public function isStatic(): bool { return (bool) ($this->flags & Modifiers::STATIC); } public function isMagic(): bool { return isset(self::$magicNames[$this->name->toLowerString()]); } public function getType(): string { return 'Stmt_ClassMethod'; } } attributes = $attributes; $this->trait = $trait; $this->method = \is_string($method) ? new Node\Identifier($method) : $method; $this->newModifier = $newModifier; $this->newName = \is_string($newName) ? new Node\Identifier($newName) : $newName; } public function getSubNodeNames(): array { return ['trait', 'method', 'newModifier', 'newName']; } public function getType(): string { return 'Stmt_TraitUseAdaptation_Alias'; } } attributes = $attributes; $this->trait = $trait; $this->method = \is_string($method) ? new Node\Identifier($method) : $method; $this->insteadof = $insteadof; } public function getSubNodeNames(): array { return ['trait', 'method', 'insteadof']; } public function getType(): string { return 'Stmt_TraitUseAdaptation_Precedence'; } } attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['cond', 'stmts']; } public function getType(): string { return 'Stmt_ElseIf'; } } attributes = $attributes; $this->flags = $flags; $this->props = $props; $this->type = $type; $this->attrGroups = $attrGroups; } public function getSubNodeNames(): array { return ['attrGroups', 'flags', 'type', 'props']; } public function isPublic(): bool { return ($this->flags & Modifiers::PUBLIC) !== 0 || ($this->flags & Modifiers::VISIBILITY_MASK) === 0; } public function isProtected(): bool { return (bool) ($this->flags & Modifiers::PROTECTED); } public function isPrivate(): bool { return (bool) ($this->flags & Modifiers::PRIVATE); } public function isStatic(): bool { return (bool) ($this->flags & Modifiers::STATIC); } public function isReadonly(): bool { return (bool) ($this->flags & Modifiers::READONLY); } public function getType(): string { return 'Stmt_Property'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Stmt_Expression'; } } stmts as $stmt) { if ($stmt instanceof TraitUse) { $traitUses[] = $stmt; } } return $traitUses; } public function getConstants(): array { $constants = []; foreach ($this->stmts as $stmt) { if ($stmt instanceof ClassConst) { $constants[] = $stmt; } } return $constants; } public function getProperties(): array { $properties = []; foreach ($this->stmts as $stmt) { if ($stmt instanceof Property) { $properties[] = $stmt; } } return $properties; } public function getProperty(string $name): ?Property { foreach ($this->stmts as $stmt) { if ($stmt instanceof Property) { foreach ($stmt->props as $prop) { if ($prop instanceof PropertyItem && $name === $prop->name->toString()) { return $stmt; } } } } return null; } public function getMethods(): array { $methods = []; foreach ($this->stmts as $stmt) { if ($stmt instanceof ClassMethod) { $methods[] = $stmt; } } return $methods; } public function getMethod(string $name): ?ClassMethod { $lowerName = strtolower($name); foreach ($this->stmts as $stmt) { if ($stmt instanceof ClassMethod && $lowerName === $stmt->name->toLowerString()) { return $stmt; } } return null; } } attributes = $attributes; $this->name = $name; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['name', 'stmts']; } public function getType(): string { return 'Stmt_Namespace'; } } attributes = $attributes; $this->type = $type; $this->uses = $uses; } public function getSubNodeNames(): array { return ['type', 'uses']; } public function getType(): string { return 'Stmt_Use'; } } attributes = $attributes; $this->consts = $consts; } public function getSubNodeNames(): array { return ['consts']; } public function getType(): string { return 'Stmt_Const'; } } attributes = $attributes; $this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->extends = $subNodes['extends'] ?? []; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } public function getSubNodeNames(): array { return ['attrGroups', 'name', 'extends', 'stmts']; } public function getType(): string { return 'Stmt_Interface'; } } attributes = $attributes; $this->type = $type; $this->prefix = $prefix; $this->uses = $uses; } public function getSubNodeNames(): array { return ['type', 'prefix', 'uses']; } public function getType(): string { return 'Stmt_GroupUse'; } } attributes = $attributes; $this->exprs = $exprs; } public function getSubNodeNames(): array { return ['exprs']; } public function getType(): string { return 'Stmt_Echo'; } } attributes = $attributes; $this->num = $num; } public function getSubNodeNames(): array { return ['num']; } public function getType(): string { return 'Stmt_Break'; } } attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['cond', 'stmts']; } public function getType(): string { return 'Stmt_While'; } } name = \is_string($name) ? new Node\Identifier($name) : $name; $this->scalarType = $subNodes['scalarType'] ?? null; $this->implements = $subNodes['implements'] ?? []; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; parent::__construct($attributes); } public function getSubNodeNames(): array { return ['attrGroups', 'name', 'scalarType', 'implements', 'stmts']; } public function getType(): string { return 'Stmt_Enum'; } } attributes = $attributes; $this->types = $types; $this->var = $var; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['types', 'var', 'stmts']; } public function getType(): string { return 'Stmt_Catch'; } } attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['cond', 'stmts']; } public function getType(): string { return 'Stmt_Case'; } } attributes = $attributes; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['stmts']; } public function getType(): string { return 'Stmt_Else'; } } attributes = $attributes; $this->vars = $vars; } public function getSubNodeNames(): array { return ['vars']; } public function getType(): string { return 'Stmt_Unset'; } } attributes = $attributes; $this->byRef = $subNodes['byRef'] ?? false; $this->name = \is_string($name) ? new Node\Identifier($name) : $name; $this->params = $subNodes['params'] ?? []; $this->returnType = $subNodes['returnType'] ?? null; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } public function getSubNodeNames(): array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } public function returnsByRef(): bool { return $this->byRef; } public function getParams(): array { return $this->params; } public function getReturnType() { return $this->returnType; } public function getAttrGroups(): array { return $this->attrGroups; } public function getStmts(): array { return $this->stmts; } public function getType(): string { return 'Stmt_Function'; } } attributes = $attributes; $this->name = \is_string($name) ? new Identifier($name) : $name; } public function getSubNodeNames(): array { return ['name']; } public function getType(): string { return 'Stmt_Label'; } } attributes = $attributes; $this->stmts = $stmts; } public function getSubNodeNames(): array { return ['stmts']; } public function getType(): string { return 'Stmt_Finally'; } } attributes = $attributes; $this->traits = $traits; $this->adaptations = $adaptations; } public function getSubNodeNames(): array { return ['traits', 'adaptations']; } public function getType(): string { return 'Stmt_TraitUse'; } } attributes = $attributes; $this->num = $num; } public function getSubNodeNames(): array { return ['num']; } public function getType(): string { return 'Stmt_Continue'; } } attributes = $attributes; $this->cond = $cond; $this->stmts = $subNodes['stmts'] ?? []; $this->elseifs = $subNodes['elseifs'] ?? []; $this->else = $subNodes['else'] ?? null; } public function getSubNodeNames(): array { return ['cond', 'stmts', 'elseifs', 'else']; } public function getType(): string { return 'Stmt_If'; } } attributes = $attributes; $this->name = $name; $this->args = $args; } public function getSubNodeNames(): array { return ['name', 'args']; } public function getType(): string { return 'Attribute'; } } attributes = $attributes; } public function getType(): string { return 'VariadicPlaceholder'; } public function getSubNodeNames(): array { return []; } } attributes = $attributes; $this->name = $name; $this->value = $value; $this->byRef = $byRef; $this->unpack = $unpack; } public function getSubNodeNames(): array { return ['name', 'value', 'byRef', 'unpack']; } public function getType(): string { return 'Arg'; } } attributes = $attributes; $this->name = \is_string($name) ? new Identifier($name) : $name; $this->value = $value; } public function getSubNodeNames(): array { return ['name', 'value']; } public function getType(): string { return 'Const'; } } attributes = $attributes; $this->value = $value; } public function getSubNodeNames(): array { return ['value']; } public function getType(): string { return 'InterpolatedStringPart'; } } class_alias(InterpolatedStringPart::class, Scalar\EncapsedStringPart::class); attributes = $attributes; $this->name = \is_string($name) ? new Node\VarLikeIdentifier($name) : $name; $this->default = $default; } public function getSubNodeNames(): array { return ['name', 'default']; } public function getType(): string { return 'PropertyItem'; } } class_alias(PropertyItem::class, Stmt\PropertyProperty::class); attributes = $attributes; $this->key = $key; $this->value = $value; $this->byRef = $byRef; $this->unpack = $unpack; } public function getSubNodeNames(): array { return ['key', 'value', 'byRef', 'unpack']; } public function getType(): string { return 'ArrayItem'; } } class_alias(ArrayItem::class, Expr\ArrayItem::class); attributes = $attributes; $this->var = $var; $this->byRef = $byRef; } public function getSubNodeNames(): array { return ['var', 'byRef']; } public function getType(): string { return 'ClosureUse'; } } class_alias(ClosureUse::class, Expr\ClosureUse::class); attributes = $attributes; $this->type = $type; $this->name = $name; $this->alias = \is_string($alias) ? new Identifier($alias) : $alias; } public function getSubNodeNames(): array { return ['type', 'name', 'alias']; } public function getAlias(): Identifier { if (null !== $this->alias) { return $this->alias; } return new Identifier($this->name->getLast()); } public function getType(): string { return 'UseItem'; } } class_alias(UseItem::class, Stmt\UseUse::class); toString(); } public function getType(): string { return 'Name_FullyQualified'; } } toString(); } public function getType(): string { return 'Name_Relative'; } } attributes = $attributes; } public function getSubNodeNames(): array { return []; } abstract public function getName(): string; } '\\', '$' => '$', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\x1B", ]; public function __construct(string $value, array $attributes = []) { $this->attributes = $attributes; $this->value = $value; } public function getSubNodeNames(): array { return ['value']; } public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = true): self { $attributes['kind'] = ($str[0] === "'" || ($str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B'))) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED; $attributes['rawValue'] = $str; $string = self::parse($str, $parseUnicodeEscape); return new self($string, $attributes); } public static function parse(string $str, bool $parseUnicodeEscape = true): string { $bLength = 0; if ('b' === $str[0] || 'B' === $str[0]) { $bLength = 1; } if ('\'' === $str[$bLength]) { return str_replace( ['\\\\', '\\\''], ['\\', '\''], substr($str, $bLength + 1, -1) ); } else { return self::parseEscapeSequences( substr($str, $bLength + 1, -1), '"', $parseUnicodeEscape ); } } public static function parseEscapeSequences(string $str, ?string $quote, bool $parseUnicodeEscape = true): string { if (null !== $quote) { $str = str_replace('\\' . $quote, $quote, $str); } $extra = ''; if ($parseUnicodeEscape) { $extra = '|u\{([0-9a-fA-F]+)\}'; } return preg_replace_callback( '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}' . $extra . ')~', function ($matches) { $str = $matches[1]; if (isset(self::$replacements[$str])) { return self::$replacements[$str]; } if ('x' === $str[0] || 'X' === $str[0]) { return chr(hexdec(substr($str, 1))); } if ('u' === $str[0]) { $dec = hexdec($matches[2]); return self::codePointToUtf8(\is_int($dec) ? $dec : \PHP_INT_MAX); } else { return chr(octdec($str)); } }, $str ); } private static function codePointToUtf8(int $num): string { if ($num <= 0x7F) { return chr($num); } if ($num <= 0x7FF) { return chr(($num >> 6) + 0xC0) . chr(($num & 0x3F) + 0x80); } if ($num <= 0xFFFF) { return chr(($num >> 12) + 0xE0) . chr((($num >> 6) & 0x3F) + 0x80) . chr(($num & 0x3F) + 0x80); } if ($num <= 0x1FFFFF) { return chr(($num >> 18) + 0xF0) . chr((($num >> 12) & 0x3F) + 0x80) . chr((($num >> 6) & 0x3F) + 0x80) . chr(($num & 0x3F) + 0x80); } throw new Error('Invalid UTF-8 codepoint escape sequence: Codepoint too large'); } public function getType(): string { return 'Scalar_String'; } } attributes = $attributes; $this->value = $value; } public function getSubNodeNames(): array { return ['value']; } public static function fromString(string $str, array $attributes = []): Float_ { $attributes['rawValue'] = $str; $float = self::parse($str); return new Float_($float, $attributes); } public static function parse(string $str): float { $str = str_replace('_', '', $str); if ('0' === $str[0]) { if ('x' === $str[1] || 'X' === $str[1]) { return hexdec($str); } if ('b' === $str[1] || 'B' === $str[1]) { return bindec($str); } if (false === strpbrk($str, '.eE')) { return octdec(substr($str, 0, strcspn($str, '89'))); } } return (float) $str; } public function getType(): string { return 'Scalar_Float'; } } class_alias(Float_::class, DNumber::class); attributes = $attributes; $this->parts = $parts; } public function getSubNodeNames(): array { return ['parts']; } public function getType(): string { return 'Scalar_InterpolatedString'; } } class_alias(InterpolatedString::class, Encapsed::class); attributes = $attributes; $this->value = $value; } public function getSubNodeNames(): array { return ['value']; } public static function fromString(string $str, array $attributes = [], bool $allowInvalidOctal = false): Int_ { $attributes['rawValue'] = $str; $str = str_replace('_', '', $str); if ('0' !== $str[0] || '0' === $str) { $attributes['kind'] = Int_::KIND_DEC; return new Int_((int) $str, $attributes); } if ('x' === $str[1] || 'X' === $str[1]) { $attributes['kind'] = Int_::KIND_HEX; return new Int_(hexdec($str), $attributes); } if ('b' === $str[1] || 'B' === $str[1]) { $attributes['kind'] = Int_::KIND_BIN; return new Int_(bindec($str), $attributes); } if (!$allowInvalidOctal && strpbrk($str, '89')) { throw new Error('Invalid numeric literal', $attributes); } if ('o' === $str[1] || 'O' === $str[1]) { $str = substr($str, 2); } $attributes['kind'] = Int_::KIND_OCT; return new Int_(intval($str, 8), $attributes); } public function getType(): string { return 'Scalar_Int'; } } class_alias(Int_::class, LNumber::class); true, 'parent' => true, 'static' => true, ]; public function __construct(string $name, array $attributes = []) { $this->attributes = $attributes; $this->name = $name; } public function getSubNodeNames(): array { return ['name']; } public function toString(): string { return $this->name; } public function toLowerString(): string { return strtolower($this->name); } public function isSpecialClassName(): bool { return isset(self::$specialClassNames[strtolower($this->name)]); } public function __toString(): string { return $this->name; } public function getType(): string { return 'Identifier'; } } attributes = $attributes; $this->types = $types; } public function getSubNodeNames(): array { return ['types']; } public function getType(): string { return 'UnionType'; } } attributes = $attributes; $this->key = \is_string($key) ? new Node\Identifier($key) : $key; $this->value = $value; } public function getSubNodeNames(): array { return ['key', 'value']; } public function getType(): string { return 'DeclareItem'; } } class_alias(DeclareItem::class, Stmt\DeclareDeclare::class); attributes = $attributes; } public function getSubNodeNames(): array { return []; } public function getType(): string { return 'Expr_Error'; } } attributes = $attributes; $this->vars = $vars; } public function getSubNodeNames(): array { return ['vars']; } public function getType(): string { return 'Expr_Isset'; } } attributes = $attributes; $this->class = $class; $this->name = \is_string($name) ? new Identifier($name) : $name; } public function getSubNodeNames(): array { return ['class', 'name']; } public function getType(): string { return 'Expr_ClassConstFetch'; } } attributes = $attributes; $this->var = $var; } public function getSubNodeNames(): array { return ['var']; } public function getType(): string { return 'Expr_PostInc'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_Eval'; } } attributes = $attributes; $this->var = $var; $this->name = \is_string($name) ? new Identifier($name) : $name; $this->args = $args; } public function getSubNodeNames(): array { return ['var', 'name', 'args']; } public function getType(): string { return 'Expr_MethodCall'; } public function getRawArgs(): array { return $this->args; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_ErrorSuppress'; } } attributes = $attributes; $this->var = $var; $this->name = \is_string($name) ? new Identifier($name) : $name; $this->args = $args; } public function getSubNodeNames(): array { return ['var', 'name', 'args']; } public function getType(): string { return 'Expr_NullsafeMethodCall'; } public function getRawArgs(): array { return $this->args; } } attributes = $attributes; $this->class = $class; $this->name = \is_string($name) ? new VarLikeIdentifier($name) : $name; } public function getSubNodeNames(): array { return ['class', 'name']; } public function getType(): string { return 'Expr_StaticPropertyFetch'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_YieldFrom'; } } attributes = $attributes; $this->var = $var; $this->name = \is_string($name) ? new Identifier($name) : $name; } public function getSubNodeNames(): array { return ['var', 'name']; } public function getType(): string { return 'Expr_NullsafePropertyFetch'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_Exit'; } } attributes = $attributes; $this->var = $var; $this->dim = $dim; } public function getSubNodeNames(): array { return ['var', 'dim']; } public function getType(): string { return 'Expr_ArrayDimFetch'; } } '; } public function getType(): string { return 'Expr_BinaryOp_Greater'; } } '; } public function getType(): string { return 'Expr_BinaryOp_Spaceship'; } } >'; } public function getType(): string { return 'Expr_BinaryOp_ShiftRight'; } } ='; } public function getType(): string { return 'Expr_BinaryOp_GreaterOrEqual'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_BooleanNot'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_Empty'; } } attributes = $attributes; $this->cond = $cond; $this->arms = $arms; } public function getSubNodeNames(): array { return ['cond', 'arms']; } public function getType(): string { return 'Expr_Match'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_BitwiseNot'; } } attributes = $attributes; $this->name = $name; } public function getSubNodeNames(): array { return ['name']; } public function getType(): string { return 'Expr_ConstFetch'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_UnaryMinus'; } } attributes = $attributes; $this->static = $subNodes['static'] ?? false; $this->byRef = $subNodes['byRef'] ?? false; $this->params = $subNodes['params'] ?? []; $this->uses = $subNodes['uses'] ?? []; $this->returnType = $subNodes['returnType'] ?? null; $this->stmts = $subNodes['stmts'] ?? []; $this->attrGroups = $subNodes['attrGroups'] ?? []; } public function getSubNodeNames(): array { return ['attrGroups', 'static', 'byRef', 'params', 'uses', 'returnType', 'stmts']; } public function returnsByRef(): bool { return $this->byRef; } public function getParams(): array { return $this->params; } public function getReturnType() { return $this->returnType; } public function getStmts(): array { return $this->stmts; } public function getAttrGroups(): array { return $this->attrGroups; } public function getType(): string { return 'Expr_Closure'; } } attributes = $attributes; $this->expr = $expr; $this->class = $class; } public function getSubNodeNames(): array { return ['expr', 'class']; } public function getType(): string { return 'Expr_Instanceof'; } } attributes = $attributes; $this->left = $left; $this->right = $right; } public function getSubNodeNames(): array { return ['left', 'right']; } abstract public function getOperatorSigil(): string; } attributes = $attributes; $this->var = $var; $this->expr = $expr; } public function getSubNodeNames(): array { return ['var', 'expr']; } public function getType(): string { return 'Expr_AssignRef'; } } attributes = $attributes; $this->var = $var; $this->expr = $expr; } public function getSubNodeNames(): array { return ['var', 'expr']; } } attributes = $attributes; $this->items = $items; } public function getSubNodeNames(): array { return ['items']; } public function getType(): string { return 'Expr_List'; } } attributes = $attributes; $this->expr = $expr; $this->type = $type; } public function getSubNodeNames(): array { return ['expr', 'type']; } public function getType(): string { return 'Expr_Include'; } } attributes = $attributes; $this->var = $var; $this->expr = $expr; } public function getSubNodeNames(): array { return ['var', 'expr']; } public function getType(): string { return 'Expr_Assign'; } } attributes = $attributes; $this->var = $var; } public function getSubNodeNames(): array { return ['var']; } public function getType(): string { return 'Expr_PreInc'; } } getRawArgs(); return count($rawArgs) === 1 && current($rawArgs) instanceof VariadicPlaceholder; } public function getArgs(): array { assert(!$this->isFirstClassCallable()); return $this->getRawArgs(); } } attributes = $attributes; $this->class = $class; $this->args = $args; } public function getSubNodeNames(): array { return ['class', 'args']; } public function getType(): string { return 'Expr_New'; } public function getRawArgs(): array { return $this->args; } } attributes = $attributes; $this->cond = $cond; $this->if = $if; $this->else = $else; } public function getSubNodeNames(): array { return ['cond', 'if', 'else']; } public function getType(): string { return 'Expr_Ternary'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_Throw'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_Clone'; } } attributes = $attributes; $this->static = $subNodes['static'] ?? false; $this->byRef = $subNodes['byRef'] ?? false; $this->params = $subNodes['params'] ?? []; $this->returnType = $subNodes['returnType'] ?? null; $this->expr = $subNodes['expr']; $this->attrGroups = $subNodes['attrGroups'] ?? []; } public function getSubNodeNames(): array { return ['attrGroups', 'static', 'byRef', 'params', 'returnType', 'expr']; } public function returnsByRef(): bool { return $this->byRef; } public function getParams(): array { return $this->params; } public function getReturnType() { return $this->returnType; } public function getAttrGroups(): array { return $this->attrGroups; } public function getStmts(): array { return [new Node\Stmt\Return_($this->expr)]; } public function getType(): string { return 'Expr_ArrowFunction'; } } attributes = $attributes; $this->key = $key; $this->value = $value; } public function getSubNodeNames(): array { return ['key', 'value']; } public function getType(): string { return 'Expr_Yield'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_UnaryPlus'; } } attributes = $attributes; $this->var = $var; $this->name = \is_string($name) ? new Identifier($name) : $name; } public function getSubNodeNames(): array { return ['var', 'name']; } public function getType(): string { return 'Expr_PropertyFetch'; } } attributes = $attributes; $this->class = $class; $this->name = \is_string($name) ? new Identifier($name) : $name; $this->args = $args; } public function getSubNodeNames(): array { return ['class', 'name', 'args']; } public function getType(): string { return 'Expr_StaticCall'; } public function getRawArgs(): array { return $this->args; } } attributes = $attributes; $this->name = $name; $this->args = $args; } public function getSubNodeNames(): array { return ['name', 'args']; } public function getType(): string { return 'Expr_FuncCall'; } public function getRawArgs(): array { return $this->args; } } attributes = $attributes; $this->parts = $parts; } public function getSubNodeNames(): array { return ['parts']; } public function getType(): string { return 'Expr_ShellExec'; } } attributes = $attributes; $this->var = $var; } public function getSubNodeNames(): array { return ['var']; } public function getType(): string { return 'Expr_PostDec'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } } attributes = $attributes; $this->name = $name; } public function getSubNodeNames(): array { return ['name']; } public function getType(): string { return 'Expr_Variable'; } } attributes = $attributes; $this->items = $items; } public function getSubNodeNames(): array { return ['items']; } public function getType(): string { return 'Expr_Array'; } } attributes = $attributes; $this->expr = $expr; } public function getSubNodeNames(): array { return ['expr']; } public function getType(): string { return 'Expr_Print'; } } attributes = $attributes; $this->var = $var; } public function getSubNodeNames(): array { return ['var']; } public function getType(): string { return 'Expr_PreDec'; } } attributes = $attributes; } public function getLine(): int { return $this->attributes['startLine'] ?? -1; } public function getStartLine(): int { return $this->attributes['startLine'] ?? -1; } public function getEndLine(): int { return $this->attributes['endLine'] ?? -1; } public function getStartTokenPos(): int { return $this->attributes['startTokenPos'] ?? -1; } public function getEndTokenPos(): int { return $this->attributes['endTokenPos'] ?? -1; } public function getStartFilePos(): int { return $this->attributes['startFilePos'] ?? -1; } public function getEndFilePos(): int { return $this->attributes['endFilePos'] ?? -1; } public function getComments(): array { return $this->attributes['comments'] ?? []; } public function getDocComment(): ?Comment\Doc { $comments = $this->getComments(); for ($i = count($comments) - 1; $i >= 0; $i--) { $comment = $comments[$i]; if ($comment instanceof Comment\Doc) { return $comment; } } return null; } public function setDocComment(Comment\Doc $docComment): void { $comments = $this->getComments(); for ($i = count($comments) - 1; $i >= 0; $i--) { if ($comments[$i] instanceof Comment\Doc) { $comments[$i] = $docComment; $this->setAttribute('comments', $comments); return; } } $comments[] = $docComment; $this->setAttribute('comments', $comments); } public function setAttribute(string $key, $value): void { $this->attributes[$key] = $value; } public function hasAttribute(string $key): bool { return array_key_exists($key, $this->attributes); } public function getAttribute(string $key, $default = null) { if (array_key_exists($key, $this->attributes)) { return $this->attributes[$key]; } return $default; } public function getAttributes(): array { return $this->attributes; } public function setAttributes(array $attributes): void { $this->attributes = $attributes; } public function jsonSerialize(): array { return ['nodeType' => $this->getType()] + get_object_vars($this); } } addStmt($stmt); } return $this; } public function setDocComment($docComment) { $this->attributes['comments'] = [ BuilderHelpers::normalizeDocComment($docComment) ]; return $this; } } name = $name; } public function setValue($value) { $this->value = BuilderHelpers::normalizeValue($value); return $this; } public function setDocComment($docComment) { $this->attributes = [ 'comments' => [BuilderHelpers::normalizeDocComment($docComment)] ]; return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): PhpParser\Node { return new Stmt\EnumCase( $this->name, $this->value, $this->attributeGroups, $this->attributes ); } } name = $name; } public function addStmt($stmt) { $stmt = BuilderHelpers::normalizeNode($stmt); if ($stmt instanceof Stmt\Property) { $this->properties[] = $stmt; } elseif ($stmt instanceof Stmt\ClassMethod) { $this->methods[] = $stmt; } elseif ($stmt instanceof Stmt\TraitUse) { $this->uses[] = $stmt; } elseif ($stmt instanceof Stmt\ClassConst) { $this->constants[] = $stmt; } else { throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): PhpParser\Node { return new Stmt\Trait_( $this->name, [ 'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups, ], $this->attributes ); } } constants = [new Const_($name, BuilderHelpers::normalizeValue($value))]; } public function addConst($name, $value) { $this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value)); return $this; } public function makePublic() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } public function makeProtected() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } public function makePrivate() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } public function makeFinal() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL); return $this; } public function setDocComment($docComment) { $this->attributes = [ 'comments' => [BuilderHelpers::normalizeDocComment($docComment)] ]; return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function setType($type) { $this->type = BuilderHelpers::normalizeType($type); return $this; } public function getNode(): PhpParser\Node { return new Stmt\ClassConst( $this->constants, $this->flags, $this->attributes, $this->attributeGroups, $this->type ); } } name = $name; } public function setDefault($value) { $this->default = BuilderHelpers::normalizeValue($value); return $this; } public function setType($type) { $this->type = BuilderHelpers::normalizeType($type); if ($this->type == 'void') { throw new \LogicException('Parameter type cannot be void'); } return $this; } public function makeByRef() { $this->byRef = true; return $this; } public function makeVariadic() { $this->variadic = true; return $this; } public function makePublic() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } public function makeProtected() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } public function makePrivate() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } public function makeReadonly() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY); return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): Node { return new Node\Param( new Node\Expr\Variable($this->name), $this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups ); } } name = $name; } public function extend($class) { $this->extends = BuilderHelpers::normalizeName($class); return $this; } public function implement(...$interfaces) { foreach ($interfaces as $interface) { $this->implements[] = BuilderHelpers::normalizeName($interface); } return $this; } public function makeAbstract() { $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::ABSTRACT); return $this; } public function makeFinal() { $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::FINAL); return $this; } public function makeReadonly() { $this->flags = BuilderHelpers::addClassModifier($this->flags, Modifiers::READONLY); return $this; } public function addStmt($stmt) { $stmt = BuilderHelpers::normalizeNode($stmt); if ($stmt instanceof Stmt\Property) { $this->properties[] = $stmt; } elseif ($stmt instanceof Stmt\ClassMethod) { $this->methods[] = $stmt; } elseif ($stmt instanceof Stmt\TraitUse) { $this->uses[] = $stmt; } elseif ($stmt instanceof Stmt\ClassConst) { $this->constants[] = $stmt; } else { throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): PhpParser\Node { return new Stmt\Class_($this->name, [ 'flags' => $this->flags, 'extends' => $this->extends, 'implements' => $this->implements, 'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods), 'attrGroups' => $this->attributeGroups, ], $this->attributes); } } returnByRef = true; return $this; } public function addParam($param) { $param = BuilderHelpers::normalizeNode($param); if (!$param instanceof Node\Param) { throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType())); } $this->params[] = $param; return $this; } public function addParams(array $params) { foreach ($params as $param) { $this->addParam($param); } return $this; } public function setReturnType($type) { $this->returnType = BuilderHelpers::normalizeType($type); return $this; } } name = $name; } public function makePublic() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } public function makeProtected() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } public function makePrivate() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } public function makeStatic() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC); return $this; } public function makeReadonly() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::READONLY); return $this; } public function setDefault($value) { $this->default = BuilderHelpers::normalizeValue($value); return $this; } public function setDocComment($docComment) { $this->attributes = [ 'comments' => [BuilderHelpers::normalizeDocComment($docComment)] ]; return $this; } public function setType($type) { $this->type = BuilderHelpers::normalizeType($type); return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): PhpParser\Node { return new Stmt\Property( $this->flags !== 0 ? $this->flags : Modifiers::PUBLIC, [ new Node\PropertyItem($this->name, $this->default) ], $this->attributes, $this->type, $this->attributeGroups ); } } name = null !== $name ? BuilderHelpers::normalizeName($name) : null; } public function addStmt($stmt) { $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); return $this; } public function getNode(): Node { return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes); } } name = BuilderHelpers::normalizeName($name); $this->type = $type; } public function as(string $alias) { $this->alias = $alias; return $this; } public function getNode(): Node { return new Stmt\Use_([ new Node\UseItem($this->name, $this->alias) ], $this->type); } } name = $name; } public function extend(...$interfaces) { foreach ($interfaces as $interface) { $this->extends[] = BuilderHelpers::normalizeName($interface); } return $this; } public function addStmt($stmt) { $stmt = BuilderHelpers::normalizeNode($stmt); if ($stmt instanceof Stmt\ClassConst) { $this->constants[] = $stmt; } elseif ($stmt instanceof Stmt\ClassMethod) { $stmt->stmts = null; $this->methods[] = $stmt; } else { throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): PhpParser\Node { return new Stmt\Interface_($this->name, [ 'extends' => $this->extends, 'stmts' => array_merge($this->constants, $this->methods), 'attrGroups' => $this->attributeGroups, ], $this->attributes); } } name = $name; } public function setScalarType($scalarType) { $this->scalarType = BuilderHelpers::normalizeType($scalarType); return $this; } public function implement(...$interfaces) { foreach ($interfaces as $interface) { $this->implements[] = BuilderHelpers::normalizeName($interface); } return $this; } public function addStmt($stmt) { $stmt = BuilderHelpers::normalizeNode($stmt); if ($stmt instanceof Stmt\EnumCase) { $this->enumCases[] = $stmt; } elseif ($stmt instanceof Stmt\ClassMethod) { $this->methods[] = $stmt; } elseif ($stmt instanceof Stmt\TraitUse) { $this->uses[] = $stmt; } elseif ($stmt instanceof Stmt\ClassConst) { $this->constants[] = $stmt; } else { throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType())); } return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): PhpParser\Node { return new Stmt\Enum_($this->name, [ 'scalarType' => $this->scalarType, 'implements' => $this->implements, 'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods), 'attrGroups' => $this->attributeGroups, ], $this->attributes); } } name = $name; } public function makePublic() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PUBLIC); return $this; } public function makeProtected() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PROTECTED); return $this; } public function makePrivate() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::PRIVATE); return $this; } public function makeStatic() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::STATIC); return $this; } public function makeAbstract() { if (!empty($this->stmts)) { throw new \LogicException('Cannot make method with statements abstract'); } $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::ABSTRACT); $this->stmts = null; return $this; } public function makeFinal() { $this->flags = BuilderHelpers::addModifier($this->flags, Modifiers::FINAL); return $this; } public function addStmt($stmt) { if (null === $this->stmts) { throw new \LogicException('Cannot add statements to an abstract method'); } $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): Node { return new Stmt\ClassMethod($this->name, [ 'flags' => $this->flags, 'byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts, 'attrGroups' => $this->attributeGroups, ], $this->attributes); } } name = $name; } public function addStmt($stmt) { $this->stmts[] = BuilderHelpers::normalizeStmt($stmt); return $this; } public function addAttribute($attribute) { $this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute); return $this; } public function getNode(): Node { return new Stmt\Function_($this->name, [ 'byRef' => $this->returnByRef, 'params' => $this->params, 'returnType' => $this->returnType, 'stmts' => $this->stmts, 'attrGroups' => $this->attributeGroups, ], $this->attributes); } } and($trait); } } public function and($trait) { $this->traits[] = BuilderHelpers::normalizeName($trait); return $this; } public function with($adaptation) { $adaptation = BuilderHelpers::normalizeNode($adaptation); if (!$adaptation instanceof Stmt\TraitUseAdaptation) { throw new \LogicException('Adaptation must have type TraitUseAdaptation'); } $this->adaptations[] = $adaptation; return $this; } public function getNode(): Node { return new Stmt\TraitUse($this->traits, $this->adaptations); } } type = self::TYPE_UNDEFINED; $this->trait = is_null($trait) ? null : BuilderHelpers::normalizeName($trait); $this->method = BuilderHelpers::normalizeIdentifier($method); } public function as($alias) { if ($this->type === self::TYPE_UNDEFINED) { $this->type = self::TYPE_ALIAS; } if ($this->type !== self::TYPE_ALIAS) { throw new \LogicException('Cannot set alias for not alias adaptation buider'); } $this->alias = BuilderHelpers::normalizeIdentifier($alias); return $this; } public function makePublic() { $this->setModifier(Modifiers::PUBLIC); return $this; } public function makeProtected() { $this->setModifier(Modifiers::PROTECTED); return $this; } public function makePrivate() { $this->setModifier(Modifiers::PRIVATE); return $this; } public function insteadof(...$traits) { if ($this->type === self::TYPE_UNDEFINED) { if (is_null($this->trait)) { throw new \LogicException('Precedence adaptation must have trait'); } $this->type = self::TYPE_PRECEDENCE; } if ($this->type !== self::TYPE_PRECEDENCE) { throw new \LogicException('Cannot add overwritten traits for not precedence adaptation buider'); } foreach ($traits as $trait) { $this->insteadof[] = BuilderHelpers::normalizeName($trait); } return $this; } protected function setModifier(int $modifier): void { if ($this->type === self::TYPE_UNDEFINED) { $this->type = self::TYPE_ALIAS; } if ($this->type !== self::TYPE_ALIAS) { throw new \LogicException('Cannot set access modifier for not alias adaptation buider'); } if (is_null($this->modifier)) { $this->modifier = $modifier; } else { throw new \LogicException('Multiple access type modifiers are not allowed'); } } public function getNode(): Node { switch ($this->type) { case self::TYPE_ALIAS: return new Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias); case self::TYPE_PRECEDENCE: return new Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof); default: throw new \LogicException('Type of adaptation is not defined'); } } } lexer = $lexer; $this->phpVersion = $phpVersion ?? PhpVersion::getNewestSupported(); $this->initReduceCallbacks(); $this->phpTokenToSymbol = $this->createTokenMap(); $this->dropTokens = array_fill_keys( [\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], true ); } public function parse(string $code, ?ErrorHandler $errorHandler = null): ?array { $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing(); $this->createdArrays = new \SplObjectStorage(); $this->tokens = $this->lexer->tokenize($code, $this->errorHandler); $result = $this->doParse(); foreach ($this->createdArrays as $node) { foreach ($node->items as $item) { if ($item->value instanceof Expr\Error) { $this->errorHandler->handleError( new Error('Cannot use empty array elements in arrays', $item->getAttributes())); } } } $this->tokenStartStack = []; $this->tokenEndStack = []; $this->semStack = []; $this->semValue = null; $this->createdArrays = null; if ($result !== null) { $traverser = new NodeTraverser(new CommentAnnotatingVisitor($this->tokens)); $traverser->traverse($result); } return $result; } public function getTokens(): array { return $this->tokens; } protected function doParse(): ?array { $symbol = self::SYMBOL_NONE; $tokenValue = null; $this->tokenPos = -1; $this->tokenStartStack = []; $this->tokenEndStack = [0]; $state = 0; $stateStack = [$state]; $this->semStack = []; $stackPos = 0; $this->errorState = 0; for (;;) { if ($this->actionBase[$state] === 0) { $rule = $this->actionDefault[$state]; } else { if ($symbol === self::SYMBOL_NONE) { do { $token = $this->tokens[++$this->tokenPos]; $tokenId = $token->id; } while (isset($this->dropTokens[$tokenId])); $tokenValue = $token->text; if (!isset($this->phpTokenToSymbol[$tokenId])) { throw new \RangeException(sprintf( 'The lexer returned an invalid token (id=%d, value=%s)', $tokenId, $tokenValue )); } $symbol = $this->phpTokenToSymbol[$tokenId]; } $idx = $this->actionBase[$state] + $symbol; if ((($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol) || ($state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol)) && ($action = $this->action[$idx]) !== $this->defaultAction) { if ($action > 0) { ++$stackPos; $stateStack[$stackPos] = $state = $action; $this->semStack[$stackPos] = $tokenValue; $this->tokenStartStack[$stackPos] = $this->tokenPos; $this->tokenEndStack[$stackPos] = $this->tokenPos; $symbol = self::SYMBOL_NONE; if ($this->errorState) { --$this->errorState; } if ($action < $this->numNonLeafStates) { continue; } $rule = $action - $this->numNonLeafStates; } else { $rule = -$action; } } else { $rule = $this->actionDefault[$state]; } } for (;;) { if ($rule === 0) { return $this->semValue; } if ($rule !== $this->unexpectedTokenRule) { $ruleLength = $this->ruleToLength[$rule]; try { $callback = $this->reduceCallbacks[$rule]; if ($callback !== null) { $callback($this, $stackPos); } elseif ($ruleLength > 0) { $this->semValue = $this->semStack[$stackPos - $ruleLength + 1]; } } catch (Error $e) { if (-1 === $e->getStartLine()) { $e->setStartLine($this->tokens[$this->tokenPos]->line); } $this->emitError($e); return null; } $lastTokenEnd = $this->tokenEndStack[$stackPos]; $stackPos -= $ruleLength; $nonTerminal = $this->ruleToNonTerminal[$rule]; $idx = $this->gotoBase[$nonTerminal] + $stateStack[$stackPos]; if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] === $nonTerminal) { $state = $this->goto[$idx]; } else { $state = $this->gotoDefault[$nonTerminal]; } ++$stackPos; $stateStack[$stackPos] = $state; $this->semStack[$stackPos] = $this->semValue; $this->tokenEndStack[$stackPos] = $lastTokenEnd; if ($ruleLength === 0) { $this->tokenStartStack[$stackPos] = $this->tokenPos; } } else { switch ($this->errorState) { case 0: $msg = $this->getErrorMessage($symbol, $state); $this->emitError(new Error($msg, $this->getAttributesForToken($this->tokenPos))); case 1: case 2: $this->errorState = 3; while (!( (($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol) || ($state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $this->errorSymbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $this->errorSymbol) ) || ($action = $this->action[$idx]) === $this->defaultAction) { if ($stackPos <= 0) { return null; } $state = $stateStack[--$stackPos]; } ++$stackPos; $stateStack[$stackPos] = $state = $action; $this->tokenStartStack[$stackPos] = $this->tokenPos; $this->tokenEndStack[$stackPos] = $this->tokenEndStack[$stackPos - 1]; break; case 3: if ($symbol === 0) { return null; } $symbol = self::SYMBOL_NONE; break 2; } } if ($state < $this->numNonLeafStates) { break; } $rule = $state - $this->numNonLeafStates; } } throw new \RuntimeException('Reached end of parser loop'); } protected function emitError(Error $error): void { $this->errorHandler->handleError($error); } protected function getErrorMessage(int $symbol, int $state): string { $expectedString = ''; if ($expected = $this->getExpectedTokens($state)) { $expectedString = ', expecting ' . implode(' or ', $expected); } return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString; } protected function getExpectedTokens(int $state): array { $expected = []; $base = $this->actionBase[$state]; foreach ($this->symbolToName as $symbol => $name) { $idx = $base + $symbol; if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol || $state < $this->YY2TBLSTATE && ($idx = $this->actionBase[$state + $this->numNonLeafStates] + $symbol) >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol ) { if ($this->action[$idx] !== $this->unexpectedTokenRule && $this->action[$idx] !== $this->defaultAction && $symbol !== $this->errorSymbol ) { if (count($expected) === 4) { return []; } $expected[] = $name; } } } return $expected; } protected function getAttributes(int $tokenStartPos, int $tokenEndPos): array { $startToken = $this->tokens[$tokenStartPos]; $afterEndToken = $this->tokens[$tokenEndPos + 1]; return [ 'startLine' => $startToken->line, 'startTokenPos' => $tokenStartPos, 'startFilePos' => $startToken->pos, 'endLine' => $afterEndToken->line, 'endTokenPos' => $tokenEndPos, 'endFilePos' => $afterEndToken->pos - 1, ]; } protected function getAttributesForToken(int $tokenPos): array { if ($tokenPos < \count($this->tokens) - 1) { return $this->getAttributes($tokenPos, $tokenPos); } $token = $this->tokens[$tokenPos]; return [ 'startLine' => $token->line, 'startTokenPos' => $tokenPos, 'startFilePos' => $token->pos, 'endLine' => $token->line, 'endTokenPos' => $tokenPos, 'endFilePos' => $token->pos, ]; } protected function handleNamespaces(array $stmts): array { $hasErrored = false; $style = $this->getNamespacingStyle($stmts); if (null === $style) { return $stmts; } if ('brace' === $style) { $afterFirstNamespace = false; foreach ($stmts as $stmt) { if ($stmt instanceof Node\Stmt\Namespace_) { $afterFirstNamespace = true; } elseif (!$stmt instanceof Node\Stmt\HaltCompiler && !$stmt instanceof Node\Stmt\Nop && $afterFirstNamespace && !$hasErrored) { $this->emitError(new Error( 'No code may exist outside of namespace {}', $stmt->getAttributes())); $hasErrored = true; } } return $stmts; } else { $resultStmts = []; $targetStmts = &$resultStmts; $lastNs = null; foreach ($stmts as $stmt) { if ($stmt instanceof Node\Stmt\Namespace_) { if ($lastNs !== null) { $this->fixupNamespaceAttributes($lastNs); } if ($stmt->stmts === null) { $stmt->stmts = []; $targetStmts = &$stmt->stmts; $resultStmts[] = $stmt; } else { $resultStmts[] = $stmt; $targetStmts = &$resultStmts; } $lastNs = $stmt; } elseif ($stmt instanceof Node\Stmt\HaltCompiler) { $resultStmts[] = $stmt; } else { $targetStmts[] = $stmt; } } if ($lastNs !== null) { $this->fixupNamespaceAttributes($lastNs); } return $resultStmts; } } private function fixupNamespaceAttributes(Node\Stmt\Namespace_ $stmt): void { if (empty($stmt->stmts)) { return; } $endAttributes = ['endLine', 'endFilePos', 'endTokenPos']; $lastStmt = $stmt->stmts[count($stmt->stmts) - 1]; foreach ($endAttributes as $endAttribute) { if ($lastStmt->hasAttribute($endAttribute)) { $stmt->setAttribute($endAttribute, $lastStmt->getAttribute($endAttribute)); } } } private function getNamespaceErrorAttributes(Namespace_ $node): array { $attrs = $node->getAttributes(); if (isset($attrs['startLine'])) { $attrs['endLine'] = $attrs['startLine']; } if (isset($attrs['startTokenPos'])) { $attrs['endTokenPos'] = $attrs['startTokenPos']; } if (isset($attrs['startFilePos'])) { $attrs['endFilePos'] = $attrs['startFilePos'] + \strlen('namespace') - 1; } return $attrs; } private function getNamespacingStyle(array $stmts): ?string { $style = null; $hasNotAllowedStmts = false; foreach ($stmts as $i => $stmt) { if ($stmt instanceof Node\Stmt\Namespace_) { $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace'; if (null === $style) { $style = $currentStyle; if ($hasNotAllowedStmts) { $this->emitError(new Error( 'Namespace declaration statement has to be the very first statement in the script', $this->getNamespaceErrorAttributes($stmt) )); } } elseif ($style !== $currentStyle) { $this->emitError(new Error( 'Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $this->getNamespaceErrorAttributes($stmt) )); return 'semicolon'; } continue; } if ($stmt instanceof Node\Stmt\Declare_ || $stmt instanceof Node\Stmt\HaltCompiler || $stmt instanceof Node\Stmt\Nop) { continue; } if ($i === 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) { continue; } $hasNotAllowedStmts = true; } return $style; } protected function handleBuiltinTypes(Name $name) { if (!$name->isUnqualified()) { return $name; } $lowerName = $name->toLowerString(); if (!$this->phpVersion->supportsBuiltinType($lowerName)) { return $name; } return new Node\Identifier($lowerName, $name->getAttributes()); } protected function getAttributesAt(int $stackPos): array { return $this->getAttributes($this->tokenStartStack[$stackPos], $this->tokenEndStack[$stackPos]); } protected function getFloatCastKind(string $cast): int { $cast = strtolower($cast); if (strpos($cast, 'float') !== false) { return Double::KIND_FLOAT; } if (strpos($cast, 'real') !== false) { return Double::KIND_REAL; } return Double::KIND_DOUBLE; } protected function parseLNumber(string $str, array $attributes, bool $allowInvalidOctal = false): Int_ { try { return Int_::fromString($str, $attributes, $allowInvalidOctal); } catch (Error $error) { $this->emitError($error); return new Int_(0, $attributes); } } protected function parseNumString(string $str, array $attributes) { if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) { return new String_($str, $attributes); } $num = +$str; if (!is_int($num)) { return new String_($str, $attributes); } return new Int_($num, $attributes); } protected function stripIndentation( string $string, int $indentLen, string $indentChar, bool $newlineAtStart, bool $newlineAtEnd, array $attributes ): string { if ($indentLen === 0) { return $string; } $start = $newlineAtStart ? '(?:(?<=\n)|\A)' : '(?<=\n)'; $end = $newlineAtEnd ? '(?:(?=[\r\n])|\z)' : '(?=[\r\n])'; $regex = '/' . $start . '([ \t]*)(' . $end . ')?/'; return preg_replace_callback( $regex, function ($matches) use ($indentLen, $indentChar, $attributes) { $prefix = substr($matches[1], 0, $indentLen); if (false !== strpos($prefix, $indentChar === " " ? "\t" : " ")) { $this->emitError(new Error( 'Invalid indentation - tabs and spaces cannot be mixed', $attributes )); } elseif (strlen($prefix) < $indentLen && !isset($matches[2])) { $this->emitError(new Error( 'Invalid body indentation level ' . '(expecting an indentation level of at least ' . $indentLen . ')', $attributes )); } return substr($matches[0], strlen($prefix)); }, $string ); } protected function parseDocString( string $startToken, $contents, string $endToken, array $attributes, array $endTokenAttributes, bool $parseUnicodeEscape ): Expr { $kind = strpos($startToken, "'") === false ? String_::KIND_HEREDOC : String_::KIND_NOWDOC; $regex = '/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/'; $result = preg_match($regex, $startToken, $matches); assert($result === 1); $label = $matches[1]; $result = preg_match('/\A[ \t]*/', $endToken, $matches); assert($result === 1); $indentation = $matches[0]; $attributes['kind'] = $kind; $attributes['docLabel'] = $label; $attributes['docIndentation'] = $indentation; $indentHasSpaces = false !== strpos($indentation, " "); $indentHasTabs = false !== strpos($indentation, "\t"); if ($indentHasSpaces && $indentHasTabs) { $this->emitError(new Error( 'Invalid indentation - tabs and spaces cannot be mixed', $endTokenAttributes )); $indentation = ''; } $indentLen = \strlen($indentation); $indentChar = $indentHasSpaces ? " " : "\t"; if (\is_string($contents)) { if ($contents === '') { $attributes['rawValue'] = $contents; return new String_('', $attributes); } $contents = $this->stripIndentation( $contents, $indentLen, $indentChar, true, true, $attributes ); $contents = preg_replace('~(\r\n|\n|\r)\z~', '', $contents); $attributes['rawValue'] = $contents; if ($kind === String_::KIND_HEREDOC) { $contents = String_::parseEscapeSequences($contents, null, $parseUnicodeEscape); } return new String_($contents, $attributes); } else { assert(count($contents) > 0); if (!$contents[0] instanceof Node\InterpolatedStringPart) { $this->stripIndentation( '', $indentLen, $indentChar, true, false, $contents[0]->getAttributes() ); } $newContents = []; foreach ($contents as $i => $part) { if ($part instanceof Node\InterpolatedStringPart) { $isLast = $i === \count($contents) - 1; $part->value = $this->stripIndentation( $part->value, $indentLen, $indentChar, $i === 0, $isLast, $part->getAttributes() ); if ($isLast) { $part->value = preg_replace('~(\r\n|\n|\r)\z~', '', $part->value); } $part->setAttribute('rawValue', $part->value); $part->value = String_::parseEscapeSequences($part->value, null, $parseUnicodeEscape); if ('' === $part->value) { continue; } } $newContents[] = $part; } return new InterpolatedString($newContents, $attributes); } } protected function createCommentFromToken(Token $token, int $tokenPos): Comment { assert($token->id === \T_COMMENT || $token->id == \T_DOC_COMMENT); return \T_DOC_COMMENT === $token->id ? new Comment\Doc($token->text, $token->line, $token->pos, $tokenPos, $token->getEndLine(), $token->getEndPos() - 1, $tokenPos) : new Comment($token->text, $token->line, $token->pos, $tokenPos, $token->getEndLine(), $token->getEndPos() - 1, $tokenPos); } protected function getCommentBeforeToken(int $tokenPos): ?Comment { while (--$tokenPos >= 0) { $token = $this->tokens[$tokenPos]; if (!isset($this->dropTokens[$token->id])) { break; } if ($token->id === \T_COMMENT || $token->id === \T_DOC_COMMENT) { return $this->createCommentFromToken($token, $tokenPos); } } return null; } protected function maybeCreateZeroLengthNop(int $tokenPos): ?Nop { $comment = $this->getCommentBeforeToken($tokenPos); if ($comment === null) { return null; } $commentEndLine = $comment->getEndLine(); $commentEndFilePos = $comment->getEndFilePos(); $commentEndTokenPos = $comment->getEndTokenPos(); $attributes = [ 'startLine' => $commentEndLine, 'endLine' => $commentEndLine, 'startFilePos' => $commentEndFilePos + 1, 'endFilePos' => $commentEndFilePos, 'startTokenPos' => $commentEndTokenPos + 1, 'endTokenPos' => $commentEndTokenPos, ]; return new Nop($attributes); } protected function maybeCreateNop(int $tokenStartPos, int $tokenEndPos): ?Nop { if ($this->getCommentBeforeToken($tokenStartPos) === null) { return null; } return new Nop($this->getAttributes($tokenStartPos, $tokenEndPos)); } protected function handleHaltCompiler(): string { $nextToken = $this->tokens[$this->tokenPos + 1]; $this->tokenPos = \count($this->tokens) - 2; return $nextToken->id === \T_INLINE_HTML ? $nextToken->text : ''; } protected function inlineHtmlHasLeadingNewline(int $stackPos): bool { $tokenPos = $this->tokenStartStack[$stackPos]; $token = $this->tokens[$tokenPos]; assert($token->id == \T_INLINE_HTML); if ($tokenPos > 0) { $prevToken = $this->tokens[$tokenPos - 1]; assert($prevToken->id == \T_CLOSE_TAG); return false !== strpos($prevToken->text, "\n") || false !== strpos($prevToken->text, "\r"); } return true; } protected function createEmptyElemAttributes(int $tokenPos): array { return $this->getAttributesForToken($tokenPos); } protected function fixupArrayDestructuring(Array_ $node): Expr\List_ { $this->createdArrays->detach($node); return new Expr\List_(array_map(function (Node\ArrayItem $item) { if ($item->value instanceof Expr\Error) { return null; } if ($item->value instanceof Array_) { return new Node\ArrayItem( $this->fixupArrayDestructuring($item->value), $item->key, $item->byRef, $item->getAttributes()); } return $item; }, $node->items), ['kind' => Expr\List_::KIND_ARRAY] + $node->getAttributes()); } protected function postprocessList(Expr\List_ $node): void { foreach ($node->items as $i => $item) { if ($item->value instanceof Expr\Error) { $node->items[$i] = null; } } } protected function fixupAlternativeElse($node): void { $numStmts = \count($node->stmts); if ($numStmts !== 0 && $node->stmts[$numStmts - 1] instanceof Nop) { $nopAttrs = $node->stmts[$numStmts - 1]->getAttributes(); if (isset($nopAttrs['endLine'])) { $node->setAttribute('endLine', $nopAttrs['endLine']); } if (isset($nopAttrs['endFilePos'])) { $node->setAttribute('endFilePos', $nopAttrs['endFilePos']); } if (isset($nopAttrs['endTokenPos'])) { $node->setAttribute('endTokenPos', $nopAttrs['endTokenPos']); } } } protected function checkClassModifier(int $a, int $b, int $modifierPos): void { try { Modifiers::verifyClassModifier($a, $b); } catch (Error $error) { $error->setAttributes($this->getAttributesAt($modifierPos)); $this->emitError($error); } } protected function checkModifier(int $a, int $b, int $modifierPos): void { try { Modifiers::verifyModifier($a, $b); } catch (Error $error) { $error->setAttributes($this->getAttributesAt($modifierPos)); $this->emitError($error); } } protected function checkParam(Param $node): void { if ($node->variadic && null !== $node->default) { $this->emitError(new Error( 'Variadic parameter cannot have a default value', $node->default->getAttributes() )); } } protected function checkTryCatch(TryCatch $node): void { if (empty($node->catches) && null === $node->finally) { $this->emitError(new Error( 'Cannot use try without catch or finally', $node->getAttributes() )); } } protected function checkNamespace(Namespace_ $node): void { if (null !== $node->stmts) { foreach ($node->stmts as $stmt) { if ($stmt instanceof Namespace_) { $this->emitError(new Error( 'Namespace declarations cannot be nested', $stmt->getAttributes() )); } } } } private function checkClassName(?Identifier $name, int $namePos): void { if (null !== $name && $name->isSpecialClassName()) { $this->emitError(new Error( sprintf('Cannot use \'%s\' as class name as it is reserved', $name), $this->getAttributesAt($namePos) )); } } private function checkImplementedInterfaces(array $interfaces): void { foreach ($interfaces as $interface) { if ($interface->isSpecialClassName()) { $this->emitError(new Error( sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface), $interface->getAttributes() )); } } } protected function checkClass(Class_ $node, int $namePos): void { $this->checkClassName($node->name, $namePos); if ($node->extends && $node->extends->isSpecialClassName()) { $this->emitError(new Error( sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends), $node->extends->getAttributes() )); } $this->checkImplementedInterfaces($node->implements); } protected function checkInterface(Interface_ $node, int $namePos): void { $this->checkClassName($node->name, $namePos); $this->checkImplementedInterfaces($node->extends); } protected function checkEnum(Enum_ $node, int $namePos): void { $this->checkClassName($node->name, $namePos); $this->checkImplementedInterfaces($node->implements); } protected function checkClassMethod(ClassMethod $node, int $modifierPos): void { if ($node->flags & Modifiers::STATIC) { switch ($node->name->toLowerString()) { case '__construct': $this->emitError(new Error( sprintf('Constructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); break; case '__destruct': $this->emitError(new Error( sprintf('Destructor %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); break; case '__clone': $this->emitError(new Error( sprintf('Clone method %s() cannot be static', $node->name), $this->getAttributesAt($modifierPos))); break; } } if ($node->flags & Modifiers::READONLY) { $this->emitError(new Error( sprintf('Method %s() cannot be readonly', $node->name), $this->getAttributesAt($modifierPos))); } } protected function checkClassConst(ClassConst $node, int $modifierPos): void { if ($node->flags & Modifiers::STATIC) { $this->emitError(new Error( "Cannot use 'static' as constant modifier", $this->getAttributesAt($modifierPos))); } if ($node->flags & Modifiers::ABSTRACT) { $this->emitError(new Error( "Cannot use 'abstract' as constant modifier", $this->getAttributesAt($modifierPos))); } if ($node->flags & Modifiers::READONLY) { $this->emitError(new Error( "Cannot use 'readonly' as constant modifier", $this->getAttributesAt($modifierPos))); } } protected function checkProperty(Property $node, int $modifierPos): void { if ($node->flags & Modifiers::ABSTRACT) { $this->emitError(new Error('Properties cannot be declared abstract', $this->getAttributesAt($modifierPos))); } if ($node->flags & Modifiers::FINAL) { $this->emitError(new Error('Properties cannot be declared final', $this->getAttributesAt($modifierPos))); } } protected function checkUseUse(UseItem $node, int $namePos): void { if ($node->alias && $node->alias->isSpecialClassName()) { $this->emitError(new Error( sprintf( 'Cannot use %s as %s because \'%2$s\' is a special class name', $node->name, $node->alias ), $this->getAttributesAt($namePos) )); } } protected function createTokenMap(): array { $tokenMap = []; for ($i = 0; $i < 1000; ++$i) { if ($i < 256) { $tokenMap[$i] = $i; } elseif (\T_DOUBLE_COLON === $i) { $tokenMap[$i] = static::T_PAAMAYIM_NEKUDOTAYIM; } elseif (\T_OPEN_TAG_WITH_ECHO === $i) { $tokenMap[$i] = static::T_ECHO; } elseif (\T_CLOSE_TAG === $i) { $tokenMap[$i] = ord(';'); } elseif ('UNKNOWN' !== $name = token_name($i)) { if (defined($name = static::class . '::' . $name)) { $tokenMap[$i] = constant($name); } } } $tokenMap[\T_FN] = static::T_FN; $tokenMap[\T_COALESCE_EQUAL] = static::T_COALESCE_EQUAL; $tokenMap[\T_NAME_QUALIFIED] = static::T_NAME_QUALIFIED; $tokenMap[\T_NAME_FULLY_QUALIFIED] = static::T_NAME_FULLY_QUALIFIED; $tokenMap[\T_NAME_RELATIVE] = static::T_NAME_RELATIVE; $tokenMap[\T_MATCH] = static::T_MATCH; $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = static::T_NULLSAFE_OBJECT_OPERATOR; $tokenMap[\T_ATTRIBUTE] = static::T_ATTRIBUTE; $tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; $tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = static::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG; $tokenMap[\T_ENUM] = static::T_ENUM; $tokenMap[\T_READONLY] = static::T_READONLY; $fullTokenMap = []; foreach ($tokenMap as $phpToken => $extSymbol) { $intSymbol = $this->tokenToSymbol[$extSymbol]; if ($intSymbol === $this->invalidSymbol) { continue; } $fullTokenMap[$phpToken] = $intSymbol; } return $fullTokenMap; } } handleOrGroup($value); } if (!\preg_match('/^[\^~*]?v?[\d.*]+(?:-.*)?$/i', $value)) { throw new UnsupportedVersionConstraintException( \sprintf('Version constraint %s is not supported.', $value) ); } switch ($value[0]) { case '~': return $this->handleTildeOperator($value); case '^': return $this->handleCaretOperator($value); } $constraint = new VersionConstraintValue($value); if ($constraint->getMajor()->isAny()) { return new AnyVersionConstraint(); } if ($constraint->getMinor()->isAny()) { return new SpecificMajorVersionConstraint( $constraint->getVersionString(), $constraint->getMajor()->getValue() ?? 0 ); } if ($constraint->getPatch()->isAny()) { return new SpecificMajorAndMinorVersionConstraint( $constraint->getVersionString(), $constraint->getMajor()->getValue() ?? 0, $constraint->getMinor()->getValue() ?? 0 ); } return new ExactVersionConstraint($constraint->getVersionString()); } private function handleOrGroup(string $value): OrVersionConstraintGroup { $constraints = []; foreach (\preg_split('{\s*\|\|?\s*}', \trim($value)) as $groupSegment) { $constraints[] = $this->parse(\trim($groupSegment)); } return new OrVersionConstraintGroup($value, $constraints); } private function handleTildeOperator(string $value): AndVersionConstraintGroup { $constraintValue = new VersionConstraintValue(\substr($value, 1)); if ($constraintValue->getPatch()->isAny()) { return $this->handleCaretOperator($value); } $constraints = [ new GreaterThanOrEqualToVersionConstraint( $value, new Version(\substr($value, 1)) ), new SpecificMajorAndMinorVersionConstraint( $value, $constraintValue->getMajor()->getValue() ?? 0, $constraintValue->getMinor()->getValue() ?? 0 ) ]; return new AndVersionConstraintGroup($value, $constraints); } private function handleCaretOperator(string $value): AndVersionConstraintGroup { $constraintValue = new VersionConstraintValue(\substr($value, 1)); $constraints = [ new GreaterThanOrEqualToVersionConstraint($value, new Version(\substr($value, 1))) ]; if ($constraintValue->getMajor()->getValue() === 0) { $constraints[] = new SpecificMajorAndMinorVersionConstraint( $value, $constraintValue->getMajor()->getValue() ?? 0, $constraintValue->getMinor()->getValue() ?? 0 ); } else { $constraints[] = new SpecificMajorVersionConstraint( $value, $constraintValue->getMajor()->getValue() ?? 0 ); } return new AndVersionConstraintGroup( $value, $constraints ); } } value = $value; } public function isAny(): bool { return $this->value === null; } public function getValue(): ?int { return $this->value; } } ensureVersionStringIsValid($versionString); $this->originalVersionString = $versionString; } public function getPreReleaseSuffix(): PreReleaseSuffix { if ($this->preReleaseSuffix === null) { throw new NoPreReleaseSuffixException('No pre-release suffix set'); } return $this->preReleaseSuffix; } public function getOriginalString(): string { return $this->originalVersionString; } public function getVersionString(): string { $str = \sprintf( '%d.%d.%d', $this->getMajor()->getValue() ?? 0, $this->getMinor()->getValue() ?? 0, $this->getPatch()->getValue() ?? 0 ); if (!$this->hasPreReleaseSuffix()) { return $str; } return $str . '-' . $this->getPreReleaseSuffix()->asString(); } public function hasPreReleaseSuffix(): bool { return $this->preReleaseSuffix !== null; } public function equals(Version $other): bool { if ($this->getVersionString() !== $other->getVersionString()) { return false; } if ($this->hasBuildMetaData() !== $other->hasBuildMetaData()) { return false; } if ($this->hasBuildMetaData() && $other->hasBuildMetaData() && !$this->getBuildMetaData()->equals($other->getBuildMetaData())) { return false; } return true; } public function isGreaterThan(Version $version): bool { if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) { return false; } if ($version->getMajor()->getValue() < $this->getMajor()->getValue()) { return true; } if ($version->getMinor()->getValue() > $this->getMinor()->getValue()) { return false; } if ($version->getMinor()->getValue() < $this->getMinor()->getValue()) { return true; } if ($version->getPatch()->getValue() > $this->getPatch()->getValue()) { return false; } if ($version->getPatch()->getValue() < $this->getPatch()->getValue()) { return true; } if (!$version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { return false; } if ($version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { return true; } if (!$version->hasPreReleaseSuffix() && $this->hasPreReleaseSuffix()) { return false; } return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix()); } public function getMajor(): VersionNumber { return $this->major; } public function getMinor(): VersionNumber { return $this->minor; } public function getPatch(): VersionNumber { return $this->patch; } public function hasBuildMetaData(): bool { return $this->buildMetadata !== null; } public function getBuildMetaData(): BuildMetaData { if (!$this->hasBuildMetaData()) { throw new NoBuildMetaDataException('No build metadata set'); } return $this->buildMetadata; } private function parseVersion(array $matches): void { $this->major = new VersionNumber((int)$matches['Major']); $this->minor = new VersionNumber((int)$matches['Minor']); $this->patch = isset($matches['Patch']) ? new VersionNumber((int)$matches['Patch']) : new VersionNumber(0); if (isset($matches['PreReleaseSuffix']) && $matches['PreReleaseSuffix'] !== '') { $this->preReleaseSuffix = new PreReleaseSuffix($matches['PreReleaseSuffix']); } if (isset($matches['BuildMetadata'])) { $this->buildMetadata = new BuildMetaData($matches['BuildMetadata']); } } private function ensureVersionStringIsValid($version): void { $regex = '/^v? (?P0|[1-9]\d*) \\. (?P0|[1-9]\d*) (\\. (?P0|[1-9]\d*) )? (?: - (?(?:(dev|beta|b|rc|alpha|a|patch|p|pl)\.?\d*)) )? (?: \\+ (?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-@]+)*) )? $/xi'; if (\preg_match($regex, $version, $matches) !== 1) { throw new InvalidVersionException( \sprintf("Version string '%s' does not follow SemVer semantics", $version) ); } $this->parseVersion($matches); } } versionString = $versionString; $this->parseVersion($versionString); } public function getLabel(): string { return $this->label; } public function getBuildMetaData(): string { return $this->buildMetaData; } public function getVersionString(): string { return $this->versionString; } public function getMajor(): VersionNumber { return $this->major; } public function getMinor(): VersionNumber { return $this->minor; } public function getPatch(): VersionNumber { return $this->patch; } private function parseVersion(string $versionString): void { $this->extractBuildMetaData($versionString); $this->extractLabel($versionString); $this->stripPotentialVPrefix($versionString); $versionSegments = \explode('.', $versionString); $this->major = new VersionNumber(\is_numeric($versionSegments[0]) ? (int)$versionSegments[0] : null); $minorValue = isset($versionSegments[1]) && \is_numeric($versionSegments[1]) ? (int)$versionSegments[1] : null; $patchValue = isset($versionSegments[2]) && \is_numeric($versionSegments[2]) ? (int)$versionSegments[2] : null; $this->minor = new VersionNumber($minorValue); $this->patch = new VersionNumber($patchValue); } private function extractBuildMetaData(string &$versionString): void { if (\preg_match('/\+(.*)/', $versionString, $matches) === 1) { $this->buildMetaData = $matches[1]; $versionString = \str_replace($matches[0], '', $versionString); } } private function extractLabel(string &$versionString): void { if (\preg_match('/-(.*)/', $versionString, $matches) === 1) { $this->label = $matches[1]; $versionString = \str_replace($matches[0], '', $versionString); } } private function stripPotentialVPrefix(string &$versionString): void { if ($versionString[0] !== 'v') { return; } $versionString = \substr($versionString, 1); } } constraints = $constraints; } public function complies(Version $version): bool { foreach ($this->constraints as $constraint) { if ($constraint->complies($version)) { return true; } } return false; } } minimalVersion = $minimalVersion; } public function complies(Version $version): bool { return $version->getVersionString() === $this->minimalVersion->getVersionString() || $version->isGreaterThan($this->minimalVersion); } } constraints = $constraints; } public function complies(Version $version): bool { foreach ($this->constraints as $constraint) { if (!$constraint->complies($version)) { return false; } } return true; } } major = $major; } public function complies(Version $version): bool { return $version->getMajor()->getValue() === $this->major; } } originalValue = $originalValue; } public function asString(): string { return $this->originalValue; } } major = $major; $this->minor = $minor; } public function complies(Version $version): bool { if ($version->getMajor()->getValue() !== $this->major) { return false; } return $version->getMinor()->getValue() === $this->minor; } } getVersionString(); if ($version->hasBuildMetaData()) { $other .= '+' . $version->getBuildMetaData()->asString(); } return $this->asString() === $other; } } 0, 'a' => 1, 'alpha' => 1, 'b' => 2, 'beta' => 2, 'rc' => 3, 'p' => 4, 'pl' => 4, 'patch' => 4, ]; private $value; private $valueScore; private $number = 0; private $full; public function __construct(string $value) { $this->parseValue($value); } public function asString(): string { return $this->full; } public function getValue(): string { return $this->value; } public function getNumber(): ?int { return $this->number; } public function isGreaterThan(PreReleaseSuffix $suffix): bool { if ($this->valueScore > $suffix->valueScore) { return true; } if ($this->valueScore < $suffix->valueScore) { return false; } return $this->getNumber() > $suffix->getNumber(); } private function mapValueToScore(string $value): int { $value = \strtolower($value); return self::valueScoreMap[$value]; } private function parseValue(string $value): void { $regex = '/-?((dev|beta|b|rc|alpha|a|patch|p|pl)\.?(\d*)).*$/i'; if (\preg_match($regex, $value, $matches) !== 1) { throw new InvalidPreReleaseSuffixException(\sprintf('Invalid label %s', $value)); } $this->full = $matches[1]; $this->value = $matches[2]; if ($matches[3] !== '') { $this->number = (int)$matches[3]; } $this->valueScore = $this->mapValueToScore($matches[2]); } } value = $value; } public function asString(): string { return $this->value; } public function equals(BuildMetaData $other): bool { return $this->asString() === $other->asString(); } } map( ManifestDocument::fromFile($filename) ); } catch (Exception $e) { throw new ManifestLoaderException( sprintf('Loading %s failed.', $filename), (int)$e->getCode(), $e ); } } public static function fromPhar(string $filename): Manifest { return self::fromFile('phar://' . $filename . '/manifest.xml'); } public static function fromString(string $manifest): Manifest { try { return (new ManifestDocumentMapper())->map( ManifestDocument::fromString($manifest) ); } catch (Exception $e) { throw new ManifestLoaderException( 'Processing string failed', (int)$e->getCode(), $e ); } } } getCurrentElement() ); } } getAttributeValue('version'); } public function hasExtElements(): bool { return $this->hasChild('ext'); } public function getExtElements(): ExtElementCollection { return new ExtElementCollection( $this->getChildrenByName('ext') ); } } getChildrenByName('author') ); } public function getLicenseElement(): LicenseElement { return new LicenseElement( $this->getChildByName('license') ); } } getAttributeValue('type'); } public function getUrl(): string { return $this->getAttributeValue('url'); } } getChildrenByName('component') ); } } loadXML($xmlString); $errors = libxml_get_errors(); libxml_use_internal_errors($prev); } catch (Throwable $t) { throw new ManifestDocumentException($t->getMessage(), 0, $t); } if (count($errors) !== 0) { throw new ManifestDocumentLoadingException($errors); } return new self($dom); } private function __construct(DOMDocument $dom) { $this->ensureCorrectDocumentType($dom); $this->dom = $dom; } public function getContainsElement(): ContainsElement { return new ContainsElement( $this->fetchElementByName('contains') ); } public function getCopyrightElement(): CopyrightElement { return new CopyrightElement( $this->fetchElementByName('copyright') ); } public function getRequiresElement(): RequiresElement { return new RequiresElement( $this->fetchElementByName('requires') ); } public function hasBundlesElement(): bool { return $this->dom->getElementsByTagNameNS(self::XMLNS, 'bundles')->length === 1; } public function getBundlesElement(): BundlesElement { return new BundlesElement( $this->fetchElementByName('bundles') ); } private function ensureCorrectDocumentType(DOMDocument $dom): void { $root = $dom->documentElement; if ($root->localName !== 'phar' || $root->namespaceURI !== self::XMLNS) { throw new ManifestDocumentException('Not a phar.io manifest document'); } } private function fetchElementByName(string $elementName): DOMElement { $element = $this->dom->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); if (!$element instanceof DOMElement) { throw new ManifestDocumentException( sprintf('Element %s missing', $elementName) ); } return $element; } } getAttributeValue('name'); } public function getVersion(): string { return $this->getAttributeValue('version'); } public function getType(): string { return $this->getAttributeValue('type'); } public function getExtensionElement(): ExtensionElement { return new ExtensionElement( $this->getChildByName('extension') ); } } position = 0; $this->importNodes($nodeList); } #[ReturnTypeWillChange] abstract public function current(); public function next(): void { $this->position++; } public function key(): int { return $this->position; } public function valid(): bool { return $this->position < count($this->nodes); } public function rewind(): void { $this->position = 0; } protected function getCurrentElement(): DOMElement { return $this->nodes[$this->position]; } private function importNodes(DOMNodeList $nodeList): void { foreach ($nodeList as $node) { if (!$node instanceof DOMElement) { throw new ElementCollectionException( sprintf('\DOMElement expected, got \%s', get_class($node)) ); } $this->nodes[] = $node; } } } getAttributeValue('name'); } public function getEmail(): string { return $this->getAttributeValue('email'); } public function hasEMail(): bool { return $this->hasAttribute('email'); } } getAttributeValue('for'); } public function getCompatible(): string { return $this->getAttributeValue('compatible'); } } getCurrentElement() ); } } getCurrentElement() ); } } getChildByName('php') ); } } getAttributeValue('name'); } } getAttributeValue('name'); } public function getVersion(): string { return $this->getAttributeValue('version'); } } element = $element; } protected function getAttributeValue(string $name): string { if (!$this->element->hasAttribute($name)) { throw new ManifestElementException( sprintf( 'Attribute %s not set on element %s', $name, $this->element->localName ) ); } return $this->element->getAttribute($name); } protected function hasAttribute(string $name): bool { return $this->element->hasAttribute($name); } protected function getChildByName(string $elementName): DOMElement { $element = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); if (!$element instanceof DOMElement) { throw new ManifestElementException( sprintf('Element %s missing', $elementName) ); } return $element; } protected function getChildrenByName(string $elementName): DOMNodeList { $elementList = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName); if ($elementList->length === 0) { throw new ManifestElementException( sprintf('Element(s) %s missing', $elementName) ); } return $elementList; } protected function hasChild(string $elementName): bool { return $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->length !== 0; } } authors[] = $author; } public function getAuthors(): array { return $this->authors; } public function count(): int { return count($this->authors); } public function getIterator(): AuthorCollectionIterator { return new AuthorCollectionIterator($this); } } ensureEmailIsValid($email); $this->email = $email; } public function asString(): string { return $this->email; } private function ensureEmailIsValid(string $url): void { if (filter_var($url, FILTER_VALIDATE_EMAIL) === false) { throw new InvalidEmailException; } } } bundledComponents[] = $bundledComponent; } public function getBundledComponents(): array { return $this->bundledComponents; } public function count(): int { return count($this->bundledComponents); } public function getIterator(): BundledComponentCollectionIterator { return new BundledComponentCollectionIterator($this); } } name = $name; $this->url = $url; } public function getName(): string { return $this->name; } public function getUrl(): Url { return $this->url; } } ensureUrlIsValid($url); $this->url = $url; } public function asString(): string { return $this->url; } private function ensureUrlIsValid(string $url): void { if (filter_var($url, FILTER_VALIDATE_URL) === false) { throw new InvalidUrlException; } } } requirements[] = $requirement; } public function getRequirements(): array { return $this->requirements; } public function count(): int { return count($this->requirements); } public function getIterator(): RequirementCollectionIterator { return new RequirementCollectionIterator($this); } } ensureValidFormat($name); $this->name = $name; } public function asString(): string { return $this->name; } public function isEqual(ApplicationName $name): bool { return $this->name === $name->name; } private function ensureValidFormat(string $name): void { if (!preg_match('#\w/\w#', $name)) { throw new InvalidApplicationNameException( sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), InvalidApplicationNameException::InvalidFormat ); } } } authors = $authors; $this->license = $license; } public function getAuthors(): AuthorCollection { return $this->authors; } public function getLicense(): License { return $this->license; } } application = $application; $this->versionConstraint = $versionConstraint; } public function getApplicationName(): ApplicationName { return $this->application; } public function getVersionConstraint(): VersionConstraint { return $this->versionConstraint; } public function isExtension(): bool { return true; } public function isExtensionFor(ApplicationName $name): bool { return $this->application->isEqual($name); } public function isCompatibleWith(ApplicationName $name, Version $version): bool { return $this->isExtensionFor($name) && $this->versionConstraint->complies($version); } } requirements = $requirements->getRequirements(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return $this->position < count($this->requirements); } public function key(): int { return $this->position; } public function current(): Requirement { return $this->requirements[$this->position]; } public function next(): void { $this->position++; } } bundledComponents = $bundledComponents->getBundledComponents(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return $this->position < count($this->bundledComponents); } public function key(): int { return $this->position; } public function current(): BundledComponent { return $this->bundledComponents[$this->position]; } public function next(): void { $this->position++; } } name = $name; $this->version = $version; $this->type = $type; $this->copyrightInformation = $copyrightInformation; $this->requirements = $requirements; $this->bundledComponents = $bundledComponents; } public function getName(): ApplicationName { return $this->name; } public function getVersion(): Version { return $this->version; } public function getType(): Type { return $this->type; } public function getCopyrightInformation(): CopyrightInformation { return $this->copyrightInformation; } public function getRequirements(): RequirementCollection { return $this->requirements; } public function getBundledComponents(): BundledComponentCollection { return $this->bundledComponents; } public function isApplication(): bool { return $this->type->isApplication(); } public function isLibrary(): bool { return $this->type->isLibrary(); } public function isExtension(): bool { return $this->type->isExtension(); } public function isExtensionFor(ApplicationName $application, ?Version $version = null): bool { if (!$this->isExtension()) { return false; } $type = $this->type; if ($version !== null) { return $type->isCompatibleWith($application, $version); } return $type->isExtensionFor($application); } } name = $name; $this->version = $version; } public function getName(): string { return $this->name; } public function getVersion(): Version { return $this->version; } } extension = $extension; } public function asString(): string { return $this->extension; } } name = $name; $this->email = $email; } public function asString(): string { if (!$this->hasEmail()) { return $this->name; } return sprintf( '%s <%s>', $this->name, $this->email->asString() ); } public function getName(): string { return $this->name; } public function hasEmail(): bool { return $this->email !== null; } public function getEmail(): Email { if (!$this->hasEmail()) { throw new NoEmailAddressException(); } return $this->email; } } versionConstraint = $versionConstraint; } public function getVersionConstraint(): VersionConstraint { return $this->versionConstraint; } } authors = $authors->getAuthors(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return $this->position < count($this->authors); } public function key(): int { return $this->position; } public function current(): Author { return $this->authors[$this->position]; } public function next(): void { $this->position++; } } serializeToString($manifest) ); } public function serializeToString(Manifest $manifest): string { $this->startDocument(); $this->addContains($manifest->getName(), $manifest->getVersion(), $manifest->getType()); $this->addCopyright($manifest->getCopyrightInformation()); $this->addRequirements($manifest->getRequirements()); $this->addBundles($manifest->getBundledComponents()); return $this->finishDocument(); } private function startDocument(): void { $xmlWriter = new XMLWriter(); $xmlWriter->openMemory(); $xmlWriter->setIndent(true); $xmlWriter->setIndentString(str_repeat(' ', 4)); $xmlWriter->startDocument('1.0', 'UTF-8'); $xmlWriter->startElement('phar'); $xmlWriter->writeAttribute('xmlns', 'https://phar.io/xml/manifest/1.0'); $this->xmlWriter = $xmlWriter; } private function finishDocument(): string { $this->xmlWriter->endElement(); $this->xmlWriter->endDocument(); return $this->xmlWriter->outputMemory(); } private function addContains(ApplicationName $name, Version $version, Type $type): void { $this->xmlWriter->startElement('contains'); $this->xmlWriter->writeAttribute('name', $name->asString()); $this->xmlWriter->writeAttribute('version', $version->getVersionString()); switch (true) { case $type->isApplication(): { $this->xmlWriter->writeAttribute('type', 'application'); break; } case $type->isLibrary(): { $this->xmlWriter->writeAttribute('type', 'library'); break; } case $type->isExtension(): { $this->xmlWriter->writeAttribute('type', 'extension'); $this->addExtension( $type->getApplicationName(), $type->getVersionConstraint() ); break; } default: { $this->xmlWriter->writeAttribute('type', 'custom'); } } $this->xmlWriter->endElement(); } private function addCopyright(CopyrightInformation $copyrightInformation): void { $this->xmlWriter->startElement('copyright'); foreach ($copyrightInformation->getAuthors() as $author) { $this->xmlWriter->startElement('author'); $this->xmlWriter->writeAttribute('name', $author->getName()); $this->xmlWriter->writeAttribute('email', $author->getEmail()->asString()); $this->xmlWriter->endElement(); } $license = $copyrightInformation->getLicense(); $this->xmlWriter->startElement('license'); $this->xmlWriter->writeAttribute('type', $license->getName()); $this->xmlWriter->writeAttribute('url', $license->getUrl()->asString()); $this->xmlWriter->endElement(); $this->xmlWriter->endElement(); } private function addRequirements(RequirementCollection $requirementCollection): void { $phpRequirement = new AnyVersionConstraint(); $extensions = []; foreach ($requirementCollection as $requirement) { if ($requirement instanceof PhpVersionRequirement) { $phpRequirement = $requirement->getVersionConstraint(); continue; } if ($requirement instanceof PhpExtensionRequirement) { $extensions[] = $requirement->asString(); } } $this->xmlWriter->startElement('requires'); $this->xmlWriter->startElement('php'); $this->xmlWriter->writeAttribute('version', $phpRequirement->asString()); foreach ($extensions as $extension) { $this->xmlWriter->startElement('ext'); $this->xmlWriter->writeAttribute('name', $extension); $this->xmlWriter->endElement(); } $this->xmlWriter->endElement(); $this->xmlWriter->endElement(); } private function addBundles(BundledComponentCollection $bundledComponentCollection): void { if (count($bundledComponentCollection) === 0) { return; } $this->xmlWriter->startElement('bundles'); foreach ($bundledComponentCollection as $bundledComponent) { $this->xmlWriter->startElement('component'); $this->xmlWriter->writeAttribute('name', $bundledComponent->getName()); $this->xmlWriter->writeAttribute('version', $bundledComponent->getVersion()->getVersionString()); $this->xmlWriter->endElement(); } $this->xmlWriter->endElement(); } private function addExtension(ApplicationName $applicationName, VersionConstraint $versionConstraint): void { $this->xmlWriter->startElement('extension'); $this->xmlWriter->writeAttribute('for', $applicationName->asString()); $this->xmlWriter->writeAttribute('compatible', $versionConstraint->asString()); $this->xmlWriter->endElement(); } } getContainsElement(); $type = $this->mapType($contains); $copyright = $this->mapCopyright($document->getCopyrightElement()); $requirements = $this->mapRequirements($document->getRequiresElement()); $bundledComponents = $this->mapBundledComponents($document); return new Manifest( new ApplicationName($contains->getName()), new Version($contains->getVersion()), $type, $copyright, $requirements, $bundledComponents ); } catch (Throwable $e) { throw new ManifestDocumentMapperException($e->getMessage(), (int)$e->getCode(), $e); } } private function mapType(ContainsElement $contains): Type { switch ($contains->getType()) { case 'application': return Type::application(); case 'library': return Type::library(); case 'extension': return $this->mapExtension($contains->getExtensionElement()); } throw new ManifestDocumentMapperException( sprintf('Unsupported type %s', $contains->getType()) ); } private function mapCopyright(CopyrightElement $copyright): CopyrightInformation { $authors = new AuthorCollection(); foreach ($copyright->getAuthorElements() as $authorElement) { $authors->add( new Author( $authorElement->getName(), $authorElement->hasEMail() ? new Email($authorElement->getEmail()) : null ) ); } $licenseElement = $copyright->getLicenseElement(); $license = new License( $licenseElement->getType(), new Url($licenseElement->getUrl()) ); return new CopyrightInformation( $authors, $license ); } private function mapRequirements(RequiresElement $requires): RequirementCollection { $collection = new RequirementCollection(); $phpElement = $requires->getPHPElement(); $parser = new VersionConstraintParser; try { $versionConstraint = $parser->parse($phpElement->getVersion()); } catch (VersionException $e) { throw new ManifestDocumentMapperException( sprintf('Unsupported version constraint - %s', $e->getMessage()), (int)$e->getCode(), $e ); } $collection->add( new PhpVersionRequirement( $versionConstraint ) ); if (!$phpElement->hasExtElements()) { return $collection; } foreach ($phpElement->getExtElements() as $extElement) { $collection->add( new PhpExtensionRequirement($extElement->getName()) ); } return $collection; } private function mapBundledComponents(ManifestDocument $document): BundledComponentCollection { $collection = new BundledComponentCollection(); if (!$document->hasBundlesElement()) { return $collection; } foreach ($document->getBundlesElement()->getComponentElements() as $componentElement) { $collection->add( new BundledComponent( $componentElement->getName(), new Version( $componentElement->getVersion() ) ) ); } return $collection; } private function mapExtension(ExtensionElement $extension): Extension { try { $versionConstraint = (new VersionConstraintParser)->parse($extension->getCompatible()); return Type::extension( new ApplicationName($extension->getFor()), $versionConstraint ); } catch (VersionException $e) { throw new ManifestDocumentMapperException( sprintf('Unsupported version constraint - %s', $e->getMessage()), (int)$e->getCode(), $e ); } } } libxmlErrors = $libxmlErrors; $first = $this->libxmlErrors[0]; parent::__construct( sprintf( '%s (Line: %d / Column: %d / File: %s)', $first->message, $first->line, $first->column, $first->file ), $first->code ); } public function getLibxmlErrors(): array { return $this->libxmlErrors; } } isTokenKindFound(T_DOC_COMMENT); } public function isRisky(): bool { return false; } public function fix(\SplFileInfo $file, Tokens $tokens): void { foreach($tokens as $index => $token) { if (!$token->isGivenKind(T_DOC_COMMENT)) { continue; } if (\stripos($token->getContent(), '@var') === false) { continue; } if (preg_match('#^/\*\*[\s\*]+(@var[^\r\n]+)[\s\*]*\*\/$#u', $token->getContent(), $matches) !== 1) { continue; } $newContent = '/** ' . \rtrim($matches[1]) . ' */'; if ($newContent === $token->getContent()) { continue; } $tokens[$index] = new Token([T_DOC_COMMENT, $newContent]); } } public function getPriority(): int { return 0; } public function getName(): string { return 'PharIo/phpdoc_single_line_var_fixer'; } public function supports(\SplFileInfo $file): bool { return true; } } lexer = $lexer; } public function parse($input, $file = null) { $this->languageSpecifierLine = null; $this->input = $input; $this->file = $file; $this->tags = array(); try { $this->lexer->analyse($this->input, 'en'); } catch (LexerException $e) { throw new ParserException( sprintf('Lexer exception "%s" thrown for file %s', $e->getMessage(), $file), 0, $e ); } $feature = null; while ('EOS' !== ($predicted = $this->predictTokenType())) { $node = $this->parseExpression(); if (null === $node || "\n" === $node) { continue; } if (!$feature && $node instanceof FeatureNode) { $feature = $node; continue; } if ($feature && $node instanceof FeatureNode) { throw new ParserException(sprintf( 'Only one feature is allowed per feature file. But %s got multiple.', $this->file )); } if (is_string($node)) { throw new ParserException(sprintf( 'Expected Feature, but got text: "%s"%s', $node, $this->file ? ' in file: ' . $this->file : '' )); } if (!$node instanceof FeatureNode) { throw new ParserException(sprintf( 'Expected Feature, but got %s on line: %d%s', $node->getKeyword(), $node->getLine(), $this->file ? ' in file: ' . $this->file : '' )); } } return $feature; } protected function expectTokenType($type) { $types = (array) $type; if (in_array($this->predictTokenType(), $types)) { return $this->lexer->getAdvancedToken(); } $token = $this->lexer->predictToken(); throw new ParserException(sprintf( 'Expected %s token, but got %s on line: %d%s', implode(' or ', $types), $this->predictTokenType(), $token['line'], $this->file ? ' in file: ' . $this->file : '' )); } protected function acceptTokenType($type) { if ($type !== $this->predictTokenType()) { return null; } return $this->lexer->getAdvancedToken(); } protected function predictTokenType() { $token = $this->lexer->predictToken(); return $token['type']; } protected function parseExpression() { $type = $this->predictTokenType(); while ($type === 'Comment') { $this->expectTokenType('Comment'); $type = $this->predictTokenType(); } switch ($type) { case 'Feature': return $this->parseFeature(); case 'Background': return $this->parseBackground(); case 'Scenario': return $this->parseScenario(); case 'Outline': return $this->parseOutline(); case 'Examples': return $this->parseExamples(); case 'TableRow': return $this->parseTable(); case 'PyStringOp': return $this->parsePyString(); case 'Step': return $this->parseStep(); case 'Text': return $this->parseText(); case 'Newline': return $this->parseNewline(); case 'Tag': return $this->parseTags(); case 'Language': return $this->parseLanguage(); case 'EOS': return ''; } throw new ParserException(sprintf('Unknown token type: %s', $type)); } protected function parseFeature() { $token = $this->expectTokenType('Feature'); $title = trim($token['value'] ?? ''); $description = null; $tags = $this->popTags(); $background = null; $scenarios = array(); $keyword = $token['keyword']; $language = $this->lexer->getLanguage(); $file = $this->file; $line = $token['line']; array_push($this->passedNodesStack, 'Feature'); while ('EOS' !== $this->predictTokenType()) { $node = $this->parseExpression(); if (is_string($node)) { $text = preg_replace('/^\s{0,' . ($token['indent'] + 2) . '}|\s*$/', '', $node); $description .= (null !== $description ? "\n" : '') . $text; continue; } if (!$background && $node instanceof BackgroundNode) { $background = $node; continue; } if ($node instanceof ScenarioInterface) { $scenarios[] = $node; continue; } if ($background instanceof BackgroundNode && $node instanceof BackgroundNode) { throw new ParserException(sprintf( 'Each Feature could have only one Background, but found multiple on lines %d and %d%s', $background->getLine(), $node->getLine(), $this->file ? ' in file: ' . $this->file : '' )); } if (!$node instanceof ScenarioNode) { throw new ParserException(sprintf( 'Expected Scenario, Outline or Background, but got %s on line: %d%s', $node->getNodeType(), $node->getLine(), $this->file ? ' in file: ' . $this->file : '' )); } } return new FeatureNode( rtrim($title) ?: null, rtrim($description ?? '') ?: null, $tags, $background, $scenarios, $keyword, $language, $file, $line ); } protected function parseBackground() { $token = $this->expectTokenType('Background'); $title = trim($token['value'] ?? ''); $keyword = $token['keyword']; $line = $token['line']; if (count($this->popTags())) { throw new ParserException(sprintf( 'Background can not be tagged, but it is on line: %d%s', $line, $this->file ? ' in file: ' . $this->file : '' )); } $steps = array(); $allowedTokenTypes = array('Step', 'Newline', 'Text', 'Comment'); while (in_array($this->predictTokenType(), $allowedTokenTypes)) { $node = $this->parseExpression(); if ($node instanceof StepNode) { $steps[] = $this->normalizeStepNodeKeywordType($node, $steps); continue; } if (!count($steps) && is_string($node)) { $text = preg_replace('/^\s{0,' . ($token['indent'] + 2) . '}|\s*$/', '', $node); $title .= "\n" . $text; continue; } if ("\n" === $node) { continue; } if (is_string($node)) { throw new ParserException(sprintf( 'Expected Step, but got text: "%s"%s', $node, $this->file ? ' in file: ' . $this->file : '' )); } if (!$node instanceof StepNode) { throw new ParserException(sprintf( 'Expected Step, but got %s on line: %d%s', $node->getNodeType(), $node->getLine(), $this->file ? ' in file: ' . $this->file : '' )); } } return new BackgroundNode(rtrim($title) ?: null, $steps, $keyword, $line); } protected function parseScenario() { $token = $this->expectTokenType('Scenario'); $title = trim($token['value'] ?? ''); $tags = $this->popTags(); $keyword = $token['keyword']; $line = $token['line']; array_push($this->passedNodesStack, 'Scenario'); $steps = array(); while (in_array($this->predictTokenType(), array('Step', 'Newline', 'Text', 'Comment'))) { $node = $this->parseExpression(); if ($node instanceof StepNode) { $steps[] = $this->normalizeStepNodeKeywordType($node, $steps); continue; } if (!count($steps) && is_string($node)) { $text = preg_replace('/^\s{0,' . ($token['indent'] + 2) . '}|\s*$/', '', $node); $title .= "\n" . $text; continue; } if ("\n" === $node) { continue; } if (is_string($node)) { throw new ParserException(sprintf( 'Expected Step, but got text: "%s"%s', $node, $this->file ? ' in file: ' . $this->file : '' )); } if (!$node instanceof StepNode) { throw new ParserException(sprintf( 'Expected Step, but got %s on line: %d%s', $node->getNodeType(), $node->getLine(), $this->file ? ' in file: ' . $this->file : '' )); } } array_pop($this->passedNodesStack); return new ScenarioNode(rtrim($title) ?: null, $tags, $steps, $keyword, $line); } protected function parseOutline() { $token = $this->expectTokenType('Outline'); $title = trim($token['value'] ?? ''); $tags = $this->popTags(); $keyword = $token['keyword']; $examples = array(); $line = $token['line']; $steps = array(); array_push($this->passedNodesStack, 'Outline'); while (in_array($nextTokenType = $this->predictTokenType(), array('Step', 'Examples', 'Newline', 'Text', 'Comment', 'Tag'))) { if ($nextTokenType === 'Comment') { $this->lexer->skipPredictedToken(); continue; } $node = $this->parseExpression(); if ($node instanceof StepNode) { $steps[] = $this->normalizeStepNodeKeywordType($node, $steps); continue; } if ($node instanceof ExampleTableNode) { $examples[] = $node; continue; } if (!count($steps) && is_string($node)) { $text = preg_replace('/^\s{0,' . ($token['indent'] + 2) . '}|\s*$/', '', $node); $title .= "\n" . $text; continue; } if ("\n" === $node) { continue; } if (is_string($node)) { throw new ParserException(sprintf( 'Expected Step or Examples table, but got text: "%s"%s', $node, $this->file ? ' in file: ' . $this->file : '' )); } if (!$node instanceof StepNode) { throw new ParserException(sprintf( 'Expected Step or Examples table, but got %s on line: %d%s', $node->getNodeType(), $node->getLine(), $this->file ? ' in file: ' . $this->file : '' )); } } if (empty($examples)) { throw new ParserException(sprintf( 'Outline should have examples table, but got none for outline "%s" on line: %d%s', rtrim($title), $line, $this->file ? ' in file: ' . $this->file : '' )); } return new OutlineNode(rtrim($title) ?: null, $tags, $steps, $examples, $keyword, $line); } protected function parseStep() { $token = $this->expectTokenType('Step'); $keyword = $token['value']; $keywordType = $token['keyword_type']; $text = trim($token['text']); $line = $token['line']; array_push($this->passedNodesStack, 'Step'); $arguments = array(); while (in_array($predicted = $this->predictTokenType(), array('PyStringOp', 'TableRow', 'Newline', 'Comment'))) { if ('Comment' === $predicted || 'Newline' === $predicted) { $this->acceptTokenType($predicted); continue; } $node = $this->parseExpression(); if ($node instanceof PyStringNode || $node instanceof TableNode) { $arguments[] = $node; } } array_pop($this->passedNodesStack); return new StepNode($keyword, $text, $arguments, $line, $keywordType); } protected function parseExamples() { $keyword = ($this->expectTokenType('Examples'))['keyword']; $tags = empty($this->tags) ? array() : $this->popTags(); $table = $this->parseTableRows(); try { return new ExampleTableNode($table, $keyword, $tags); } catch(NodeException $e) { $this->rethrowNodeException($e); } } protected function parseTable() { $table = $this->parseTableRows(); try { return new TableNode($table); } catch(NodeException $e) { $this->rethrowNodeException($e); } } protected function parsePyString() { $token = $this->expectTokenType('PyStringOp'); $line = $token['line']; $strings = array(); while ('PyStringOp' !== ($predicted = $this->predictTokenType()) && 'Text' === $predicted) { $token = $this->expectTokenType('Text'); $strings[] = $token['value']; } $this->expectTokenType('PyStringOp'); return new PyStringNode($strings, $line); } protected function parseTags() { $token = $this->expectTokenType('Tag'); $this->guardTags($token['tags']); $this->tags = array_merge($this->tags, $token['tags']); $possibleTransitions = array( 'Outline' => array( 'Examples', 'Step' ) ); $currentType = '-1'; if (!empty($this->passedNodesStack)) { $currentType = $this->passedNodesStack[count($this->passedNodesStack) - 1]; } $nextType = $this->predictTokenType(); if (!isset($possibleTransitions[$currentType]) || in_array($nextType, $possibleTransitions[$currentType])) { return $this->parseExpression(); } return "\n"; } protected function popTags() { $tags = $this->tags; $this->tags = array(); return $tags; } protected function guardTags(array $tags) { foreach ($tags as $tag) { if (preg_match('/\s/', $tag)) { trigger_error('Whitespace in tags is deprecated, found "$tag"', E_USER_DEPRECATED); } } } protected function parseText() { $token = $this->expectTokenType('Text'); return $token['value']; } protected function parseNewline() { $this->expectTokenType('Newline'); return "\n"; } protected function parseLanguage() { $token = $this->expectTokenType('Language'); if (null === $this->languageSpecifierLine) { $this->lexer->analyse($this->input, $token['value']); $this->languageSpecifierLine = $token['line']; } elseif ($token['line'] !== $this->languageSpecifierLine) { throw new ParserException(sprintf( 'Ambiguous language specifiers on lines: %d and %d%s', $this->languageSpecifierLine, $token['line'], $this->file ? ' in file: ' . $this->file : '' )); } return $this->parseExpression(); } private function parseTableRows() { $table = array(); while (in_array($predicted = $this->predictTokenType(), array('TableRow', 'Newline', 'Comment'))) { if ('Comment' === $predicted || 'Newline' === $predicted) { $this->acceptTokenType($predicted); continue; } $token = $this->expectTokenType('TableRow'); $table[$token['line']] = $token['columns']; } return $table; } private function normalizeStepNodeKeywordType(StepNode $node, array $steps = array()) { if (in_array($node->getKeywordType(), array('And', 'But'))) { if (($prev = end($steps))) { $keywordType = $prev->getKeywordType(); } else { $keywordType = 'Given'; } $node = new StepNode( $node->getKeyword(), $node->getText(), $node->getArguments(), $node->getLine(), $keywordType ); } return $node; } private function rethrowNodeException(NodeException $e): void { throw new ParserException( $e->getMessage() . ($this->file ? ' in file ' . $this->file : ''), 0, $e ); } } $hash) { $feature = $this->loadFeatureHash($hash, $iterator); $features[] = $feature; } } elseif (isset($resource['feature'])) { $feature = $this->loadFeatureHash($resource['feature']); $features[] = $feature; } return $features; } protected function loadFeatureHash(array $hash, $line = 0) { $hash = array_merge( array( 'title' => null, 'description' => null, 'tags' => array(), 'keyword' => 'Feature', 'language' => 'en', 'line' => $line, 'scenarios' => array(), ), $hash ); $background = isset($hash['background']) ? $this->loadBackgroundHash($hash['background']) : null; $scenarios = array(); foreach ((array) $hash['scenarios'] as $scenarioIterator => $scenarioHash) { if (isset($scenarioHash['type']) && 'outline' === $scenarioHash['type']) { $scenarios[] = $this->loadOutlineHash($scenarioHash, $scenarioIterator); } else { $scenarios[] = $this->loadScenarioHash($scenarioHash, $scenarioIterator); } } return new FeatureNode($hash['title'], $hash['description'], $hash['tags'], $background, $scenarios, $hash['keyword'], $hash['language'], null, $hash['line']); } protected function loadBackgroundHash(array $hash) { $hash = array_merge( array( 'title' => null, 'keyword' => 'Background', 'line' => 0, 'steps' => array(), ), $hash ); $steps = $this->loadStepsHash($hash['steps']); return new BackgroundNode($hash['title'], $steps, $hash['keyword'], $hash['line']); } protected function loadScenarioHash(array $hash, $line = 0) { $hash = array_merge( array( 'title' => null, 'tags' => array(), 'keyword' => 'Scenario', 'line' => $line, 'steps' => array(), ), $hash ); $steps = $this->loadStepsHash($hash['steps']); return new ScenarioNode($hash['title'], $hash['tags'], $steps, $hash['keyword'], $hash['line']); } protected function loadOutlineHash(array $hash, $line = 0) { $hash = array_merge( array( 'title' => null, 'tags' => array(), 'keyword' => 'Scenario Outline', 'line' => $line, 'steps' => array(), 'examples' => array(), ), $hash ); $steps = $this->loadStepsHash($hash['steps']); if (isset($hash['examples']['keyword'])) { $examplesKeyword = $hash['examples']['keyword']; unset($hash['examples']['keyword']); } else { $examplesKeyword = 'Examples'; } $exHash = $hash['examples']; $examples = array(); if ($this->examplesAreInArray($exHash)) { $examples = $this->processExamplesArray($exHash, $examplesKeyword, $examples); } else { $examples[] = new ExampleTableNode($exHash, $examplesKeyword);; } return new OutlineNode($hash['title'], $hash['tags'], $steps, $examples, $hash['keyword'], $hash['line']); } private function loadStepsHash(array $hash) { $steps = array(); foreach ($hash as $stepIterator => $stepHash) { $steps[] = $this->loadStepHash($stepHash, $stepIterator); } return $steps; } protected function loadStepHash(array $hash, $line = 0) { $hash = array_merge( array( 'keyword_type' => 'Given', 'type' => 'Given', 'text' => null, 'keyword' => 'Scenario', 'line' => $line, 'arguments' => array(), ), $hash ); $arguments = array(); foreach ($hash['arguments'] as $argumentHash) { if ('table' === $argumentHash['type']) { $arguments[] = $this->loadTableHash($argumentHash['rows']); } elseif ('pystring' === $argumentHash['type']) { $arguments[] = $this->loadPyStringHash($argumentHash, $hash['line'] + 1); } } return new StepNode($hash['type'], $hash['text'], $arguments, $hash['line'], $hash['keyword_type']); } protected function loadTableHash(array $hash) { return new TableNode($hash); } protected function loadPyStringHash(array $hash, $line = 0) { $line = isset($hash['line']) ? $hash['line'] : $line; $strings = array(); foreach (explode("\n", $hash['text']) as $string) { $strings[] = $string; } return new PyStringNode($strings, $line); } private function examplesAreInArray($exHash) { return isset($exHash[0]); } private function processExamplesArray($exHash, $examplesKeyword, $examples) { for ($i = 0; $i < count($exHash); $i++) { if (isset($exHash[$i]['table'])) { $exHashTags = isset($exHash[$i]['tags']) ? $exHash[$i]['tags'] : array(); $examples[] = new ExampleTableNode($exHash[$i]['table'], $examplesKeyword, $exHashTags); } else { $examples[] = new ExampleTableNode($exHash[$i], $examplesKeyword); } } return $examples; } } loader = new ArrayLoader(); } public function supports($path) { return is_string($path) && is_file($absolute = $this->findAbsolutePath($path)) && 'yml' === pathinfo($absolute, PATHINFO_EXTENSION); } public function load($path) { $path = $this->findAbsolutePath($path); $hash = Yaml::parse(file_get_contents($path)); $features = $this->loader->load($hash); return array_map(function (FeatureNode $feature) use ($path) { return new FeatureNode( $feature->getTitle(), $feature->getDescription(), $feature->getTags(), $feature->getBackground(), $feature->getScenarios(), $feature->getKeyword(), $feature->getLanguage(), $path, $feature->getLine() ); }, $features); } } gherkin = $gherkin; } public function supports($path) { return is_string($path) && is_dir($this->findAbsolutePath($path)); } public function load($path) { $path = $this->findAbsolutePath($path); $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS) ); $paths = array_map('strval', iterator_to_array($iterator)); uasort($paths, 'strnatcasecmp'); $features = array(); foreach ($paths as $path) { $path = (string) $path; $loader = $this->gherkin->resolveLoader($path); if (null !== $loader) { $features = array_merge($features, $loader->load($path)); } } return $features; } } parser = $parser; $this->cache = $cache; } public function setCache(CacheInterface $cache) { $this->cache = $cache; } public function supports($path) { return is_string($path) && is_file($absolute = $this->findAbsolutePath($path)) && 'feature' === pathinfo($absolute, PATHINFO_EXTENSION); } public function load($path) { $path = $this->findAbsolutePath($path); if ($this->cache) { if ($this->cache->isFresh($path, filemtime($path))) { $feature = $this->cache->read($path); } elseif (null !== $feature = $this->parseFeature($path)) { $this->cache->write($path, $feature); } } else { $feature = $this->parseFeature($path); } return null !== $feature ? array($feature) : array(); } protected function parseFeature($path) { $content = file_get_contents($path); $feature = $this->parser->parse($content, $path); return $feature; } } basePath = realpath($path); } protected function findRelativePath($path) { if (null !== $this->basePath) { return strtr($path, array($this->basePath . DIRECTORY_SEPARATOR => '')); } return $path; } protected function findAbsolutePath($path) { if (is_file($path) || is_dir($path)) { return realpath($path); } if (null === $this->basePath) { return false; } if (is_file($this->basePath . DIRECTORY_SEPARATOR . $path) || is_dir($this->basePath . DIRECTORY_SEPARATOR . $path)) { return realpath($this->basePath . DIRECTORY_SEPARATOR . $path); } return false; } } keywords = $keywords; } public function analyse($input, $language = 'en') { if ('UTF-8' !== mb_detect_encoding($input, 'UTF-8', true)) { throw new LexerException('Feature file is not in UTF8 encoding'); } $input = strtr($input, array("\r\n" => "\n", "\r" => "\n")); $this->lines = explode("\n", $input); $this->linesCount = count($this->lines); $this->line = $this->lines[0]; $this->lineNumber = 1; $this->trimmedLine = null; $this->eos = false; $this->deferredObjects = array(); $this->deferredObjectsCount = 0; $this->stashedToken = null; $this->inPyString = false; $this->pyStringSwallow = 0; $this->featureStarted = false; $this->allowMultilineArguments = false; $this->allowSteps = false; $this->keywords->setLanguage($this->language = $language); $this->keywordsCache = array(); $this->stepKeywordTypesCache = array(); } public function getLanguage() { return $this->language; } public function getAdvancedToken() { return $this->getStashedToken() ?: $this->getNextToken(); } public function deferToken(array $token) { $token['deferred'] = true; $this->deferredObjects[] = $token; ++$this->deferredObjectsCount; } public function predictToken() { if (null === $this->stashedToken) { $this->stashedToken = $this->getNextToken(); } return $this->stashedToken; } public function skipPredictedToken() { $this->stashedToken = null; } public function takeToken($type, $value = null) { return array( 'type' => $type, 'line' => $this->lineNumber, 'value' => $value ?: null, 'deferred' => false ); } protected function consumeLine() { ++$this->lineNumber; if (($this->lineNumber - 1) === $this->linesCount) { $this->eos = true; return; } $this->line = $this->lines[$this->lineNumber - 1]; $this->trimmedLine = null; } protected function consumeLineUntil(int $trimmedOffset) { $this->line = mb_substr(ltrim($this->line), $trimmedOffset, null, 'utf-8'); $this->trimmedLine = null; } protected function getTrimmedLine() { return null !== $this->trimmedLine ? $this->trimmedLine : $this->trimmedLine = trim($this->line); } protected function getStashedToken() { $stashedToken = $this->stashedToken; $this->stashedToken = null; return $stashedToken; } protected function getDeferredToken() { if (!$this->deferredObjectsCount) { return null; } --$this->deferredObjectsCount; return array_shift($this->deferredObjects); } protected function getNextToken() { return $this->getDeferredToken() ?: $this->scanEOS() ?: $this->scanLanguage() ?: $this->scanComment() ?: $this->scanPyStringOp() ?: $this->scanPyStringContent() ?: $this->scanStep() ?: $this->scanScenario() ?: $this->scanBackground() ?: $this->scanOutline() ?: $this->scanExamples() ?: $this->scanFeature() ?: $this->scanTags() ?: $this->scanTableRow() ?: $this->scanNewline() ?: $this->scanText(); } protected function scanInput($regex, $type) { if (!preg_match($regex, $this->line, $matches)) { return null; } $token = $this->takeToken($type, $matches[1]); $this->consumeLine(); return $token; } protected function scanInputForKeywords($keywords, $type) { if (!preg_match('/^(\s*)(' . $keywords . '):\s*(.*)/u', $this->line, $matches)) { return null; } $token = $this->takeToken($type, $matches[3]); $token['keyword'] = $matches[2]; $token['indent'] = mb_strlen($matches[1], 'utf8'); $this->consumeLine(); if ('Feature' === $type) { $this->featureStarted = true; } if ('Feature' === $type || 'Scenario' === $type || 'Outline' === $type) { $this->allowMultilineArguments = false; } elseif ('Examples' === $type) { $this->allowMultilineArguments = true; } if ('Scenario' === $type || 'Background' === $type || 'Outline' === $type) { $this->allowSteps = true; } return $token; } protected function scanEOS() { if (!$this->eos) { return null; } return $this->takeToken('EOS'); } protected function getKeywords($type) { if (!isset($this->keywordsCache[$type])) { $getter = 'get' . $type . 'Keywords'; $keywords = $this->keywords->$getter(); if ('Step' === $type) { $padded = array(); foreach (explode('|', $keywords) as $keyword) { $padded[] = false !== mb_strpos($keyword, '<', 0, 'utf8') ? preg_quote(mb_substr($keyword, 0, -1, 'utf8'), '/') . '\s*' : preg_quote($keyword, '/') . '\s+'; } $keywords = implode('|', $padded); } $this->keywordsCache[$type] = $keywords; } return $this->keywordsCache[$type]; } protected function scanFeature() { return $this->scanInputForKeywords($this->getKeywords('Feature'), 'Feature'); } protected function scanBackground() { return $this->scanInputForKeywords($this->getKeywords('Background'), 'Background'); } protected function scanScenario() { return $this->scanInputForKeywords($this->getKeywords('Scenario'), 'Scenario'); } protected function scanOutline() { return $this->scanInputForKeywords($this->getKeywords('Outline'), 'Outline'); } protected function scanExamples() { return $this->scanInputForKeywords($this->getKeywords('Examples'), 'Examples'); } protected function scanStep() { if (!$this->allowSteps) { return null; } $keywords = $this->getKeywords('Step'); if (!preg_match('/^\s*(' . $keywords . ')([^\s].*)/u', $this->line, $matches)) { return null; } $keyword = trim($matches[1]); $token = $this->takeToken('Step', $keyword); $token['keyword_type'] = $this->getStepKeywordType($keyword); $token['text'] = $matches[2]; $this->consumeLine(); $this->allowMultilineArguments = true; return $token; } protected function scanPyStringOp() { if (!$this->allowMultilineArguments) { return null; } if(!preg_match('/^\s*(?"""|```)/u', $this->line, $matches, PREG_OFFSET_CAPTURE)) { return null; } ['delimiter' => [0 => $delimiter, 1 => $indent]] = $matches; if ($this->inPyString) { if ($this->pyStringDelimiter !== $delimiter) { return null; } $this->pyStringDelimiter = null; } else { $this->pyStringDelimiter= $delimiter; } $this->inPyString = !$this->inPyString; $token = $this->takeToken('PyStringOp'); $this->pyStringSwallow = $indent; $this->consumeLine(); return $token; } protected function scanPyStringContent() { if (!$this->inPyString) { return null; } $token = $this->scanText(); $token['value'] = preg_replace('/^\s{0,' . $this->pyStringSwallow . '}/u', '', $token['value'] ?? ''); return $token; } protected function scanTableRow() { if (!$this->allowMultilineArguments) { return null; } $line = $this->getTrimmedLine(); if (!isset($line[0]) || '|' !== $line[0] || '|' !== substr($line, -1)) { return null; } $token = $this->takeToken('TableRow'); $line = mb_substr($line, 1, mb_strlen($line, 'utf8') - 2, 'utf8'); $columns = array_map(function ($column) { return trim(str_replace('\\|', '|', $column)); }, preg_split('/(?consumeLine(); return $token; } protected function scanTags() { $line = $this->getTrimmedLine(); if (!isset($line[0]) || '@' !== $line[0]) { return null; } if(preg_match('/^(?.*)\s+#.*$/', $line, $matches)) { ['line' => $line] = $matches; $this->consumeLineUntil(mb_strlen($line, 'utf-8')); } else { $this->consumeLine(); } $token = $this->takeToken('Tag'); $tags = explode('@', mb_substr($line, 1, mb_strlen($line, 'utf8') - 1, 'utf8')); $tags = array_map('trim', $tags); $token['tags'] = $tags; return $token; } protected function scanLanguage() { if ($this->featureStarted) { return null; } if ($this->inPyString) { return null; } if (0 !== mb_strpos(ltrim($this->line), '#', 0, 'utf8')) { return null; } return $this->scanInput('/^\s*\#\s*language:\s*([\w_\-]+)\s*$/', 'Language'); } protected function scanComment() { if ($this->inPyString) { return null; } $line = $this->getTrimmedLine(); if (0 !== mb_strpos($line, '#', 0, 'utf8')) { return null; } $token = $this->takeToken('Comment', $line); $this->consumeLine(); return $token; } protected function scanNewline() { if ('' !== $this->getTrimmedLine()) { return null; } $token = $this->takeToken('Newline', mb_strlen($this->line, 'utf8')); $this->consumeLine(); return $token; } protected function scanText() { $token = $this->takeToken('Text', $this->line); $this->consumeLine(); return $token; } private function getStepKeywordType($native) { if ('*' === $native) { return 'And'; } if (empty($this->stepKeywordTypesCache)) { $this->stepKeywordTypesCache = array( 'Given' => explode('|', $this->keywords->getGivenKeywords()), 'When' => explode('|', $this->keywords->getWhenKeywords()), 'Then' => explode('|', $this->keywords->getThenKeywords()), 'And' => explode('|', $this->keywords->getAndKeywords()), 'But' => explode('|', $this->keywords->getButKeywords()) ); } foreach ($this->stepKeywordTypesCache as $type => $keywords) { if (in_array($native, $keywords) || in_array($native . '<', $keywords)) { return $type; } } return 'Given'; } } features[$path])) { return false; } return $this->timestamps[$path] > $timestamp; } public function read($path) { return $this->features[$path]; } public function write($path, FeatureNode $feature) { $this->features[$path] = $feature; $this->timestamps[$path] = time(); } } path = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'v'.Gherkin::VERSION; if (!is_dir($this->path)) { @mkdir($this->path, 0777, true); } if (!is_writeable($this->path)) { throw new CacheException(sprintf('Cache path "%s" is not writeable. Check your filesystem permissions or disable Gherkin file cache.', $this->path)); } } public function isFresh($path, $timestamp) { $cachePath = $this->getCachePathFor($path); if (!file_exists($cachePath)) { return false; } return filemtime($cachePath) > $timestamp; } public function read($path) { $cachePath = $this->getCachePathFor($path); $feature = unserialize(file_get_contents($cachePath)); if (!$feature instanceof FeatureNode) { throw new CacheException(sprintf('Can not load cache for a feature "%s" from "%s".', $path, $cachePath )); } return $feature; } public function write($path, FeatureNode $feature) { file_put_contents($this->getCachePathFor($path), serialize($feature)); } protected function getCachePathFor($path) { return $this->path.'/'.md5($path).'.feature.cache'; } } loaders[] = $loader; } public function addFilter(FeatureFilterInterface $filter) { $this->filters[] = $filter; } public function setFilters(array $filters) { $this->filters = array(); array_map(array($this, 'addFilter'), $filters); } public function setBasePath($path) { foreach ($this->loaders as $loader) { if ($loader instanceof FileLoaderInterface) { $loader->setBasePath($path); } } } public function load($resource, array $filters = array()) { $filters = array_merge($this->filters, $filters); $matches = array(); if (preg_match('/^(.*)\:(\d+)-(\d+|\*)$/', $resource, $matches)) { $resource = $matches[1]; $filters[] = new LineRangeFilter($matches[2], $matches[3]); } elseif (preg_match('/^(.*)\:(\d+)$/', $resource, $matches)) { $resource = $matches[1]; $filters[] = new LineFilter($matches[2]); } $loader = $this->resolveLoader($resource); if (null === $loader) { return array(); } $features = array(); foreach ($loader->load($resource) as $feature) { foreach ($filters as $filter) { $feature = $filter->filterFeature($feature); if (!$feature->hasScenarios() && !$filter->isFeatureMatch($feature)) { continue 2; } } $features[] = $feature; } return $features; } public function resolveLoader($resource) { foreach ($this->loaders as $loader) { if ($loader->supports($resource)) { return $loader; } } return null; } } title = $title; $this->tags = $tags; $this->steps = $steps; $this->keyword = $keyword; $this->line = $line; if (!is_array($tables)) { $this->tables = array($tables); } else { $this->tables = $tables; } } public function getNodeType() { return 'Outline'; } public function getTitle() { return $this->title; } public function hasTag($tag) { return in_array($tag, $this->getTags()); } public function hasTags() { return 0 < count($this->getTags()); } public function getTags() { return $this->tags; } public function hasSteps() { return 0 < count($this->steps); } public function getSteps() { return $this->steps; } public function hasExamples() { return 0 < count($this->tables); } public function getExampleTable() { $table = array(); foreach ($this->tables[0]->getTable() as $k => $v) { $table[$k] = $v; } $exampleTableNode = new ExampleTableNode($table, $this->tables[0]->getKeyword()); for ($i = 1; $i < count($this->tables); $i++) { $exampleTableNode->mergeRowsFromTable($this->tables[$i]); } return $exampleTableNode; } public function getExamples() { return $this->examples = $this->examples ?: $this->createExamples(); } public function getExampleTables() { return $this->tables; } public function getKeyword() { return $this->keyword; } public function getLine() { return $this->line; } protected function createExamples() { $examples = array(); foreach ($this->getExampleTables() as $exampleTable) { foreach ($exampleTable->getColumnsHash() as $rowNum => $row) { $examples[] = new ExampleNode( $exampleTable->getRowAsString($rowNum + 1), array_merge($this->tags, $exampleTable->getTags()), $this->getSteps(), $row, $exampleTable->getRowLine($rowNum + 1), $this->getTitle() ); } } return $examples; } } isAbsolutePath($file)) { throw new \InvalidArgumentException('The file should be an absolute path.'); } $this->title = $title; $this->description = $description; $this->tags = $tags; $this->background = $background; $this->scenarios = $scenarios; $this->keyword = $keyword; $this->language = $language; $this->file = $file; $this->line = $line; } public function getNodeType() { return 'Feature'; } public function getTitle() { return $this->title; } public function hasDescription() { return !empty($this->description); } public function getDescription() { return $this->description; } public function hasTag($tag) { return in_array($tag, $this->tags); } public function hasTags() { return 0 < count($this->tags); } public function getTags() { return $this->tags; } public function hasBackground() { return null !== $this->background; } public function getBackground() { return $this->background; } public function hasScenarios() { return 0 < count($this->scenarios); } public function getScenarios() { return $this->scenarios; } public function getKeyword() { return $this->keyword; } public function getLanguage() { return $this->language; } public function getFile() { return $this->file; } public function getLine() { return $this->line; } protected function isAbsolutePath($file) { if (null === $file) { @trigger_error(sprintf('Calling "%s()" with a null in the $file argument is deprecated since Symfony 4.4.', __METHOD__), E_USER_DEPRECATED); } return strspn($file, '/\\', 0, 1) || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1) ) || null !== parse_url($file, PHP_URL_SCHEME) ; } } keyword = $keyword; $this->tags = $tags; parent::__construct($table); } public function getNodeType() { return 'ExampleTable'; } public function getTags() { return $this->tags; } public function getKeyword() { return $this->keyword; } } 1) { throw new NodeException(sprintf( 'Steps could have only one argument, but `%s %s` have %d.', $keyword, $text, count($arguments) )); } $this->keyword = $keyword; $this->text = $text; $this->arguments = $arguments; $this->line = $line; $this->keywordType = $keywordType ?: 'Given'; } public function getNodeType() { return 'Step'; } public function getType() { return $this->getKeyword(); } public function getKeyword() { return $this->keyword; } public function getKeywordType() { return $this->keywordType; } public function getText() { return $this->text; } public function hasArguments() { return 0 < count($this->arguments); } public function getArguments() { return $this->arguments; } public function getLine() { return $this->line; } } strings = $strings; $this->line = $line; } public function getNodeType() { return 'PyString'; } public function getStrings() { return $this->strings; } public function getRaw() { return implode("\n", $this->strings); } public function __toString() { return $this->getRaw(); } public function getLine() { return $this->line; } } title = $title; $this->tags = $tags; $this->steps = $steps; $this->keyword = $keyword; $this->line = $line; } public function getNodeType() { return 'Scenario'; } public function getTitle() { return $this->title; } public function hasTag($tag) { return in_array($tag, $this->getTags()); } public function hasTags() { return 0 < count($this->getTags()); } public function getTags() { return $this->tags; } public function hasSteps() { return 0 < count($this->steps); } public function getSteps() { return $this->steps; } public function getKeyword() { return $this->keyword; } public function getLine() { return $this->line; } } table = $table; $columnCount = null; foreach ($this->getRows() as $ridx => $row) { if (!is_array($row)) { throw new NodeException(sprintf( "Table row '%s' is expected to be array, got %s", $ridx, gettype($row) )); } if ($columnCount === null) { $columnCount = count($row); } if (count($row) !== $columnCount) { throw new NodeException(sprintf( "Table row '%s' is expected to have %s columns, got %s", $ridx, $columnCount, count($row) )); } foreach ($row as $column => $string) { if (!isset($this->maxLineLength[$column])) { $this->maxLineLength[$column] = 0; } if (!is_scalar($string)) { throw new NodeException(sprintf( "Table cell at row '%s', col '%s' is expected to be scalar, got %s", $ridx, $column, gettype($string) )); } $this->maxLineLength[$column] = max($this->maxLineLength[$column], mb_strlen($string, 'utf8')); } } } public static function fromList(array $list) { if (count($list) !== count($list, COUNT_RECURSIVE)) { throw new NodeException('List is not a one-dimensional array.'); } array_walk($list, function (&$item) { $item = array($item); }); return new self($list); } public function getNodeType() { return 'Table'; } public function getHash() { return $this->getColumnsHash(); } public function getColumnsHash() { $rows = $this->getRows(); $keys = array_shift($rows); $hash = array(); foreach ($rows as $row) { $hash[] = array_combine($keys, $row); } return $hash; } public function getRowsHash() { $hash = array(); foreach ($this->getRows() as $row) { $hash[array_shift($row)] = (1 == count($row)) ? $row[0] : $row; } return $hash; } public function getTable() { return $this->table; } public function getRows() { return array_values($this->table); } public function getLines() { return array_keys($this->table); } public function getRow($index) { $rows = $this->getRows(); if (!isset($rows[$index])) { throw new NodeException(sprintf('Rows #%d does not exist in table.', $index)); } return $rows[$index]; } public function getColumn($index) { if ($index >= count($this->getRow(0))) { throw new NodeException(sprintf('Column #%d does not exist in table.', $index)); } $rows = $this->getRows(); $column = array(); foreach ($rows as $row) { $column[] = $row[$index]; } return $column; } public function getRowLine($index) { $lines = array_keys($this->table); if (!isset($lines[$index])) { throw new NodeException(sprintf('Rows #%d does not exist in table.', $index)); } return $lines[$index]; } public function getRowAsString($rowNum) { $values = array(); foreach ($this->getRow($rowNum) as $column => $value) { $values[] = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2); } return sprintf('|%s|', implode('|', $values)); } public function getRowAsStringWithWrappedValues($rowNum, $wrapper) { $values = array(); foreach ($this->getRow($rowNum) as $column => $value) { $value = $this->padRight(' ' . $value . ' ', $this->maxLineLength[$column] + 2); $values[] = call_user_func($wrapper, $value, $column); } return sprintf('|%s|', implode('|', $values)); } public function getTableAsString() { $lines = array(); for ($i = 0; $i < count($this->getRows()); $i++) { $lines[] = $this->getRowAsString($i); } return implode("\n", $lines); } public function getLine() { return $this->getRowLine(0); } public function __toString() { return $this->getTableAsString(); } #[ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->getHash()); } public function mergeRowsFromTable(TableNode $node) { if ($this->getRow(0) !== $node->getRow(0)) { throw new NodeException("Tables have different structure. Cannot merge one into another"); } $firstLine = $node->getLine(); foreach ($node->getTable() as $line => $value) { if ($line === $firstLine) { continue; } $this->table[$line] = $value; } } protected function padRight($text, $length) { while ($length > mb_strlen($text, 'utf8')) { $text = $text . ' '; } return $text; } } title = $title; $this->tags = $tags; $this->outlineSteps = $outlineSteps; $this->tokens = $tokens; $this->line = $line; $this->outlineTitle = $outlineTitle; } public function getNodeType() { return 'Example'; } public function getKeyword() { return $this->getNodeType(); } public function getTitle() { return $this->title; } public function hasTag($tag) { return in_array($tag, $this->getTags()); } public function hasTags() { return 0 < count($this->getTags()); } public function getTags() { return $this->tags; } public function hasSteps() { return 0 < count($this->outlineSteps); } public function getSteps() { return $this->steps = $this->steps ? : $this->createExampleSteps(); } public function getTokens() { return $this->tokens; } public function getLine() { return $this->line; } public function getOutlineTitle() { return $this->outlineTitle; } protected function createExampleSteps() { $steps = array(); foreach ($this->outlineSteps as $outlineStep) { $keyword = $outlineStep->getKeyword(); $keywordType = $outlineStep->getKeywordType(); $text = $this->replaceTextTokens($outlineStep->getText()); $args = $this->replaceArgumentsTokens($outlineStep->getArguments()); $line = $outlineStep->getLine(); $steps[] = new StepNode($keyword, $text, $args, $line, $keywordType); } return $steps; } protected function replaceArgumentsTokens(array $arguments) { foreach ($arguments as $num => $argument) { if ($argument instanceof TableNode) { $arguments[$num] = $this->replaceTableArgumentTokens($argument); } if ($argument instanceof PyStringNode) { $arguments[$num] = $this->replacePyStringArgumentTokens($argument); } } return $arguments; } protected function replaceTableArgumentTokens(TableNode $argument) { $table = $argument->getTable(); foreach ($table as $line => $row) { foreach (array_keys($row) as $col) { $table[$line][$col] = $this->replaceTextTokens($table[$line][$col]); } } return new TableNode($table); } protected function replacePyStringArgumentTokens(PyStringNode $argument) { $strings = $argument->getStrings(); foreach ($strings as $line => $string) { $strings[$line] = $this->replaceTextTokens($strings[$line]); } return new PyStringNode($strings, $argument->getLine()); } protected function replaceTextTokens($text) { foreach ($this->tokens as $key => $val) { $text = str_replace('<' . $key . '>', $val, $text); } return $text; } } title = $title; $this->steps = $steps; $this->keyword = $keyword; $this->line = $line; } public function getNodeType() { return 'Background'; } public function getTitle() { return $this->title; } public function hasSteps() { return 0 < count($this->steps); } public function getSteps() { return $this->steps; } public function getKeyword() { return $this->keyword; } public function getLine() { return $this->line; } } pattern = '/as an? ' . strtr(preg_quote($role, '/'), array( '\*' => '.*', '\?' => '.', '\[' => '[', '\]' => ']' )) . '[$\n]/i'; } public function isFeatureMatch(FeatureNode $feature) { return 1 === preg_match($this->pattern, $feature->getDescription() ?? ''); } public function isScenarioMatch(ScenarioInterface $scenario) { return false; } } filterString = trim($filterString); if(preg_match('/\s/u', $this->filterString)) { trigger_error( "Tags with whitespace are deprecated and may be removed in a future version", E_USER_DEPRECATED ); } } public function filterFeature(FeatureNode $feature) { $scenarios = array(); foreach ($feature->getScenarios() as $scenario) { if (!$this->isScenarioMatch($feature, $scenario)) { continue; } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { $exampleTables = array(); foreach ($scenario->getExampleTables() as $exampleTable) { if ($this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags(), $exampleTable->getTags()))) { $exampleTables[] = $exampleTable; } } $scenario = new OutlineNode( $scenario->getTitle(), $scenario->getTags(), $scenario->getSteps(), $exampleTables, $scenario->getKeyword(), $scenario->getLine() ); } $scenarios[] = $scenario; } return new FeatureNode( $feature->getTitle(), $feature->getDescription(), $feature->getTags(), $feature->getBackground(), $scenarios, $feature->getKeyword(), $feature->getLanguage(), $feature->getFile(), $feature->getLine() ); } public function isFeatureMatch(FeatureNode $feature) { return $this->isTagsMatchCondition($feature->getTags()); } public function isScenarioMatch(FeatureNode $feature, ScenarioInterface $scenario) { if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { foreach ($scenario->getExampleTables() as $example) { if ($this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags(), $example->getTags()))) { return true; } } return false; } return $this->isTagsMatchCondition(array_merge($feature->getTags(), $scenario->getTags())); } protected function isTagsMatchCondition($tags) { $satisfies = true; foreach (explode('&&', $this->filterString) as $andTags) { $satisfiesComma = false; foreach (explode(',', $andTags) as $tag) { $tag = str_replace('@', '', trim($tag)); if ('~' === $tag[0]) { $tag = mb_substr($tag, 1, mb_strlen($tag, 'utf8') - 1, 'utf8'); $satisfiesComma = !in_array($tag, $tags) || $satisfiesComma; } else { $satisfiesComma = in_array($tag, $tags) || $satisfiesComma; } } $satisfies = $satisfiesComma && $satisfies; } return $satisfies; } } isFeatureMatch($feature)) { return $feature; } $scenarios = array(); foreach ($feature->getScenarios() as $scenario) { if (!$this->isScenarioMatch($scenario)) { continue; } $scenarios[] = $scenario; } return new FeatureNode( $feature->getTitle(), $feature->getDescription(), $feature->getTags(), $feature->getBackground(), $scenarios, $feature->getKeyword(), $feature->getLanguage(), $feature->getFile(), $feature->getLine() ); } } filterString = trim($filterString); } public function isFeatureMatch(FeatureNode $feature) { if (null === $feature->getTitle()) { return false; } if ('/' === $this->filterString[0]) { return 1 === preg_match($this->filterString, $feature->getTitle()); } return false !== mb_strpos($feature->getTitle(), $this->filterString, 0, 'utf8'); } public function isScenarioMatch(ScenarioInterface $scenario) { if (null === $scenario->getTitle()) { return false; } if ('/' === $this->filterString[0] && 1 === preg_match($this->filterString, $scenario->getTitle())) { return true; } elseif (false !== mb_strpos($scenario->getTitle(), $this->filterString, 0, 'utf8')) { return true; } return false; } } filterMinLine = intval($filterMinLine); if ($filterMaxLine == '*') { $this->filterMaxLine = PHP_INT_MAX; } else { $this->filterMaxLine = intval($filterMaxLine); } } public function isFeatureMatch(FeatureNode $feature) { return $this->filterMinLine <= $feature->getLine() && $this->filterMaxLine >= $feature->getLine(); } public function isScenarioMatch(ScenarioInterface $scenario) { if ($this->filterMinLine <= $scenario->getLine() && $this->filterMaxLine >= $scenario->getLine()) { return true; } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { foreach ($scenario->getExampleTable()->getLines() as $line) { if ($this->filterMinLine <= $line && $this->filterMaxLine >= $line) { return true; } } } return false; } public function filterFeature(FeatureNode $feature) { $scenarios = array(); foreach ($feature->getScenarios() as $scenario) { if (!$this->isScenarioMatch($scenario)) { continue; } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { $exampleTableNodes = array(); foreach ($scenario->getExampleTables() as $exampleTable) { $table = $exampleTable->getTable(); $lines = array_keys($table); $filteredTable = array($lines[0] => $table[$lines[0]]); unset($table[$lines[0]]); foreach ($table as $line => $row) { if ($this->filterMinLine <= $line && $this->filterMaxLine >= $line) { $filteredTable[$line] = $row; } } if (count($filteredTable) > 1) { $exampleTableNodes[] = new ExampleTableNode($filteredTable, $exampleTable->getKeyword(), $exampleTable->getTags()); } } $scenario = new OutlineNode( $scenario->getTitle(), $scenario->getTags(), $scenario->getSteps(), $exampleTableNodes, $scenario->getKeyword(), $scenario->getLine() ); } $scenarios[] = $scenario; } return new FeatureNode( $feature->getTitle(), $feature->getDescription(), $feature->getTags(), $feature->getBackground(), $scenarios, $feature->getKeyword(), $feature->getLanguage(), $feature->getFile(), $feature->getLine() ); } } regex = $regex; } public function isFeatureMatch(FeatureNode $feature) { return 1 === preg_match($this->regex, $feature->getDescription() ?? ''); } public function isScenarioMatch(ScenarioInterface $scenario) { return false; } } getScenarios() as $scenario) { if (!$this->isScenarioMatch($feature, $scenario)) { continue; } $scenarios[] = $scenario; } return new FeatureNode( $feature->getTitle(), $feature->getDescription(), $feature->getTags(), $feature->getBackground(), $scenarios, $feature->getKeyword(), $feature->getLanguage(), $feature->getFile(), $feature->getLine() ); } } filterPaths = array_map( function ($realpath) { return rtrim($realpath, DIRECTORY_SEPARATOR) . (is_dir($realpath) ? DIRECTORY_SEPARATOR : ''); }, array_filter( array_map('realpath', $paths) ) ); } public function isFeatureMatch(FeatureNode $feature) { foreach ($this->filterPaths as $path) { if (0 === strpos(realpath($feature->getFile()), $path)) { return true; } } return false; } public function isScenarioMatch(ScenarioInterface $scenario) { return false; } } filterLine = intval($filterLine); } public function isFeatureMatch(FeatureNode $feature) { return $this->filterLine === $feature->getLine(); } public function isScenarioMatch(ScenarioInterface $scenario) { if ($this->filterLine === $scenario->getLine()) { return true; } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { return $this->filterLine === $scenario->getLine() || in_array($this->filterLine, $scenario->getExampleTable()->getLines()); } return false; } public function filterFeature(FeatureNode $feature) { $scenarios = array(); foreach ($feature->getScenarios() as $scenario) { if (!$this->isScenarioMatch($scenario)) { continue; } if ($scenario instanceof OutlineNode && $scenario->hasExamples()) { foreach ($scenario->getExampleTables() as $exampleTable) { $table = $exampleTable->getTable(); $lines = array_keys($table); if (in_array($this->filterLine, $lines)) { $filteredTable = array($lines[0] => $table[$lines[0]]); if ($lines[0] !== $this->filterLine) { $filteredTable[$this->filterLine] = $table[$this->filterLine]; } $scenario = new OutlineNode( $scenario->getTitle(), $scenario->getTags(), $scenario->getSteps(), array(new ExampleTableNode($filteredTable, $exampleTable->getKeyword(), $exampleTable->getTags())), $scenario->getKeyword(), $scenario->getLine() ); } } } $scenarios[] = $scenario; } return new FeatureNode( $feature->getTitle(), $feature->getDescription(), $feature->getTags(), $feature->getBackground(), $scenarios, $feature->getKeyword(), $feature->getLanguage(), $feature->getFile(), $feature->getLine() ); } } keywords = $keywords; $this->keywordsDumper = array($this, 'dumpKeywords'); } public function setKeywordsDumperFunction($mapper) { $this->keywordsDumper = $mapper; } public function dumpKeywords(array $keywords, $isShort) { if ($isShort) { return 1 < count($keywords) ? '(' . implode('|', $keywords) . ')' : $keywords[0]; } return $keywords[0]; } public function dump($language, $short = true, $excludeAsterisk = false) { $this->keywords->setLanguage($language); $languageComment = ''; if ('en' !== $language) { $languageComment = "# language: $language\n"; } $keywords = explode('|', $this->keywords->getFeatureKeywords()); if ($short) { $keywords = call_user_func($this->keywordsDumper, $keywords, $short); return trim($languageComment . $this->dumpFeature($keywords, $short, $excludeAsterisk)); } $features = array(); foreach ($keywords as $keyword) { $keyword = call_user_func($this->keywordsDumper, array($keyword), $short); $features[] = trim($languageComment . $this->dumpFeature($keyword, $short, $excludeAsterisk)); } return $features; } protected function dumpFeature($keyword, $short = true, $excludeAsterisk = false) { $dump = <<keywords->getBackgroundKeywords()); if ($short) { $keywords = call_user_func($this->keywordsDumper, $keywords, $short); $dump .= $this->dumpBackground($keywords, $short, $excludeAsterisk); } else { $keyword = call_user_func($this->keywordsDumper, array($keywords[0]), $short); $dump .= $this->dumpBackground($keyword, $short, $excludeAsterisk); } $keywords = explode('|', $this->keywords->getScenarioKeywords()); if ($short) { $keywords = call_user_func($this->keywordsDumper, $keywords, $short); $dump .= $this->dumpScenario($keywords, $short, $excludeAsterisk); } else { foreach ($keywords as $keyword) { $keyword = call_user_func($this->keywordsDumper, array($keyword), $short); $dump .= $this->dumpScenario($keyword, $short, $excludeAsterisk); } } $keywords = explode('|', $this->keywords->getOutlineKeywords()); if ($short) { $keywords = call_user_func($this->keywordsDumper, $keywords, $short); $dump .= $this->dumpOutline($keywords, $short, $excludeAsterisk); } else { foreach ($keywords as $keyword) { $keyword = call_user_func($this->keywordsDumper, array($keyword), $short); $dump .= $this->dumpOutline($keyword, $short, $excludeAsterisk); } } return $dump; } protected function dumpBackground($keyword, $short = true, $excludeAsterisk = false) { $dump = <<dumpStep( $this->keywords->getGivenKeywords(), 'there is agent A', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getAndKeywords(), 'there is agent B', $short, $excludeAsterisk ); return $dump . "\n"; } protected function dumpScenario($keyword, $short = true, $excludeAsterisk = false) { $dump = <<dumpStep( $this->keywords->getGivenKeywords(), 'there is agent J', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getAndKeywords(), 'there is agent K', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getWhenKeywords(), 'I erase agent K\'s memory', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getThenKeywords(), 'there should be agent J', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getButKeywords(), 'there should not be agent K', $short, $excludeAsterisk ); return $dump . "\n"; } protected function dumpOutline($keyword, $short = true, $excludeAsterisk = false) { $dump = <<dumpStep( $this->keywords->getGivenKeywords(), 'there is agent ', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getAndKeywords(), 'there is agent ', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getWhenKeywords(), 'I erase agent \'s memory', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getThenKeywords(), 'there should be agent ', $short, $excludeAsterisk ); $dump .= $this->dumpStep( $this->keywords->getButKeywords(), 'there should not be agent ', $short, $excludeAsterisk ); $keywords = explode('|', $this->keywords->getExamplesKeywords()); if ($short) { $keyword = call_user_func($this->keywordsDumper, $keywords, $short); } else { $keyword = call_user_func($this->keywordsDumper, array($keywords[0]), $short); } $dump .= <<keywordsDumper, $keywords, $short); $dump .= <<keywordsDumper, array($keyword), $short); $dump .= <<setParsedFile($file); } throw $e; } parent::__construct($content); } public function getGivenKeywords() { return $this->prepareStepString(parent::getGivenKeywords()); } public function getWhenKeywords() { return $this->prepareStepString(parent::getWhenKeywords()); } public function getThenKeywords() { return $this->prepareStepString(parent::getThenKeywords()); } public function getAndKeywords() { return $this->prepareStepString(parent::getAndKeywords()); } public function getButKeywords() { return $this->prepareStepString(parent::getButKeywords()); } private function prepareStepString($keywordsString) { if (0 === mb_strpos($keywordsString, '*|', 0, 'UTF-8')) { $keywordsString = mb_substr($keywordsString, 2, mb_strlen($keywordsString, 'utf8') - 2, 'utf8'); } return $keywordsString; } } keywords = $keywords; } public function setLanguage($language) { if (!isset($this->keywords[$language])) { $this->language = 'en'; } else { $this->language = $language; } } public function getFeatureKeywords() { return $this->keywords[$this->language]['feature']; } public function getBackgroundKeywords() { return $this->keywords[$this->language]['background']; } public function getScenarioKeywords() { return $this->keywords[$this->language]['scenario']; } public function getOutlineKeywords() { return $this->keywords[$this->language]['scenario_outline']; } public function getExamplesKeywords() { return $this->keywords[$this->language]['examples']; } public function getGivenKeywords() { return $this->keywords[$this->language]['given']; } public function getWhenKeywords() { return $this->keywords[$this->language]['when']; } public function getThenKeywords() { return $this->keywords[$this->language]['then']; } public function getAndKeywords() { return $this->keywords[$this->language]['and']; } public function getButKeywords() { return $this->keywords[$this->language]['but']; } public function getStepKeywords() { if (!isset($this->keywordString[$this->language])) { $keywords = array_merge( explode('|', $this->getGivenKeywords()), explode('|', $this->getWhenKeywords()), explode('|', $this->getThenKeywords()), explode('|', $this->getAndKeywords()), explode('|', $this->getButKeywords()) ); usort($keywords, function ($keyword1, $keyword2) { return mb_strlen($keyword2, 'utf8') - mb_strlen($keyword1, 'utf8'); }); $this->keywordString[$this->language] = implode('|', $keywords); } return $this->keywordString[$this->language]; } } array ( 'and' => 'And|*', 'background' => 'Background', 'but' => 'But|*', 'examples' => 'Scenarios|Examples', 'feature' => 'Business Need|Ability|Feature', 'given' => 'Given|*', 'name' => 'English', 'native' => 'English', 'rule' => 'Rule', 'scenario' => 'Scenario|Example', 'scenario_outline' => 'Scenario Template|Scenario Outline', 'then' => 'Then|*', 'when' => 'When|*', ), 'af' => array ( 'and' => 'En|*', 'background' => 'Agtergrond', 'but' => 'Maar|*', 'examples' => 'Voorbeelde', 'feature' => 'Besigheid Behoefte|Funksie|Vermoë', 'given' => 'Gegewe|*', 'name' => 'Afrikaans', 'native' => 'Afrikaans', 'rule' => 'Regel', 'scenario' => 'Voorbeeld|Situasie', 'scenario_outline' => 'Situasie Uiteensetting', 'then' => 'Dan|*', 'when' => 'Wanneer|*', ), 'am' => array ( 'and' => 'Եվ|*', 'background' => 'Կոնտեքստ', 'but' => 'Բայց|*', 'examples' => 'Օրինակներ', 'feature' => 'Ֆունկցիոնալություն|Հատկություն', 'given' => 'Դիցուք|*', 'name' => 'Armenian', 'native' => 'հայերեն', 'rule' => 'Rule', 'scenario' => 'Սցենար|Օրինակ', 'scenario_outline' => 'Սցենարի կառուցվացքը', 'then' => 'Ապա|*', 'when' => 'Եթե|Երբ|*', ), 'an' => array ( 'and' => '*|E|Y', 'background' => 'Antecedents', 'but' => 'Pero|*', 'examples' => 'Eixemplos', 'feature' => 'Caracteristica', 'given' => 'Dadas|Dada|Daus|Dau|*', 'name' => 'Aragonese', 'native' => 'Aragonés', 'rule' => 'Rule', 'scenario' => 'Eixemplo|Caso', 'scenario_outline' => 'Esquema del caso', 'then' => 'Antonces|Alavez|Allora|*', 'when' => 'Cuan|*', ), 'ar' => array ( 'and' => '*|و', 'background' => 'الخلفية', 'but' => 'لكن|*', 'examples' => 'امثلة', 'feature' => 'خاصية', 'given' => 'بفرض|*', 'name' => 'Arabic', 'native' => 'العربية', 'rule' => 'Rule', 'scenario' => 'سيناريو|مثال', 'scenario_outline' => 'سيناريو مخطط', 'then' => 'اذاً|ثم|*', 'when' => 'عندما|متى|*', ), 'ast' => array ( 'and' => 'Ya|*|Y', 'background' => 'Antecedentes', 'but' => 'Peru|*', 'examples' => 'Exemplos', 'feature' => 'Carauterística', 'given' => 'Dada|Daes|Daos|Dáu|*', 'name' => 'Asturian', 'native' => 'asturianu', 'rule' => 'Rule', 'scenario' => 'Exemplo|Casu', 'scenario_outline' => 'Esbozu del casu', 'then' => 'Entós|*', 'when' => 'Cuando|*', ), 'az' => array ( 'and' => 'Həm|Və|*', 'background' => 'Kontekst|Keçmiş', 'but' => 'Ancaq|Amma|*', 'examples' => 'Nümunələr', 'feature' => 'Özəllik', 'given' => 'Tutaq ki|Verilir|*', 'name' => 'Azerbaijani', 'native' => 'Azərbaycanca', 'rule' => 'Rule', 'scenario' => 'Ssenari|Nümunə', 'scenario_outline' => 'Ssenarinin strukturu', 'then' => 'O halda|*', 'when' => 'Nə vaxt ki|Əgər|*', ), 'bg' => array ( 'and' => '*|И', 'background' => 'Предистория', 'but' => 'Но|*', 'examples' => 'Примери', 'feature' => 'Функционалност', 'given' => 'Дадено|*', 'name' => 'Bulgarian', 'native' => 'български', 'rule' => 'Rule', 'scenario' => 'Сценарий|Пример', 'scenario_outline' => 'Рамка на сценарий', 'then' => 'То|*', 'when' => 'Когато|*', ), 'bm' => array ( 'and' => 'Dan|*', 'background' => 'Latar Belakang', 'but' => 'Tetapi|Tapi|*', 'examples' => 'Contoh', 'feature' => 'Fungsi', 'given' => 'Diberi|Bagi|*', 'name' => 'Malay', 'native' => 'Bahasa Melayu', 'rule' => 'Rule', 'scenario' => 'Keadaan|Senario|Situasi', 'scenario_outline' => 'Garis Panduan Senario|Kerangka Keadaan|Kerangka Senario|Kerangka Situasi', 'then' => 'Kemudian|Maka|*', 'when' => 'Apabila|*', ), 'bs' => array ( 'and' => '*|A|I', 'background' => 'Pozadina', 'but' => 'Ali|*', 'examples' => 'Primjeri', 'feature' => 'Karakteristika', 'given' => 'Dato|*', 'name' => 'Bosnian', 'native' => 'Bosanski', 'rule' => 'Rule', 'scenario' => 'Scenariju|Scenario|Primjer', 'scenario_outline' => 'Scenario-outline|Scenariju-obris', 'then' => 'Zatim|*', 'when' => 'Kada|*', ), 'ca' => array ( 'and' => '*|I', 'background' => 'Antecedents|Rerefons', 'but' => 'Però|*', 'examples' => 'Exemples', 'feature' => 'Característica|Funcionalitat', 'given' => 'Donada|Atesa|Donat|Atès|*', 'name' => 'Catalan', 'native' => 'català', 'rule' => 'Rule', 'scenario' => 'Escenari|Exemple', 'scenario_outline' => 'Esquema de l\'escenari', 'then' => 'Aleshores|Cal|*', 'when' => 'Quan|*', ), 'cs' => array ( 'and' => 'A také|*|A', 'background' => 'Kontext|Pozadí', 'but' => 'Ale|*', 'examples' => 'Příklady', 'feature' => 'Požadavek', 'given' => 'Za předpokladu|Pokud|*', 'name' => 'Czech', 'native' => 'Česky', 'rule' => 'Pravidlo', 'scenario' => 'Příklad|Scénář', 'scenario_outline' => 'Osnova scénáře|Náčrt Scénáře', 'then' => 'Pak|*', 'when' => 'Když|*', ), 'cy-GB' => array ( 'and' => '*|A', 'background' => 'Cefndir', 'but' => 'Ond|*', 'examples' => 'Enghreifftiau', 'feature' => 'Arwedd', 'given' => 'Anrhegedig a|*', 'name' => 'Welsh', 'native' => 'Cymraeg', 'rule' => 'Rule', 'scenario' => 'Enghraifft|Scenario', 'scenario_outline' => 'Scenario Amlinellol', 'then' => 'Yna|*', 'when' => 'Pryd|*', ), 'da' => array ( 'and' => 'Og|*', 'background' => 'Baggrund', 'but' => 'Men|*', 'examples' => 'Eksempler', 'feature' => 'Egenskab', 'given' => 'Givet|*', 'name' => 'Danish', 'native' => 'dansk', 'rule' => 'Rule', 'scenario' => 'Eksempel|Scenarie', 'scenario_outline' => 'Abstrakt Scenario', 'then' => 'Så|*', 'when' => 'Når|*', ), 'de' => array ( 'and' => 'Und|*', 'background' => 'Voraussetzungen|Vorbedingungen|Hintergrund|Grundlage', 'but' => 'Aber|*', 'examples' => 'Beispiele', 'feature' => 'Funktionalität|Funktion', 'given' => 'Gegeben seien|Gegeben sei|Angenommen|*', 'name' => 'German', 'native' => 'Deutsch', 'rule' => 'Regel|Rule', 'scenario' => 'Beispiel|Szenario', 'scenario_outline' => 'Szenariogrundriss|Szenarien', 'then' => 'Dann|*', 'when' => 'Wenn|*', ), 'el' => array ( 'and' => 'Και|*', 'background' => 'Υπόβαθρο', 'but' => 'Αλλά|*', 'examples' => 'Παραδείγματα|Σενάρια', 'feature' => 'Δυνατότητα|Λειτουργία', 'given' => 'Δεδομένου|*', 'name' => 'Greek', 'native' => 'Ελληνικά', 'rule' => 'Rule', 'scenario' => 'Παράδειγμα|Σενάριο', 'scenario_outline' => 'Περίγραμμα Σεναρίου|Περιγραφή Σεναρίου', 'then' => 'Τότε|*', 'when' => 'Όταν|*', ), 'em' => array ( 'and' => '😂<|*', 'background' => '💤', 'but' => '😔<|*', 'examples' => '📓', 'feature' => '📚', 'given' => '😐<|*', 'name' => 'Emoji', 'native' => '😀', 'rule' => 'Rule', 'scenario' => '📕|🥒', 'scenario_outline' => '📖', 'then' => '🙏<|*', 'when' => '🎬<|*', ), 'en-Scouse' => array ( 'and' => 'An|*', 'background' => 'Dis is what went down', 'but' => 'Buh|*', 'examples' => 'Examples', 'feature' => 'Feature', 'given' => 'Youse know when youse got|Givun|*', 'name' => 'Scouse', 'native' => 'Scouse', 'rule' => 'Rule', 'scenario' => 'The thing of it is', 'scenario_outline' => 'Wharrimean is', 'then' => 'Den youse gotta|Dun|*', 'when' => 'Youse know like when|Wun|*', ), 'en-au' => array ( 'and' => 'Too right|*', 'background' => 'First off', 'but' => 'Yeah nah|*', 'examples' => 'You\'ll wanna', 'feature' => 'Pretty much', 'given' => 'Y\'know|*', 'name' => 'Australian', 'native' => 'Australian', 'rule' => 'Rule', 'scenario' => 'Awww, look mate', 'scenario_outline' => 'Reckon it\'s like', 'then' => 'But at the end of the day I reckon|*', 'when' => 'It\'s just unbelievable|*', ), 'en-lol' => array ( 'and' => 'AN|*', 'background' => 'B4', 'but' => 'BUT|*', 'examples' => 'EXAMPLZ', 'feature' => 'OH HAI', 'given' => 'I CAN HAZ|*', 'name' => 'LOLCAT', 'native' => 'LOLCAT', 'rule' => 'Rule', 'scenario' => 'MISHUN', 'scenario_outline' => 'MISHUN SRSLY', 'then' => 'DEN|*', 'when' => 'WEN|*', ), 'en-old' => array ( 'and' => 'Ond|*|7', 'background' => 'Aer|Ær', 'but' => 'Ac|*', 'examples' => 'Se the|Se ðe|Se þe', 'feature' => 'Hwaet|Hwæt', 'given' => 'Thurh|Ðurh|Þurh|*', 'name' => 'Old English', 'native' => 'Englisc', 'rule' => 'Rule', 'scenario' => 'Swa', 'scenario_outline' => 'Swa hwaer swa|Swa hwær swa', 'then' => 'Tha the|Ða ðe|Þa þe|Tha|Ða|Þa|*', 'when' => 'Tha|Ða|Þa|*', ), 'en-pirate' => array ( 'and' => 'Aye|*', 'background' => 'Yo-ho-ho', 'but' => 'Avast!|*', 'examples' => 'Dead men tell no tales', 'feature' => 'Ahoy matey!', 'given' => 'Gangway!|*', 'name' => 'Pirate', 'native' => 'Pirate', 'rule' => 'Rule', 'scenario' => 'Heave to', 'scenario_outline' => 'Shiver me timbers', 'then' => 'Let go and haul|*', 'when' => 'Blimey!|*', ), 'en-tx' => array ( 'and' => 'Come hell or high water', 'background' => 'Lemme tell y\'all a story', 'but' => 'Well now hold on, I\'ll you what', 'examples' => 'Now that\'s a story longer than a cattle drive in July', 'feature' => 'This ain’t my first rodeo|All gussied up', 'given' => 'All git out|Fixin\' to', 'name' => 'Texas', 'native' => 'Texas', 'rule' => 'Rule ', 'scenario' => 'All hat and no cattle', 'scenario_outline' => 'Busy as a hound in flea season|Serious as a snake bite', 'then' => 'There’s no tree but bears some fruit', 'when' => 'Quick out of the chute', ), 'eo' => array ( 'and' => 'Kaj|*', 'background' => 'Fono', 'but' => 'Sed|*', 'examples' => 'Ekzemploj', 'feature' => 'Trajto', 'given' => 'Donitaĵo|Komence|*', 'name' => 'Esperanto', 'native' => 'Esperanto', 'rule' => 'Rule', 'scenario' => 'Ekzemplo|Scenaro|Kazo', 'scenario_outline' => 'Konturo de la scenaro|Kazo-skizo|Skizo', 'then' => 'Do|*', 'when' => 'Se|*', ), 'es' => array ( 'and' => '*|E|Y', 'background' => 'Antecedentes', 'but' => 'Pero|*', 'examples' => 'Ejemplos', 'feature' => 'Necesidad del negocio|Característica|Requisito', 'given' => 'Dadas|Dados|Dada|Dado|*', 'name' => 'Spanish', 'native' => 'español', 'rule' => 'Regla de negocio|Regla', 'scenario' => 'Escenario|Ejemplo', 'scenario_outline' => 'Esquema del escenario', 'then' => 'Entonces|*', 'when' => 'Cuando|*', ), 'et' => array ( 'and' => 'Ja|*', 'background' => 'Taust', 'but' => 'Kuid|*', 'examples' => 'Juhtumid', 'feature' => 'Omadus', 'given' => 'Eeldades|*', 'name' => 'Estonian', 'native' => 'eesti keel', 'rule' => 'Reegel', 'scenario' => 'Stsenaarium|Juhtum', 'scenario_outline' => 'Raamstsenaarium|Raamjuhtum', 'then' => 'Siis|*', 'when' => 'Kui|*', ), 'fa' => array ( 'and' => '*|و', 'background' => 'زمینه', 'but' => 'اما|*', 'examples' => 'نمونه ها', 'feature' => 'وِیژگی', 'given' => 'با فرض|*', 'name' => 'Persian', 'native' => 'فارسی', 'rule' => 'Rule', 'scenario' => 'سناریو|مثال', 'scenario_outline' => 'الگوی سناریو', 'then' => 'آنگاه|*', 'when' => 'هنگامی|*', ), 'fi' => array ( 'and' => 'Ja|*', 'background' => 'Tausta', 'but' => 'Mutta|*', 'examples' => 'Tapaukset', 'feature' => 'Ominaisuus', 'given' => 'Oletetaan|*', 'name' => 'Finnish', 'native' => 'suomi', 'rule' => 'Rule', 'scenario' => 'Tapaus', 'scenario_outline' => 'Tapausaihio', 'then' => 'Niin|*', 'when' => 'Kun|*', ), 'fr' => array ( 'and' => 'Et qu\'<|Et que|Et|*', 'background' => 'Contexte', 'but' => 'Mais qu\'<|Mais que|Mais|*', 'examples' => 'Exemples', 'feature' => 'Fonctionnalité', 'given' => 'Etant donné qu\'<|Étant donné qu\'<|Etant donné que|Étant donné que|Etant données|Étant données|Etant donnée|Etant donnés|Sachant qu\'<|Étant donnée|Étant donnés|Etant donné|Sachant que|Étant donné|Sachant|Soit|*', 'name' => 'French', 'native' => 'français', 'rule' => 'Règle', 'scenario' => 'Scénario|Exemple', 'scenario_outline' => 'Plan du Scénario|Plan du scénario', 'then' => 'Alors|Donc|*', 'when' => 'Lorsqu\'<|Lorsque|Quand|*', ), 'ga' => array ( 'and' => 'Agus<|*', 'background' => 'Cúlra', 'but' => 'Ach<|*', 'examples' => 'Samplaí', 'feature' => 'Gné', 'given' => 'Cuir i gcás nach<|Cuir i gcás gur<|Cuir i gcás nár<|Cuir i gcás go<|*', 'name' => 'Irish', 'native' => 'Gaeilge', 'rule' => 'Rule', 'scenario' => 'Sampla|Cás', 'scenario_outline' => 'Cás Achomair', 'then' => 'Ansin<|*', 'when' => 'Nuair nach<|Nuair nár<|Nuair ba<|Nuair a<|*', ), 'gj' => array ( 'and' => 'અને|*', 'background' => 'બેકગ્રાઉન્ડ', 'but' => 'પણ|*', 'examples' => 'ઉદાહરણો', 'feature' => 'વ્યાપાર જરૂર|ક્ષમતા|લક્ષણ', 'given' => 'આપેલ છે|*', 'name' => 'Gujarati', 'native' => 'ગુજરાતી', 'rule' => 'Rule', 'scenario' => 'ઉદાહરણ|સ્થિતિ', 'scenario_outline' => 'પરિદ્દશ્ય રૂપરેખા|પરિદ્દશ્ય ઢાંચો', 'then' => 'પછી|*', 'when' => 'ક્યારે|*', ), 'gl' => array ( 'and' => '*|E', 'background' => 'Contexto', 'but' => 'Mais|Pero|*', 'examples' => 'Exemplos', 'feature' => 'Característica', 'given' => 'Dadas|Dados|Dada|Dado|*', 'name' => 'Galician', 'native' => 'galego', 'rule' => 'Rule', 'scenario' => 'Escenario|Exemplo', 'scenario_outline' => 'Esbozo do escenario', 'then' => 'Entón|Logo|*', 'when' => 'Cando|*', ), 'he' => array ( 'and' => 'וגם|*', 'background' => 'רקע', 'but' => 'אבל|*', 'examples' => 'דוגמאות', 'feature' => 'תכונה', 'given' => 'בהינתן|*', 'name' => 'Hebrew', 'native' => 'עברית', 'rule' => 'כלל', 'scenario' => 'דוגמא|תרחיש', 'scenario_outline' => 'תבנית תרחיש', 'then' => 'אזי|אז|*', 'when' => 'כאשר|*', ), 'hi' => array ( 'and' => 'तथा|और|*', 'background' => 'पृष्ठभूमि', 'but' => 'किन्तु|परन्तु|पर|*', 'examples' => 'उदाहरण', 'feature' => 'रूप लेख', 'given' => 'चूंकि|अगर|यदि|*', 'name' => 'Hindi', 'native' => 'हिंदी', 'rule' => 'नियम', 'scenario' => 'परिदृश्य', 'scenario_outline' => 'परिदृश्य रूपरेखा', 'then' => 'तदा|तब|*', 'when' => 'कदा|जब|*', ), 'hr' => array ( 'and' => '*|I', 'background' => 'Pozadina', 'but' => 'Ali|*', 'examples' => 'Scenariji|Primjeri', 'feature' => 'Mogucnost|Mogućnost|Osobina', 'given' => 'Ukoliko|Zadani|Zadano|Zadan|*', 'name' => 'Croatian', 'native' => 'hrvatski', 'rule' => 'Rule', 'scenario' => 'Scenarij|Primjer', 'scenario_outline' => 'Koncept|Skica', 'then' => 'Onda|*', 'when' => 'Kada|Kad|*', ), 'ht' => array ( 'and' => 'Epi|Ak|*|E', 'background' => 'Istorik|Kontèks', 'but' => 'Men|*', 'examples' => 'Egzanp', 'feature' => 'Fonksyonalite|Karakteristik|Mak', 'given' => 'Sipoze Ke|Sipoze ke|Sipoze|*', 'name' => 'Creole', 'native' => 'kreyòl', 'rule' => 'Rule', 'scenario' => 'Senaryo', 'scenario_outline' => 'Senaryo Deskripsyon|Senaryo deskripsyon|Dyagram Senaryo|Dyagram senaryo|Plan Senaryo|Plan senaryo', 'then' => 'Le sa a|Lè sa a|*', 'when' => 'Le|Lè|*', ), 'hu' => array ( 'and' => 'És|*', 'background' => 'Háttér', 'but' => 'De|*', 'examples' => 'Példák', 'feature' => 'Jellemző', 'given' => 'Amennyiben|Adott|*', 'name' => 'Hungarian', 'native' => 'magyar', 'rule' => 'Szabály', 'scenario' => 'Forgatókönyv|Példa', 'scenario_outline' => 'Forgatókönyv vázlat', 'then' => 'Akkor|*', 'when' => 'Amikor|Majd|Ha|*', ), 'id' => array ( 'and' => 'Dan|*', 'background' => 'Latar Belakang|Dasar', 'but' => 'Tetapi|Tapi|*', 'examples' => 'Contoh|Misal', 'feature' => 'Fitur', 'given' => 'Diasumsikan|Diketahui|Dengan|Bila|Jika|*', 'name' => 'Indonesian', 'native' => 'Bahasa Indonesia', 'rule' => 'Aturan|Rule', 'scenario' => 'Skenario', 'scenario_outline' => 'Garis-Besar Skenario|Skenario konsep', 'then' => 'Kemudian|Maka|*', 'when' => 'Ketika|*', ), 'is' => array ( 'and' => 'Og|*', 'background' => 'Bakgrunnur', 'but' => 'En|*', 'examples' => 'Atburðarásir|Dæmi', 'feature' => 'Eiginleiki', 'given' => 'Ef|*', 'name' => 'Icelandic', 'native' => 'Íslenska', 'rule' => 'Rule', 'scenario' => 'Atburðarás', 'scenario_outline' => 'Lýsing Atburðarásar|Lýsing Dæma', 'then' => 'Þá|*', 'when' => 'Þegar|*', ), 'it' => array ( 'and' => '*|E', 'background' => 'Contesto', 'but' => 'Ma|*', 'examples' => 'Esempi', 'feature' => 'Esigenza di Business|Funzionalità|Abilità', 'given' => 'Data|Date|Dati|Dato|*', 'name' => 'Italian', 'native' => 'italiano', 'rule' => 'Regola', 'scenario' => 'Scenario|Esempio', 'scenario_outline' => 'Schema dello scenario', 'then' => 'Allora|*', 'when' => 'Quando|*', ), 'ja' => array ( 'and' => 'かつ<|*', 'background' => '背景', 'but' => 'しかし<|ただし<|但し<|*', 'examples' => 'サンプル|例', 'feature' => 'フィーチャ|機能', 'given' => '前提<|*', 'name' => 'Japanese', 'native' => '日本語', 'rule' => 'Rule', 'scenario' => 'シナリオ', 'scenario_outline' => 'シナリオアウトライン|シナリオテンプレート|シナリオテンプレ|テンプレ', 'then' => 'ならば<|*', 'when' => 'もし<|*', ), 'jv' => array ( 'and' => 'Lan|*', 'background' => 'Dasar', 'but' => 'Ananging|Nanging|Tapi|*', 'examples' => 'Contone|Conto', 'feature' => 'Fitur', 'given' => 'Nalikaning|Nalika|*', 'name' => 'Javanese', 'native' => 'Basa Jawa', 'rule' => 'Rule', 'scenario' => 'Skenario', 'scenario_outline' => 'Konsep skenario', 'then' => 'Banjur|Njuk|*', 'when' => 'Manawa|Menawa|*', ), 'ka' => array ( 'and' => 'და<|*', 'background' => 'კონტექსტი', 'but' => 'მაგ­რამ<|*', 'examples' => 'მაგალითები', 'feature' => 'თვისება', 'given' => 'მოცემული<|*', 'name' => 'Georgian', 'native' => 'ქართველი', 'rule' => 'Rule', 'scenario' => 'მაგალითად|სცენარის', 'scenario_outline' => 'სცენარის ნიმუში', 'then' => 'მაშინ<|*', 'when' => 'როდესაც<|*', ), 'kn' => array ( 'and' => 'ಮತ್ತು|*', 'background' => 'ಹಿನ್ನೆಲೆ', 'but' => 'ಆದರೆ|*', 'examples' => 'ಉದಾಹರಣೆಗಳು', 'feature' => 'ಹೆಚ್ಚಳ', 'given' => 'ನೀಡಿದ|*', 'name' => 'Kannada', 'native' => 'ಕನ್ನಡ', 'rule' => 'Rule', 'scenario' => 'ಕಥಾಸಾರಾಂಶ|ಉದಾಹರಣೆ', 'scenario_outline' => 'ವಿವರಣೆ', 'then' => 'ನಂತರ|*', 'when' => 'ಸ್ಥಿತಿಯನ್ನು|*', ), 'ko' => array ( 'and' => '그리고<|*', 'background' => '배경', 'but' => '하지만<|단<|*', 'examples' => '예', 'feature' => '기능', 'given' => '먼저<|조건<|*', 'name' => 'Korean', 'native' => '한국어', 'rule' => 'Rule', 'scenario' => '시나리오', 'scenario_outline' => '시나리오 개요', 'then' => '그러면<|*', 'when' => '만약<|만일<|*', ), 'lt' => array ( 'and' => 'Ir|*', 'background' => 'Kontekstas', 'but' => 'Bet|*', 'examples' => 'Pavyzdžiai|Scenarijai|Variantai', 'feature' => 'Savybė', 'given' => 'Duota|*', 'name' => 'Lithuanian', 'native' => 'lietuvių kalba', 'rule' => 'Rule', 'scenario' => 'Scenarijus|Pavyzdys', 'scenario_outline' => 'Scenarijaus šablonas', 'then' => 'Tada|*', 'when' => 'Kai|*', ), 'lu' => array ( 'and' => 'an|*|a', 'background' => 'Hannergrond', 'but' => 'awer|mä|*', 'examples' => 'Beispiller', 'feature' => 'Funktionalitéit', 'given' => 'ugeholl|*', 'name' => 'Luxemburgish', 'native' => 'Lëtzebuergesch', 'rule' => 'Rule', 'scenario' => 'Beispill|Szenario', 'scenario_outline' => 'Plang vum Szenario', 'then' => 'dann|*', 'when' => 'wann|*', ), 'lv' => array ( 'and' => 'Un|*', 'background' => 'Konteksts|Situācija', 'but' => 'Bet|*', 'examples' => 'Paraugs|Piemēri', 'feature' => 'Funkcionalitāte|Fīča', 'given' => 'Kad|*', 'name' => 'Latvian', 'native' => 'latviešu', 'rule' => 'Rule', 'scenario' => 'Scenārijs|Piemērs', 'scenario_outline' => 'Scenārijs pēc parauga', 'then' => 'Tad|*', 'when' => 'Ja|*', ), 'mk-Cyrl' => array ( 'and' => '*|И', 'background' => 'Контекст|Содржина', 'but' => 'Но|*', 'examples' => 'Сценарија|Примери', 'feature' => 'Бизнис потреба|Функционалност|Можност', 'given' => 'Дадена|Дадено|*', 'name' => 'Macedonian', 'native' => 'Македонски', 'rule' => 'Rule', 'scenario' => 'На пример|Сценарио|Пример', 'scenario_outline' => 'Преглед на сценарија|Концепт|Скица', 'then' => 'Тогаш|*', 'when' => 'Кога|*', ), 'mk-Latn' => array ( 'and' => '*|I', 'background' => 'Sodrzhina|Kontekst', 'but' => 'No|*', 'examples' => 'Scenaria|Primeri', 'feature' => 'Biznis potreba|Funkcionalnost|Mozhnost', 'given' => 'Dadena|Dadeno|*', 'name' => 'Macedonian (Latin)', 'native' => 'Makedonski (Latinica)', 'rule' => 'Rule', 'scenario' => 'Na primer|Scenario', 'scenario_outline' => 'Pregled na scenarija|Koncept|Skica', 'then' => 'Togash|*', 'when' => 'Koga|*', ), 'mn' => array ( 'and' => 'Тэгээд|Мөн|*', 'background' => 'Агуулга', 'but' => 'Гэхдээ|Харин|*', 'examples' => 'Тухайлбал', 'feature' => 'Функционал|Функц', 'given' => 'Өгөгдсөн нь|Анх|*', 'name' => 'Mongolian', 'native' => 'монгол', 'rule' => 'Rule', 'scenario' => 'Сценар', 'scenario_outline' => 'Сценарын төлөвлөгөө', 'then' => 'Үүний дараа|Тэгэхэд|*', 'when' => 'Хэрэв|*', ), 'mr' => array ( 'and' => 'तसेच|आणि|*', 'background' => 'पार्श्वभूमी', 'but' => 'परंतु|पण|*', 'examples' => 'उदाहरण', 'feature' => 'वैशिष्ट्य|सुविधा', 'given' => 'दिलेल्या प्रमाणे|जर<|*', 'name' => 'Marathi', 'native' => 'मराठी', 'rule' => 'नियम', 'scenario' => 'परिदृश्य', 'scenario_outline' => 'परिदृश्य रूपरेखा', 'then' => 'तेव्हा|मग|*', 'when' => 'जेव्हा|*', ), 'ne' => array ( 'and' => 'अनी|*|र', 'background' => 'पृष्ठभूमी', 'but' => 'तर|*', 'examples' => 'उदाहरणहरु|उदाहरण', 'feature' => 'विशेषता|सुविधा', 'given' => 'दिइएको|दिएको|यदि|*', 'name' => 'Nepali', 'native' => 'नेपाली', 'rule' => 'नियम', 'scenario' => 'परिदृश्य', 'scenario_outline' => 'परिदृश्य रूपरेखा', 'then' => 'त्यसपछि|अनी|*', 'when' => 'जब|*', ), 'nl' => array ( 'and' => 'En|*', 'background' => 'Achtergrond', 'but' => 'Maar|*', 'examples' => 'Voorbeelden', 'feature' => 'Functionaliteit', 'given' => 'Gegeven|Stel|*', 'name' => 'Dutch', 'native' => 'Nederlands', 'rule' => 'Rule', 'scenario' => 'Voorbeeld|Scenario', 'scenario_outline' => 'Abstract Scenario', 'then' => 'Dan|*', 'when' => 'Wanneer|Als|*', ), 'no' => array ( 'and' => 'Og|*', 'background' => 'Bakgrunn', 'but' => 'Men|*', 'examples' => 'Eksempler', 'feature' => 'Egenskap', 'given' => 'Gitt|*', 'name' => 'Norwegian', 'native' => 'norsk', 'rule' => 'Regel', 'scenario' => 'Eksempel|Scenario', 'scenario_outline' => 'Abstrakt Scenario|Scenariomal', 'then' => 'Så|*', 'when' => 'Når|*', ), 'pa' => array ( 'and' => 'ਅਤੇ|*', 'background' => 'ਪਿਛੋਕੜ', 'but' => 'ਪਰ|*', 'examples' => 'ਉਦਾਹਰਨਾਂ', 'feature' => 'ਨਕਸ਼ ਨੁਹਾਰ|ਮੁਹਾਂਦਰਾ|ਖਾਸੀਅਤ', 'given' => 'ਜਿਵੇਂ ਕਿ|ਜੇਕਰ|*', 'name' => 'Panjabi', 'native' => 'ਪੰਜਾਬੀ', 'rule' => 'Rule', 'scenario' => 'ਉਦਾਹਰਨ|ਪਟਕਥਾ', 'scenario_outline' => 'ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ|ਪਟਕਥਾ ਢਾਂਚਾ', 'then' => 'ਤਦ|*', 'when' => 'ਜਦੋਂ|*', ), 'pl' => array ( 'and' => 'Oraz|*|I', 'background' => 'Założenia', 'but' => 'Ale|*', 'examples' => 'Przykłady', 'feature' => 'Potrzeba biznesowa|Właściwość|Funkcja|Aspekt', 'given' => 'Zakładając, że|Zakładając|Mając|*', 'name' => 'Polish', 'native' => 'polski', 'rule' => 'Reguła|Zasada', 'scenario' => 'Scenariusz|Przykład', 'scenario_outline' => 'Szablon scenariusza', 'then' => 'Wtedy|*', 'when' => 'Jeżeli|Jeśli|Kiedy|Gdy|*', ), 'pt' => array ( 'and' => '*|E', 'background' => 'Cenario de Fundo|Cenário de Fundo|Contexto|Fundo', 'but' => 'Mas|*', 'examples' => 'Cenarios|Cenários|Exemplos', 'feature' => 'Caracteristica|Característica|Funcionalidade', 'given' => 'Dadas|Dados|Dada|Dado|*', 'name' => 'Portuguese', 'native' => 'português', 'rule' => 'Regra', 'scenario' => 'Cenario|Cenário|Exemplo', 'scenario_outline' => 'Delineacao do Cenario|Delineação do Cenário|Esquema do Cenario|Esquema do Cenário', 'then' => 'Entao|Então|*', 'when' => 'Quando|*', ), 'ro' => array ( 'and' => 'Si|Şi|Și|*', 'background' => 'Context', 'but' => 'Dar|*', 'examples' => 'Exemple', 'feature' => 'Functionalitate|Funcţionalitate|Funcționalitate', 'given' => 'Dată fiind<|Date fiind|Dati fiind|Daţi fiind|Dați fiind|Dat fiind|*', 'name' => 'Romanian', 'native' => 'română', 'rule' => 'Rule', 'scenario' => 'Scenariu|Exemplu', 'scenario_outline' => 'Structura scenariu|Structură scenariu', 'then' => 'Atunci|*', 'when' => 'Cand|Când|*', ), 'ru' => array ( 'and' => 'К тому же|Также|*|И', 'background' => 'Предыстория|Контекст', 'but' => 'Иначе|Но|*|А', 'examples' => 'Примеры', 'feature' => 'Функциональность|Функционал|Свойство|Функция', 'given' => 'Допустим|Пусть|Дано|*', 'name' => 'Russian', 'native' => 'русский', 'rule' => 'Правило', 'scenario' => 'Сценарий|Пример', 'scenario_outline' => 'Структура сценария|Шаблон сценария', 'then' => 'Затем|Тогда|То|*', 'when' => 'Когда|Если|*', ), 'sk' => array ( 'and' => 'A taktiež|A zároveň|A tiež|*|A', 'background' => 'Pozadie', 'but' => 'Ale|*', 'examples' => 'Príklady', 'feature' => 'Požiadavka|Vlastnosť|Funkcia', 'given' => 'Za predpokladu|Pokiaľ|*', 'name' => 'Slovak', 'native' => 'Slovensky', 'rule' => 'Rule', 'scenario' => 'Príklad|Scenár', 'scenario_outline' => 'Osnova Scenára|Náčrt Scenára|Náčrt Scenáru', 'then' => 'Potom|Tak|*', 'when' => 'Keď|Ak|*', ), 'sl' => array ( 'and' => 'Ter|In', 'background' => 'Kontekst|Osnova|Ozadje', 'but' => 'Vendar|Ampak|Toda', 'examples' => 'Scenariji|Primeri', 'feature' => 'Funkcionalnost|Značilnost|Funkcija|Lastnost|Moznosti|Možnosti', 'given' => 'Privzeto|Podano|Zaradi|Dano', 'name' => 'Slovenian', 'native' => 'Slovenski', 'rule' => 'Rule', 'scenario' => 'Scenarij|Primer', 'scenario_outline' => 'Struktura scenarija|Oris scenarija|Koncept|Osnutek|Skica', 'then' => 'Takrat|Potem|Nato', 'when' => 'Kadar|Ce|Ko|Če', ), 'sr-Cyrl' => array ( 'and' => '*|И', 'background' => 'Контекст|Позадина|Основа', 'but' => 'Али|*', 'examples' => 'Сценарији|Примери', 'feature' => 'Функционалност|Могућност|Особина', 'given' => 'За дате|За дати|За дато|*', 'name' => 'Serbian', 'native' => 'Српски', 'rule' => 'Правило', 'scenario' => 'Сценарио|Пример|Пример', 'scenario_outline' => 'Структура сценарија|Концепт|Скица', 'then' => 'Онда|*', 'when' => 'Када|Кад|*', ), 'sr-Latn' => array ( 'and' => '*|I', 'background' => 'Kontekst|Pozadina|Osnova', 'but' => 'Ali|*', 'examples' => 'Scenariji|Primeri', 'feature' => 'Funkcionalnost|Mogucnost|Mogućnost|Osobina', 'given' => 'Za date|Za dati|Za dato|*', 'name' => 'Serbian (Latin)', 'native' => 'Srpski (Latinica)', 'rule' => 'Pravilo', 'scenario' => 'Scenario|Primer', 'scenario_outline' => 'Struktura scenarija|Koncept|Skica', 'then' => 'Onda|*', 'when' => 'Kada|Kad|*', ), 'sv' => array ( 'and' => 'Och|*', 'background' => 'Bakgrund', 'but' => 'Men|*', 'examples' => 'Exempel', 'feature' => 'Egenskap', 'given' => 'Givet|*', 'name' => 'Swedish', 'native' => 'Svenska', 'rule' => 'Regel', 'scenario' => 'Scenario', 'scenario_outline' => 'Abstrakt Scenario|Scenariomall', 'then' => 'Så|*', 'when' => 'När|*', ), 'ta' => array ( 'and' => 'மற்றும்|மேலும்|*', 'background' => 'பின்னணி', 'but' => 'ஆனால்|*', 'examples' => 'எடுத்துக்காட்டுகள்|நிலைமைகளில்|காட்சிகள்', 'feature' => 'வணிக தேவை|அம்சம்|திறன்', 'given' => 'கொடுக்கப்பட்ட|*', 'name' => 'Tamil', 'native' => 'தமிழ்', 'rule' => 'Rule', 'scenario' => 'உதாரணமாக|காட்சி', 'scenario_outline' => 'காட்சி வார்ப்புரு|காட்சி சுருக்கம்', 'then' => 'அப்பொழுது|*', 'when' => 'எப்போது|*', ), 'te' => array ( 'and' => 'మరియు|*', 'background' => 'నేపథ్యం', 'but' => 'కాని|*', 'examples' => 'ఉదాహరణలు', 'feature' => 'గుణము', 'given' => 'చెప్పబడినది|*', 'name' => 'Telugu', 'native' => 'తెలుగు', 'rule' => 'Rule', 'scenario' => 'సన్నివేశం|ఉదాహరణ', 'scenario_outline' => 'కథనం', 'then' => 'అప్పుడు|*', 'when' => 'ఈ పరిస్థితిలో|*', ), 'th' => array ( 'and' => 'และ|*', 'background' => 'แนวคิด', 'but' => 'แต่|*', 'examples' => 'ชุดของเหตุการณ์|ชุดของตัวอย่าง', 'feature' => 'ความต้องการทางธุรกิจ|ความสามารถ|โครงหลัก', 'given' => 'กำหนดให้|*', 'name' => 'Thai', 'native' => 'ไทย', 'rule' => 'Rule', 'scenario' => 'เหตุการณ์', 'scenario_outline' => 'โครงสร้างของเหตุการณ์|สรุปเหตุการณ์', 'then' => 'ดังนั้น|*', 'when' => 'เมื่อ|*', ), 'tlh' => array ( 'and' => 'latlh|\'ej|*', 'background' => 'mo\'', 'but' => '\'ach|\'a|*', 'examples' => 'ghantoH|lutmey', 'feature' => 'poQbogh malja\'|Qu\'meH \'ut|perbogh|Qap|laH', 'given' => 'DaH ghu\' bejlu\'|ghu\' noblu\'|*', 'name' => 'Klingon', 'native' => 'tlhIngan', 'rule' => 'Rule', 'scenario' => 'lut', 'scenario_outline' => 'lut chovnatlh', 'then' => 'vaj|*', 'when' => 'qaSDI\'|*', ), 'tr' => array ( 'and' => 'Ve|*', 'background' => 'Geçmiş', 'but' => 'Fakat|Ama|*', 'examples' => 'Örnekler', 'feature' => 'Özellik', 'given' => 'Diyelim ki|*', 'name' => 'Turkish', 'native' => 'Türkçe', 'rule' => 'Kural', 'scenario' => 'Senaryo|Örnek', 'scenario_outline' => 'Senaryo taslağı', 'then' => 'O zaman|*', 'when' => 'Eğer ki|*', ), 'tt' => array ( 'and' => 'Һәм|Вә|*', 'background' => 'Кереш', 'but' => 'Ләкин|Әмма|*', 'examples' => 'Мисаллар|Үрнәкләр', 'feature' => 'Үзенчәлеклелек|Мөмкинлек', 'given' => 'Әйтик|*', 'name' => 'Tatar', 'native' => 'Татарча', 'rule' => 'Rule', 'scenario' => 'Сценарий', 'scenario_outline' => 'Сценарийның төзелеше', 'then' => 'Нәтиҗәдә|*', 'when' => 'Әгәр|*', ), 'uk' => array ( 'and' => 'А також|Та|*|І', 'background' => 'Передумова', 'but' => 'Але|*', 'examples' => 'Приклади', 'feature' => 'Функціонал', 'given' => 'Припустимо, що|Припустимо|Нехай|Дано|*', 'name' => 'Ukrainian', 'native' => 'Українська', 'rule' => 'Rule', 'scenario' => 'Сценарій|Приклад', 'scenario_outline' => 'Структура сценарію', 'then' => 'Тоді|То|*', 'when' => 'Коли|Якщо|*', ), 'ur' => array ( 'and' => 'اور|*', 'background' => 'پس منظر', 'but' => 'لیکن|*', 'examples' => 'مثالیں', 'feature' => 'کاروبار کی ضرورت|خصوصیت|صلاحیت', 'given' => 'فرض کیا|بالفرض|اگر|*', 'name' => 'Urdu', 'native' => 'اردو', 'rule' => 'Rule', 'scenario' => 'منظرنامہ', 'scenario_outline' => 'منظر نامے کا خاکہ', 'then' => 'پھر|تب|*', 'when' => 'جب|*', ), 'uz' => array ( 'and' => 'Ва|*', 'background' => 'Тарих', 'but' => 'Бирок|Лекин|Аммо|*', 'examples' => 'Мисоллар', 'feature' => 'Функционал', 'given' => 'Агар|*', 'name' => 'Uzbek', 'native' => 'Узбекча', 'rule' => 'Rule', 'scenario' => 'Сценарий', 'scenario_outline' => 'Сценарий структураси', 'then' => 'Унда|*', 'when' => 'Агар|*', ), 'vi' => array ( 'and' => 'Và|*', 'background' => 'Bối cảnh', 'but' => 'Nhưng|*', 'examples' => 'Dữ liệu', 'feature' => 'Tính năng', 'given' => 'Biết|Cho|*', 'name' => 'Vietnamese', 'native' => 'Tiếng Việt', 'rule' => 'Rule', 'scenario' => 'Tình huống|Kịch bản', 'scenario_outline' => 'Khung tình huống|Khung kịch bản', 'then' => 'Thì|*', 'when' => 'Khi|*', ), 'zh-CN' => array ( 'and' => '同时<|并且<|而且<|*', 'background' => '背景', 'but' => '但是<|*', 'examples' => '例子', 'feature' => '功能', 'given' => '假如<|假定<|假设<|*', 'name' => 'Chinese simplified', 'native' => '简体中文', 'rule' => 'Rule', 'scenario' => '剧本|场景', 'scenario_outline' => '剧本大纲|场景大纲', 'then' => '那么<|*', 'when' => '当<|*', ), 'zh-TW' => array ( 'and' => '並且<|同時<|而且<|*', 'background' => '背景', 'but' => '但是<|*', 'examples' => '例子', 'feature' => '功能', 'given' => '假如<|假定<|假設<|*', 'name' => 'Chinese traditional', 'native' => '繁體中文', 'rule' => 'Rule', 'scenario' => '劇本|場景', 'scenario_outline' => '劇本大綱|場景大綱', 'then' => '那麼<|*', 'when' => '當<|*', ), ); $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); } } 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]; } } contentType; } } 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; } } 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; } } '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; } } 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), '.'); } } 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; } } '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; } } 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); } } '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; } } 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); } } 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)); } } } } 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); } } } } } 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); } } {$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)); } } 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)); } } 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); } } 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; } } 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); } } 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; } } 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; } } tokens[] = $token; } public function current(): Token { return \current($this->tokens); } public function key(): int { return \key($this->tokens); } public function next(): void { \next($this->tokens); $this->pos++; } public function valid(): bool { return $this->count() > $this->pos; } public function rewind(): void { \reset($this->tokens); $this->pos = 0; } public function count(): int { return \count($this->tokens); } public function offsetExists($offset): bool { return isset($this->tokens[$offset]); } public function offsetGet($offset): Token { if (!$this->offsetExists($offset)) { throw new TokenCollectionException( \sprintf('No Token at offest %s', $offset) ); } return $this->tokens[$offset]; } public function offsetSet($offset, $value): void { if (!\is_int($offset)) { $type = \gettype($offset); throw new TokenCollectionException( \sprintf( 'Offset must be of type integer, %s given', $type === 'object' ? \get_class($value) : $type ) ); } if (!$value instanceof Token) { $type = \gettype($value); throw new TokenCollectionException( \sprintf( 'Value must be of type %s, %s given', Token::class, $type === 'object' ? \get_class($value) : $type ) ); } $this->tokens[$offset] = $value; } public function offsetUnset($offset): void { unset($this->tokens[$offset]); } } xmlns = $xmlns; } public function toDom(TokenCollection $tokens): DOMDocument { $dom = new DOMDocument(); $dom->preserveWhiteSpace = false; $dom->loadXML($this->toXML($tokens)); return $dom; } public function toXML(TokenCollection $tokens): string { $this->writer = new \XMLWriter(); $this->writer->openMemory(); $this->writer->setIndent(true); $this->writer->startDocument(); $this->writer->startElement('source'); $this->writer->writeAttribute('xmlns', $this->xmlns->asString()); if (\count($tokens) > 0) { $this->writer->startElement('line'); $this->writer->writeAttribute('no', '1'); $this->previousToken = $tokens[0]; foreach ($tokens as $token) { $this->addToken($token); } } $this->writer->endElement(); $this->writer->endElement(); $this->writer->endDocument(); return $this->writer->outputMemory(); } private function addToken(Token $token): void { if ($this->previousToken->getLine() < $token->getLine()) { $this->writer->endElement(); $this->writer->startElement('line'); $this->writer->writeAttribute('no', (string)$token->getLine()); $this->previousToken = $token; } if ($token->getValue() !== '') { $this->writer->startElement('token'); $this->writer->writeAttribute('name', $token->getName()); $this->writer->writeRaw(\htmlspecialchars($token->getValue(), \ENT_NOQUOTES | \ENT_DISALLOWED | \ENT_XML1)); $this->writer->endElement(); } } } ensureValidUri($value); $this->value = $value; } public function asString(): string { return $this->value; } private function ensureValidUri($value): void { if (\strpos($value, ':') === false) { throw new NamespaceUriException( \sprintf("Namespace URI '%s' must contain at least one colon", $value) ); } } } 'T_OPEN_BRACKET', ')' => 'T_CLOSE_BRACKET', '[' => 'T_OPEN_SQUARE', ']' => 'T_CLOSE_SQUARE', '{' => 'T_OPEN_CURLY', '}' => 'T_CLOSE_CURLY', ';' => 'T_SEMICOLON', '.' => 'T_DOT', ',' => 'T_COMMA', '=' => 'T_EQUAL', '<' => 'T_LT', '>' => 'T_GT', '+' => 'T_PLUS', '-' => 'T_MINUS', '*' => 'T_MULT', '/' => 'T_DIV', '?' => 'T_QUESTION_MARK', '!' => 'T_EXCLAMATION_MARK', ':' => 'T_COLON', '"' => 'T_DOUBLE_QUOTES', '@' => 'T_AT', '&' => 'T_AMPERSAND', '%' => 'T_PERCENT', '|' => 'T_PIPE', '$' => 'T_DOLLAR', '^' => 'T_CARET', '~' => 'T_TILDE', '`' => 'T_BACKTICK' ]; public function parse(string $source): TokenCollection { $result = new TokenCollection(); if ($source === '') { return $result; } $tokens = \token_get_all($source); $lastToken = new Token( $tokens[0][2], 'Placeholder', '' ); foreach ($tokens as $pos => $tok) { if (\is_string($tok)) { $token = new Token( $lastToken->getLine(), $this->map[$tok], $tok ); $result->addToken($token); $lastToken = $token; continue; } $line = $tok[2]; $values = \preg_split('/\R+/Uu', $tok[1]); if (!$values) { $result->addToken( new Token( $line, \token_name($tok[0]), '{binary data}' ) ); continue; } foreach ($values as $v) { $token = new Token( $line, \token_name($tok[0]), $v ); $lastToken = $token; $line++; if ($v === '') { continue; } $result->addToken($token); } } return $this->fillBlanks($result, $lastToken->getLine()); } private function fillBlanks(TokenCollection $tokens, int $maxLine): TokenCollection { $prev = new Token( 0, 'Placeholder', '' ); $final = new TokenCollection(); foreach ($tokens as $token) { $gap = $token->getLine() - $prev->getLine(); while ($gap > 1) { $linebreak = new Token( $prev->getLine() + 1, 'T_WHITESPACE', '' ); $final->addToken($linebreak); $prev = $linebreak; $gap--; } $final->addToken($token); $prev = $token; } $gap = $maxLine - $prev->getLine(); while ($gap > 0) { $linebreak = new Token( $prev->getLine() + 1, 'T_WHITESPACE', '' ); $final->addToken($linebreak); $prev = $linebreak; $gap--; } return $final; } } line = $line; $this->name = $name; $this->value = $value; } public function getLine(): int { return $this->line; } public function getName(): string { return $this->name; } public function getValue(): string { return $this->value; } } logger = $logger; } } log(LogLevel::EMERGENCY, $message, $context); } public function alert(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::ALERT, $message, $context); } public function critical(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::CRITICAL, $message, $context); } public function error(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::ERROR, $message, $context); } public function warning(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::WARNING, $message, $context); } public function notice(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::NOTICE, $message, $context); } public function info(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::INFO, $message, $context); } public function debug(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::DEBUG, $message, $context); } abstract public function log($level, string|\Stringable $message, array $context = []): void; } = 80002)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.2". 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 ); } register(true); $filesToLoad = \Composer\Autoload\ComposerStaticInit6df945c87d8c91a0cdc33b5e2833ee2d::$files; $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; require $file; } }, null, null); foreach ($filesToLoad as $fileIdentifier => $file) { $requireFile($fileIdentifier, $file); } return $loader; } } $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 || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; } } return false; } public static function satisfies(VersionParser $parser, $packageName, $constraint) { $constraint = $parser->parseConstraints((string) $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')) { $required = require $vendorDir.'/composer/installed.php'; $installed[] = self::$installedByVendor[$vendorDir] = $required; 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') { $required = require __DIR__ . '/installed.php'; self::$installed = $required; } else { self::$installed = array(); } } if (self::$installed !== array()) { $installed[] = self::$installed; } return $installed; } } array($vendorDir . '/phpseclib/phpseclib/phpseclib'), 'Symfony\\Polyfill\\Php81\\' => array($vendorDir . '/symfony/polyfill-php81'), 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'), 'Symfony\\Polyfill\\Intl\\Grapheme\\' => array($vendorDir . '/symfony/polyfill-intl-grapheme'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), 'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'), 'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'), 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'), 'Symfony\\Component\\String\\' => array($vendorDir . '/symfony/string'), '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\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Symfony\\Component\\BrowserKit\\' => array($vendorDir . '/symfony/browser-kit'), 'Psy\\' => array($vendorDir . '/psy/psysh/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), 'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'), 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'Predis\\' => array($vendorDir . '/predis/predis/src'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'PhpAmqpLib\\' => array($vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib'), 'MongoDB\\' => array($vendorDir . '/mongodb/mongodb/src'), 'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src/JsonSchema'), 'Jean85\\' => array($vendorDir . '/jean85/pretty-package-versions/src'), 'Humbug\\SelfUpdate\\' => array($vendorDir . '/laravel-zero/phar-updater/src'), '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'), 'Codeception\\Extension\\' => array($vendorDir . '/codeception/codeception/ext'), 'Codeception\\' => array($vendorDir . '/codeception/codeception/src/Codeception', $vendorDir . '/codeception/stub/src'), ); array( 'name' => 'codeception/phar-file', 'pretty_version' => 'dev-master', 'version' => 'dev-master', 'reference' => 'c08af555e81f41cf0eae0a7ac9d22b82e822ccf3', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => true, ), 'versions' => array( 'behat/gherkin' => array( 'pretty_version' => 'v4.9.0', 'version' => '4.9.0.0', 'reference' => '0bc8d1e30e96183e4f36db9dc79caead300beff4', 'type' => 'library', 'install_path' => __DIR__ . '/../behat/gherkin', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/codeception' => array( 'pretty_version' => '5.1.2', 'version' => '5.1.2.0', 'reference' => '3b2d7d1a88e7e1d9dc0acb6d3c8f0acda0a37374', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/codeception', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/lib-asserts' => array( 'pretty_version' => '2.1.0', 'version' => '2.1.0.0', 'reference' => 'b8c7dff552249e560879c682ba44a4b963af91bc', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/lib-asserts', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/lib-innerbrowser' => array( 'pretty_version' => '3.1.3', 'version' => '3.1.3.0', 'reference' => '10482f7e34c0537bf5b87bc82a3d65a1842a8b4f', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/lib-innerbrowser', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/lib-web' => array( 'pretty_version' => '1.0.6', 'version' => '1.0.6.0', 'reference' => '01ff7f9ed8760ba0b0805a0b3a843b4e74165a60', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/lib-web', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/lib-xml' => array( 'pretty_version' => '1.0.3', 'version' => '1.0.3.0', 'reference' => 'ba49213e60807e3612513f404a5c93aec63f4c72', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/lib-xml', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-amqp' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => '81c5e70d167a6c7b9b39c2d15e10119b45b5fd3d', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-amqp', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-apc' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => 'abe29ab930cdff0b9b7680f9656cf8b9bd6cb471', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-apc', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-asserts' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => '1b6b150b30586c3614e7e5761b31834ed7968603', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-asserts', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-cli' => array( 'pretty_version' => '2.0.1', 'version' => '2.0.1.0', 'reference' => 'a3a101fae4049fa2f810107f7bd5db3b3266ce63', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-cli', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-db' => array( 'pretty_version' => '3.1.3', 'version' => '3.1.3.0', 'reference' => '0d636cb8fa0d61fea10f42f6d434e998dfc80d53', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-db', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-filesystem' => array( 'pretty_version' => '3.0.1', 'version' => '3.0.1.0', 'reference' => '0fd78cf941cb72dc2a650c6132c5999c26ad4f9a', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-filesystem', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-ftp' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => '03c8d8fdb2055467927b90106693f2b72dae10e2', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-ftp', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-memcache' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => '9d1149997a4f6dbf713ec2529992536f6b8e28b5', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-memcache', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-mongodb' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => 'f9d3ed6d46892d6192ede90f17439e99cdd8ea62', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-mongodb', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-phpbrowser' => array( 'pretty_version' => '3.0.1', 'version' => '3.0.1.0', 'reference' => 'a972411f60cd00d00d5e5e3b35496ba4a23bcffc', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-phpbrowser', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-queue' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => '1f97bd070876b2d165b20e8d6a3c53af0a3c0433', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-queue', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-redis' => array( 'pretty_version' => '3.1.0', 'version' => '3.1.0.0', 'reference' => '53378c72d0da7454389b9f21648c09ad1efc3cbd', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-redis', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-rest' => array( 'pretty_version' => '3.3.2', 'version' => '3.3.2.0', 'reference' => 'bb545d4f7c261472472da8730267d9df162199cb', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-rest', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-sequence' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => '9738e2eb06035a0975171a0aa3fce00d284b4dfb', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-sequence', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-soap' => array( 'pretty_version' => '3.0.1', 'version' => '3.0.1.0', 'reference' => '0b92c8f24f221d888ccb3a270a5ee223807e11e4', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-soap', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/module-webdriver' => array( 'pretty_version' => '3.2.2', 'version' => '3.2.2.0', 'reference' => '06fe128460a313e171bfef894882c7c880aef6b8', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/module-webdriver', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/phar-file' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', 'reference' => 'c08af555e81f41cf0eae0a7ac9d22b82e822ccf3', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), 'codeception/phpunit-wrapper' => array( 'dev_requirement' => false, 'replaced' => array( 0 => '*', ), ), 'codeception/stub' => array( 'pretty_version' => '4.1.3', 'version' => '4.1.3.0', 'reference' => '4fcad2c165f365377486dc3fd8703b07f1f2fcae', 'type' => 'library', 'install_path' => __DIR__ . '/../codeception/stub', 'aliases' => array(), 'dev_requirement' => false, ), 'doctrine/instantiator' => array( 'pretty_version' => '1.5.0', 'version' => '1.5.0.0', 'reference' => '0a0fa9780f5d4e507415a065172d26a98d02047b', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/instantiator', 'aliases' => array(), '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' => '7.8.1', 'version' => '7.8.1.0', 'reference' => '41042bc7ab002487b876a0683fc8dce04ddce104', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'aliases' => array(), 'dev_requirement' => false, ), 'guzzlehttp/promises' => array( 'pretty_version' => '2.0.2', 'version' => '2.0.2.0', 'reference' => 'bbff78d96034045e58e13dedd6ad91b5d1253223', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/promises', 'aliases' => array(), 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( 'pretty_version' => '2.6.2', 'version' => '2.6.2.0', 'reference' => '45b30f99ac27b5ca93cb4831afe16285f57b8221', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), 'dev_requirement' => false, ), 'jean85/pretty-package-versions' => array( 'pretty_version' => '2.0.5', 'version' => '2.0.5.0', 'reference' => 'ae547e455a3d8babd07b96966b17d7fd21d9c6af', 'type' => 'library', 'install_path' => __DIR__ . '/../jean85/pretty-package-versions', 'aliases' => array(), 'dev_requirement' => false, ), 'justinrainbow/json-schema' => array( 'pretty_version' => 'v5.2.13', 'version' => '5.2.13.0', 'reference' => 'fbbe7e5d79f618997bc3332a6f49246036c45793', 'type' => 'library', 'install_path' => __DIR__ . '/../justinrainbow/json-schema', 'aliases' => array(), 'dev_requirement' => false, ), 'laravel-zero/phar-updater' => array( 'pretty_version' => 'v1.3.0', 'version' => '1.3.0.0', 'reference' => '0b09e35a7c2296992b2f92d921f828cd311eebb5', 'type' => 'library', 'install_path' => __DIR__ . '/../laravel-zero/phar-updater', 'aliases' => array(), 'dev_requirement' => false, ), 'mongodb/mongodb' => array( 'pretty_version' => '1.17.0', 'version' => '1.17.0.0', 'reference' => '9d9c917cf7ff275ed6bd63c596efeb6e49fd0e53', 'type' => 'library', 'install_path' => __DIR__ . '/../mongodb/mongodb', 'aliases' => array(), 'dev_requirement' => false, ), 'myclabs/deep-copy' => array( 'pretty_version' => '1.11.1', 'version' => '1.11.1.0', 'reference' => '7284c22080590fb39f2ffa3e9057f10a4ddd0e0c', 'type' => 'library', 'install_path' => __DIR__ . '/../myclabs/deep-copy', 'aliases' => array(), 'dev_requirement' => false, ), 'nikic/php-parser' => array( 'pretty_version' => 'v5.0.2', 'version' => '5.0.2.0', 'reference' => '139676794dc1e9231bf7bcd123cfc0c99182cb13', 'type' => 'library', 'install_path' => __DIR__ . '/../nikic/php-parser', 'aliases' => array(), 'dev_requirement' => false, ), 'phar-io/manifest' => array( 'pretty_version' => '2.0.4', 'version' => '2.0.4.0', 'reference' => '54750ef60c58e43759730615a392c31c80e23176', 'type' => 'library', 'install_path' => __DIR__ . '/../phar-io/manifest', 'aliases' => array(), 'dev_requirement' => false, ), 'phar-io/version' => array( 'pretty_version' => '3.2.1', 'version' => '3.2.1.0', 'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74', 'type' => 'library', 'install_path' => __DIR__ . '/../phar-io/version', 'aliases' => array(), 'dev_requirement' => false, ), 'php-amqplib/php-amqplib' => array( 'pretty_version' => 'v3.6.1', 'version' => '3.6.1.0', 'reference' => '76eee289e98b0b309a761787e65cbe1acbaf8c72', 'type' => 'library', 'install_path' => __DIR__ . '/../php-amqplib/php-amqplib', 'aliases' => array(), 'dev_requirement' => false, ), 'php-webdriver/webdriver' => array( 'pretty_version' => '1.15.1', 'version' => '1.15.1.0', 'reference' => 'cd52d9342c5aa738c2e75a67e47a1b6df97154e8', 'type' => 'library', 'install_path' => __DIR__ . '/../php-webdriver/webdriver', 'aliases' => array(), 'dev_requirement' => false, ), 'phpseclib/phpseclib' => array( 'pretty_version' => '2.0.47', 'version' => '2.0.47.0', 'reference' => 'b7d7d90ee7df7f33a664b4aea32d50a305d35adb', 'type' => 'library', 'install_path' => __DIR__ . '/../phpseclib/phpseclib', 'aliases' => array(), 'dev_requirement' => false, ), 'phpunit/php-code-coverage' => array( 'pretty_version' => '9.2.31', 'version' => '9.2.31.0', 'reference' => '48c34b5d8d983006bd2adc2d0de92963b9155965', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-code-coverage', 'aliases' => array(), 'dev_requirement' => false, ), 'phpunit/php-file-iterator' => array( 'pretty_version' => '3.0.6', 'version' => '3.0.6.0', 'reference' => 'cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-file-iterator', 'aliases' => array(), 'dev_requirement' => false, ), 'phpunit/php-invoker' => array( 'pretty_version' => '3.1.1', 'version' => '3.1.1.0', 'reference' => '5a10147d0aaf65b58940a0b72f71c9ac0423cc67', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-invoker', 'aliases' => array(), 'dev_requirement' => false, ), 'phpunit/php-text-template' => array( 'pretty_version' => '2.0.4', 'version' => '2.0.4.0', 'reference' => '5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-text-template', 'aliases' => array(), 'dev_requirement' => false, ), 'phpunit/php-timer' => array( 'pretty_version' => '5.0.3', 'version' => '5.0.3.0', 'reference' => '5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-timer', 'aliases' => array(), 'dev_requirement' => false, ), 'phpunit/phpunit' => array( 'pretty_version' => '9.6.17', 'version' => '9.6.17.0', 'reference' => '1a156980d78a6666721b7e8e8502fe210b587fcd', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/phpunit', 'aliases' => array(), 'dev_requirement' => false, ), 'predis/predis' => array( 'pretty_version' => 'v2.2.2', 'version' => '2.2.2.0', 'reference' => 'b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1', 'type' => 'library', 'install_path' => __DIR__ . '/../predis/predis', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/container' => array( 'pretty_version' => '2.0.2', 'version' => '2.0.2.0', 'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/container', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/event-dispatcher' => array( 'pretty_version' => '1.0.0', 'version' => '1.0.0.0', 'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/event-dispatcher', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/event-dispatcher-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '1.0', ), ), 'psr/http-client' => array( 'pretty_version' => '1.0.3', 'version' => '1.0.3.0', 'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-client', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/http-client-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '1.0', ), ), 'psr/http-factory' => array( 'pretty_version' => '1.0.2', 'version' => '1.0.2.0', 'reference' => 'e616d01114759c4c489f93b099585439f795fe35', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-factory', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/http-factory-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '1.0', ), ), 'psr/http-message' => array( 'pretty_version' => '2.0', 'version' => '2.0.0.0', 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/http-message-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '1.0', ), ), 'psr/log' => array( 'pretty_version' => '3.0.0', 'version' => '3.0.0.0', 'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), 'dev_requirement' => false, ), 'psr/log-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '1.0|2.0|3.0', ), ), 'psy/psysh' => array( 'pretty_version' => 'v0.12.0', 'version' => '0.12.0.0', 'reference' => '750bf031a48fd07c673dbe3f11f72362ea306d0d', 'type' => 'library', 'install_path' => __DIR__ . '/../psy/psysh', 'aliases' => array(), 'dev_requirement' => false, ), 'ralouphie/getallheaders' => array( 'pretty_version' => '3.0.3', 'version' => '3.0.3.0', 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'type' => 'library', 'install_path' => __DIR__ . '/../ralouphie/getallheaders', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/cli-parser' => array( 'pretty_version' => '1.0.2', 'version' => '1.0.2.0', 'reference' => '2b56bea83a09de3ac06bb18b92f068e60cc6f50b', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/cli-parser', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/code-unit' => array( 'pretty_version' => '1.0.8', 'version' => '1.0.8.0', 'reference' => '1fc9f64c0927627ef78ba436c9b17d967e68e120', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/code-unit', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/code-unit-reverse-lookup' => array( 'pretty_version' => '2.0.3', 'version' => '2.0.3.0', 'reference' => 'ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/comparator' => array( 'pretty_version' => '4.0.8', 'version' => '4.0.8.0', 'reference' => 'fa0f136dd2334583309d32b62544682ee972b51a', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/comparator', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/complexity' => array( 'pretty_version' => '2.0.3', 'version' => '2.0.3.0', 'reference' => '25f207c40d62b8b7aa32f5ab026c53561964053a', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/complexity', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/diff' => array( 'pretty_version' => '4.0.6', 'version' => '4.0.6.0', 'reference' => 'ba01945089c3a293b01ba9badc29ad55b106b0bc', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/diff', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/environment' => array( 'pretty_version' => '5.1.5', 'version' => '5.1.5.0', 'reference' => '830c43a844f1f8d5b7a1f6d6076b784454d8b7ed', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/environment', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/exporter' => array( 'pretty_version' => '4.0.6', 'version' => '4.0.6.0', 'reference' => '78c00df8f170e02473b682df15bfcdacc3d32d72', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/exporter', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/global-state' => array( 'pretty_version' => '5.0.7', 'version' => '5.0.7.0', 'reference' => 'bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/global-state', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/lines-of-code' => array( 'pretty_version' => '1.0.4', 'version' => '1.0.4.0', 'reference' => 'e1e4a170560925c26d424b6a03aed157e7dcc5c5', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/lines-of-code', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/object-enumerator' => array( 'pretty_version' => '4.0.4', 'version' => '4.0.4.0', 'reference' => '5c9eeac41b290a3712d88851518825ad78f45c71', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/object-enumerator', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/object-reflector' => array( 'pretty_version' => '2.0.4', 'version' => '2.0.4.0', 'reference' => 'b4f479ebdbf63ac605d183ece17d8d7fe49c15c7', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/object-reflector', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/recursion-context' => array( 'pretty_version' => '4.0.5', 'version' => '4.0.5.0', 'reference' => 'e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/recursion-context', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/resource-operations' => array( 'pretty_version' => '3.0.3', 'version' => '3.0.3.0', 'reference' => '0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/resource-operations', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/type' => array( 'pretty_version' => '3.2.1', 'version' => '3.2.1.0', 'reference' => '75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/type', 'aliases' => array(), 'dev_requirement' => false, ), 'sebastian/version' => array( 'pretty_version' => '3.0.2', 'version' => '3.0.2.0', 'reference' => 'c6c1022351a901512170118436c764e473f6de8c', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/version', 'aliases' => array(), 'dev_requirement' => false, ), 'softcreatr/jsonpath' => array( 'pretty_version' => '0.8.3', 'version' => '0.8.3.0', 'reference' => 'fc12dee0b46f3fa3a175c4051dbab60984acef4b', 'type' => 'library', 'install_path' => __DIR__ . '/../softcreatr/jsonpath', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/browser-kit' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => '4d1bf7886e2af0a194332486273debcd6662cfc9', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/browser-kit', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/console' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => 'c3ebc83d031b71c39da318ca8b7a07ecc67507ed', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/console', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/css-selector' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => 'f1d00bddb83a4cb2138564b2150001cb6ce272b1', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/css-selector', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/deprecation-contracts' => array( 'pretty_version' => 'v3.0.2', 'version' => '3.0.2.0', 'reference' => '26954b3d62a6c5fd0ea8a2a00c0353a14978d05c', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/dom-crawler' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => '622578ff158318b1b49d95068bd6b66c713601e9', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/dom-crawler', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/event-dispatcher' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => '2eaf8e63bc5b8cefabd4a800157f0d0c094f677a', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/event-dispatcher', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/event-dispatcher-contracts' => array( 'pretty_version' => 'v3.0.2', 'version' => '3.0.2.0', 'reference' => '7bc61cc2db649b4637d331240c5346dcc7708051', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/event-dispatcher-contracts', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/event-dispatcher-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '2.0|3.0', ), ), 'symfony/finder' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => '5cc9cac6586fc0c28cd173780ca696e419fefa11', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/finder', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/polyfill-ctype' => array( 'pretty_version' => 'v1.29.0', 'version' => '1.29.0.0', 'reference' => 'ef4d7e442ca910c4764bce785146269b30cb5fc4', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/polyfill-intl-grapheme' => array( 'pretty_version' => 'v1.29.0', 'version' => '1.29.0.0', 'reference' => '32a9da87d7b3245e09ac426c83d334ae9f06f80f', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-grapheme', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/polyfill-intl-normalizer' => array( 'pretty_version' => 'v1.29.0', 'version' => '1.29.0.0', 'reference' => 'bc45c394692b948b4d383a08d7753968bed9a83d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/polyfill-mbstring' => array( 'pretty_version' => 'v1.29.0', 'version' => '1.29.0.0', 'reference' => '9773676c8a1bb1f8d4340a62efe641cf76eda7ec', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/polyfill-php80' => array( 'pretty_version' => 'v1.29.0', 'version' => '1.29.0.0', 'reference' => '87b68208d5c1188808dd7839ee1e6c8ec3b02f1b', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php80', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/polyfill-php81' => array( 'pretty_version' => 'v1.29.0', 'version' => '1.29.0.0', 'reference' => 'c565ad1e63f30e7477fc40738343c62b40bc672d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php81', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/process' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => '2114fd60f26a296cc403a7939ab91478475a33d4', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/process', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/service-contracts' => array( 'pretty_version' => 'v3.0.2', 'version' => '3.0.2.0', 'reference' => 'd78d39c1599bd1188b8e26bb341da52c3c6d8a66', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/service-contracts', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/string' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => 'd9e72497367c23e08bf94176d2be45b00a9d232a', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/string', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/var-dumper' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => 'eb980457fa6899840fe1687e8627a03a7d8a3d52', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/var-dumper', 'aliases' => array(), 'dev_requirement' => false, ), 'symfony/yaml' => array( 'pretty_version' => 'v6.0.19', 'version' => '6.0.19.0', 'reference' => 'deec3a812a0305a50db8ae689b183f43d915c884', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/yaml', 'aliases' => array(), 'dev_requirement' => false, ), 'theseer/tokenizer' => array( 'pretty_version' => '1.2.3', 'version' => '1.2.3.0', 'reference' => '737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2', 'type' => 'library', 'install_path' => __DIR__ . '/../theseer/tokenizer', 'aliases' => array(), 'dev_requirement' => false, ), 'videlalvaro/php-amqplib' => array( 'dev_requirement' => false, 'replaced' => array( 0 => 'v3.6.1', ), ), ), ); __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', 'ec07570ca5a812141189b1fa81503674' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert/Functions.php', '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', '801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php', 'e3b2795a8a512b6083af088fb53afe6c' => __DIR__ . '/..' . '/codeception/codeception/functions.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '3a37ebac017bc098e9a86b35401e7a68' => __DIR__ . '/..' . '/mongodb/mongodb/src/functions.php', '2a3c2110e8e0295330dc3d11a4cbc4cb' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php', 'a3b8fb13a112e3be232ab3c07b8a884f' => __DIR__ . '/..' . '/codeception/module-sequence/src/Codeception/Util/sq.php', ); public static $prefixLengthsPsr4 = array ( 'p' => array ( 'phpseclib\\' => 10, ), 'S' => array ( 'Symfony\\Polyfill\\Php81\\' => 23, 'Symfony\\Polyfill\\Php80\\' => 23, 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33, 'Symfony\\Polyfill\\Intl\\Grapheme\\' => 31, 'Symfony\\Polyfill\\Ctype\\' => 23, 'Symfony\\Contracts\\Service\\' => 26, 'Symfony\\Contracts\\EventDispatcher\\' => 34, 'Symfony\\Component\\Yaml\\' => 23, 'Symfony\\Component\\VarDumper\\' => 28, 'Symfony\\Component\\String\\' => 25, 'Symfony\\Component\\Process\\' => 26, 'Symfony\\Component\\Finder\\' => 25, 'Symfony\\Component\\EventDispatcher\\' => 34, 'Symfony\\Component\\DomCrawler\\' => 29, 'Symfony\\Component\\CssSelector\\' => 30, 'Symfony\\Component\\Console\\' => 26, 'Symfony\\Component\\BrowserKit\\' => 29, ), 'P' => array ( 'Psy\\' => 4, 'Psr\\Log\\' => 8, 'Psr\\Http\\Message\\' => 17, 'Psr\\Http\\Client\\' => 16, 'Psr\\EventDispatcher\\' => 20, 'Psr\\Container\\' => 14, 'Predis\\' => 7, 'PhpParser\\' => 10, 'PhpAmqpLib\\' => 11, ), 'M' => array ( 'MongoDB\\' => 8, ), 'J' => array ( 'JsonSchema\\' => 11, 'Jean85\\' => 7, ), 'H' => array ( 'Humbug\\SelfUpdate\\' => 18, ), '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 ( 'Codeception\\Extension\\' => 22, 'Codeception\\' => 12, ), ); public static $prefixDirsPsr4 = array ( 'phpseclib\\' => array ( 0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib', ), 'Symfony\\Polyfill\\Php81\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php81', ), 'Symfony\\Polyfill\\Php80\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php80', ), 'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ), 'Symfony\\Polyfill\\Intl\\Normalizer\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer', ), 'Symfony\\Polyfill\\Intl\\Grapheme\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme', ), 'Symfony\\Polyfill\\Ctype\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', ), 'Symfony\\Contracts\\Service\\' => array ( 0 => __DIR__ . '/..' . '/symfony/service-contracts', ), 'Symfony\\Contracts\\EventDispatcher\\' => array ( 0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts', ), 'Symfony\\Component\\Yaml\\' => array ( 0 => __DIR__ . '/..' . '/symfony/yaml', ), 'Symfony\\Component\\VarDumper\\' => array ( 0 => __DIR__ . '/..' . '/symfony/var-dumper', ), 'Symfony\\Component\\String\\' => array ( 0 => __DIR__ . '/..' . '/symfony/string', ), '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\\CssSelector\\' => array ( 0 => __DIR__ . '/..' . '/symfony/css-selector', ), 'Symfony\\Component\\Console\\' => array ( 0 => __DIR__ . '/..' . '/symfony/console', ), 'Symfony\\Component\\BrowserKit\\' => array ( 0 => __DIR__ . '/..' . '/symfony/browser-kit', ), 'Psy\\' => array ( 0 => __DIR__ . '/..' . '/psy/psysh/src', ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/src', ), 'Psr\\Http\\Message\\' => array ( 0 => __DIR__ . '/..' . '/psr/http-factory/src', 1 => __DIR__ . '/..' . '/psr/http-message/src', ), 'Psr\\Http\\Client\\' => array ( 0 => __DIR__ . '/..' . '/psr/http-client/src', ), 'Psr\\EventDispatcher\\' => array ( 0 => __DIR__ . '/..' . '/psr/event-dispatcher/src', ), 'Psr\\Container\\' => array ( 0 => __DIR__ . '/..' . '/psr/container/src', ), 'Predis\\' => array ( 0 => __DIR__ . '/..' . '/predis/predis/src', ), 'PhpParser\\' => array ( 0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser', ), 'PhpAmqpLib\\' => array ( 0 => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib', ), 'MongoDB\\' => array ( 0 => __DIR__ . '/..' . '/mongodb/mongodb/src', ), 'JsonSchema\\' => array ( 0 => __DIR__ . '/..' . '/justinrainbow/json-schema/src/JsonSchema', ), 'Jean85\\' => array ( 0 => __DIR__ . '/..' . '/jean85/pretty-package-versions/src', ), 'Humbug\\SelfUpdate\\' => array ( 0 => __DIR__ . '/..' . '/laravel-zero/phar-updater/src', ), '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', ), 'Codeception\\Extension\\' => array ( 0 => __DIR__ . '/..' . '/codeception/codeception/ext', ), 'Codeception\\' => array ( 0 => __DIR__ . '/..' . '/codeception/codeception/src/Codeception', 1 => __DIR__ . '/..' . '/codeception/stub/src', ), ); public static $prefixesPsr0 = array ( 'B' => array ( 'Behat\\Gherkin' => array ( 0 => __DIR__ . '/..' . '/behat/gherkin/src', ), ), ); public static $classMap = array ( 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.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', 'CURLStringFile' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php', 'Codeception\\Actor' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Actor.php', 'Codeception\\Application' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Application.php', 'Codeception\\Attribute\\After' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/After.php', 'Codeception\\Attribute\\AfterClass' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/AfterClass.php', 'Codeception\\Attribute\\Before' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Before.php', 'Codeception\\Attribute\\BeforeClass' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/BeforeClass.php', 'Codeception\\Attribute\\DataProvider' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/DataProvider.php', 'Codeception\\Attribute\\Depends' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Depends.php', 'Codeception\\Attribute\\Env' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Env.php', 'Codeception\\Attribute\\Examples' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Examples.php', 'Codeception\\Attribute\\Given' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Given.php', 'Codeception\\Attribute\\Group' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Group.php', 'Codeception\\Attribute\\Incomplete' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Incomplete.php', 'Codeception\\Attribute\\Prepare' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Prepare.php', 'Codeception\\Attribute\\Skip' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Skip.php', 'Codeception\\Attribute\\Then' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/Then.php', 'Codeception\\Attribute\\When' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Attribute/When.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\\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\\ActorTrait' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Shared/ActorTrait.php', 'Codeception\\Command\\Shared\\ConfigTrait' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Shared/ConfigTrait.php', 'Codeception\\Command\\Shared\\FileSystemTrait' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Shared/FileSystemTrait.php', 'Codeception\\Command\\Shared\\StyleTrait' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Command/Shared/StyleTrait.php', 'Codeception\\Configuration' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Configuration.php', 'Codeception\\Constraint\\Crawler' => __DIR__ . '/..' . '/codeception/lib-innerbrowser/src/Codeception/Constraint/Crawler.php', 'Codeception\\Constraint\\CrawlerNot' => __DIR__ . '/..' . '/codeception/lib-innerbrowser/src/Codeception/Constraint/CrawlerNot.php', 'Codeception\\Constraint\\Page' => __DIR__ . '/..' . '/codeception/lib-web/src/Constraint/Page.php', 'Codeception\\Constraint\\WebDriver' => __DIR__ . '/..' . '/codeception/module-webdriver/src/Codeception/Constraint/WebDriver.php', 'Codeception\\Constraint\\WebDriverNot' => __DIR__ . '/..' . '/codeception/module-webdriver/src/Codeception/Constraint/WebDriverNot.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\\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\\Deprecation' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/Deprecation.php', 'Codeception\\Exception\\ElementNotFound' => __DIR__ . '/..' . '/codeception/lib-web/src/Exception/ElementNotFound.php', 'Codeception\\Exception\\Error' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/Error.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\\InvalidTestException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/InvalidTestException.php', 'Codeception\\Exception\\MalformedLocatorException' => __DIR__ . '/..' . '/codeception/lib-web/src/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\\Notice' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/Notice.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\\Exception\\ThrowableWrapper' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/ThrowableWrapper.php', 'Codeception\\Exception\\UselessTestException' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/UselessTestException.php', 'Codeception\\Exception\\Warning' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Exception/Warning.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\\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\\ConsolePrinter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/ConsolePrinter.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/lib-web/src/Lib/Interfaces/ElementLocator.php', 'Codeception\\Lib\\Interfaces\\MultiSession' => __DIR__ . '/..' . '/codeception/lib-web/src/Lib/Interfaces/MultiSession.php', 'Codeception\\Lib\\Interfaces\\ORM' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/ORM.php', 'Codeception\\Lib\\Interfaces\\PageSourceSaver' => __DIR__ . '/..' . '/codeception/lib-web/src/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/lib-web/src/Lib/Interfaces/Remote.php', 'Codeception\\Lib\\Interfaces\\RequiresPackage' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/Interfaces/RequiresPackage.php', 'Codeception\\Lib\\Interfaces\\ScreenshotSaver' => __DIR__ . '/..' . '/codeception/lib-web/src/Lib/Interfaces/ScreenshotSaver.php', 'Codeception\\Lib\\Interfaces\\SessionSnapshot' => __DIR__ . '/..' . '/codeception/lib-web/src/Lib/Interfaces/SessionSnapshot.php', 'Codeception\\Lib\\Interfaces\\Web' => __DIR__ . '/..' . '/codeception/lib-web/src/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\\Lib\\PauseShell' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Lib/PauseShell.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\\Constraint\\JsonContains' => __DIR__ . '/..' . '/codeception/module-rest/src/Codeception/Constraint/JsonContains.php', 'Codeception\\PHPUnit\\Constraint\\JsonType' => __DIR__ . '/..' . '/codeception/module-rest/src/Codeception/Constraint/JsonType.php', 'Codeception\\PHPUnit\\TestCase' => __DIR__ . '/..' . '/codeception/codeception/src/PHPUnit/TestCase.php', 'Codeception\\Reporter\\HtmlReporter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Reporter/HtmlReporter.php', 'Codeception\\Reporter\\JUnitReporter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Reporter/JUnitReporter.php', 'Codeception\\Reporter\\PhpUnitReporter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Reporter/PhpUnitReporter.php', 'Codeception\\Reporter\\ReportPrinter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Reporter/ReportPrinter.php', 'Codeception\\ResultAggregator' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/ResultAggregator.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\\AsJson' => __DIR__ . '/..' . '/codeception/module-rest/src/Codeception/Step/AsJson.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\\Deprecation' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/Deprecation.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\\StaticEventsTrait' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Subscriber/Shared/StaticEventsTrait.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\\Dependencies' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Template/Dependencies.php', 'Codeception\\Template\\Unit' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Template/Unit.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\\DataProvider' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/DataProvider.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\\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\\Filter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/Filter.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\\TestCaseWrapper' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Test/TestCaseWrapper.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/module-rest/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/lib-web/src/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\\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/lib-xml/src/Util/Soap.php', 'Codeception\\Util\\StackTraceFilter' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/StackTraceFilter.php', 'Codeception\\Util\\Template' => __DIR__ . '/..' . '/codeception/codeception/src/Codeception/Util/Template.php', 'Codeception\\Util\\Uri' => __DIR__ . '/..' . '/codeception/lib-web/src/Util/Uri.php', 'Codeception\\Util\\Xml' => __DIR__ . '/..' . '/codeception/lib-xml/src/Util/Xml.php', 'Codeception\\Util\\XmlBuilder' => __DIR__ . '/..' . '/codeception/lib-xml/src/Util/XmlBuilder.php', 'Codeception\\Util\\XmlStructure' => __DIR__ . '/..' . '/codeception/lib-xml/src/Util/XmlStructure.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\\ChainableFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.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\\ArrayObjectFilter' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.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', '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', '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\\DetachedShadowRootException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/DetachedShadowRootException.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\\Internal\\DriverServerDiedException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/Internal/DriverServerDiedException.php', 'Facebook\\WebDriver\\Exception\\Internal\\IOException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/Internal/IOException.php', 'Facebook\\WebDriver\\Exception\\Internal\\LogicException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/Internal/LogicException.php', 'Facebook\\WebDriver\\Exception\\Internal\\RuntimeException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/Internal/RuntimeException.php', 'Facebook\\WebDriver\\Exception\\Internal\\UnexpectedResponseException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/Internal/UnexpectedResponseException.php', 'Facebook\\WebDriver\\Exception\\Internal\\WebDriverCurlException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/Internal/WebDriverCurlException.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\\NoSuchShadowRootException' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/NoSuchShadowRootException.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\\PhpWebDriverExceptionInterface' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/PhpWebDriverExceptionInterface.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\\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\\ShadowRoot' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Remote/ShadowRoot.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\\IsElementDisplayedAtom' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Support/IsElementDisplayedAtom.php', 'Facebook\\WebDriver\\Support\\ScreenshotHelper' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Support/ScreenshotHelper.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', '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\\BodySummarizer' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/BodySummarizer.php', 'GuzzleHttp\\BodySummarizerInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/BodySummarizerInterface.php', 'GuzzleHttp\\Client' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Client.php', 'GuzzleHttp\\ClientInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ClientInterface.php', 'GuzzleHttp\\ClientTrait' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ClientTrait.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\\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\\HeaderProcessor' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.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\\MessageFormatterInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatterInterface.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\\Exception\\MalformedUriException' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Exception/MalformedUriException.php', 'GuzzleHttp\\Psr7\\FnStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/FnStream.php', 'GuzzleHttp\\Psr7\\Header' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Header.php', 'GuzzleHttp\\Psr7\\HttpFactory' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/HttpFactory.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\\Utils' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Utils.php', 'Humbug\\SelfUpdate\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Exception/ExceptionInterface.php', 'Humbug\\SelfUpdate\\Exception\\FilesystemException' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Exception/FilesystemException.php', 'Humbug\\SelfUpdate\\Exception\\HttpRequestException' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Exception/HttpRequestException.php', 'Humbug\\SelfUpdate\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Exception/InvalidArgumentException.php', 'Humbug\\SelfUpdate\\Exception\\JsonParsingException' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Exception/JsonParsingException.php', 'Humbug\\SelfUpdate\\Exception\\NoSignatureException' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Exception/NoSignatureException.php', 'Humbug\\SelfUpdate\\Exception\\RuntimeException' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Exception/RuntimeException.php', 'Humbug\\SelfUpdate\\Strategy\\DirectDownloadStrategyAbstract' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Strategy/DirectDownloadStrategyAbstract.php', 'Humbug\\SelfUpdate\\Strategy\\GithubStrategy' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Strategy/GithubStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\Sha256Strategy' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Strategy/Sha256Strategy.php', 'Humbug\\SelfUpdate\\Strategy\\Sha512Strategy' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Strategy/Sha512Strategy.php', 'Humbug\\SelfUpdate\\Strategy\\ShaStrategy' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Strategy/ShaStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\ShaStrategyAbstract' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Strategy/ShaStrategyAbstract.php', 'Humbug\\SelfUpdate\\Strategy\\StrategyInterface' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Strategy/StrategyInterface.php', 'Humbug\\SelfUpdate\\Updater' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/Updater.php', 'Humbug\\SelfUpdate\\VersionParser' => __DIR__ . '/..' . '/laravel-zero/phar-updater/src/VersionParser.php', 'Jean85\\Exception\\ProvidedPackageException' => __DIR__ . '/..' . '/jean85/pretty-package-versions/src/Exception/ProvidedPackageException.php', 'Jean85\\Exception\\ReplacedPackageException' => __DIR__ . '/..' . '/jean85/pretty-package-versions/src/Exception/ReplacedPackageException.php', 'Jean85\\Exception\\VersionMissingExceptionInterface' => __DIR__ . '/..' . '/jean85/pretty-package-versions/src/Exception/VersionMissingExceptionInterface.php', 'Jean85\\PrettyVersions' => __DIR__ . '/..' . '/jean85/pretty-package-versions/src/PrettyVersions.php', 'Jean85\\Version' => __DIR__ . '/..' . '/jean85/pretty-package-versions/src/Version.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', 'MongoDB\\BulkWriteResult' => __DIR__ . '/..' . '/mongodb/mongodb/src/BulkWriteResult.php', 'MongoDB\\ChangeStream' => __DIR__ . '/..' . '/mongodb/mongodb/src/ChangeStream.php', 'MongoDB\\Client' => __DIR__ . '/..' . '/mongodb/mongodb/src/Client.php', 'MongoDB\\Codec\\Codec' => __DIR__ . '/..' . '/mongodb/mongodb/src/Codec/Codec.php', 'MongoDB\\Codec\\DecodeIfSupported' => __DIR__ . '/..' . '/mongodb/mongodb/src/Codec/DecodeIfSupported.php', 'MongoDB\\Codec\\Decoder' => __DIR__ . '/..' . '/mongodb/mongodb/src/Codec/Decoder.php', 'MongoDB\\Codec\\DocumentCodec' => __DIR__ . '/..' . '/mongodb/mongodb/src/Codec/DocumentCodec.php', 'MongoDB\\Codec\\EncodeIfSupported' => __DIR__ . '/..' . '/mongodb/mongodb/src/Codec/EncodeIfSupported.php', 'MongoDB\\Codec\\Encoder' => __DIR__ . '/..' . '/mongodb/mongodb/src/Codec/Encoder.php', 'MongoDB\\Collection' => __DIR__ . '/..' . '/mongodb/mongodb/src/Collection.php', 'MongoDB\\Command\\ListCollections' => __DIR__ . '/..' . '/mongodb/mongodb/src/Command/ListCollections.php', 'MongoDB\\Command\\ListDatabases' => __DIR__ . '/..' . '/mongodb/mongodb/src/Command/ListDatabases.php', 'MongoDB\\Database' => __DIR__ . '/..' . '/mongodb/mongodb/src/Database.php', 'MongoDB\\DeleteResult' => __DIR__ . '/..' . '/mongodb/mongodb/src/DeleteResult.php', 'MongoDB\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/BadMethodCallException.php', 'MongoDB\\Exception\\CreateEncryptedCollectionException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/CreateEncryptedCollectionException.php', 'MongoDB\\Exception\\Exception' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/Exception.php', 'MongoDB\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/InvalidArgumentException.php', 'MongoDB\\Exception\\ResumeTokenException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/ResumeTokenException.php', 'MongoDB\\Exception\\RuntimeException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/RuntimeException.php', 'MongoDB\\Exception\\UnexpectedValueException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/UnexpectedValueException.php', 'MongoDB\\Exception\\UnsupportedException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/UnsupportedException.php', 'MongoDB\\Exception\\UnsupportedValueException' => __DIR__ . '/..' . '/mongodb/mongodb/src/Exception/UnsupportedValueException.php', 'MongoDB\\GridFS\\Bucket' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/Bucket.php', 'MongoDB\\GridFS\\CollectionWrapper' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/CollectionWrapper.php', 'MongoDB\\GridFS\\Exception\\CorruptFileException' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/Exception/CorruptFileException.php', 'MongoDB\\GridFS\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/Exception/FileNotFoundException.php', 'MongoDB\\GridFS\\Exception\\StreamException' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/Exception/StreamException.php', 'MongoDB\\GridFS\\ReadableStream' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/ReadableStream.php', 'MongoDB\\GridFS\\StreamWrapper' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/StreamWrapper.php', 'MongoDB\\GridFS\\WritableStream' => __DIR__ . '/..' . '/mongodb/mongodb/src/GridFS/WritableStream.php', 'MongoDB\\InsertManyResult' => __DIR__ . '/..' . '/mongodb/mongodb/src/InsertManyResult.php', 'MongoDB\\InsertOneResult' => __DIR__ . '/..' . '/mongodb/mongodb/src/InsertOneResult.php', 'MongoDB\\MapReduceResult' => __DIR__ . '/..' . '/mongodb/mongodb/src/MapReduceResult.php', 'MongoDB\\Model\\BSONArray' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/BSONArray.php', 'MongoDB\\Model\\BSONDocument' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/BSONDocument.php', 'MongoDB\\Model\\BSONIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/BSONIterator.php', 'MongoDB\\Model\\CachingIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/CachingIterator.php', 'MongoDB\\Model\\CallbackIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/CallbackIterator.php', 'MongoDB\\Model\\ChangeStreamIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/ChangeStreamIterator.php', 'MongoDB\\Model\\CodecCursor' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/CodecCursor.php', 'MongoDB\\Model\\CollectionInfo' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/CollectionInfo.php', 'MongoDB\\Model\\CollectionInfoCommandIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/CollectionInfoCommandIterator.php', 'MongoDB\\Model\\CollectionInfoIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/CollectionInfoIterator.php', 'MongoDB\\Model\\DatabaseInfo' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/DatabaseInfo.php', 'MongoDB\\Model\\DatabaseInfoIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/DatabaseInfoIterator.php', 'MongoDB\\Model\\DatabaseInfoLegacyIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/DatabaseInfoLegacyIterator.php', 'MongoDB\\Model\\IndexInfo' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/IndexInfo.php', 'MongoDB\\Model\\IndexInfoIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/IndexInfoIterator.php', 'MongoDB\\Model\\IndexInfoIteratorIterator' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/IndexInfoIteratorIterator.php', 'MongoDB\\Model\\IndexInput' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/IndexInput.php', 'MongoDB\\Model\\SearchIndexInput' => __DIR__ . '/..' . '/mongodb/mongodb/src/Model/SearchIndexInput.php', 'MongoDB\\Operation\\Aggregate' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Aggregate.php', 'MongoDB\\Operation\\BulkWrite' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/BulkWrite.php', 'MongoDB\\Operation\\Count' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Count.php', 'MongoDB\\Operation\\CountDocuments' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/CountDocuments.php', 'MongoDB\\Operation\\CreateCollection' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/CreateCollection.php', 'MongoDB\\Operation\\CreateEncryptedCollection' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/CreateEncryptedCollection.php', 'MongoDB\\Operation\\CreateIndexes' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/CreateIndexes.php', 'MongoDB\\Operation\\CreateSearchIndexes' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/CreateSearchIndexes.php', 'MongoDB\\Operation\\DatabaseCommand' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DatabaseCommand.php', 'MongoDB\\Operation\\Delete' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Delete.php', 'MongoDB\\Operation\\DeleteMany' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DeleteMany.php', 'MongoDB\\Operation\\DeleteOne' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DeleteOne.php', 'MongoDB\\Operation\\Distinct' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Distinct.php', 'MongoDB\\Operation\\DropCollection' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DropCollection.php', 'MongoDB\\Operation\\DropDatabase' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DropDatabase.php', 'MongoDB\\Operation\\DropEncryptedCollection' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DropEncryptedCollection.php', 'MongoDB\\Operation\\DropIndexes' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DropIndexes.php', 'MongoDB\\Operation\\DropSearchIndex' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/DropSearchIndex.php', 'MongoDB\\Operation\\EstimatedDocumentCount' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/EstimatedDocumentCount.php', 'MongoDB\\Operation\\Executable' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Executable.php', 'MongoDB\\Operation\\Explain' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Explain.php', 'MongoDB\\Operation\\Explainable' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Explainable.php', 'MongoDB\\Operation\\Find' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Find.php', 'MongoDB\\Operation\\FindAndModify' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/FindAndModify.php', 'MongoDB\\Operation\\FindOne' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/FindOne.php', 'MongoDB\\Operation\\FindOneAndDelete' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/FindOneAndDelete.php', 'MongoDB\\Operation\\FindOneAndReplace' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/FindOneAndReplace.php', 'MongoDB\\Operation\\FindOneAndUpdate' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/FindOneAndUpdate.php', 'MongoDB\\Operation\\InsertMany' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/InsertMany.php', 'MongoDB\\Operation\\InsertOne' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/InsertOne.php', 'MongoDB\\Operation\\ListCollectionNames' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ListCollectionNames.php', 'MongoDB\\Operation\\ListCollections' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ListCollections.php', 'MongoDB\\Operation\\ListDatabaseNames' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ListDatabaseNames.php', 'MongoDB\\Operation\\ListDatabases' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ListDatabases.php', 'MongoDB\\Operation\\ListIndexes' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ListIndexes.php', 'MongoDB\\Operation\\ListSearchIndexes' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ListSearchIndexes.php', 'MongoDB\\Operation\\MapReduce' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/MapReduce.php', 'MongoDB\\Operation\\ModifyCollection' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ModifyCollection.php', 'MongoDB\\Operation\\RenameCollection' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/RenameCollection.php', 'MongoDB\\Operation\\ReplaceOne' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/ReplaceOne.php', 'MongoDB\\Operation\\Update' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Update.php', 'MongoDB\\Operation\\UpdateMany' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/UpdateMany.php', 'MongoDB\\Operation\\UpdateOne' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/UpdateOne.php', 'MongoDB\\Operation\\UpdateSearchIndex' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/UpdateSearchIndex.php', 'MongoDB\\Operation\\Watch' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/Watch.php', 'MongoDB\\Operation\\WithTransaction' => __DIR__ . '/..' . '/mongodb/mongodb/src/Operation/WithTransaction.php', 'MongoDB\\PsrLogAdapter' => __DIR__ . '/..' . '/mongodb/mongodb/src/PsrLogAdapter.php', 'MongoDB\\UpdateResult' => __DIR__ . '/..' . '/mongodb/mongodb/src/UpdateResult.php', 'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPUnit\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php', 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.php', 'PHPUnit\\Framework\\CodeCoverageException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotExistException.php', 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/ArrayHasKey.php', 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/BinaryOperator.php', 'PHPUnit\\Framework\\Constraint\\Callback' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Callback.php', 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasAttribute.php', 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasStaticAttribute.php', 'PHPUnit\\Framework\\Constraint\\Constraint' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Constraint.php', 'PHPUnit\\Framework\\Constraint\\Count' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/Count.php', 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/DirectoryExists.php', 'PHPUnit\\Framework\\Constraint\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/Exception.php', 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionCode.php', 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessage.php', 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', 'PHPUnit\\Framework\\Constraint\\FileExists' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/FileExists.php', 'PHPUnit\\Framework\\Constraint\\GreaterThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/GreaterThan.php', 'PHPUnit\\Framework\\Constraint\\IsAnything' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php', 'PHPUnit\\Framework\\Constraint\\IsEmpty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/IsEmpty.php', 'PHPUnit\\Framework\\Constraint\\IsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php', 'PHPUnit\\Framework\\Constraint\\IsEqualCanonicalizing' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php', 'PHPUnit\\Framework\\Constraint\\IsEqualIgnoringCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php', 'PHPUnit\\Framework\\Constraint\\IsEqualWithDelta' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php', 'PHPUnit\\Framework\\Constraint\\IsFalse' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsFalse.php', 'PHPUnit\\Framework\\Constraint\\IsFinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Math/IsFinite.php', 'PHPUnit\\Framework\\Constraint\\IsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php', 'PHPUnit\\Framework\\Constraint\\IsInfinite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Math/IsInfinite.php', 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Type/IsInstanceOf.php', 'PHPUnit\\Framework\\Constraint\\IsJson' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/IsJson.php', 'PHPUnit\\Framework\\Constraint\\IsNan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Math/IsNan.php', 'PHPUnit\\Framework\\Constraint\\IsNull' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Type/IsNull.php', 'PHPUnit\\Framework\\Constraint\\IsReadable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsReadable.php', 'PHPUnit\\Framework\\Constraint\\IsTrue' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsTrue.php', 'PHPUnit\\Framework\\Constraint\\IsType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Type/IsType.php', 'PHPUnit\\Framework\\Constraint\\IsWritable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsWritable.php', 'PHPUnit\\Framework\\Constraint\\JsonMatches' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php', 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php', 'PHPUnit\\Framework\\Constraint\\LessThan' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/LessThan.php', 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalAnd.php', 'PHPUnit\\Framework\\Constraint\\LogicalNot' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalNot.php', 'PHPUnit\\Framework\\Constraint\\LogicalOr' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalOr.php', 'PHPUnit\\Framework\\Constraint\\LogicalXor' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php', 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php', 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php', 'PHPUnit\\Framework\\Constraint\\ObjectHasProperty' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasProperty.php', 'PHPUnit\\Framework\\Constraint\\Operator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php', 'PHPUnit\\Framework\\Constraint\\RegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php', 'PHPUnit\\Framework\\Constraint\\SameSize' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php', 'PHPUnit\\Framework\\Constraint\\StringContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringContains.php', 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringEndsWith.php', 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringMatchesFormatDescription.php', 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/String/StringStartsWith.php', 'PHPUnit\\Framework\\Constraint\\TraversableContains' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContains.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsEqual.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsIdentical.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsOnly.php', 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Constraint/Operator/UnaryOperator.php', 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php', 'PHPUnit\\Framework\\DataProviderTestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/DataProviderTestSuite.php', 'PHPUnit\\Framework\\Error' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/Error.php', 'PHPUnit\\Framework\\ErrorTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ErrorTestCase.php', 'PHPUnit\\Framework\\Error\\Deprecated' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Deprecated.php', 'PHPUnit\\Framework\\Error\\Error' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Error/Error.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/Exception.php', 'PHPUnit\\Framework\\ExceptionWrapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php', 'PHPUnit\\Framework\\ExecutionOrderDependency' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/ExecutionOrderDependency.php', 'PHPUnit\\Framework\\ExpectationFailedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/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/Exception/IncompleteTestError.php', 'PHPUnit\\Framework\\InvalidArgumentException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/InvalidArgumentException.php', 'PHPUnit\\Framework\\InvalidCoversTargetException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/InvalidCoversTargetException.php', 'PHPUnit\\Framework\\InvalidDataProviderException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/InvalidDataProviderException.php', 'PHPUnit\\Framework\\InvalidParameterGroupException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/InvalidParameterGroupException.php', 'PHPUnit\\Framework\\MissingCoversAnnotationException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/MissingCoversAnnotationException.php', 'PHPUnit\\Framework\\MockObject\\Api' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Api/Api.php', 'PHPUnit\\Framework\\MockObject\\BadMethodCallException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/BadMethodCallException.php', 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php', 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php', 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php', 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassAlreadyExistsException.php', 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsFinalException.php', 'PHPUnit\\Framework\\MockObject\\ClassIsReadonlyException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsReadonlyException.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/DuplicateMethodException.php', 'PHPUnit\\Framework\\MockObject\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php', 'PHPUnit\\Framework\\MockObject\\Generator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Generator.php', 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php', 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/InvalidMethodNameException.php', 'PHPUnit\\Framework\\MockObject\\Invocation' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Invocation.php', 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php', 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', 'PHPUnit\\Framework\\MockObject\\Matcher' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Matcher.php', 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', 'PHPUnit\\Framework\\MockObject\\Method' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Api/Method.php', 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php', 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MockBuilder' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockBuilder.php', 'PHPUnit\\Framework\\MockObject\\MockClass' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockClass.php', 'PHPUnit\\Framework\\MockObject\\MockMethod' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockMethod.php', 'PHPUnit\\Framework\\MockObject\\MockMethodSet' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockMethodSet.php', 'PHPUnit\\Framework\\MockObject\\MockObject' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockObject.php', 'PHPUnit\\Framework\\MockObject\\MockTrait' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockTrait.php', 'PHPUnit\\Framework\\MockObject\\MockType' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/MockType.php', 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', 'PHPUnit\\Framework\\MockObject\\ReflectionException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReflectionException.php', 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvocationOrder.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtIndex' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtIndex.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastOnce.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtMostCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/MethodName.php', 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php', 'PHPUnit\\Framework\\MockObject\\RuntimeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php', 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', 'PHPUnit\\Framework\\MockObject\\Stub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnArgument.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnCallback.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnReference.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnSelf.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php', 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownClassException.php', 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTraitException.php', 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTypeException.php', 'PHPUnit\\Framework\\MockObject\\Verifiable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/MockObject/Verifiable.php', 'PHPUnit\\Framework\\NoChildTestSuiteException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/NoChildTestSuiteException.php', 'PHPUnit\\Framework\\OutputError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/OutputError.php', 'PHPUnit\\Framework\\PHPTAssertionFailedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php', 'PHPUnit\\Framework\\Reorderable' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Reorderable.php', 'PHPUnit\\Framework\\RiskyTestError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/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/Exception/SkippedTestError.php', 'PHPUnit\\Framework\\SkippedTestSuiteError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/SkippedTestSuiteError.php', 'PHPUnit\\Framework\\SyntheticError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/SyntheticError.php', 'PHPUnit\\Framework\\SyntheticSkippedError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.php', 'PHPUnit\\Framework\\Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Test.php', 'PHPUnit\\Framework\\TestBuilder' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestBuilder.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\\TestListenerDefaultImplementation' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestListenerDefaultImplementation.php', 'PHPUnit\\Framework\\TestResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestResult.php', 'PHPUnit\\Framework\\TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuite.php', 'PHPUnit\\Framework\\TestSuiteIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/TestSuiteIterator.php', 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.php', 'PHPUnit\\Framework\\Warning' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/Warning.php', 'PHPUnit\\Framework\\WarningTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/WarningTestCase.php', 'PHPUnit\\Runner\\AfterIncompleteTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterIncompleteTestHook.php', 'PHPUnit\\Runner\\AfterLastTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterLastTestHook.php', 'PHPUnit\\Runner\\AfterRiskyTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterRiskyTestHook.php', 'PHPUnit\\Runner\\AfterSkippedTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterSkippedTestHook.php', 'PHPUnit\\Runner\\AfterSuccessfulTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterSuccessfulTestHook.php', 'PHPUnit\\Runner\\AfterTestErrorHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterTestErrorHook.php', 'PHPUnit\\Runner\\AfterTestFailureHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterTestFailureHook.php', 'PHPUnit\\Runner\\AfterTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterTestHook.php', 'PHPUnit\\Runner\\AfterTestWarningHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/AfterTestWarningHook.php', 'PHPUnit\\Runner\\BaseTestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/BaseTestRunner.php', 'PHPUnit\\Runner\\BeforeFirstTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/BeforeFirstTestHook.php', 'PHPUnit\\Runner\\BeforeTestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php', 'PHPUnit\\Runner\\DefaultTestResultCache' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/DefaultTestResultCache.php', 'PHPUnit\\Runner\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Exception.php', 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Extension/ExtensionHandler.php', 'PHPUnit\\Runner\\Extension\\PharLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Extension/PharLoader.php', 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\Factory' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/Factory.php', 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\IncludeGroupFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/IncludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\NameFilterIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Filter/NameFilterIterator.php', 'PHPUnit\\Runner\\Hook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/Hook.php', 'PHPUnit\\Runner\\NullTestResultCache' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/NullTestResultCache.php', 'PHPUnit\\Runner\\PhptTestCase' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/PhptTestCase.php', 'PHPUnit\\Runner\\ResultCacheExtension' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/ResultCacheExtension.php', 'PHPUnit\\Runner\\StandardTestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php', 'PHPUnit\\Runner\\TestHook' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/TestHook.php', 'PHPUnit\\Runner\\TestListenerAdapter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Hook/TestListenerAdapter.php', 'PHPUnit\\Runner\\TestResultCache' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestResultCache.php', 'PHPUnit\\Runner\\TestSuiteLoader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/TestSuiteSorter.php', 'PHPUnit\\Runner\\Version' => __DIR__ . '/..' . '/phpunit/phpunit/src/Runner/Version.php', 'PHPUnit\\TextUI\\CliArguments\\Builder' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Builder.php', 'PHPUnit\\TextUI\\CliArguments\\Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Configuration.php', 'PHPUnit\\TextUI\\CliArguments\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Exception.php', 'PHPUnit\\TextUI\\CliArguments\\Mapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/CliArguments/Mapper.php', 'PHPUnit\\TextUI\\Command' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Command.php', 'PHPUnit\\TextUI\\DefaultResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php', 'PHPUnit\\TextUI\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/Exception.php', 'PHPUnit\\TextUI\\Help' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Help.php', 'PHPUnit\\TextUI\\ReflectionException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/ReflectionException.php', 'PHPUnit\\TextUI\\ResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/ResultPrinter.php', 'PHPUnit\\TextUI\\RuntimeException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/RuntimeException.php', 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/TestDirectoryNotFoundException.php', 'PHPUnit\\TextUI\\TestFileNotFoundException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/Exception/TestFileNotFoundException.php', 'PHPUnit\\TextUI\\TestRunner' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/TestRunner.php', 'PHPUnit\\TextUI\\TestSuiteMapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/TestSuiteMapper.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Configuration.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Constant.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/Directory.php', 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Exception.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/Extension.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\File' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/File.php', 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Generator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Group.php', 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Groups.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Loader.php', 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Junit.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Logging.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TeamCity.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Html.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Text.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Text.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationException.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php', 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php', 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFile.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuite.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Variable.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => __DIR__ . '/..' . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', 'PHPUnit\\Util\\Annotation\\DocBlock' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Annotation/DocBlock.php', 'PHPUnit\\Util\\Annotation\\Registry' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Annotation/Registry.php', 'PHPUnit\\Util\\Blacklist' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Blacklist.php', 'PHPUnit\\Util\\Cloner' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Cloner.php', 'PHPUnit\\Util\\Color' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Color.php', 'PHPUnit\\Util\\ErrorHandler' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ErrorHandler.php', 'PHPUnit\\Util\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Exception.php', 'PHPUnit\\Util\\ExcludeList' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/ExcludeList.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\\GlobalState' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/GlobalState.php', 'PHPUnit\\Util\\InvalidDataSetException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/InvalidDataSetException.php', 'PHPUnit\\Util\\Json' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Json.php', 'PHPUnit\\Util\\Log\\JUnit' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/JUnit.php', 'PHPUnit\\Util\\Log\\TeamCity' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Log/TeamCity.php', 'PHPUnit\\Util\\PHP\\AbstractPhpProcess' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php', 'PHPUnit\\Util\\PHP\\DefaultPhpProcess' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/DefaultPhpProcess.php', 'PHPUnit\\Util\\PHP\\WindowsPhpProcess' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/PHP/WindowsPhpProcess.php', 'PHPUnit\\Util\\Printer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Printer.php', 'PHPUnit\\Util\\Reflection' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Reflection.php', 'PHPUnit\\Util\\RegularExpression' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/RegularExpression.php', 'PHPUnit\\Util\\Test' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Test.php', 'PHPUnit\\Util\\TestDox\\CliTestDoxPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php', 'PHPUnit\\Util\\TestDox\\HtmlResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.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\\TestDoxPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/TestDoxPrinter.php', 'PHPUnit\\Util\\TestDox\\TextResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/TextResultPrinter.php', 'PHPUnit\\Util\\TestDox\\XmlResultPrinter' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.php', 'PHPUnit\\Util\\TextTestListRenderer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/TextTestListRenderer.php', 'PHPUnit\\Util\\Type' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Type.php', 'PHPUnit\\Util\\VersionComparisonOperator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/VersionComparisonOperator.php', 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php', 'PHPUnit\\Util\\Xml' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml.php', 'PHPUnit\\Util\\XmlTestListRenderer' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/XmlTestListRenderer.php', 'PHPUnit\\Util\\Xml\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/Exception.php', 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/FailedSchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\Loader' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/Loader.php', 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\SchemaDetector' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SchemaDetector.php', 'PHPUnit\\Util\\Xml\\SchemaFinder' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SchemaFinder.php', 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SnapshotNodeList.php', 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/SuccessfulSchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\ValidationResult' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/ValidationResult.php', 'PHPUnit\\Util\\Xml\\Validator' => __DIR__ . '/..' . '/phpunit/phpunit/src/Util/Xml/Validator.php', 'PharIo\\Manifest\\Application' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Application.php', 'PharIo\\Manifest\\ApplicationName' => __DIR__ . '/..' . '/phar-io/manifest/src/values/ApplicationName.php', 'PharIo\\Manifest\\Author' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Author.php', 'PharIo\\Manifest\\AuthorCollection' => __DIR__ . '/..' . '/phar-io/manifest/src/values/AuthorCollection.php', 'PharIo\\Manifest\\AuthorCollectionIterator' => __DIR__ . '/..' . '/phar-io/manifest/src/values/AuthorCollectionIterator.php', 'PharIo\\Manifest\\AuthorElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/AuthorElement.php', 'PharIo\\Manifest\\AuthorElementCollection' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/AuthorElementCollection.php', 'PharIo\\Manifest\\BundledComponent' => __DIR__ . '/..' . '/phar-io/manifest/src/values/BundledComponent.php', 'PharIo\\Manifest\\BundledComponentCollection' => __DIR__ . '/..' . '/phar-io/manifest/src/values/BundledComponentCollection.php', 'PharIo\\Manifest\\BundledComponentCollectionIterator' => __DIR__ . '/..' . '/phar-io/manifest/src/values/BundledComponentCollectionIterator.php', 'PharIo\\Manifest\\BundlesElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/BundlesElement.php', 'PharIo\\Manifest\\ComponentElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ComponentElement.php', 'PharIo\\Manifest\\ComponentElementCollection' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ComponentElementCollection.php', 'PharIo\\Manifest\\ContainsElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ContainsElement.php', 'PharIo\\Manifest\\CopyrightElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/CopyrightElement.php', 'PharIo\\Manifest\\CopyrightInformation' => __DIR__ . '/..' . '/phar-io/manifest/src/values/CopyrightInformation.php', 'PharIo\\Manifest\\ElementCollection' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ElementCollection.php', 'PharIo\\Manifest\\ElementCollectionException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/ElementCollectionException.php', 'PharIo\\Manifest\\Email' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Email.php', 'PharIo\\Manifest\\Exception' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/Exception.php', 'PharIo\\Manifest\\ExtElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ExtElement.php', 'PharIo\\Manifest\\ExtElementCollection' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ExtElementCollection.php', 'PharIo\\Manifest\\Extension' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Extension.php', 'PharIo\\Manifest\\ExtensionElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ExtensionElement.php', 'PharIo\\Manifest\\InvalidApplicationNameException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/InvalidApplicationNameException.php', 'PharIo\\Manifest\\InvalidEmailException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/InvalidEmailException.php', 'PharIo\\Manifest\\InvalidUrlException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/InvalidUrlException.php', 'PharIo\\Manifest\\Library' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Library.php', 'PharIo\\Manifest\\License' => __DIR__ . '/..' . '/phar-io/manifest/src/values/License.php', 'PharIo\\Manifest\\LicenseElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/LicenseElement.php', 'PharIo\\Manifest\\Manifest' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Manifest.php', 'PharIo\\Manifest\\ManifestDocument' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ManifestDocument.php', 'PharIo\\Manifest\\ManifestDocumentException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/ManifestDocumentException.php', 'PharIo\\Manifest\\ManifestDocumentLoadingException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php', 'PharIo\\Manifest\\ManifestDocumentMapper' => __DIR__ . '/..' . '/phar-io/manifest/src/ManifestDocumentMapper.php', 'PharIo\\Manifest\\ManifestDocumentMapperException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php', 'PharIo\\Manifest\\ManifestElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/ManifestElement.php', 'PharIo\\Manifest\\ManifestElementException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/ManifestElementException.php', 'PharIo\\Manifest\\ManifestLoader' => __DIR__ . '/..' . '/phar-io/manifest/src/ManifestLoader.php', 'PharIo\\Manifest\\ManifestLoaderException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/ManifestLoaderException.php', 'PharIo\\Manifest\\ManifestSerializer' => __DIR__ . '/..' . '/phar-io/manifest/src/ManifestSerializer.php', 'PharIo\\Manifest\\NoEmailAddressException' => __DIR__ . '/..' . '/phar-io/manifest/src/exceptions/NoEmailAddressException.php', 'PharIo\\Manifest\\PhpElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/PhpElement.php', 'PharIo\\Manifest\\PhpExtensionRequirement' => __DIR__ . '/..' . '/phar-io/manifest/src/values/PhpExtensionRequirement.php', 'PharIo\\Manifest\\PhpVersionRequirement' => __DIR__ . '/..' . '/phar-io/manifest/src/values/PhpVersionRequirement.php', 'PharIo\\Manifest\\Requirement' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Requirement.php', 'PharIo\\Manifest\\RequirementCollection' => __DIR__ . '/..' . '/phar-io/manifest/src/values/RequirementCollection.php', 'PharIo\\Manifest\\RequirementCollectionIterator' => __DIR__ . '/..' . '/phar-io/manifest/src/values/RequirementCollectionIterator.php', 'PharIo\\Manifest\\RequiresElement' => __DIR__ . '/..' . '/phar-io/manifest/src/xml/RequiresElement.php', 'PharIo\\Manifest\\Type' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Type.php', 'PharIo\\Manifest\\Url' => __DIR__ . '/..' . '/phar-io/manifest/src/values/Url.php', 'PharIo\\Version\\AbstractVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/AbstractVersionConstraint.php', 'PharIo\\Version\\AndVersionConstraintGroup' => __DIR__ . '/..' . '/phar-io/version/src/constraints/AndVersionConstraintGroup.php', 'PharIo\\Version\\AnyVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/AnyVersionConstraint.php', 'PharIo\\Version\\BuildMetaData' => __DIR__ . '/..' . '/phar-io/version/src/BuildMetaData.php', 'PharIo\\Version\\ExactVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/ExactVersionConstraint.php', 'PharIo\\Version\\Exception' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/Exception.php', 'PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php', 'PharIo\\Version\\InvalidPreReleaseSuffixException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.php', 'PharIo\\Version\\InvalidVersionException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/InvalidVersionException.php', 'PharIo\\Version\\NoBuildMetaDataException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/NoBuildMetaDataException.php', 'PharIo\\Version\\NoPreReleaseSuffixException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/NoPreReleaseSuffixException.php', 'PharIo\\Version\\OrVersionConstraintGroup' => __DIR__ . '/..' . '/phar-io/version/src/constraints/OrVersionConstraintGroup.php', 'PharIo\\Version\\PreReleaseSuffix' => __DIR__ . '/..' . '/phar-io/version/src/PreReleaseSuffix.php', 'PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/SpecificMajorAndMinorVersionConstraint.php', 'PharIo\\Version\\SpecificMajorVersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/SpecificMajorVersionConstraint.php', 'PharIo\\Version\\UnsupportedVersionConstraintException' => __DIR__ . '/..' . '/phar-io/version/src/exceptions/UnsupportedVersionConstraintException.php', 'PharIo\\Version\\Version' => __DIR__ . '/..' . '/phar-io/version/src/Version.php', 'PharIo\\Version\\VersionConstraint' => __DIR__ . '/..' . '/phar-io/version/src/constraints/VersionConstraint.php', 'PharIo\\Version\\VersionConstraintParser' => __DIR__ . '/..' . '/phar-io/version/src/VersionConstraintParser.php', 'PharIo\\Version\\VersionConstraintValue' => __DIR__ . '/..' . '/phar-io/version/src/VersionConstraintValue.php', 'PharIo\\Version\\VersionNumber' => __DIR__ . '/..' . '/phar-io/version/src/VersionNumber.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\\Channel\\Frame' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Frame.php', 'PhpAmqpLib\\Channel\\Method' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Method.php', 'PhpAmqpLib\\Connection\\AMQPConnectionConfig' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionConfig.php', 'PhpAmqpLib\\Connection\\AMQPConnectionFactory' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionFactory.php', 'PhpAmqpLib\\Connection\\AMQPLazyConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.php', 'PhpAmqpLib\\Connection\\AMQPLazySSLConnection' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySSLConnection.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\\AbstractSignalHeartbeatSender' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/AbstractSignalHeartbeatSender.php', 'PhpAmqpLib\\Connection\\Heartbeat\\PCNTLHeartbeatSender' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.php', 'PhpAmqpLib\\Connection\\Heartbeat\\SIGHeartbeatSender' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/SIGHeartbeatSender.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\\AMQPConnectionBlockedException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionClosedException' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionClosedException.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\\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\\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\\AMQPBufferReader' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPBufferReader.php', 'PhpAmqpLib\\Wire\\AMQPByteStream' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPByteStream.php', 'PhpAmqpLib\\Wire\\AMQPDecimal' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.php', 'PhpAmqpLib\\Wire\\AMQPIOReader' => __DIR__ . '/..' . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPIOReader.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\\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', 'PhpParser\\Builder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder.php', 'PhpParser\\BuilderFactory' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php', 'PhpParser\\BuilderHelpers' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php', 'PhpParser\\Builder\\ClassConst' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php', 'PhpParser\\Builder\\Class_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Class_.php', 'PhpParser\\Builder\\Declaration' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Declaration.php', 'PhpParser\\Builder\\EnumCase' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php', 'PhpParser\\Builder\\Enum_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Enum_.php', 'PhpParser\\Builder\\FunctionLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php', 'PhpParser\\Builder\\Function_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Function_.php', 'PhpParser\\Builder\\Interface_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Interface_.php', 'PhpParser\\Builder\\Method' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Method.php', 'PhpParser\\Builder\\Namespace_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php', 'PhpParser\\Builder\\Param' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Param.php', 'PhpParser\\Builder\\Property' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Property.php', 'PhpParser\\Builder\\TraitUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php', 'PhpParser\\Builder\\TraitUseAdaptation' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php', 'PhpParser\\Builder\\Trait_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Trait_.php', 'PhpParser\\Builder\\Use_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Builder/Use_.php', 'PhpParser\\Comment' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Comment.php', 'PhpParser\\Comment\\Doc' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Comment/Doc.php', 'PhpParser\\ConstExprEvaluationException' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php', 'PhpParser\\ConstExprEvaluator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php', 'PhpParser\\Error' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Error.php', 'PhpParser\\ErrorHandler' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ErrorHandler.php', 'PhpParser\\ErrorHandler\\Collecting' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php', 'PhpParser\\ErrorHandler\\Throwing' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php', 'PhpParser\\Internal\\DiffElem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php', 'PhpParser\\Internal\\Differ' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/Differ.php', 'PhpParser\\Internal\\PrintableNewAnonClassNode' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php', 'PhpParser\\Internal\\TokenPolyfill' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php', 'PhpParser\\Internal\\TokenStream' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php', 'PhpParser\\JsonDecoder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/JsonDecoder.php', 'PhpParser\\Lexer' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer.php', 'PhpParser\\Lexer\\Emulative' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php', 'PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', 'PhpParser\\Modifiers' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Modifiers.php', 'PhpParser\\NameContext' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NameContext.php', 'PhpParser\\Node' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node.php', 'PhpParser\\NodeAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeAbstract.php', 'PhpParser\\NodeDumper' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeDumper.php', 'PhpParser\\NodeFinder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeFinder.php', 'PhpParser\\NodeTraverser' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeTraverser.php', 'PhpParser\\NodeTraverserInterface' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php', 'PhpParser\\NodeVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor.php', 'PhpParser\\NodeVisitorAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php', 'PhpParser\\NodeVisitor\\CloningVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php', 'PhpParser\\NodeVisitor\\CommentAnnotatingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php', 'PhpParser\\NodeVisitor\\FindingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php', 'PhpParser\\NodeVisitor\\FirstFindingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php', 'PhpParser\\NodeVisitor\\NameResolver' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php', 'PhpParser\\NodeVisitor\\NodeConnectingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php', 'PhpParser\\NodeVisitor\\ParentConnectingVisitor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php', 'PhpParser\\Node\\Arg' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Arg.php', 'PhpParser\\Node\\ArrayItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php', 'PhpParser\\Node\\Attribute' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Attribute.php', 'PhpParser\\Node\\AttributeGroup' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php', 'PhpParser\\Node\\ClosureUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php', 'PhpParser\\Node\\ComplexType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/ComplexType.php', 'PhpParser\\Node\\Const_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Const_.php', 'PhpParser\\Node\\DeclareItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/DeclareItem.php', 'PhpParser\\Node\\Expr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr.php', 'PhpParser\\Node\\Expr\\ArrayDimFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php', 'PhpParser\\Node\\Expr\\Array_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php', 'PhpParser\\Node\\Expr\\ArrowFunction' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php', 'PhpParser\\Node\\Expr\\Assign' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php', 'PhpParser\\Node\\Expr\\AssignOp' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php', 'PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', 'PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', 'PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', 'PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php', 'PhpParser\\Node\\Expr\\AssignOp\\Concat' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php', 'PhpParser\\Node\\Expr\\AssignOp\\Div' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php', 'PhpParser\\Node\\Expr\\AssignOp\\Minus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php', 'PhpParser\\Node\\Expr\\AssignOp\\Mod' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php', 'PhpParser\\Node\\Expr\\AssignOp\\Mul' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php', 'PhpParser\\Node\\Expr\\AssignOp\\Plus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php', 'PhpParser\\Node\\Expr\\AssignOp\\Pow' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php', 'PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', 'PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php', 'PhpParser\\Node\\Expr\\AssignRef' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php', 'PhpParser\\Node\\Expr\\BinaryOp' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Concat' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Div' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Equal' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Greater' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php', 'PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Identical' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php', 'PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', 'PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', 'PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Minus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Mod' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Mul' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php', 'PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php', 'PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Plus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Pow' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php', 'PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', 'PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php', 'PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php', 'PhpParser\\Node\\Expr\\BitwiseNot' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php', 'PhpParser\\Node\\Expr\\BooleanNot' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php', 'PhpParser\\Node\\Expr\\CallLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php', 'PhpParser\\Node\\Expr\\Cast' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php', 'PhpParser\\Node\\Expr\\Cast\\Array_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php', 'PhpParser\\Node\\Expr\\Cast\\Bool_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php', 'PhpParser\\Node\\Expr\\Cast\\Double' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php', 'PhpParser\\Node\\Expr\\Cast\\Int_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php', 'PhpParser\\Node\\Expr\\Cast\\Object_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php', 'PhpParser\\Node\\Expr\\Cast\\String_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php', 'PhpParser\\Node\\Expr\\Cast\\Unset_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php', 'PhpParser\\Node\\Expr\\ClassConstFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.php', 'PhpParser\\Node\\Expr\\Clone_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php', 'PhpParser\\Node\\Expr\\Closure' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php', 'PhpParser\\Node\\Expr\\ConstFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php', 'PhpParser\\Node\\Expr\\Empty_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php', 'PhpParser\\Node\\Expr\\Error' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php', 'PhpParser\\Node\\Expr\\ErrorSuppress' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php', 'PhpParser\\Node\\Expr\\Eval_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php', 'PhpParser\\Node\\Expr\\Exit_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php', 'PhpParser\\Node\\Expr\\FuncCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php', 'PhpParser\\Node\\Expr\\Include_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php', 'PhpParser\\Node\\Expr\\Instanceof_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php', 'PhpParser\\Node\\Expr\\Isset_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php', 'PhpParser\\Node\\Expr\\List_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php', 'PhpParser\\Node\\Expr\\Match_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php', 'PhpParser\\Node\\Expr\\MethodCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php', 'PhpParser\\Node\\Expr\\New_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php', 'PhpParser\\Node\\Expr\\NullsafeMethodCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php', 'PhpParser\\Node\\Expr\\NullsafePropertyFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php', 'PhpParser\\Node\\Expr\\PostDec' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php', 'PhpParser\\Node\\Expr\\PostInc' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php', 'PhpParser\\Node\\Expr\\PreDec' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php', 'PhpParser\\Node\\Expr\\PreInc' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php', 'PhpParser\\Node\\Expr\\Print_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php', 'PhpParser\\Node\\Expr\\PropertyFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php', 'PhpParser\\Node\\Expr\\ShellExec' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php', 'PhpParser\\Node\\Expr\\StaticCall' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php', 'PhpParser\\Node\\Expr\\StaticPropertyFetch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php', 'PhpParser\\Node\\Expr\\Ternary' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php', 'PhpParser\\Node\\Expr\\Throw_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php', 'PhpParser\\Node\\Expr\\UnaryMinus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php', 'PhpParser\\Node\\Expr\\UnaryPlus' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php', 'PhpParser\\Node\\Expr\\Variable' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php', 'PhpParser\\Node\\Expr\\YieldFrom' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php', 'PhpParser\\Node\\Expr\\Yield_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php', 'PhpParser\\Node\\FunctionLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php', 'PhpParser\\Node\\Identifier' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Identifier.php', 'PhpParser\\Node\\InterpolatedStringPart' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php', 'PhpParser\\Node\\IntersectionType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php', 'PhpParser\\Node\\MatchArm' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/MatchArm.php', 'PhpParser\\Node\\Name' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Name.php', 'PhpParser\\Node\\Name\\FullyQualified' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php', 'PhpParser\\Node\\Name\\Relative' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php', 'PhpParser\\Node\\NullableType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/NullableType.php', 'PhpParser\\Node\\Param' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Param.php', 'PhpParser\\Node\\PropertyItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php', 'PhpParser\\Node\\Scalar' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar.php', 'PhpParser\\Node\\Scalar\\Float_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.php', 'PhpParser\\Node\\Scalar\\Int_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.php', 'PhpParser\\Node\\Scalar\\InterpolatedString' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php', 'PhpParser\\Node\\Scalar\\MagicConst' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Class_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Dir' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php', 'PhpParser\\Node\\Scalar\\MagicConst\\File' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Function_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Line' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Method' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php', 'PhpParser\\Node\\Scalar\\String_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php', 'PhpParser\\Node\\StaticVar' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/StaticVar.php', 'PhpParser\\Node\\Stmt' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt.php', 'PhpParser\\Node\\Stmt\\Block' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php', 'PhpParser\\Node\\Stmt\\Break_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php', 'PhpParser\\Node\\Stmt\\Case_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php', 'PhpParser\\Node\\Stmt\\Catch_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php', 'PhpParser\\Node\\Stmt\\ClassConst' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php', 'PhpParser\\Node\\Stmt\\ClassLike' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php', 'PhpParser\\Node\\Stmt\\ClassMethod' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php', 'PhpParser\\Node\\Stmt\\Class_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php', 'PhpParser\\Node\\Stmt\\Const_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php', 'PhpParser\\Node\\Stmt\\Continue_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php', 'PhpParser\\Node\\Stmt\\Declare_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php', 'PhpParser\\Node\\Stmt\\Do_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php', 'PhpParser\\Node\\Stmt\\Echo_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php', 'PhpParser\\Node\\Stmt\\ElseIf_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php', 'PhpParser\\Node\\Stmt\\Else_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php', 'PhpParser\\Node\\Stmt\\EnumCase' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.php', 'PhpParser\\Node\\Stmt\\Enum_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php', 'PhpParser\\Node\\Stmt\\Expression' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php', 'PhpParser\\Node\\Stmt\\Finally_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php', 'PhpParser\\Node\\Stmt\\For_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php', 'PhpParser\\Node\\Stmt\\Foreach_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php', 'PhpParser\\Node\\Stmt\\Function_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php', 'PhpParser\\Node\\Stmt\\Global_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php', 'PhpParser\\Node\\Stmt\\Goto_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php', 'PhpParser\\Node\\Stmt\\GroupUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php', 'PhpParser\\Node\\Stmt\\HaltCompiler' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php', 'PhpParser\\Node\\Stmt\\If_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php', 'PhpParser\\Node\\Stmt\\InlineHTML' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php', 'PhpParser\\Node\\Stmt\\Interface_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php', 'PhpParser\\Node\\Stmt\\Label' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php', 'PhpParser\\Node\\Stmt\\Namespace_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php', 'PhpParser\\Node\\Stmt\\Nop' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php', 'PhpParser\\Node\\Stmt\\Property' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php', 'PhpParser\\Node\\Stmt\\Return_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php', 'PhpParser\\Node\\Stmt\\Static_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php', 'PhpParser\\Node\\Stmt\\Switch_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php', 'PhpParser\\Node\\Stmt\\TraitUse' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php', 'PhpParser\\Node\\Stmt\\TraitUseAdaptation' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php', 'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', 'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', 'PhpParser\\Node\\Stmt\\Trait_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php', 'PhpParser\\Node\\Stmt\\TryCatch' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php', 'PhpParser\\Node\\Stmt\\Unset_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php', 'PhpParser\\Node\\Stmt\\Use_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php', 'PhpParser\\Node\\Stmt\\While_' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php', 'PhpParser\\Node\\UnionType' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/UnionType.php', 'PhpParser\\Node\\UseItem' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/UseItem.php', 'PhpParser\\Node\\VarLikeIdentifier' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php', 'PhpParser\\Node\\VariadicPlaceholder' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php', 'PhpParser\\Parser' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Parser.php', 'PhpParser\\ParserAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ParserAbstract.php', 'PhpParser\\ParserFactory' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/ParserFactory.php', 'PhpParser\\Parser\\Php7' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Parser/Php7.php', 'PhpParser\\Parser\\Php8' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Parser/Php8.php', 'PhpParser\\PhpVersion' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PhpVersion.php', 'PhpParser\\PrettyPrinter' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PrettyPrinter.php', 'PhpParser\\PrettyPrinterAbstract' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php', 'PhpParser\\PrettyPrinter\\Standard' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php', 'PhpParser\\Token' => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser/Token.php', 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'Predis\\Autoloader' => __DIR__ . '/..' . '/predis/predis/src/Autoloader.php', 'Predis\\Client' => __DIR__ . '/..' . '/predis/predis/src/Client.php', 'Predis\\ClientConfiguration' => __DIR__ . '/..' . '/predis/predis/src/ClientConfiguration.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\\Hash\\PhpiredisCRC16' => __DIR__ . '/..' . '/predis/predis/src/Cluster/Hash/PhpiredisCRC16.php', 'Predis\\Cluster\\PredisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/PredisStrategy.php', 'Predis\\Cluster\\RedisStrategy' => __DIR__ . '/..' . '/predis/predis/src/Cluster/RedisStrategy.php', 'Predis\\Cluster\\SlotMap' => __DIR__ . '/..' . '/predis/predis/src/Cluster/SlotMap.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\\Argument\\ArrayableArgument' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/ArrayableArgument.php', 'Predis\\Command\\Argument\\Geospatial\\AbstractBy' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/AbstractBy.php', 'Predis\\Command\\Argument\\Geospatial\\ByBox' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/ByBox.php', 'Predis\\Command\\Argument\\Geospatial\\ByInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/ByInterface.php', 'Predis\\Command\\Argument\\Geospatial\\ByRadius' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/ByRadius.php', 'Predis\\Command\\Argument\\Geospatial\\FromInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/FromInterface.php', 'Predis\\Command\\Argument\\Geospatial\\FromLonLat' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/FromLonLat.php', 'Predis\\Command\\Argument\\Geospatial\\FromMember' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Geospatial/FromMember.php', 'Predis\\Command\\Argument\\Search\\AggregateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/AggregateArguments.php', 'Predis\\Command\\Argument\\Search\\AlterArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/AlterArguments.php', 'Predis\\Command\\Argument\\Search\\CommonArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/CommonArguments.php', 'Predis\\Command\\Argument\\Search\\CreateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/CreateArguments.php', 'Predis\\Command\\Argument\\Search\\CursorArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/CursorArguments.php', 'Predis\\Command\\Argument\\Search\\DropArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/DropArguments.php', 'Predis\\Command\\Argument\\Search\\ExplainArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/ExplainArguments.php', 'Predis\\Command\\Argument\\Search\\ProfileArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/ProfileArguments.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\AbstractField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/AbstractField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\FieldInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/FieldInterface.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\GeoField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/GeoField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\NumericField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/NumericField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\TagField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/TagField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\TextField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/TextField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\VectorField' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SchemaFields/VectorField.php', 'Predis\\Command\\Argument\\Search\\SearchArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SearchArguments.php', 'Predis\\Command\\Argument\\Search\\SpellcheckArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SpellcheckArguments.php', 'Predis\\Command\\Argument\\Search\\SugAddArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SugAddArguments.php', 'Predis\\Command\\Argument\\Search\\SugGetArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SugGetArguments.php', 'Predis\\Command\\Argument\\Search\\SynUpdateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Search/SynUpdateArguments.php', 'Predis\\Command\\Argument\\Server\\LimitInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Server/LimitInterface.php', 'Predis\\Command\\Argument\\Server\\LimitOffsetCount' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Server/LimitOffsetCount.php', 'Predis\\Command\\Argument\\Server\\To' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/Server/To.php', 'Predis\\Command\\Argument\\TimeSeries\\AddArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/AddArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\AlterArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/AlterArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\CommonArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/CommonArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\CreateArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/CreateArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\DecrByArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/DecrByArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\GetArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/GetArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\IncrByArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/IncrByArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\InfoArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/InfoArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\MGetArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/MGetArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\MRangeArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/MRangeArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\RangeArguments' => __DIR__ . '/..' . '/predis/predis/src/Command/Argument/TimeSeries/RangeArguments.php', 'Predis\\Command\\Command' => __DIR__ . '/..' . '/predis/predis/src/Command/Command.php', 'Predis\\Command\\CommandInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/CommandInterface.php', 'Predis\\Command\\Factory' => __DIR__ . '/..' . '/predis/predis/src/Command/Factory.php', 'Predis\\Command\\FactoryInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/FactoryInterface.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\\RawCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/RawCommand.php', 'Predis\\Command\\RawFactory' => __DIR__ . '/..' . '/predis/predis/src/Command/RawFactory.php', 'Predis\\Command\\RedisFactory' => __DIR__ . '/..' . '/predis/predis/src/Command/RedisFactory.php', 'Predis\\Command\\Redis\\ACL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ACL.php', 'Predis\\Command\\Redis\\APPEND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/APPEND.php', 'Predis\\Command\\Redis\\AUTH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/AUTH.php', 'Predis\\Command\\Redis\\AbstractCommand\\BZPOPBase' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/AbstractCommand/BZPOPBase.php', 'Predis\\Command\\Redis\\BGREWRITEAOF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BGREWRITEAOF.php', 'Predis\\Command\\Redis\\BGSAVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BGSAVE.php', 'Predis\\Command\\Redis\\BITCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITCOUNT.php', 'Predis\\Command\\Redis\\BITFIELD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITFIELD.php', 'Predis\\Command\\Redis\\BITOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITOP.php', 'Predis\\Command\\Redis\\BITPOS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BITPOS.php', 'Predis\\Command\\Redis\\BLMOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BLMOVE.php', 'Predis\\Command\\Redis\\BLMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BLMPOP.php', 'Predis\\Command\\Redis\\BLPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BLPOP.php', 'Predis\\Command\\Redis\\BRPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BRPOP.php', 'Predis\\Command\\Redis\\BRPOPLPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BRPOPLPUSH.php', 'Predis\\Command\\Redis\\BZMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BZMPOP.php', 'Predis\\Command\\Redis\\BZPOPMAX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BZPOPMAX.php', 'Predis\\Command\\Redis\\BZPOPMIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BZPOPMIN.php', 'Predis\\Command\\Redis\\BloomFilter\\BFADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFADD.php', 'Predis\\Command\\Redis\\BloomFilter\\BFEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFEXISTS.php', 'Predis\\Command\\Redis\\BloomFilter\\BFINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFINFO.php', 'Predis\\Command\\Redis\\BloomFilter\\BFINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFINSERT.php', 'Predis\\Command\\Redis\\BloomFilter\\BFLOADCHUNK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFLOADCHUNK.php', 'Predis\\Command\\Redis\\BloomFilter\\BFMADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFMADD.php', 'Predis\\Command\\Redis\\BloomFilter\\BFMEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFMEXISTS.php', 'Predis\\Command\\Redis\\BloomFilter\\BFRESERVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFRESERVE.php', 'Predis\\Command\\Redis\\BloomFilter\\BFSCANDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/BloomFilter/BFSCANDUMP.php', 'Predis\\Command\\Redis\\CLIENT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CLIENT.php', 'Predis\\Command\\Redis\\CLUSTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CLUSTER.php', 'Predis\\Command\\Redis\\COMMAND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/COMMAND.php', 'Predis\\Command\\Redis\\CONFIG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CONFIG.php', 'Predis\\Command\\Redis\\COPY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/COPY.php', 'Predis\\Command\\Redis\\Container\\ACL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/ACL.php', 'Predis\\Command\\Redis\\Container\\AbstractContainer' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/AbstractContainer.php', 'Predis\\Command\\Redis\\Container\\CLUSTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/CLUSTER.php', 'Predis\\Command\\Redis\\Container\\ContainerFactory' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/ContainerFactory.php', 'Predis\\Command\\Redis\\Container\\ContainerInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/ContainerInterface.php', 'Predis\\Command\\Redis\\Container\\FunctionContainer' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/FunctionContainer.php', 'Predis\\Command\\Redis\\Container\\Json\\JSONDEBUG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/Json/JSONDEBUG.php', 'Predis\\Command\\Redis\\Container\\Search\\FTCONFIG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/Search/FTCONFIG.php', 'Predis\\Command\\Redis\\Container\\Search\\FTCURSOR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Container/Search/FTCURSOR.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINCRBY.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINFO.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYDIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYDIM.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYPROB' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYPROB.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSMERGE.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSQUERY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CountMinSketch/CMSQUERY.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFADD.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFADDNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFADDNX.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFCOUNT.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFDEL.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFEXISTS.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFINFO.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERT.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFINSERTNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERTNX.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFLOADCHUNK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFLOADCHUNK.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFMEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFMEXISTS.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFRESERVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFRESERVE.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFSCANDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/CuckooFilter/CFSCANDUMP.php', 'Predis\\Command\\Redis\\DBSIZE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DBSIZE.php', 'Predis\\Command\\Redis\\DECR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DECR.php', 'Predis\\Command\\Redis\\DECRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DECRBY.php', 'Predis\\Command\\Redis\\DEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DEL.php', 'Predis\\Command\\Redis\\DISCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DISCARD.php', 'Predis\\Command\\Redis\\DUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/DUMP.php', 'Predis\\Command\\Redis\\ECHO_' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ECHO_.php', 'Predis\\Command\\Redis\\EVALSHA' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVALSHA.php', 'Predis\\Command\\Redis\\EVALSHA_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVALSHA_RO.php', 'Predis\\Command\\Redis\\EVAL_' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVAL_.php', 'Predis\\Command\\Redis\\EVAL_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EVAL_RO.php', 'Predis\\Command\\Redis\\EXEC' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXEC.php', 'Predis\\Command\\Redis\\EXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXISTS.php', 'Predis\\Command\\Redis\\EXPIRE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXPIRE.php', 'Predis\\Command\\Redis\\EXPIREAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXPIREAT.php', 'Predis\\Command\\Redis\\EXPIRETIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/EXPIRETIME.php', 'Predis\\Command\\Redis\\FAILOVER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FAILOVER.php', 'Predis\\Command\\Redis\\FCALL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FCALL.php', 'Predis\\Command\\Redis\\FCALL_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FCALL_RO.php', 'Predis\\Command\\Redis\\FLUSHALL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FLUSHALL.php', 'Predis\\Command\\Redis\\FLUSHDB' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FLUSHDB.php', 'Predis\\Command\\Redis\\FUNCTIONS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/FUNCTIONS.php', 'Predis\\Command\\Redis\\GEOADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOADD.php', 'Predis\\Command\\Redis\\GEODIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEODIST.php', 'Predis\\Command\\Redis\\GEOHASH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOHASH.php', 'Predis\\Command\\Redis\\GEOPOS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOPOS.php', 'Predis\\Command\\Redis\\GEORADIUS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEORADIUS.php', 'Predis\\Command\\Redis\\GEORADIUSBYMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEORADIUSBYMEMBER.php', 'Predis\\Command\\Redis\\GEOSEARCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOSEARCH.php', 'Predis\\Command\\Redis\\GEOSEARCHSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GEOSEARCHSTORE.php', 'Predis\\Command\\Redis\\GET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GET.php', 'Predis\\Command\\Redis\\GETBIT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETBIT.php', 'Predis\\Command\\Redis\\GETDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETDEL.php', 'Predis\\Command\\Redis\\GETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETEX.php', 'Predis\\Command\\Redis\\GETRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETRANGE.php', 'Predis\\Command\\Redis\\GETSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/GETSET.php', 'Predis\\Command\\Redis\\HDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HDEL.php', 'Predis\\Command\\Redis\\HEXISTS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HEXISTS.php', 'Predis\\Command\\Redis\\HGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HGET.php', 'Predis\\Command\\Redis\\HGETALL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HGETALL.php', 'Predis\\Command\\Redis\\HINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HINCRBY.php', 'Predis\\Command\\Redis\\HINCRBYFLOAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HINCRBYFLOAT.php', 'Predis\\Command\\Redis\\HKEYS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HKEYS.php', 'Predis\\Command\\Redis\\HLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HLEN.php', 'Predis\\Command\\Redis\\HMGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HMGET.php', 'Predis\\Command\\Redis\\HMSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HMSET.php', 'Predis\\Command\\Redis\\HRANDFIELD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HRANDFIELD.php', 'Predis\\Command\\Redis\\HSCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSCAN.php', 'Predis\\Command\\Redis\\HSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSET.php', 'Predis\\Command\\Redis\\HSETNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSETNX.php', 'Predis\\Command\\Redis\\HSTRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HSTRLEN.php', 'Predis\\Command\\Redis\\HVALS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/HVALS.php', 'Predis\\Command\\Redis\\INCR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INCR.php', 'Predis\\Command\\Redis\\INCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INCRBY.php', 'Predis\\Command\\Redis\\INCRBYFLOAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INCRBYFLOAT.php', 'Predis\\Command\\Redis\\INFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/INFO.php', 'Predis\\Command\\Redis\\Json\\JSONARRAPPEND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRAPPEND.php', 'Predis\\Command\\Redis\\Json\\JSONARRINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRINDEX.php', 'Predis\\Command\\Redis\\Json\\JSONARRINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRINSERT.php', 'Predis\\Command\\Redis\\Json\\JSONARRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRLEN.php', 'Predis\\Command\\Redis\\Json\\JSONARRPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRPOP.php', 'Predis\\Command\\Redis\\Json\\JSONARRTRIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONARRTRIM.php', 'Predis\\Command\\Redis\\Json\\JSONCLEAR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONCLEAR.php', 'Predis\\Command\\Redis\\Json\\JSONDEBUG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONDEBUG.php', 'Predis\\Command\\Redis\\Json\\JSONDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONDEL.php', 'Predis\\Command\\Redis\\Json\\JSONFORGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONFORGET.php', 'Predis\\Command\\Redis\\Json\\JSONGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONGET.php', 'Predis\\Command\\Redis\\Json\\JSONMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONMERGE.php', 'Predis\\Command\\Redis\\Json\\JSONMGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONMGET.php', 'Predis\\Command\\Redis\\Json\\JSONMSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONMSET.php', 'Predis\\Command\\Redis\\Json\\JSONNUMINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONNUMINCRBY.php', 'Predis\\Command\\Redis\\Json\\JSONOBJKEYS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONOBJKEYS.php', 'Predis\\Command\\Redis\\Json\\JSONOBJLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONOBJLEN.php', 'Predis\\Command\\Redis\\Json\\JSONRESP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONRESP.php', 'Predis\\Command\\Redis\\Json\\JSONSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONSET.php', 'Predis\\Command\\Redis\\Json\\JSONSTRAPPEND' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONSTRAPPEND.php', 'Predis\\Command\\Redis\\Json\\JSONSTRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONSTRLEN.php', 'Predis\\Command\\Redis\\Json\\JSONTOGGLE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONTOGGLE.php', 'Predis\\Command\\Redis\\Json\\JSONTYPE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Json/JSONTYPE.php', 'Predis\\Command\\Redis\\KEYS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/KEYS.php', 'Predis\\Command\\Redis\\LASTSAVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LASTSAVE.php', 'Predis\\Command\\Redis\\LCS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LCS.php', 'Predis\\Command\\Redis\\LINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LINDEX.php', 'Predis\\Command\\Redis\\LINSERT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LINSERT.php', 'Predis\\Command\\Redis\\LLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LLEN.php', 'Predis\\Command\\Redis\\LMOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LMOVE.php', 'Predis\\Command\\Redis\\LMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LMPOP.php', 'Predis\\Command\\Redis\\LPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LPOP.php', 'Predis\\Command\\Redis\\LPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LPUSH.php', 'Predis\\Command\\Redis\\LPUSHX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LPUSHX.php', 'Predis\\Command\\Redis\\LRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LRANGE.php', 'Predis\\Command\\Redis\\LREM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LREM.php', 'Predis\\Command\\Redis\\LSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LSET.php', 'Predis\\Command\\Redis\\LTRIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/LTRIM.php', 'Predis\\Command\\Redis\\MGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MGET.php', 'Predis\\Command\\Redis\\MIGRATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MIGRATE.php', 'Predis\\Command\\Redis\\MONITOR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MONITOR.php', 'Predis\\Command\\Redis\\MOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MOVE.php', 'Predis\\Command\\Redis\\MSET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MSET.php', 'Predis\\Command\\Redis\\MSETNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MSETNX.php', 'Predis\\Command\\Redis\\MULTI' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/MULTI.php', 'Predis\\Command\\Redis\\OBJECT_' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/OBJECT_.php', 'Predis\\Command\\Redis\\PERSIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PERSIST.php', 'Predis\\Command\\Redis\\PEXPIRE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PEXPIRE.php', 'Predis\\Command\\Redis\\PEXPIREAT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PEXPIREAT.php', 'Predis\\Command\\Redis\\PEXPIRETIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PEXPIRETIME.php', 'Predis\\Command\\Redis\\PFADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PFADD.php', 'Predis\\Command\\Redis\\PFCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PFCOUNT.php', 'Predis\\Command\\Redis\\PFMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PFMERGE.php', 'Predis\\Command\\Redis\\PING' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PING.php', 'Predis\\Command\\Redis\\PSETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PSETEX.php', 'Predis\\Command\\Redis\\PSUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PSUBSCRIBE.php', 'Predis\\Command\\Redis\\PTTL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PTTL.php', 'Predis\\Command\\Redis\\PUBLISH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PUBLISH.php', 'Predis\\Command\\Redis\\PUBSUB' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PUBSUB.php', 'Predis\\Command\\Redis\\PUNSUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/PUNSUBSCRIBE.php', 'Predis\\Command\\Redis\\QUIT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/QUIT.php', 'Predis\\Command\\Redis\\RANDOMKEY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RANDOMKEY.php', 'Predis\\Command\\Redis\\RENAME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RENAME.php', 'Predis\\Command\\Redis\\RENAMENX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RENAMENX.php', 'Predis\\Command\\Redis\\RESTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RESTORE.php', 'Predis\\Command\\Redis\\RPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPOP.php', 'Predis\\Command\\Redis\\RPOPLPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPOPLPUSH.php', 'Predis\\Command\\Redis\\RPUSH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPUSH.php', 'Predis\\Command\\Redis\\RPUSHX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/RPUSHX.php', 'Predis\\Command\\Redis\\SADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SADD.php', 'Predis\\Command\\Redis\\SAVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SAVE.php', 'Predis\\Command\\Redis\\SCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SCAN.php', 'Predis\\Command\\Redis\\SCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SCARD.php', 'Predis\\Command\\Redis\\SCRIPT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SCRIPT.php', 'Predis\\Command\\Redis\\SDIFF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SDIFF.php', 'Predis\\Command\\Redis\\SDIFFSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SDIFFSTORE.php', 'Predis\\Command\\Redis\\SELECT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SELECT.php', 'Predis\\Command\\Redis\\SENTINEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SENTINEL.php', 'Predis\\Command\\Redis\\SET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SET.php', 'Predis\\Command\\Redis\\SETBIT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETBIT.php', 'Predis\\Command\\Redis\\SETEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETEX.php', 'Predis\\Command\\Redis\\SETNX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETNX.php', 'Predis\\Command\\Redis\\SETRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SETRANGE.php', 'Predis\\Command\\Redis\\SHUTDOWN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SHUTDOWN.php', 'Predis\\Command\\Redis\\SINTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SINTER.php', 'Predis\\Command\\Redis\\SINTERCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SINTERCARD.php', 'Predis\\Command\\Redis\\SINTERSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SINTERSTORE.php', 'Predis\\Command\\Redis\\SISMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SISMEMBER.php', 'Predis\\Command\\Redis\\SLAVEOF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SLAVEOF.php', 'Predis\\Command\\Redis\\SLOWLOG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SLOWLOG.php', 'Predis\\Command\\Redis\\SMEMBERS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SMEMBERS.php', 'Predis\\Command\\Redis\\SMISMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SMISMEMBER.php', 'Predis\\Command\\Redis\\SMOVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SMOVE.php', 'Predis\\Command\\Redis\\SORT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SORT.php', 'Predis\\Command\\Redis\\SORT_RO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SORT_RO.php', 'Predis\\Command\\Redis\\SPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SPOP.php', 'Predis\\Command\\Redis\\SRANDMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SRANDMEMBER.php', 'Predis\\Command\\Redis\\SREM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SREM.php', 'Predis\\Command\\Redis\\SSCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SSCAN.php', 'Predis\\Command\\Redis\\STRLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/STRLEN.php', 'Predis\\Command\\Redis\\SUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUBSCRIBE.php', 'Predis\\Command\\Redis\\SUBSTR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUBSTR.php', 'Predis\\Command\\Redis\\SUNION' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUNION.php', 'Predis\\Command\\Redis\\SUNIONSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/SUNIONSTORE.php', 'Predis\\Command\\Redis\\Search\\FTAGGREGATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTAGGREGATE.php', 'Predis\\Command\\Redis\\Search\\FTALIASADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALIASADD.php', 'Predis\\Command\\Redis\\Search\\FTALIASDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALIASDEL.php', 'Predis\\Command\\Redis\\Search\\FTALIASUPDATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALIASUPDATE.php', 'Predis\\Command\\Redis\\Search\\FTALTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTALTER.php', 'Predis\\Command\\Redis\\Search\\FTCONFIG' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTCONFIG.php', 'Predis\\Command\\Redis\\Search\\FTCREATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTCREATE.php', 'Predis\\Command\\Redis\\Search\\FTCURSOR' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTCURSOR.php', 'Predis\\Command\\Redis\\Search\\FTDICTADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDICTADD.php', 'Predis\\Command\\Redis\\Search\\FTDICTDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDICTDEL.php', 'Predis\\Command\\Redis\\Search\\FTDICTDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDICTDUMP.php', 'Predis\\Command\\Redis\\Search\\FTDROPINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTDROPINDEX.php', 'Predis\\Command\\Redis\\Search\\FTEXPLAIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTEXPLAIN.php', 'Predis\\Command\\Redis\\Search\\FTINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTINFO.php', 'Predis\\Command\\Redis\\Search\\FTPROFILE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTPROFILE.php', 'Predis\\Command\\Redis\\Search\\FTSEARCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSEARCH.php', 'Predis\\Command\\Redis\\Search\\FTSPELLCHECK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSPELLCHECK.php', 'Predis\\Command\\Redis\\Search\\FTSUGADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGADD.php', 'Predis\\Command\\Redis\\Search\\FTSUGDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGDEL.php', 'Predis\\Command\\Redis\\Search\\FTSUGGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGGET.php', 'Predis\\Command\\Redis\\Search\\FTSUGLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSUGLEN.php', 'Predis\\Command\\Redis\\Search\\FTSYNDUMP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSYNDUMP.php', 'Predis\\Command\\Redis\\Search\\FTSYNUPDATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTSYNUPDATE.php', 'Predis\\Command\\Redis\\Search\\FTTAGVALS' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/Search/FTTAGVALS.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTADD.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTBYRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTBYREVRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYREVRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTCDF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCDF.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTCREATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCREATE.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTINFO.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTMAX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMAX.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTMERGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMERGE.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTMIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMIN.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTQUANTILE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTQUANTILE.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTRESET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRESET.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTREVRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTREVRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTTRIMMED_MEAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TDigest/TDIGESTTRIMMED_MEAN.php', 'Predis\\Command\\Redis\\TIME' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TIME.php', 'Predis\\Command\\Redis\\TOUCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TOUCH.php', 'Predis\\Command\\Redis\\TTL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TTL.php', 'Predis\\Command\\Redis\\TYPE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TYPE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSADD.php', 'Predis\\Command\\Redis\\TimeSeries\\TSALTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSALTER.php', 'Predis\\Command\\Redis\\TimeSeries\\TSCREATE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSCREATERULE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATERULE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSDECRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSDECRBY.php', 'Predis\\Command\\Redis\\TimeSeries\\TSDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSDEL.php', 'Predis\\Command\\Redis\\TimeSeries\\TSDELETERULE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSDELETERULE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSGET.php', 'Predis\\Command\\Redis\\TimeSeries\\TSINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSINCRBY.php', 'Predis\\Command\\Redis\\TimeSeries\\TSINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSINFO.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMADD.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMGET' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMGET.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMRANGE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSMREVRANGE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSQUERYINDEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSQUERYINDEX.php', 'Predis\\Command\\Redis\\TimeSeries\\TSRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSRANGE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TimeSeries/TSREVRANGE.php', 'Predis\\Command\\Redis\\TopK\\TOPKADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKADD.php', 'Predis\\Command\\Redis\\TopK\\TOPKINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKINCRBY.php', 'Predis\\Command\\Redis\\TopK\\TOPKINFO' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKINFO.php', 'Predis\\Command\\Redis\\TopK\\TOPKLIST' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKLIST.php', 'Predis\\Command\\Redis\\TopK\\TOPKQUERY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKQUERY.php', 'Predis\\Command\\Redis\\TopK\\TOPKRESERVE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/TopK/TOPKRESERVE.php', 'Predis\\Command\\Redis\\UNSUBSCRIBE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/UNSUBSCRIBE.php', 'Predis\\Command\\Redis\\UNWATCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/UNWATCH.php', 'Predis\\Command\\Redis\\WAITAOF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/WAITAOF.php', 'Predis\\Command\\Redis\\WATCH' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/WATCH.php', 'Predis\\Command\\Redis\\XADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XADD.php', 'Predis\\Command\\Redis\\XDEL' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XDEL.php', 'Predis\\Command\\Redis\\XLEN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XLEN.php', 'Predis\\Command\\Redis\\XRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XRANGE.php', 'Predis\\Command\\Redis\\XREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XREVRANGE.php', 'Predis\\Command\\Redis\\XTRIM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/XTRIM.php', 'Predis\\Command\\Redis\\ZADD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZADD.php', 'Predis\\Command\\Redis\\ZCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZCARD.php', 'Predis\\Command\\Redis\\ZCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZCOUNT.php', 'Predis\\Command\\Redis\\ZDIFF' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZDIFF.php', 'Predis\\Command\\Redis\\ZDIFFSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZDIFFSTORE.php', 'Predis\\Command\\Redis\\ZINCRBY' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINCRBY.php', 'Predis\\Command\\Redis\\ZINTER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINTER.php', 'Predis\\Command\\Redis\\ZINTERCARD' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINTERCARD.php', 'Predis\\Command\\Redis\\ZINTERSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZINTERSTORE.php', 'Predis\\Command\\Redis\\ZLEXCOUNT' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZLEXCOUNT.php', 'Predis\\Command\\Redis\\ZMPOP' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZMPOP.php', 'Predis\\Command\\Redis\\ZMSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZMSCORE.php', 'Predis\\Command\\Redis\\ZPOPMAX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZPOPMAX.php', 'Predis\\Command\\Redis\\ZPOPMIN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZPOPMIN.php', 'Predis\\Command\\Redis\\ZRANDMEMBER' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANDMEMBER.php', 'Predis\\Command\\Redis\\ZRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGE.php', 'Predis\\Command\\Redis\\ZRANGEBYLEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGEBYLEX.php', 'Predis\\Command\\Redis\\ZRANGEBYSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGEBYSCORE.php', 'Predis\\Command\\Redis\\ZRANGESTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANGESTORE.php', 'Predis\\Command\\Redis\\ZRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZRANK.php', 'Predis\\Command\\Redis\\ZREM' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREM.php', 'Predis\\Command\\Redis\\ZREMRANGEBYLEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREMRANGEBYLEX.php', 'Predis\\Command\\Redis\\ZREMRANGEBYRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREMRANGEBYRANK.php', 'Predis\\Command\\Redis\\ZREMRANGEBYSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREMRANGEBYSCORE.php', 'Predis\\Command\\Redis\\ZREVRANGE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANGE.php', 'Predis\\Command\\Redis\\ZREVRANGEBYLEX' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANGEBYLEX.php', 'Predis\\Command\\Redis\\ZREVRANGEBYSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANGEBYSCORE.php', 'Predis\\Command\\Redis\\ZREVRANK' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZREVRANK.php', 'Predis\\Command\\Redis\\ZSCAN' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZSCAN.php', 'Predis\\Command\\Redis\\ZSCORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZSCORE.php', 'Predis\\Command\\Redis\\ZUNION' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZUNION.php', 'Predis\\Command\\Redis\\ZUNIONSTORE' => __DIR__ . '/..' . '/predis/predis/src/Command/Redis/ZUNIONSTORE.php', 'Predis\\Command\\ScriptCommand' => __DIR__ . '/..' . '/predis/predis/src/Command/ScriptCommand.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DeleteStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DeleteStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DumpStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\FlushStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/FlushStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\KillStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/KillStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\ListStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/ListStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\LoadStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/LoadStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\RestoreStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/RestoreStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\StatsStrategy' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/StatsStrategy.php', 'Predis\\Command\\Strategy\\StrategyResolverInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/StrategyResolverInterface.php', 'Predis\\Command\\Strategy\\SubcommandStrategyInterface' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/SubcommandStrategyInterface.php', 'Predis\\Command\\Strategy\\SubcommandStrategyResolver' => __DIR__ . '/..' . '/predis/predis/src/Command/Strategy/SubcommandStrategyResolver.php', 'Predis\\Command\\Traits\\Aggregate' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Aggregate.php', 'Predis\\Command\\Traits\\BitByte' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BitByte.php', 'Predis\\Command\\Traits\\BloomFilters\\BucketSize' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/BucketSize.php', 'Predis\\Command\\Traits\\BloomFilters\\Capacity' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Capacity.php', 'Predis\\Command\\Traits\\BloomFilters\\Error' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Error.php', 'Predis\\Command\\Traits\\BloomFilters\\Expansion' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Expansion.php', 'Predis\\Command\\Traits\\BloomFilters\\Items' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/Items.php', 'Predis\\Command\\Traits\\BloomFilters\\MaxIterations' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/MaxIterations.php', 'Predis\\Command\\Traits\\BloomFilters\\NoCreate' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/BloomFilters/NoCreate.php', 'Predis\\Command\\Traits\\By\\ByArgument' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/By/ByArgument.php', 'Predis\\Command\\Traits\\By\\ByLexByScore' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/By/ByLexByScore.php', 'Predis\\Command\\Traits\\By\\GeoBy' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/By/GeoBy.php', 'Predis\\Command\\Traits\\Count' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Count.php', 'Predis\\Command\\Traits\\DB' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/DB.php', 'Predis\\Command\\Traits\\Expire\\ExpireOptions' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Expire/ExpireOptions.php', 'Predis\\Command\\Traits\\From\\GeoFrom' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/From/GeoFrom.php', 'Predis\\Command\\Traits\\Get\\Get' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Get/Get.php', 'Predis\\Command\\Traits\\Json\\Indent' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/Indent.php', 'Predis\\Command\\Traits\\Json\\Newline' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/Newline.php', 'Predis\\Command\\Traits\\Json\\NxXxArgument' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/NxXxArgument.php', 'Predis\\Command\\Traits\\Json\\Space' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Json/Space.php', 'Predis\\Command\\Traits\\Keys' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Keys.php', 'Predis\\Command\\Traits\\LeftRight' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/LeftRight.php', 'Predis\\Command\\Traits\\Limit\\Limit' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Limit/Limit.php', 'Predis\\Command\\Traits\\Limit\\LimitObject' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Limit/LimitObject.php', 'Predis\\Command\\Traits\\MinMaxModifier' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/MinMaxModifier.php', 'Predis\\Command\\Traits\\Replace' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Replace.php', 'Predis\\Command\\Traits\\Rev' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Rev.php', 'Predis\\Command\\Traits\\Sorting' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Sorting.php', 'Predis\\Command\\Traits\\Storedist' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Storedist.php', 'Predis\\Command\\Traits\\Timeout' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Timeout.php', 'Predis\\Command\\Traits\\To\\ServerTo' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/To/ServerTo.php', 'Predis\\Command\\Traits\\Weights' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/Weights.php', 'Predis\\Command\\Traits\\With\\WithCoord' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithCoord.php', 'Predis\\Command\\Traits\\With\\WithDist' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithDist.php', 'Predis\\Command\\Traits\\With\\WithHash' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithHash.php', 'Predis\\Command\\Traits\\With\\WithScores' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithScores.php', 'Predis\\Command\\Traits\\With\\WithValues' => __DIR__ . '/..' . '/predis/predis/src/Command/Traits/With/WithValues.php', 'Predis\\CommunicationException' => __DIR__ . '/..' . '/predis/predis/src/CommunicationException.php', 'Predis\\Configuration\\OptionInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionInterface.php', 'Predis\\Configuration\\Option\\Aggregate' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Aggregate.php', 'Predis\\Configuration\\Option\\CRC16' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/CRC16.php', 'Predis\\Configuration\\Option\\Cluster' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Cluster.php', 'Predis\\Configuration\\Option\\Commands' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Commands.php', 'Predis\\Configuration\\Option\\Connections' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Connections.php', 'Predis\\Configuration\\Option\\Exceptions' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Exceptions.php', 'Predis\\Configuration\\Option\\Prefix' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Prefix.php', 'Predis\\Configuration\\Option\\Replication' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Option/Replication.php', 'Predis\\Configuration\\Options' => __DIR__ . '/..' . '/predis/predis/src/Configuration/Options.php', 'Predis\\Configuration\\OptionsInterface' => __DIR__ . '/..' . '/predis/predis/src/Configuration/OptionsInterface.php', 'Predis\\Connection\\AbstractConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/AbstractConnection.php', 'Predis\\Connection\\AggregateConnectionInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/AggregateConnectionInterface.php', 'Predis\\Connection\\Cluster\\ClusterInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Cluster/ClusterInterface.php', 'Predis\\Connection\\Cluster\\PredisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Cluster/PredisCluster.php', 'Predis\\Connection\\Cluster\\RedisCluster' => __DIR__ . '/..' . '/predis/predis/src/Connection/Cluster/RedisCluster.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\\RelayConnection' => __DIR__ . '/..' . '/predis/predis/src/Connection/RelayConnection.php', 'Predis\\Connection\\RelayMethods' => __DIR__ . '/..' . '/predis/predis/src/Connection/RelayMethods.php', 'Predis\\Connection\\Replication\\MasterSlaveReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Replication/MasterSlaveReplication.php', 'Predis\\Connection\\Replication\\ReplicationInterface' => __DIR__ . '/..' . '/predis/predis/src/Connection/Replication/ReplicationInterface.php', 'Predis\\Connection\\Replication\\SentinelReplication' => __DIR__ . '/..' . '/predis/predis/src/Connection/Replication/SentinelReplication.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\\Pipeline\\RelayAtomic' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/RelayAtomic.php', 'Predis\\Pipeline\\RelayPipeline' => __DIR__ . '/..' . '/predis/predis/src/Pipeline/RelayPipeline.php', 'Predis\\PredisException' => __DIR__ . '/..' . '/predis/predis/src/PredisException.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\\PubSub\\RelayConsumer' => __DIR__ . '/..' . '/predis/predis/src/PubSub/RelayConsumer.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', 'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php', 'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php', 'Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php', 'Psr\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/psr/event-dispatcher/src/EventDispatcherInterface.php', 'Psr\\EventDispatcher\\ListenerProviderInterface' => __DIR__ . '/..' . '/psr/event-dispatcher/src/ListenerProviderInterface.php', 'Psr\\EventDispatcher\\StoppableEventInterface' => __DIR__ . '/..' . '/psr/event-dispatcher/src/StoppableEventInterface.php', 'Psr\\Http\\Client\\ClientExceptionInterface' => __DIR__ . '/..' . '/psr/http-client/src/ClientExceptionInterface.php', 'Psr\\Http\\Client\\ClientInterface' => __DIR__ . '/..' . '/psr/http-client/src/ClientInterface.php', 'Psr\\Http\\Client\\NetworkExceptionInterface' => __DIR__ . '/..' . '/psr/http-client/src/NetworkExceptionInterface.php', 'Psr\\Http\\Client\\RequestExceptionInterface' => __DIR__ . '/..' . '/psr/http-client/src/RequestExceptionInterface.php', 'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php', 'Psr\\Http\\Message\\RequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/RequestFactoryInterface.php', 'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php', 'Psr\\Http\\Message\\ResponseFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ResponseFactoryInterface.php', 'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php', 'Psr\\Http\\Message\\ServerRequestFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/ServerRequestFactoryInterface.php', 'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php', 'Psr\\Http\\Message\\StreamFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/StreamFactoryInterface.php', 'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php', 'Psr\\Http\\Message\\UploadedFileFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UploadedFileFactoryInterface.php', 'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php', 'Psr\\Http\\Message\\UriFactoryInterface' => __DIR__ . '/..' . '/psr/http-factory/src/UriFactoryInterface.php', 'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.php', 'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/src/AbstractLogger.php', 'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/src/InvalidArgumentException.php', 'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/src/LogLevel.php', 'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareInterface.php', 'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareTrait.php', 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerTrait.php', 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/src/NullLogger.php', 'Psy\\CodeCleaner' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner.php', 'Psy\\CodeCleaner\\AbstractClassPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/AbstractClassPass.php', 'Psy\\CodeCleaner\\AssignThisVariablePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/AssignThisVariablePass.php', 'Psy\\CodeCleaner\\CallTimePassByReferencePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/CallTimePassByReferencePass.php', 'Psy\\CodeCleaner\\CalledClassPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/CalledClassPass.php', 'Psy\\CodeCleaner\\CodeCleanerPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/CodeCleanerPass.php', 'Psy\\CodeCleaner\\EmptyArrayDimFetchPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/EmptyArrayDimFetchPass.php', 'Psy\\CodeCleaner\\ExitPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/ExitPass.php', 'Psy\\CodeCleaner\\FinalClassPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/FinalClassPass.php', 'Psy\\CodeCleaner\\FunctionContextPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/FunctionContextPass.php', 'Psy\\CodeCleaner\\FunctionReturnInWriteContextPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/FunctionReturnInWriteContextPass.php', 'Psy\\CodeCleaner\\ImplicitReturnPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/ImplicitReturnPass.php', 'Psy\\CodeCleaner\\IssetPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/IssetPass.php', 'Psy\\CodeCleaner\\LabelContextPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/LabelContextPass.php', 'Psy\\CodeCleaner\\LeavePsyshAlonePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/LeavePsyshAlonePass.php', 'Psy\\CodeCleaner\\ListPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/ListPass.php', 'Psy\\CodeCleaner\\LoopContextPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/LoopContextPass.php', 'Psy\\CodeCleaner\\MagicConstantsPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/MagicConstantsPass.php', 'Psy\\CodeCleaner\\NamespaceAwarePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/NamespaceAwarePass.php', 'Psy\\CodeCleaner\\NamespacePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/NamespacePass.php', 'Psy\\CodeCleaner\\NoReturnValue' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/NoReturnValue.php', 'Psy\\CodeCleaner\\PassableByReferencePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/PassableByReferencePass.php', 'Psy\\CodeCleaner\\RequirePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/RequirePass.php', 'Psy\\CodeCleaner\\ReturnTypePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/ReturnTypePass.php', 'Psy\\CodeCleaner\\StrictTypesPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/StrictTypesPass.php', 'Psy\\CodeCleaner\\UseStatementPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/UseStatementPass.php', 'Psy\\CodeCleaner\\ValidClassNamePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/ValidClassNamePass.php', 'Psy\\CodeCleaner\\ValidConstructorPass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/ValidConstructorPass.php', 'Psy\\CodeCleaner\\ValidFunctionNamePass' => __DIR__ . '/..' . '/psy/psysh/src/CodeCleaner/ValidFunctionNamePass.php', 'Psy\\Command\\BufferCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/BufferCommand.php', 'Psy\\Command\\ClearCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/ClearCommand.php', 'Psy\\Command\\CodeArgumentParser' => __DIR__ . '/..' . '/psy/psysh/src/Command/CodeArgumentParser.php', 'Psy\\Command\\Command' => __DIR__ . '/..' . '/psy/psysh/src/Command/Command.php', 'Psy\\Command\\DocCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/DocCommand.php', 'Psy\\Command\\DumpCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/DumpCommand.php', 'Psy\\Command\\EditCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/EditCommand.php', 'Psy\\Command\\ExitCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/ExitCommand.php', 'Psy\\Command\\HelpCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/HelpCommand.php', 'Psy\\Command\\HistoryCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/HistoryCommand.php', 'Psy\\Command\\ListCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand.php', 'Psy\\Command\\ListCommand\\ClassConstantEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/ClassConstantEnumerator.php', 'Psy\\Command\\ListCommand\\ClassEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/ClassEnumerator.php', 'Psy\\Command\\ListCommand\\ConstantEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/ConstantEnumerator.php', 'Psy\\Command\\ListCommand\\Enumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/Enumerator.php', 'Psy\\Command\\ListCommand\\FunctionEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/FunctionEnumerator.php', 'Psy\\Command\\ListCommand\\GlobalVariableEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/GlobalVariableEnumerator.php', 'Psy\\Command\\ListCommand\\MethodEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/MethodEnumerator.php', 'Psy\\Command\\ListCommand\\PropertyEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/PropertyEnumerator.php', 'Psy\\Command\\ListCommand\\VariableEnumerator' => __DIR__ . '/..' . '/psy/psysh/src/Command/ListCommand/VariableEnumerator.php', 'Psy\\Command\\ParseCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/ParseCommand.php', 'Psy\\Command\\PsyVersionCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/PsyVersionCommand.php', 'Psy\\Command\\ReflectingCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/ReflectingCommand.php', 'Psy\\Command\\ShowCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/ShowCommand.php', 'Psy\\Command\\SudoCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/SudoCommand.php', 'Psy\\Command\\ThrowUpCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/ThrowUpCommand.php', 'Psy\\Command\\TimeitCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/TimeitCommand.php', 'Psy\\Command\\TimeitCommand\\TimeitVisitor' => __DIR__ . '/..' . '/psy/psysh/src/Command/TimeitCommand/TimeitVisitor.php', 'Psy\\Command\\TraceCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/TraceCommand.php', 'Psy\\Command\\WhereamiCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/WhereamiCommand.php', 'Psy\\Command\\WtfCommand' => __DIR__ . '/..' . '/psy/psysh/src/Command/WtfCommand.php', 'Psy\\ConfigPaths' => __DIR__ . '/..' . '/psy/psysh/src/ConfigPaths.php', 'Psy\\Configuration' => __DIR__ . '/..' . '/psy/psysh/src/Configuration.php', 'Psy\\Context' => __DIR__ . '/..' . '/psy/psysh/src/Context.php', 'Psy\\ContextAware' => __DIR__ . '/..' . '/psy/psysh/src/ContextAware.php', 'Psy\\EnvInterface' => __DIR__ . '/..' . '/psy/psysh/src/EnvInterface.php', 'Psy\\Exception\\BreakException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/BreakException.php', 'Psy\\Exception\\DeprecatedException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/DeprecatedException.php', 'Psy\\Exception\\ErrorException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/ErrorException.php', 'Psy\\Exception\\Exception' => __DIR__ . '/..' . '/psy/psysh/src/Exception/Exception.php', 'Psy\\Exception\\FatalErrorException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/FatalErrorException.php', 'Psy\\Exception\\ParseErrorException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/ParseErrorException.php', 'Psy\\Exception\\RuntimeException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/RuntimeException.php', 'Psy\\Exception\\ThrowUpException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/ThrowUpException.php', 'Psy\\Exception\\UnexpectedTargetException' => __DIR__ . '/..' . '/psy/psysh/src/Exception/UnexpectedTargetException.php', 'Psy\\ExecutionClosure' => __DIR__ . '/..' . '/psy/psysh/src/ExecutionClosure.php', 'Psy\\ExecutionLoopClosure' => __DIR__ . '/..' . '/psy/psysh/src/ExecutionLoopClosure.php', 'Psy\\ExecutionLoop\\AbstractListener' => __DIR__ . '/..' . '/psy/psysh/src/ExecutionLoop/AbstractListener.php', 'Psy\\ExecutionLoop\\Listener' => __DIR__ . '/..' . '/psy/psysh/src/ExecutionLoop/Listener.php', 'Psy\\ExecutionLoop\\ProcessForker' => __DIR__ . '/..' . '/psy/psysh/src/ExecutionLoop/ProcessForker.php', 'Psy\\ExecutionLoop\\RunkitReloader' => __DIR__ . '/..' . '/psy/psysh/src/ExecutionLoop/RunkitReloader.php', 'Psy\\Formatter\\CodeFormatter' => __DIR__ . '/..' . '/psy/psysh/src/Formatter/CodeFormatter.php', 'Psy\\Formatter\\DocblockFormatter' => __DIR__ . '/..' . '/psy/psysh/src/Formatter/DocblockFormatter.php', 'Psy\\Formatter\\ReflectorFormatter' => __DIR__ . '/..' . '/psy/psysh/src/Formatter/ReflectorFormatter.php', 'Psy\\Formatter\\SignatureFormatter' => __DIR__ . '/..' . '/psy/psysh/src/Formatter/SignatureFormatter.php', 'Psy\\Formatter\\TraceFormatter' => __DIR__ . '/..' . '/psy/psysh/src/Formatter/TraceFormatter.php', 'Psy\\Input\\CodeArgument' => __DIR__ . '/..' . '/psy/psysh/src/Input/CodeArgument.php', 'Psy\\Input\\FilterOptions' => __DIR__ . '/..' . '/psy/psysh/src/Input/FilterOptions.php', 'Psy\\Input\\ShellInput' => __DIR__ . '/..' . '/psy/psysh/src/Input/ShellInput.php', 'Psy\\Input\\SilentInput' => __DIR__ . '/..' . '/psy/psysh/src/Input/SilentInput.php', 'Psy\\Output\\OutputPager' => __DIR__ . '/..' . '/psy/psysh/src/Output/OutputPager.php', 'Psy\\Output\\PassthruPager' => __DIR__ . '/..' . '/psy/psysh/src/Output/PassthruPager.php', 'Psy\\Output\\ProcOutputPager' => __DIR__ . '/..' . '/psy/psysh/src/Output/ProcOutputPager.php', 'Psy\\Output\\ShellOutput' => __DIR__ . '/..' . '/psy/psysh/src/Output/ShellOutput.php', 'Psy\\Output\\Theme' => __DIR__ . '/..' . '/psy/psysh/src/Output/Theme.php', 'Psy\\ParserFactory' => __DIR__ . '/..' . '/psy/psysh/src/ParserFactory.php', 'Psy\\Readline\\GNUReadline' => __DIR__ . '/..' . '/psy/psysh/src/Readline/GNUReadline.php', 'Psy\\Readline\\Hoa\\Autocompleter' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Autocompleter.php', 'Psy\\Readline\\Hoa\\AutocompleterAggregate' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/AutocompleterAggregate.php', 'Psy\\Readline\\Hoa\\AutocompleterPath' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/AutocompleterPath.php', 'Psy\\Readline\\Hoa\\AutocompleterWord' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/AutocompleterWord.php', 'Psy\\Readline\\Hoa\\Console' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Console.php', 'Psy\\Readline\\Hoa\\ConsoleCursor' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ConsoleCursor.php', 'Psy\\Readline\\Hoa\\ConsoleException' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ConsoleException.php', 'Psy\\Readline\\Hoa\\ConsoleInput' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ConsoleInput.php', 'Psy\\Readline\\Hoa\\ConsoleOutput' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ConsoleOutput.php', 'Psy\\Readline\\Hoa\\ConsoleProcessus' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ConsoleProcessus.php', 'Psy\\Readline\\Hoa\\ConsoleTput' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ConsoleTput.php', 'Psy\\Readline\\Hoa\\ConsoleWindow' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ConsoleWindow.php', 'Psy\\Readline\\Hoa\\Event' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Event.php', 'Psy\\Readline\\Hoa\\EventBucket' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/EventBucket.php', 'Psy\\Readline\\Hoa\\EventException' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/EventException.php', 'Psy\\Readline\\Hoa\\EventListenable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/EventListenable.php', 'Psy\\Readline\\Hoa\\EventListener' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/EventListener.php', 'Psy\\Readline\\Hoa\\EventListens' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/EventListens.php', 'Psy\\Readline\\Hoa\\EventSource' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/EventSource.php', 'Psy\\Readline\\Hoa\\Exception' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Exception.php', 'Psy\\Readline\\Hoa\\ExceptionIdle' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ExceptionIdle.php', 'Psy\\Readline\\Hoa\\File' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/File.php', 'Psy\\Readline\\Hoa\\FileDirectory' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileDirectory.php', 'Psy\\Readline\\Hoa\\FileDoesNotExistException' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileDoesNotExistException.php', 'Psy\\Readline\\Hoa\\FileException' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileException.php', 'Psy\\Readline\\Hoa\\FileFinder' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileFinder.php', 'Psy\\Readline\\Hoa\\FileGeneric' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileGeneric.php', 'Psy\\Readline\\Hoa\\FileLink' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileLink.php', 'Psy\\Readline\\Hoa\\FileLinkRead' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileLinkRead.php', 'Psy\\Readline\\Hoa\\FileLinkReadWrite' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileLinkReadWrite.php', 'Psy\\Readline\\Hoa\\FileRead' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileRead.php', 'Psy\\Readline\\Hoa\\FileReadWrite' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/FileReadWrite.php', 'Psy\\Readline\\Hoa\\IStream' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/IStream.php', 'Psy\\Readline\\Hoa\\IteratorFileSystem' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/IteratorFileSystem.php', 'Psy\\Readline\\Hoa\\IteratorRecursiveDirectory' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/IteratorRecursiveDirectory.php', 'Psy\\Readline\\Hoa\\IteratorSplFileInfo' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/IteratorSplFileInfo.php', 'Psy\\Readline\\Hoa\\Protocol' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Protocol.php', 'Psy\\Readline\\Hoa\\ProtocolException' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ProtocolException.php', 'Psy\\Readline\\Hoa\\ProtocolNode' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ProtocolNode.php', 'Psy\\Readline\\Hoa\\ProtocolNodeLibrary' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ProtocolNodeLibrary.php', 'Psy\\Readline\\Hoa\\ProtocolWrapper' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/ProtocolWrapper.php', 'Psy\\Readline\\Hoa\\Readline' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Readline.php', 'Psy\\Readline\\Hoa\\Stream' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Stream.php', 'Psy\\Readline\\Hoa\\StreamBufferable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamBufferable.php', 'Psy\\Readline\\Hoa\\StreamContext' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamContext.php', 'Psy\\Readline\\Hoa\\StreamException' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamException.php', 'Psy\\Readline\\Hoa\\StreamIn' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamIn.php', 'Psy\\Readline\\Hoa\\StreamLockable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamLockable.php', 'Psy\\Readline\\Hoa\\StreamOut' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamOut.php', 'Psy\\Readline\\Hoa\\StreamPathable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamPathable.php', 'Psy\\Readline\\Hoa\\StreamPointable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamPointable.php', 'Psy\\Readline\\Hoa\\StreamStatable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamStatable.php', 'Psy\\Readline\\Hoa\\StreamTouchable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/StreamTouchable.php', 'Psy\\Readline\\Hoa\\Ustring' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Ustring.php', 'Psy\\Readline\\Hoa\\Xcallable' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Hoa/Xcallable.php', 'Psy\\Readline\\Libedit' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Libedit.php', 'Psy\\Readline\\Readline' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Readline.php', 'Psy\\Readline\\Transient' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Transient.php', 'Psy\\Readline\\Userland' => __DIR__ . '/..' . '/psy/psysh/src/Readline/Userland.php', 'Psy\\Reflection\\ReflectionConstant' => __DIR__ . '/..' . '/psy/psysh/src/Reflection/ReflectionConstant.php', 'Psy\\Reflection\\ReflectionLanguageConstruct' => __DIR__ . '/..' . '/psy/psysh/src/Reflection/ReflectionLanguageConstruct.php', 'Psy\\Reflection\\ReflectionLanguageConstructParameter' => __DIR__ . '/..' . '/psy/psysh/src/Reflection/ReflectionLanguageConstructParameter.php', 'Psy\\Reflection\\ReflectionNamespace' => __DIR__ . '/..' . '/psy/psysh/src/Reflection/ReflectionNamespace.php', 'Psy\\Shell' => __DIR__ . '/..' . '/psy/psysh/src/Shell.php', 'Psy\\Sudo' => __DIR__ . '/..' . '/psy/psysh/src/Sudo.php', 'Psy\\Sudo\\SudoVisitor' => __DIR__ . '/..' . '/psy/psysh/src/Sudo/SudoVisitor.php', 'Psy\\SuperglobalsEnv' => __DIR__ . '/..' . '/psy/psysh/src/SuperglobalsEnv.php', 'Psy\\SystemEnv' => __DIR__ . '/..' . '/psy/psysh/src/SystemEnv.php', 'Psy\\TabCompletion\\AutoCompleter' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/AutoCompleter.php', 'Psy\\TabCompletion\\Matcher\\AbstractContextAwareMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/AbstractContextAwareMatcher.php', 'Psy\\TabCompletion\\Matcher\\AbstractDefaultParametersMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/AbstractDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\AbstractMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/AbstractMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassAttributesMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ClassAttributesMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassMethodDefaultParametersMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ClassMethodDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassMethodsMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ClassMethodsMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassNamesMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ClassNamesMatcher.php', 'Psy\\TabCompletion\\Matcher\\CommandsMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/CommandsMatcher.php', 'Psy\\TabCompletion\\Matcher\\ConstantsMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ConstantsMatcher.php', 'Psy\\TabCompletion\\Matcher\\FunctionDefaultParametersMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/FunctionDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\FunctionsMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/FunctionsMatcher.php', 'Psy\\TabCompletion\\Matcher\\KeywordsMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/KeywordsMatcher.php', 'Psy\\TabCompletion\\Matcher\\MongoClientMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/MongoClientMatcher.php', 'Psy\\TabCompletion\\Matcher\\MongoDatabaseMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/MongoDatabaseMatcher.php', 'Psy\\TabCompletion\\Matcher\\ObjectAttributesMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ObjectAttributesMatcher.php', 'Psy\\TabCompletion\\Matcher\\ObjectMethodDefaultParametersMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ObjectMethodDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\ObjectMethodsMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/ObjectMethodsMatcher.php', 'Psy\\TabCompletion\\Matcher\\VariablesMatcher' => __DIR__ . '/..' . '/psy/psysh/src/TabCompletion/Matcher/VariablesMatcher.php', 'Psy\\Util\\Docblock' => __DIR__ . '/..' . '/psy/psysh/src/Util/Docblock.php', 'Psy\\Util\\Json' => __DIR__ . '/..' . '/psy/psysh/src/Util/Json.php', 'Psy\\Util\\Mirror' => __DIR__ . '/..' . '/psy/psysh/src/Util/Mirror.php', 'Psy\\Util\\Str' => __DIR__ . '/..' . '/psy/psysh/src/Util/Str.php', 'Psy\\VarDumper\\Cloner' => __DIR__ . '/..' . '/psy/psysh/src/VarDumper/Cloner.php', 'Psy\\VarDumper\\Dumper' => __DIR__ . '/..' . '/psy/psysh/src/VarDumper/Dumper.php', 'Psy\\VarDumper\\Presenter' => __DIR__ . '/..' . '/psy/psysh/src/VarDumper/Presenter.php', 'Psy\\VarDumper\\PresenterAware' => __DIR__ . '/..' . '/psy/psysh/src/VarDumper/PresenterAware.php', 'Psy\\VersionUpdater\\Checker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/Checker.php', 'Psy\\VersionUpdater\\Downloader' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/Downloader.php', 'Psy\\VersionUpdater\\Downloader\\CurlDownloader' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/Downloader/CurlDownloader.php', 'Psy\\VersionUpdater\\Downloader\\Factory' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/Downloader/Factory.php', 'Psy\\VersionUpdater\\Downloader\\FileDownloader' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/Downloader/FileDownloader.php', 'Psy\\VersionUpdater\\GitHubChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/GitHubChecker.php', 'Psy\\VersionUpdater\\Installer' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/Installer.php', 'Psy\\VersionUpdater\\IntervalChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/IntervalChecker.php', 'Psy\\VersionUpdater\\NoopChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/NoopChecker.php', 'Psy\\VersionUpdater\\SelfUpdate' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/SelfUpdate.php', 'ReturnTypeWillChange' => __DIR__ . '/..' . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'SebastianBergmann\\CliParser\\AmbiguousOptionException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.php', 'SebastianBergmann\\CliParser\\Exception' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/Exception.php', 'SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/OptionDoesNotAllowArgumentException.php', 'SebastianBergmann\\CliParser\\Parser' => __DIR__ . '/..' . '/sebastian/cli-parser/src/Parser.php', 'SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/RequiredOptionArgumentMissingException.php', 'SebastianBergmann\\CliParser\\UnknownOptionException' => __DIR__ . '/..' . '/sebastian/cli-parser/src/exceptions/UnknownOptionException.php', 'SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/CodeCoverage.php', 'SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PcovDriver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Selector' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Selector.php', 'SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.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\\NoCodeCoverageDriverAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php', 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.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\\CrapIndex' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Node/CrapIndex.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\\ParserException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/ParserException.php', 'SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php', 'SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/RawCodeCoverageData.php', 'SebastianBergmann\\CodeCoverage\\ReflectionException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/ReflectionException.php', 'SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php', 'SebastianBergmann\\CodeCoverage\\Report\\Clover' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Clover.php', 'SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Cobertura.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\\BuildInformation' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/BuildInformation.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\\Source' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Report/Xml/Source.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\\StaticAnalysisCacheNotConfiguredException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\TestIdMissingException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', 'SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php', 'SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Util/Filesystem.php', 'SebastianBergmann\\CodeCoverage\\Util\\Percentage' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Util/Percentage.php', 'SebastianBergmann\\CodeCoverage\\Version' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Version.php', 'SebastianBergmann\\CodeCoverage\\XmlException' => __DIR__ . '/..' . '/phpunit/php-code-coverage/src/Exception/XmlException.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => __DIR__ . '/..' . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', 'SebastianBergmann\\CodeUnit\\ClassMethodUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/ClassMethodUnit.php', 'SebastianBergmann\\CodeUnit\\ClassUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/ClassUnit.php', 'SebastianBergmann\\CodeUnit\\CodeUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/CodeUnit.php', 'SebastianBergmann\\CodeUnit\\CodeUnitCollection' => __DIR__ . '/..' . '/sebastian/code-unit/src/CodeUnitCollection.php', 'SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => __DIR__ . '/..' . '/sebastian/code-unit/src/CodeUnitCollectionIterator.php', 'SebastianBergmann\\CodeUnit\\Exception' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/Exception.php', 'SebastianBergmann\\CodeUnit\\FunctionUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/FunctionUnit.php', 'SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/InterfaceMethodUnit.php', 'SebastianBergmann\\CodeUnit\\InterfaceUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/InterfaceUnit.php', 'SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/InvalidCodeUnitException.php', 'SebastianBergmann\\CodeUnit\\Mapper' => __DIR__ . '/..' . '/sebastian/code-unit/src/Mapper.php', 'SebastianBergmann\\CodeUnit\\NoTraitException' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/NoTraitException.php', 'SebastianBergmann\\CodeUnit\\ReflectionException' => __DIR__ . '/..' . '/sebastian/code-unit/src/exceptions/ReflectionException.php', 'SebastianBergmann\\CodeUnit\\TraitMethodUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/TraitMethodUnit.php', 'SebastianBergmann\\CodeUnit\\TraitUnit' => __DIR__ . '/..' . '/sebastian/code-unit/src/TraitUnit.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\\Exception' => __DIR__ . '/..' . '/sebastian/comparator/src/exceptions/Exception.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\\RuntimeException' => __DIR__ . '/..' . '/sebastian/comparator/src/exceptions/RuntimeException.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\\Complexity\\Calculator' => __DIR__ . '/..' . '/sebastian/complexity/src/Calculator.php', 'SebastianBergmann\\Complexity\\Complexity' => __DIR__ . '/..' . '/sebastian/complexity/src/Complexity/Complexity.php', 'SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => __DIR__ . '/..' . '/sebastian/complexity/src/Visitor/ComplexityCalculatingVisitor.php', 'SebastianBergmann\\Complexity\\ComplexityCollection' => __DIR__ . '/..' . '/sebastian/complexity/src/Complexity/ComplexityCollection.php', 'SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => __DIR__ . '/..' . '/sebastian/complexity/src/Complexity/ComplexityCollectionIterator.php', 'SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => __DIR__ . '/..' . '/sebastian/complexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.php', 'SebastianBergmann\\Complexity\\Exception' => __DIR__ . '/..' . '/sebastian/complexity/src/Exception/Exception.php', 'SebastianBergmann\\Complexity\\RuntimeException' => __DIR__ . '/..' . '/sebastian/complexity/src/Exception/RuntimeException.php', 'SebastianBergmann\\Diff\\Chunk' => __DIR__ . '/..' . '/sebastian/diff/src/Chunk.php', 'SebastianBergmann\\Diff\\ConfigurationException' => __DIR__ . '/..' . '/sebastian/diff/src/Exception/ConfigurationException.php', 'SebastianBergmann\\Diff\\Diff' => __DIR__ . '/..' . '/sebastian/diff/src/Diff.php', 'SebastianBergmann\\Diff\\Differ' => __DIR__ . '/..' . '/sebastian/diff/src/Differ.php', 'SebastianBergmann\\Diff\\Exception' => __DIR__ . '/..' . '/sebastian/diff/src/Exception/Exception.php', 'SebastianBergmann\\Diff\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/diff/src/Exception/InvalidArgumentException.php', 'SebastianBergmann\\Diff\\Line' => __DIR__ . '/..' . '/sebastian/diff/src/Line.php', 'SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => __DIR__ . '/..' . '/sebastian/diff/src/LongestCommonSubsequenceCalculator.php', 'SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => __DIR__ . '/..' . '/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php', 'SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/AbstractChunkOutputBuilder.php', 'SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php', 'SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => __DIR__ . '/..' . '/sebastian/diff/src/Output/DiffOutputBuilderInterface.php', 'SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php', 'SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => __DIR__ . '/..' . '/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php', 'SebastianBergmann\\Diff\\Parser' => __DIR__ . '/..' . '/sebastian/diff/src/Parser.php', 'SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => __DIR__ . '/..' . '/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php', 'SebastianBergmann\\Environment\\Console' => __DIR__ . '/..' . '/sebastian/environment/src/Console.php', 'SebastianBergmann\\Environment\\OperatingSystem' => __DIR__ . '/..' . '/sebastian/environment/src/OperatingSystem.php', 'SebastianBergmann\\Environment\\Runtime' => __DIR__ . '/..' . '/sebastian/environment/src/Runtime.php', 'SebastianBergmann\\Exporter\\Exporter' => __DIR__ . '/..' . '/sebastian/exporter/src/Exporter.php', 'SebastianBergmann\\FileIterator\\Facade' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Facade.php', 'SebastianBergmann\\FileIterator\\Factory' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Factory.php', 'SebastianBergmann\\FileIterator\\Iterator' => __DIR__ . '/..' . '/phpunit/php-file-iterator/src/Iterator.php', 'SebastianBergmann\\GlobalState\\CodeExporter' => __DIR__ . '/..' . '/sebastian/global-state/src/CodeExporter.php', 'SebastianBergmann\\GlobalState\\Exception' => __DIR__ . '/..' . '/sebastian/global-state/src/exceptions/Exception.php', 'SebastianBergmann\\GlobalState\\ExcludeList' => __DIR__ . '/..' . '/sebastian/global-state/src/ExcludeList.php', 'SebastianBergmann\\GlobalState\\Restorer' => __DIR__ . '/..' . '/sebastian/global-state/src/Restorer.php', 'SebastianBergmann\\GlobalState\\RuntimeException' => __DIR__ . '/..' . '/sebastian/global-state/src/exceptions/RuntimeException.php', 'SebastianBergmann\\GlobalState\\Snapshot' => __DIR__ . '/..' . '/sebastian/global-state/src/Snapshot.php', 'SebastianBergmann\\Invoker\\Exception' => __DIR__ . '/..' . '/phpunit/php-invoker/src/exceptions/Exception.php', 'SebastianBergmann\\Invoker\\Invoker' => __DIR__ . '/..' . '/phpunit/php-invoker/src/Invoker.php', 'SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => __DIR__ . '/..' . '/phpunit/php-invoker/src/exceptions/ProcessControlExtensionNotLoadedException.php', 'SebastianBergmann\\Invoker\\TimeoutException' => __DIR__ . '/..' . '/phpunit/php-invoker/src/exceptions/TimeoutException.php', 'SebastianBergmann\\LinesOfCode\\Counter' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Counter.php', 'SebastianBergmann\\LinesOfCode\\Exception' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/Exception.php', 'SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/IllogicalValuesException.php', 'SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/LineCountingVisitor.php', 'SebastianBergmann\\LinesOfCode\\LinesOfCode' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/LinesOfCode.php', 'SebastianBergmann\\LinesOfCode\\NegativeValueException' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/NegativeValueException.php', 'SebastianBergmann\\LinesOfCode\\RuntimeException' => __DIR__ . '/..' . '/sebastian/lines-of-code/src/Exception/RuntimeException.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\\ObjectReflector\\Exception' => __DIR__ . '/..' . '/sebastian/object-reflector/src/Exception.php', 'SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => __DIR__ . '/..' . '/sebastian/object-reflector/src/InvalidArgumentException.php', 'SebastianBergmann\\ObjectReflector\\ObjectReflector' => __DIR__ . '/..' . '/sebastian/object-reflector/src/ObjectReflector.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\\Template\\Exception' => __DIR__ . '/..' . '/phpunit/php-text-template/src/exceptions/Exception.php', 'SebastianBergmann\\Template\\InvalidArgumentException' => __DIR__ . '/..' . '/phpunit/php-text-template/src/exceptions/InvalidArgumentException.php', 'SebastianBergmann\\Template\\RuntimeException' => __DIR__ . '/..' . '/phpunit/php-text-template/src/exceptions/RuntimeException.php', 'SebastianBergmann\\Template\\Template' => __DIR__ . '/..' . '/phpunit/php-text-template/src/Template.php', 'SebastianBergmann\\Timer\\Duration' => __DIR__ . '/..' . '/phpunit/php-timer/src/Duration.php', 'SebastianBergmann\\Timer\\Exception' => __DIR__ . '/..' . '/phpunit/php-timer/src/exceptions/Exception.php', 'SebastianBergmann\\Timer\\NoActiveTimerException' => __DIR__ . '/..' . '/phpunit/php-timer/src/exceptions/NoActiveTimerException.php', 'SebastianBergmann\\Timer\\ResourceUsageFormatter' => __DIR__ . '/..' . '/phpunit/php-timer/src/ResourceUsageFormatter.php', 'SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => __DIR__ . '/..' . '/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php', 'SebastianBergmann\\Timer\\Timer' => __DIR__ . '/..' . '/phpunit/php-timer/src/Timer.php', 'SebastianBergmann\\Type\\CallableType' => __DIR__ . '/..' . '/sebastian/type/src/type/CallableType.php', 'SebastianBergmann\\Type\\Exception' => __DIR__ . '/..' . '/sebastian/type/src/exception/Exception.php', 'SebastianBergmann\\Type\\FalseType' => __DIR__ . '/..' . '/sebastian/type/src/type/FalseType.php', 'SebastianBergmann\\Type\\GenericObjectType' => __DIR__ . '/..' . '/sebastian/type/src/type/GenericObjectType.php', 'SebastianBergmann\\Type\\IntersectionType' => __DIR__ . '/..' . '/sebastian/type/src/type/IntersectionType.php', 'SebastianBergmann\\Type\\IterableType' => __DIR__ . '/..' . '/sebastian/type/src/type/IterableType.php', 'SebastianBergmann\\Type\\MixedType' => __DIR__ . '/..' . '/sebastian/type/src/type/MixedType.php', 'SebastianBergmann\\Type\\NeverType' => __DIR__ . '/..' . '/sebastian/type/src/type/NeverType.php', 'SebastianBergmann\\Type\\NullType' => __DIR__ . '/..' . '/sebastian/type/src/type/NullType.php', 'SebastianBergmann\\Type\\ObjectType' => __DIR__ . '/..' . '/sebastian/type/src/type/ObjectType.php', 'SebastianBergmann\\Type\\Parameter' => __DIR__ . '/..' . '/sebastian/type/src/Parameter.php', 'SebastianBergmann\\Type\\ReflectionMapper' => __DIR__ . '/..' . '/sebastian/type/src/ReflectionMapper.php', 'SebastianBergmann\\Type\\RuntimeException' => __DIR__ . '/..' . '/sebastian/type/src/exception/RuntimeException.php', 'SebastianBergmann\\Type\\SimpleType' => __DIR__ . '/..' . '/sebastian/type/src/type/SimpleType.php', 'SebastianBergmann\\Type\\StaticType' => __DIR__ . '/..' . '/sebastian/type/src/type/StaticType.php', 'SebastianBergmann\\Type\\TrueType' => __DIR__ . '/..' . '/sebastian/type/src/type/TrueType.php', 'SebastianBergmann\\Type\\Type' => __DIR__ . '/..' . '/sebastian/type/src/type/Type.php', 'SebastianBergmann\\Type\\TypeName' => __DIR__ . '/..' . '/sebastian/type/src/TypeName.php', 'SebastianBergmann\\Type\\UnionType' => __DIR__ . '/..' . '/sebastian/type/src/type/UnionType.php', 'SebastianBergmann\\Type\\UnknownType' => __DIR__ . '/..' . '/sebastian/type/src/type/UnknownType.php', 'SebastianBergmann\\Type\\VoidType' => __DIR__ . '/..' . '/sebastian/type/src/type/VoidType.php', 'SebastianBergmann\\Version' => __DIR__ . '/..' . '/sebastian/version/src/Version.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'Symfony\\Component\\BrowserKit\\AbstractBrowser' => __DIR__ . '/..' . '/symfony/browser-kit/AbstractBrowser.php', 'Symfony\\Component\\BrowserKit\\Cookie' => __DIR__ . '/..' . '/symfony/browser-kit/Cookie.php', 'Symfony\\Component\\BrowserKit\\CookieJar' => __DIR__ . '/..' . '/symfony/browser-kit/CookieJar.php', 'Symfony\\Component\\BrowserKit\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/symfony/browser-kit/Exception/BadMethodCallException.php', 'Symfony\\Component\\BrowserKit\\History' => __DIR__ . '/..' . '/symfony/browser-kit/History.php', 'Symfony\\Component\\BrowserKit\\HttpBrowser' => __DIR__ . '/..' . '/symfony/browser-kit/HttpBrowser.php', 'Symfony\\Component\\BrowserKit\\Request' => __DIR__ . '/..' . '/symfony/browser-kit/Request.php', 'Symfony\\Component\\BrowserKit\\Response' => __DIR__ . '/..' . '/symfony/browser-kit/Response.php', 'Symfony\\Component\\BrowserKit\\Test\\Constraint\\BrowserCookieValueSame' => __DIR__ . '/..' . '/symfony/browser-kit/Test/Constraint/BrowserCookieValueSame.php', 'Symfony\\Component\\BrowserKit\\Test\\Constraint\\BrowserHasCookie' => __DIR__ . '/..' . '/symfony/browser-kit/Test/Constraint/BrowserHasCookie.php', 'Symfony\\Component\\Console\\Application' => __DIR__ . '/..' . '/symfony/console/Application.php', 'Symfony\\Component\\Console\\Attribute\\AsCommand' => __DIR__ . '/..' . '/symfony/console/Attribute/AsCommand.php', 'Symfony\\Component\\Console\\CI\\GithubActionReporter' => __DIR__ . '/..' . '/symfony/console/CI/GithubActionReporter.php', 'Symfony\\Component\\Console\\Color' => __DIR__ . '/..' . '/symfony/console/Color.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\\CompleteCommand' => __DIR__ . '/..' . '/symfony/console/Command/CompleteCommand.php', 'Symfony\\Component\\Console\\Command\\DumpCompletionCommand' => __DIR__ . '/..' . '/symfony/console/Command/DumpCompletionCommand.php', 'Symfony\\Component\\Console\\Command\\HelpCommand' => __DIR__ . '/..' . '/symfony/console/Command/HelpCommand.php', 'Symfony\\Component\\Console\\Command\\LazyCommand' => __DIR__ . '/..' . '/symfony/console/Command/LazyCommand.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\\Command\\SignalableCommandInterface' => __DIR__ . '/..' . '/symfony/console/Command/SignalableCommandInterface.php', 'Symfony\\Component\\Console\\Completion\\CompletionInput' => __DIR__ . '/..' . '/symfony/console/Completion/CompletionInput.php', 'Symfony\\Component\\Console\\Completion\\CompletionSuggestions' => __DIR__ . '/..' . '/symfony/console/Completion/CompletionSuggestions.php', 'Symfony\\Component\\Console\\Completion\\Output\\BashCompletionOutput' => __DIR__ . '/..' . '/symfony/console/Completion/Output/BashCompletionOutput.php', 'Symfony\\Component\\Console\\Completion\\Output\\CompletionOutputInterface' => __DIR__ . '/..' . '/symfony/console/Completion/Output/CompletionOutputInterface.php', 'Symfony\\Component\\Console\\Completion\\Suggestion' => __DIR__ . '/..' . '/symfony/console/Completion/Suggestion.php', 'Symfony\\Component\\Console\\ConsoleEvents' => __DIR__ . '/..' . '/symfony/console/ConsoleEvents.php', 'Symfony\\Component\\Console\\Cursor' => __DIR__ . '/..' . '/symfony/console/Cursor.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\\ConsoleSignalEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleSignalEvent.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\\MissingInputException' => __DIR__ . '/..' . '/symfony/console/Exception/MissingInputException.php', 'Symfony\\Component\\Console\\Exception\\NamespaceNotFoundException' => __DIR__ . '/..' . '/symfony/console/Exception/NamespaceNotFoundException.php', 'Symfony\\Component\\Console\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/console/Exception/RuntimeException.php', 'Symfony\\Component\\Console\\Formatter\\NullOutputFormatter' => __DIR__ . '/..' . '/symfony/console/Formatter/NullOutputFormatter.php', 'Symfony\\Component\\Console\\Formatter\\NullOutputFormatterStyle' => __DIR__ . '/..' . '/symfony/console/Formatter/NullOutputFormatterStyle.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\\Formatter\\WrappableOutputFormatterInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/WrappableOutputFormatterInterface.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\\Dumper' => __DIR__ . '/..' . '/symfony/console/Helper/Dumper.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\\TableCellStyle' => __DIR__ . '/..' . '/symfony/console/Helper/TableCellStyle.php', 'Symfony\\Component\\Console\\Helper\\TableRows' => __DIR__ . '/..' . '/symfony/console/Helper/TableRows.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\\ConsoleSectionOutput' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleSectionOutput.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\\Output\\TrimmedBufferOutput' => __DIR__ . '/..' . '/symfony/console/Output/TrimmedBufferOutput.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\\SignalRegistry\\SignalRegistry' => __DIR__ . '/..' . '/symfony/console/SignalRegistry/SignalRegistry.php', 'Symfony\\Component\\Console\\SingleCommandApplication' => __DIR__ . '/..' . '/symfony/console/SingleCommandApplication.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\\CommandCompletionTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandCompletionTester.php', 'Symfony\\Component\\Console\\Tester\\CommandTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandTester.php', 'Symfony\\Component\\Console\\Tester\\Constraint\\CommandIsSuccessful' => __DIR__ . '/..' . '/symfony/console/Tester/Constraint/CommandIsSuccessful.php', 'Symfony\\Component\\Console\\Tester\\TesterTrait' => __DIR__ . '/..' . '/symfony/console/Tester/TesterTrait.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\\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\\DomCrawler\\Test\\Constraint\\CrawlerSelectorAttributeValueSame' => __DIR__ . '/..' . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorAttributeValueSame.php', 'Symfony\\Component\\DomCrawler\\Test\\Constraint\\CrawlerSelectorExists' => __DIR__ . '/..' . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorExists.php', 'Symfony\\Component\\DomCrawler\\Test\\Constraint\\CrawlerSelectorTextContains' => __DIR__ . '/..' . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextContains.php', 'Symfony\\Component\\DomCrawler\\Test\\Constraint\\CrawlerSelectorTextSame' => __DIR__ . '/..' . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextSame.php', 'Symfony\\Component\\DomCrawler\\UriResolver' => __DIR__ . '/..' . '/symfony/dom-crawler/UriResolver.php', 'Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Attribute/AsEventListener.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/WrappedListener.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\AddEventAliasesPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.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\\DirectoryNotFoundException' => __DIR__ . '/..' . '/symfony/finder/Exception/DirectoryNotFoundException.php', 'Symfony\\Component\\Finder\\Finder' => __DIR__ . '/..' . '/symfony/finder/Finder.php', 'Symfony\\Component\\Finder\\Gitignore' => __DIR__ . '/..' . '/symfony/finder/Gitignore.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\\LazyIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/LazyIterator.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\\Iterator\\VcsIgnoredFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/VcsIgnoredFilterIterator.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\\ProcessSignaledException' => __DIR__ . '/..' . '/symfony/process/Exception/ProcessSignaledException.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\\ProcessUtils' => __DIR__ . '/..' . '/symfony/process/ProcessUtils.php', 'Symfony\\Component\\String\\AbstractString' => __DIR__ . '/..' . '/symfony/string/AbstractString.php', 'Symfony\\Component\\String\\AbstractUnicodeString' => __DIR__ . '/..' . '/symfony/string/AbstractUnicodeString.php', 'Symfony\\Component\\String\\ByteString' => __DIR__ . '/..' . '/symfony/string/ByteString.php', 'Symfony\\Component\\String\\CodePointString' => __DIR__ . '/..' . '/symfony/string/CodePointString.php', 'Symfony\\Component\\String\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/string/Exception/ExceptionInterface.php', 'Symfony\\Component\\String\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/string/Exception/InvalidArgumentException.php', 'Symfony\\Component\\String\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/string/Exception/RuntimeException.php', 'Symfony\\Component\\String\\Inflector\\EnglishInflector' => __DIR__ . '/..' . '/symfony/string/Inflector/EnglishInflector.php', 'Symfony\\Component\\String\\Inflector\\FrenchInflector' => __DIR__ . '/..' . '/symfony/string/Inflector/FrenchInflector.php', 'Symfony\\Component\\String\\Inflector\\InflectorInterface' => __DIR__ . '/..' . '/symfony/string/Inflector/InflectorInterface.php', 'Symfony\\Component\\String\\LazyString' => __DIR__ . '/..' . '/symfony/string/LazyString.php', 'Symfony\\Component\\String\\Slugger\\AsciiSlugger' => __DIR__ . '/..' . '/symfony/string/Slugger/AsciiSlugger.php', 'Symfony\\Component\\String\\Slugger\\SluggerInterface' => __DIR__ . '/..' . '/symfony/string/Slugger/SluggerInterface.php', 'Symfony\\Component\\String\\UnicodeString' => __DIR__ . '/..' . '/symfony/string/UnicodeString.php', 'Symfony\\Component\\VarDumper\\Caster\\AmqpCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/AmqpCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ArgsStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ArgsStub.php', 'Symfony\\Component\\VarDumper\\Caster\\Caster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/Caster.php', 'Symfony\\Component\\VarDumper\\Caster\\ClassStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ClassStub.php', 'Symfony\\Component\\VarDumper\\Caster\\ConstStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ConstStub.php', 'Symfony\\Component\\VarDumper\\Caster\\CutArrayStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/CutArrayStub.php', 'Symfony\\Component\\VarDumper\\Caster\\CutStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/CutStub.php', 'Symfony\\Component\\VarDumper\\Caster\\DOMCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DOMCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DateCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DateCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DoctrineCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DoctrineCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DsCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DsCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DsPairStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DsPairStub.php', 'Symfony\\Component\\VarDumper\\Caster\\EnumStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/EnumStub.php', 'Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ExceptionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\FiberCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/FiberCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\FrameStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/FrameStub.php', 'Symfony\\Component\\VarDumper\\Caster\\GmpCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/GmpCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ImagineCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ImagineCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ImgStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ImgStub.php', 'Symfony\\Component\\VarDumper\\Caster\\IntlCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/IntlCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\LinkStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/LinkStub.php', 'Symfony\\Component\\VarDumper\\Caster\\MemcachedCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/MemcachedCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\MysqliCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/MysqliCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PdoCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/PdoCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PgSqlCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/PgSqlCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ProxyManagerCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ProxyManagerCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\RdKafkaCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/RdKafkaCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\RedisCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/RedisCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ReflectionCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ReflectionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ResourceCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ResourceCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\SplCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/SplCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\StubCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/StubCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\SymfonyCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/SymfonyCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\TraceStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/TraceStub.php', 'Symfony\\Component\\VarDumper\\Caster\\UuidCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/UuidCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlReaderCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/XmlReaderCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlResourceCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/XmlResourceCaster.php', 'Symfony\\Component\\VarDumper\\Cloner\\AbstractCloner' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/AbstractCloner.php', 'Symfony\\Component\\VarDumper\\Cloner\\ClonerInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/ClonerInterface.php', 'Symfony\\Component\\VarDumper\\Cloner\\Cursor' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/Cursor.php', 'Symfony\\Component\\VarDumper\\Cloner\\Data' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/Data.php', 'Symfony\\Component\\VarDumper\\Cloner\\DumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/DumperInterface.php', 'Symfony\\Component\\VarDumper\\Cloner\\Stub' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/Stub.php', 'Symfony\\Component\\VarDumper\\Cloner\\VarCloner' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/VarCloner.php', 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\CliDescriptor' => __DIR__ . '/..' . '/symfony/var-dumper/Command/Descriptor/CliDescriptor.php', 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\DumpDescriptorInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php', 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\HtmlDescriptor' => __DIR__ . '/..' . '/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php', 'Symfony\\Component\\VarDumper\\Command\\ServerDumpCommand' => __DIR__ . '/..' . '/symfony/var-dumper/Command/ServerDumpCommand.php', 'Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/AbstractDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\CliDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/CliDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\CliContextProvider' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\ContextProviderInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\RequestContextProvider' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\SourceContextProvider' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextualizedDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextualizedDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/DataDumperInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/HtmlDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\ServerDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ServerDumper.php', 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => __DIR__ . '/..' . '/symfony/var-dumper/Exception/ThrowingCasterException.php', 'Symfony\\Component\\VarDumper\\Server\\Connection' => __DIR__ . '/..' . '/symfony/var-dumper/Server/Connection.php', 'Symfony\\Component\\VarDumper\\Server\\DumpServer' => __DIR__ . '/..' . '/symfony/var-dumper/Server/DumpServer.php', 'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => __DIR__ . '/..' . '/symfony/var-dumper/Test/VarDumperTestTrait.php', 'Symfony\\Component\\VarDumper\\VarDumper' => __DIR__ . '/..' . '/symfony/var-dumper/VarDumper.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\\Contracts\\EventDispatcher\\Event' => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts/Event.php', 'Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts/EventDispatcherInterface.php', 'Symfony\\Contracts\\Service\\Attribute\\Required' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/Required.php', 'Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/SubscribedService.php', 'Symfony\\Contracts\\Service\\ResetInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ResetInterface.php', 'Symfony\\Contracts\\Service\\ServiceLocatorTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceLocatorTrait.php', 'Symfony\\Contracts\\Service\\ServiceProviderInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceProviderInterface.php', 'Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberInterface.php', 'Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberTrait.php', 'Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => __DIR__ . '/..' . '/symfony/service-contracts/Test/ServiceLocatorTest.php', 'Symfony\\Polyfill\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php', 'Symfony\\Polyfill\\Intl\\Grapheme\\Grapheme' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/Grapheme.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\\Php80\\Php80' => __DIR__ . '/..' . '/symfony/polyfill-php80/Php80.php', 'Symfony\\Polyfill\\Php80\\PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/PhpToken.php', 'Symfony\\Polyfill\\Php81\\Php81' => __DIR__ . '/..' . '/symfony/polyfill-php81/Php81.php', 'TheSeer\\Tokenizer\\Exception' => __DIR__ . '/..' . '/theseer/tokenizer/src/Exception.php', 'TheSeer\\Tokenizer\\NamespaceUri' => __DIR__ . '/..' . '/theseer/tokenizer/src/NamespaceUri.php', 'TheSeer\\Tokenizer\\NamespaceUriException' => __DIR__ . '/..' . '/theseer/tokenizer/src/NamespaceUriException.php', 'TheSeer\\Tokenizer\\Token' => __DIR__ . '/..' . '/theseer/tokenizer/src/Token.php', 'TheSeer\\Tokenizer\\TokenCollection' => __DIR__ . '/..' . '/theseer/tokenizer/src/TokenCollection.php', 'TheSeer\\Tokenizer\\TokenCollectionException' => __DIR__ . '/..' . '/theseer/tokenizer/src/TokenCollectionException.php', 'TheSeer\\Tokenizer\\Tokenizer' => __DIR__ . '/..' . '/theseer/tokenizer/src/Tokenizer.php', 'TheSeer\\Tokenizer\\XMLSerializer' => __DIR__ . '/..' . '/theseer/tokenizer/src/XMLSerializer.php', 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.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 = ComposerStaticInit6df945c87d8c91a0cdc33b5e2833ee2d::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit6df945c87d8c91a0cdc33b5e2833ee2d::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInit6df945c87d8c91a0cdc33b5e2833ee2d::$prefixesPsr0; $loader->classMap = ComposerStaticInit6df945c87d8c91a0cdc33b5e2833ee2d::$classMap; }, null, ClassLoader::class); } } $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.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', 'CURLStringFile' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/CURLStringFile.php', 'Codeception\\Actor' => $vendorDir . '/codeception/codeception/src/Codeception/Actor.php', 'Codeception\\Application' => $vendorDir . '/codeception/codeception/src/Codeception/Application.php', 'Codeception\\Attribute\\After' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/After.php', 'Codeception\\Attribute\\AfterClass' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/AfterClass.php', 'Codeception\\Attribute\\Before' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Before.php', 'Codeception\\Attribute\\BeforeClass' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/BeforeClass.php', 'Codeception\\Attribute\\DataProvider' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/DataProvider.php', 'Codeception\\Attribute\\Depends' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Depends.php', 'Codeception\\Attribute\\Env' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Env.php', 'Codeception\\Attribute\\Examples' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Examples.php', 'Codeception\\Attribute\\Given' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Given.php', 'Codeception\\Attribute\\Group' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Group.php', 'Codeception\\Attribute\\Incomplete' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Incomplete.php', 'Codeception\\Attribute\\Prepare' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Prepare.php', 'Codeception\\Attribute\\Skip' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Skip.php', 'Codeception\\Attribute\\Then' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/Then.php', 'Codeception\\Attribute\\When' => $vendorDir . '/codeception/codeception/src/Codeception/Attribute/When.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\\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\\ActorTrait' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Shared/ActorTrait.php', 'Codeception\\Command\\Shared\\ConfigTrait' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Shared/ConfigTrait.php', 'Codeception\\Command\\Shared\\FileSystemTrait' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Shared/FileSystemTrait.php', 'Codeception\\Command\\Shared\\StyleTrait' => $vendorDir . '/codeception/codeception/src/Codeception/Command/Shared/StyleTrait.php', 'Codeception\\Configuration' => $vendorDir . '/codeception/codeception/src/Codeception/Configuration.php', 'Codeception\\Constraint\\Crawler' => $vendorDir . '/codeception/lib-innerbrowser/src/Codeception/Constraint/Crawler.php', 'Codeception\\Constraint\\CrawlerNot' => $vendorDir . '/codeception/lib-innerbrowser/src/Codeception/Constraint/CrawlerNot.php', 'Codeception\\Constraint\\Page' => $vendorDir . '/codeception/lib-web/src/Constraint/Page.php', 'Codeception\\Constraint\\WebDriver' => $vendorDir . '/codeception/module-webdriver/src/Codeception/Constraint/WebDriver.php', 'Codeception\\Constraint\\WebDriverNot' => $vendorDir . '/codeception/module-webdriver/src/Codeception/Constraint/WebDriverNot.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\\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\\Deprecation' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/Deprecation.php', 'Codeception\\Exception\\ElementNotFound' => $vendorDir . '/codeception/lib-web/src/Exception/ElementNotFound.php', 'Codeception\\Exception\\Error' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/Error.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\\InvalidTestException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/InvalidTestException.php', 'Codeception\\Exception\\MalformedLocatorException' => $vendorDir . '/codeception/lib-web/src/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\\Notice' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/Notice.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\\Exception\\ThrowableWrapper' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/ThrowableWrapper.php', 'Codeception\\Exception\\UselessTestException' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/UselessTestException.php', 'Codeception\\Exception\\Warning' => $vendorDir . '/codeception/codeception/src/Codeception/Exception/Warning.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\\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\\ConsolePrinter' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/ConsolePrinter.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/lib-web/src/Lib/Interfaces/ElementLocator.php', 'Codeception\\Lib\\Interfaces\\MultiSession' => $vendorDir . '/codeception/lib-web/src/Lib/Interfaces/MultiSession.php', 'Codeception\\Lib\\Interfaces\\ORM' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/ORM.php', 'Codeception\\Lib\\Interfaces\\PageSourceSaver' => $vendorDir . '/codeception/lib-web/src/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/lib-web/src/Lib/Interfaces/Remote.php', 'Codeception\\Lib\\Interfaces\\RequiresPackage' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/Interfaces/RequiresPackage.php', 'Codeception\\Lib\\Interfaces\\ScreenshotSaver' => $vendorDir . '/codeception/lib-web/src/Lib/Interfaces/ScreenshotSaver.php', 'Codeception\\Lib\\Interfaces\\SessionSnapshot' => $vendorDir . '/codeception/lib-web/src/Lib/Interfaces/SessionSnapshot.php', 'Codeception\\Lib\\Interfaces\\Web' => $vendorDir . '/codeception/lib-web/src/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\\Lib\\PauseShell' => $vendorDir . '/codeception/codeception/src/Codeception/Lib/PauseShell.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\\Constraint\\JsonContains' => $vendorDir . '/codeception/module-rest/src/Codeception/Constraint/JsonContains.php', 'Codeception\\PHPUnit\\Constraint\\JsonType' => $vendorDir . '/codeception/module-rest/src/Codeception/Constraint/JsonType.php', 'Codeception\\PHPUnit\\TestCase' => $vendorDir . '/codeception/codeception/src/PHPUnit/TestCase.php', 'Codeception\\Reporter\\HtmlReporter' => $vendorDir . '/codeception/codeception/src/Codeception/Reporter/HtmlReporter.php', 'Codeception\\Reporter\\JUnitReporter' => $vendorDir . '/codeception/codeception/src/Codeception/Reporter/JUnitReporter.php', 'Codeception\\Reporter\\PhpUnitReporter' => $vendorDir . '/codeception/codeception/src/Codeception/Reporter/PhpUnitReporter.php', 'Codeception\\Reporter\\ReportPrinter' => $vendorDir . '/codeception/codeception/src/Codeception/Reporter/ReportPrinter.php', 'Codeception\\ResultAggregator' => $vendorDir . '/codeception/codeception/src/Codeception/ResultAggregator.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\\AsJson' => $vendorDir . '/codeception/module-rest/src/Codeception/Step/AsJson.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\\Deprecation' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/Deprecation.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\\StaticEventsTrait' => $vendorDir . '/codeception/codeception/src/Codeception/Subscriber/Shared/StaticEventsTrait.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\\Dependencies' => $vendorDir . '/codeception/codeception/src/Codeception/Template/Dependencies.php', 'Codeception\\Template\\Unit' => $vendorDir . '/codeception/codeception/src/Codeception/Template/Unit.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\\DataProvider' => $vendorDir . '/codeception/codeception/src/Codeception/Test/DataProvider.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\\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\\Filter' => $vendorDir . '/codeception/codeception/src/Codeception/Test/Filter.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\\TestCaseWrapper' => $vendorDir . '/codeception/codeception/src/Codeception/Test/TestCaseWrapper.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/module-rest/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/lib-web/src/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\\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/lib-xml/src/Util/Soap.php', 'Codeception\\Util\\StackTraceFilter' => $vendorDir . '/codeception/codeception/src/Codeception/Util/StackTraceFilter.php', 'Codeception\\Util\\Template' => $vendorDir . '/codeception/codeception/src/Codeception/Util/Template.php', 'Codeception\\Util\\Uri' => $vendorDir . '/codeception/lib-web/src/Util/Uri.php', 'Codeception\\Util\\Xml' => $vendorDir . '/codeception/lib-xml/src/Util/Xml.php', 'Codeception\\Util\\XmlBuilder' => $vendorDir . '/codeception/lib-xml/src/Util/XmlBuilder.php', 'Codeception\\Util\\XmlStructure' => $vendorDir . '/codeception/lib-xml/src/Util/XmlStructure.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\\ChainableFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.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\\ArrayObjectFilter' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.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', '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', '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\\DetachedShadowRootException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/DetachedShadowRootException.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\\Internal\\DriverServerDiedException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/Internal/DriverServerDiedException.php', 'Facebook\\WebDriver\\Exception\\Internal\\IOException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/Internal/IOException.php', 'Facebook\\WebDriver\\Exception\\Internal\\LogicException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/Internal/LogicException.php', 'Facebook\\WebDriver\\Exception\\Internal\\RuntimeException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/Internal/RuntimeException.php', 'Facebook\\WebDriver\\Exception\\Internal\\UnexpectedResponseException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/Internal/UnexpectedResponseException.php', 'Facebook\\WebDriver\\Exception\\Internal\\WebDriverCurlException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/Internal/WebDriverCurlException.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\\NoSuchShadowRootException' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/NoSuchShadowRootException.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\\PhpWebDriverExceptionInterface' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/PhpWebDriverExceptionInterface.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\\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\\ShadowRoot' => $vendorDir . '/php-webdriver/webdriver/lib/Remote/ShadowRoot.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\\IsElementDisplayedAtom' => $vendorDir . '/php-webdriver/webdriver/lib/Support/IsElementDisplayedAtom.php', 'Facebook\\WebDriver\\Support\\ScreenshotHelper' => $vendorDir . '/php-webdriver/webdriver/lib/Support/ScreenshotHelper.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', '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\\BodySummarizer' => $vendorDir . '/guzzlehttp/guzzle/src/BodySummarizer.php', 'GuzzleHttp\\BodySummarizerInterface' => $vendorDir . '/guzzlehttp/guzzle/src/BodySummarizerInterface.php', 'GuzzleHttp\\Client' => $vendorDir . '/guzzlehttp/guzzle/src/Client.php', 'GuzzleHttp\\ClientInterface' => $vendorDir . '/guzzlehttp/guzzle/src/ClientInterface.php', 'GuzzleHttp\\ClientTrait' => $vendorDir . '/guzzlehttp/guzzle/src/ClientTrait.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\\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\\HeaderProcessor' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.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\\MessageFormatterInterface' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatterInterface.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\\Exception\\MalformedUriException' => $vendorDir . '/guzzlehttp/psr7/src/Exception/MalformedUriException.php', 'GuzzleHttp\\Psr7\\FnStream' => $vendorDir . '/guzzlehttp/psr7/src/FnStream.php', 'GuzzleHttp\\Psr7\\Header' => $vendorDir . '/guzzlehttp/psr7/src/Header.php', 'GuzzleHttp\\Psr7\\HttpFactory' => $vendorDir . '/guzzlehttp/psr7/src/HttpFactory.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\\Utils' => $vendorDir . '/guzzlehttp/guzzle/src/Utils.php', 'Humbug\\SelfUpdate\\Exception\\ExceptionInterface' => $vendorDir . '/laravel-zero/phar-updater/src/Exception/ExceptionInterface.php', 'Humbug\\SelfUpdate\\Exception\\FilesystemException' => $vendorDir . '/laravel-zero/phar-updater/src/Exception/FilesystemException.php', 'Humbug\\SelfUpdate\\Exception\\HttpRequestException' => $vendorDir . '/laravel-zero/phar-updater/src/Exception/HttpRequestException.php', 'Humbug\\SelfUpdate\\Exception\\InvalidArgumentException' => $vendorDir . '/laravel-zero/phar-updater/src/Exception/InvalidArgumentException.php', 'Humbug\\SelfUpdate\\Exception\\JsonParsingException' => $vendorDir . '/laravel-zero/phar-updater/src/Exception/JsonParsingException.php', 'Humbug\\SelfUpdate\\Exception\\NoSignatureException' => $vendorDir . '/laravel-zero/phar-updater/src/Exception/NoSignatureException.php', 'Humbug\\SelfUpdate\\Exception\\RuntimeException' => $vendorDir . '/laravel-zero/phar-updater/src/Exception/RuntimeException.php', 'Humbug\\SelfUpdate\\Strategy\\DirectDownloadStrategyAbstract' => $vendorDir . '/laravel-zero/phar-updater/src/Strategy/DirectDownloadStrategyAbstract.php', 'Humbug\\SelfUpdate\\Strategy\\GithubStrategy' => $vendorDir . '/laravel-zero/phar-updater/src/Strategy/GithubStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\Sha256Strategy' => $vendorDir . '/laravel-zero/phar-updater/src/Strategy/Sha256Strategy.php', 'Humbug\\SelfUpdate\\Strategy\\Sha512Strategy' => $vendorDir . '/laravel-zero/phar-updater/src/Strategy/Sha512Strategy.php', 'Humbug\\SelfUpdate\\Strategy\\ShaStrategy' => $vendorDir . '/laravel-zero/phar-updater/src/Strategy/ShaStrategy.php', 'Humbug\\SelfUpdate\\Strategy\\ShaStrategyAbstract' => $vendorDir . '/laravel-zero/phar-updater/src/Strategy/ShaStrategyAbstract.php', 'Humbug\\SelfUpdate\\Strategy\\StrategyInterface' => $vendorDir . '/laravel-zero/phar-updater/src/Strategy/StrategyInterface.php', 'Humbug\\SelfUpdate\\Updater' => $vendorDir . '/laravel-zero/phar-updater/src/Updater.php', 'Humbug\\SelfUpdate\\VersionParser' => $vendorDir . '/laravel-zero/phar-updater/src/VersionParser.php', 'Jean85\\Exception\\ProvidedPackageException' => $vendorDir . '/jean85/pretty-package-versions/src/Exception/ProvidedPackageException.php', 'Jean85\\Exception\\ReplacedPackageException' => $vendorDir . '/jean85/pretty-package-versions/src/Exception/ReplacedPackageException.php', 'Jean85\\Exception\\VersionMissingExceptionInterface' => $vendorDir . '/jean85/pretty-package-versions/src/Exception/VersionMissingExceptionInterface.php', 'Jean85\\PrettyVersions' => $vendorDir . '/jean85/pretty-package-versions/src/PrettyVersions.php', 'Jean85\\Version' => $vendorDir . '/jean85/pretty-package-versions/src/Version.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', 'MongoDB\\BulkWriteResult' => $vendorDir . '/mongodb/mongodb/src/BulkWriteResult.php', 'MongoDB\\ChangeStream' => $vendorDir . '/mongodb/mongodb/src/ChangeStream.php', 'MongoDB\\Client' => $vendorDir . '/mongodb/mongodb/src/Client.php', 'MongoDB\\Codec\\Codec' => $vendorDir . '/mongodb/mongodb/src/Codec/Codec.php', 'MongoDB\\Codec\\DecodeIfSupported' => $vendorDir . '/mongodb/mongodb/src/Codec/DecodeIfSupported.php', 'MongoDB\\Codec\\Decoder' => $vendorDir . '/mongodb/mongodb/src/Codec/Decoder.php', 'MongoDB\\Codec\\DocumentCodec' => $vendorDir . '/mongodb/mongodb/src/Codec/DocumentCodec.php', 'MongoDB\\Codec\\EncodeIfSupported' => $vendorDir . '/mongodb/mongodb/src/Codec/EncodeIfSupported.php', 'MongoDB\\Codec\\Encoder' => $vendorDir . '/mongodb/mongodb/src/Codec/Encoder.php', 'MongoDB\\Collection' => $vendorDir . '/mongodb/mongodb/src/Collection.php', 'MongoDB\\Command\\ListCollections' => $vendorDir . '/mongodb/mongodb/src/Command/ListCollections.php', 'MongoDB\\Command\\ListDatabases' => $vendorDir . '/mongodb/mongodb/src/Command/ListDatabases.php', 'MongoDB\\Database' => $vendorDir . '/mongodb/mongodb/src/Database.php', 'MongoDB\\DeleteResult' => $vendorDir . '/mongodb/mongodb/src/DeleteResult.php', 'MongoDB\\Exception\\BadMethodCallException' => $vendorDir . '/mongodb/mongodb/src/Exception/BadMethodCallException.php', 'MongoDB\\Exception\\CreateEncryptedCollectionException' => $vendorDir . '/mongodb/mongodb/src/Exception/CreateEncryptedCollectionException.php', 'MongoDB\\Exception\\Exception' => $vendorDir . '/mongodb/mongodb/src/Exception/Exception.php', 'MongoDB\\Exception\\InvalidArgumentException' => $vendorDir . '/mongodb/mongodb/src/Exception/InvalidArgumentException.php', 'MongoDB\\Exception\\ResumeTokenException' => $vendorDir . '/mongodb/mongodb/src/Exception/ResumeTokenException.php', 'MongoDB\\Exception\\RuntimeException' => $vendorDir . '/mongodb/mongodb/src/Exception/RuntimeException.php', 'MongoDB\\Exception\\UnexpectedValueException' => $vendorDir . '/mongodb/mongodb/src/Exception/UnexpectedValueException.php', 'MongoDB\\Exception\\UnsupportedException' => $vendorDir . '/mongodb/mongodb/src/Exception/UnsupportedException.php', 'MongoDB\\Exception\\UnsupportedValueException' => $vendorDir . '/mongodb/mongodb/src/Exception/UnsupportedValueException.php', 'MongoDB\\GridFS\\Bucket' => $vendorDir . '/mongodb/mongodb/src/GridFS/Bucket.php', 'MongoDB\\GridFS\\CollectionWrapper' => $vendorDir . '/mongodb/mongodb/src/GridFS/CollectionWrapper.php', 'MongoDB\\GridFS\\Exception\\CorruptFileException' => $vendorDir . '/mongodb/mongodb/src/GridFS/Exception/CorruptFileException.php', 'MongoDB\\GridFS\\Exception\\FileNotFoundException' => $vendorDir . '/mongodb/mongodb/src/GridFS/Exception/FileNotFoundException.php', 'MongoDB\\GridFS\\Exception\\StreamException' => $vendorDir . '/mongodb/mongodb/src/GridFS/Exception/StreamException.php', 'MongoDB\\GridFS\\ReadableStream' => $vendorDir . '/mongodb/mongodb/src/GridFS/ReadableStream.php', 'MongoDB\\GridFS\\StreamWrapper' => $vendorDir . '/mongodb/mongodb/src/GridFS/StreamWrapper.php', 'MongoDB\\GridFS\\WritableStream' => $vendorDir . '/mongodb/mongodb/src/GridFS/WritableStream.php', 'MongoDB\\InsertManyResult' => $vendorDir . '/mongodb/mongodb/src/InsertManyResult.php', 'MongoDB\\InsertOneResult' => $vendorDir . '/mongodb/mongodb/src/InsertOneResult.php', 'MongoDB\\MapReduceResult' => $vendorDir . '/mongodb/mongodb/src/MapReduceResult.php', 'MongoDB\\Model\\BSONArray' => $vendorDir . '/mongodb/mongodb/src/Model/BSONArray.php', 'MongoDB\\Model\\BSONDocument' => $vendorDir . '/mongodb/mongodb/src/Model/BSONDocument.php', 'MongoDB\\Model\\BSONIterator' => $vendorDir . '/mongodb/mongodb/src/Model/BSONIterator.php', 'MongoDB\\Model\\CachingIterator' => $vendorDir . '/mongodb/mongodb/src/Model/CachingIterator.php', 'MongoDB\\Model\\CallbackIterator' => $vendorDir . '/mongodb/mongodb/src/Model/CallbackIterator.php', 'MongoDB\\Model\\ChangeStreamIterator' => $vendorDir . '/mongodb/mongodb/src/Model/ChangeStreamIterator.php', 'MongoDB\\Model\\CodecCursor' => $vendorDir . '/mongodb/mongodb/src/Model/CodecCursor.php', 'MongoDB\\Model\\CollectionInfo' => $vendorDir . '/mongodb/mongodb/src/Model/CollectionInfo.php', 'MongoDB\\Model\\CollectionInfoCommandIterator' => $vendorDir . '/mongodb/mongodb/src/Model/CollectionInfoCommandIterator.php', 'MongoDB\\Model\\CollectionInfoIterator' => $vendorDir . '/mongodb/mongodb/src/Model/CollectionInfoIterator.php', 'MongoDB\\Model\\DatabaseInfo' => $vendorDir . '/mongodb/mongodb/src/Model/DatabaseInfo.php', 'MongoDB\\Model\\DatabaseInfoIterator' => $vendorDir . '/mongodb/mongodb/src/Model/DatabaseInfoIterator.php', 'MongoDB\\Model\\DatabaseInfoLegacyIterator' => $vendorDir . '/mongodb/mongodb/src/Model/DatabaseInfoLegacyIterator.php', 'MongoDB\\Model\\IndexInfo' => $vendorDir . '/mongodb/mongodb/src/Model/IndexInfo.php', 'MongoDB\\Model\\IndexInfoIterator' => $vendorDir . '/mongodb/mongodb/src/Model/IndexInfoIterator.php', 'MongoDB\\Model\\IndexInfoIteratorIterator' => $vendorDir . '/mongodb/mongodb/src/Model/IndexInfoIteratorIterator.php', 'MongoDB\\Model\\IndexInput' => $vendorDir . '/mongodb/mongodb/src/Model/IndexInput.php', 'MongoDB\\Model\\SearchIndexInput' => $vendorDir . '/mongodb/mongodb/src/Model/SearchIndexInput.php', 'MongoDB\\Operation\\Aggregate' => $vendorDir . '/mongodb/mongodb/src/Operation/Aggregate.php', 'MongoDB\\Operation\\BulkWrite' => $vendorDir . '/mongodb/mongodb/src/Operation/BulkWrite.php', 'MongoDB\\Operation\\Count' => $vendorDir . '/mongodb/mongodb/src/Operation/Count.php', 'MongoDB\\Operation\\CountDocuments' => $vendorDir . '/mongodb/mongodb/src/Operation/CountDocuments.php', 'MongoDB\\Operation\\CreateCollection' => $vendorDir . '/mongodb/mongodb/src/Operation/CreateCollection.php', 'MongoDB\\Operation\\CreateEncryptedCollection' => $vendorDir . '/mongodb/mongodb/src/Operation/CreateEncryptedCollection.php', 'MongoDB\\Operation\\CreateIndexes' => $vendorDir . '/mongodb/mongodb/src/Operation/CreateIndexes.php', 'MongoDB\\Operation\\CreateSearchIndexes' => $vendorDir . '/mongodb/mongodb/src/Operation/CreateSearchIndexes.php', 'MongoDB\\Operation\\DatabaseCommand' => $vendorDir . '/mongodb/mongodb/src/Operation/DatabaseCommand.php', 'MongoDB\\Operation\\Delete' => $vendorDir . '/mongodb/mongodb/src/Operation/Delete.php', 'MongoDB\\Operation\\DeleteMany' => $vendorDir . '/mongodb/mongodb/src/Operation/DeleteMany.php', 'MongoDB\\Operation\\DeleteOne' => $vendorDir . '/mongodb/mongodb/src/Operation/DeleteOne.php', 'MongoDB\\Operation\\Distinct' => $vendorDir . '/mongodb/mongodb/src/Operation/Distinct.php', 'MongoDB\\Operation\\DropCollection' => $vendorDir . '/mongodb/mongodb/src/Operation/DropCollection.php', 'MongoDB\\Operation\\DropDatabase' => $vendorDir . '/mongodb/mongodb/src/Operation/DropDatabase.php', 'MongoDB\\Operation\\DropEncryptedCollection' => $vendorDir . '/mongodb/mongodb/src/Operation/DropEncryptedCollection.php', 'MongoDB\\Operation\\DropIndexes' => $vendorDir . '/mongodb/mongodb/src/Operation/DropIndexes.php', 'MongoDB\\Operation\\DropSearchIndex' => $vendorDir . '/mongodb/mongodb/src/Operation/DropSearchIndex.php', 'MongoDB\\Operation\\EstimatedDocumentCount' => $vendorDir . '/mongodb/mongodb/src/Operation/EstimatedDocumentCount.php', 'MongoDB\\Operation\\Executable' => $vendorDir . '/mongodb/mongodb/src/Operation/Executable.php', 'MongoDB\\Operation\\Explain' => $vendorDir . '/mongodb/mongodb/src/Operation/Explain.php', 'MongoDB\\Operation\\Explainable' => $vendorDir . '/mongodb/mongodb/src/Operation/Explainable.php', 'MongoDB\\Operation\\Find' => $vendorDir . '/mongodb/mongodb/src/Operation/Find.php', 'MongoDB\\Operation\\FindAndModify' => $vendorDir . '/mongodb/mongodb/src/Operation/FindAndModify.php', 'MongoDB\\Operation\\FindOne' => $vendorDir . '/mongodb/mongodb/src/Operation/FindOne.php', 'MongoDB\\Operation\\FindOneAndDelete' => $vendorDir . '/mongodb/mongodb/src/Operation/FindOneAndDelete.php', 'MongoDB\\Operation\\FindOneAndReplace' => $vendorDir . '/mongodb/mongodb/src/Operation/FindOneAndReplace.php', 'MongoDB\\Operation\\FindOneAndUpdate' => $vendorDir . '/mongodb/mongodb/src/Operation/FindOneAndUpdate.php', 'MongoDB\\Operation\\InsertMany' => $vendorDir . '/mongodb/mongodb/src/Operation/InsertMany.php', 'MongoDB\\Operation\\InsertOne' => $vendorDir . '/mongodb/mongodb/src/Operation/InsertOne.php', 'MongoDB\\Operation\\ListCollectionNames' => $vendorDir . '/mongodb/mongodb/src/Operation/ListCollectionNames.php', 'MongoDB\\Operation\\ListCollections' => $vendorDir . '/mongodb/mongodb/src/Operation/ListCollections.php', 'MongoDB\\Operation\\ListDatabaseNames' => $vendorDir . '/mongodb/mongodb/src/Operation/ListDatabaseNames.php', 'MongoDB\\Operation\\ListDatabases' => $vendorDir . '/mongodb/mongodb/src/Operation/ListDatabases.php', 'MongoDB\\Operation\\ListIndexes' => $vendorDir . '/mongodb/mongodb/src/Operation/ListIndexes.php', 'MongoDB\\Operation\\ListSearchIndexes' => $vendorDir . '/mongodb/mongodb/src/Operation/ListSearchIndexes.php', 'MongoDB\\Operation\\MapReduce' => $vendorDir . '/mongodb/mongodb/src/Operation/MapReduce.php', 'MongoDB\\Operation\\ModifyCollection' => $vendorDir . '/mongodb/mongodb/src/Operation/ModifyCollection.php', 'MongoDB\\Operation\\RenameCollection' => $vendorDir . '/mongodb/mongodb/src/Operation/RenameCollection.php', 'MongoDB\\Operation\\ReplaceOne' => $vendorDir . '/mongodb/mongodb/src/Operation/ReplaceOne.php', 'MongoDB\\Operation\\Update' => $vendorDir . '/mongodb/mongodb/src/Operation/Update.php', 'MongoDB\\Operation\\UpdateMany' => $vendorDir . '/mongodb/mongodb/src/Operation/UpdateMany.php', 'MongoDB\\Operation\\UpdateOne' => $vendorDir . '/mongodb/mongodb/src/Operation/UpdateOne.php', 'MongoDB\\Operation\\UpdateSearchIndex' => $vendorDir . '/mongodb/mongodb/src/Operation/UpdateSearchIndex.php', 'MongoDB\\Operation\\Watch' => $vendorDir . '/mongodb/mongodb/src/Operation/Watch.php', 'MongoDB\\Operation\\WithTransaction' => $vendorDir . '/mongodb/mongodb/src/Operation/WithTransaction.php', 'MongoDB\\PsrLogAdapter' => $vendorDir . '/mongodb/mongodb/src/PsrLogAdapter.php', 'MongoDB\\UpdateResult' => $vendorDir . '/mongodb/mongodb/src/UpdateResult.php', 'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php', 'PHPUnit\\Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php', 'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php', 'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php', 'PHPUnit\\Framework\\AssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/AssertionFailedError.php', 'PHPUnit\\Framework\\CodeCoverageException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/CodeCoverageException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotAcceptParameterTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotAcceptParameterTypeException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareBoolReturnTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareBoolReturnTypeException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareExactlyOneParameterException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareExactlyOneParameterException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotDeclareParameterTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotDeclareParameterTypeException.php', 'PHPUnit\\Framework\\ComparisonMethodDoesNotExistException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ComparisonMethodDoesNotExistException.php', 'PHPUnit\\Framework\\Constraint\\ArrayHasKey' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/ArrayHasKey.php', 'PHPUnit\\Framework\\Constraint\\BinaryOperator' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/BinaryOperator.php', 'PHPUnit\\Framework\\Constraint\\Callback' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Callback.php', 'PHPUnit\\Framework\\Constraint\\ClassHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasAttribute.php', 'PHPUnit\\Framework\\Constraint\\ClassHasStaticAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ClassHasStaticAttribute.php', 'PHPUnit\\Framework\\Constraint\\Constraint' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Constraint.php', 'PHPUnit\\Framework\\Constraint\\Count' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/Count.php', 'PHPUnit\\Framework\\Constraint\\DirectoryExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/DirectoryExists.php', 'PHPUnit\\Framework\\Constraint\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/Exception.php', 'PHPUnit\\Framework\\Constraint\\ExceptionCode' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionCode.php', 'PHPUnit\\Framework\\Constraint\\ExceptionMessage' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessage.php', 'PHPUnit\\Framework\\Constraint\\ExceptionMessageRegularExpression' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Exception/ExceptionMessageRegularExpression.php', 'PHPUnit\\Framework\\Constraint\\FileExists' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/FileExists.php', 'PHPUnit\\Framework\\Constraint\\GreaterThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/GreaterThan.php', 'PHPUnit\\Framework\\Constraint\\IsAnything' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsAnything.php', 'PHPUnit\\Framework\\Constraint\\IsEmpty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/IsEmpty.php', 'PHPUnit\\Framework\\Constraint\\IsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqual.php', 'PHPUnit\\Framework\\Constraint\\IsEqualCanonicalizing' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualCanonicalizing.php', 'PHPUnit\\Framework\\Constraint\\IsEqualIgnoringCase' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualIgnoringCase.php', 'PHPUnit\\Framework\\Constraint\\IsEqualWithDelta' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php', 'PHPUnit\\Framework\\Constraint\\IsFalse' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsFalse.php', 'PHPUnit\\Framework\\Constraint\\IsFinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Math/IsFinite.php', 'PHPUnit\\Framework\\Constraint\\IsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/IsIdentical.php', 'PHPUnit\\Framework\\Constraint\\IsInfinite' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Math/IsInfinite.php', 'PHPUnit\\Framework\\Constraint\\IsInstanceOf' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Type/IsInstanceOf.php', 'PHPUnit\\Framework\\Constraint\\IsJson' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/IsJson.php', 'PHPUnit\\Framework\\Constraint\\IsNan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Math/IsNan.php', 'PHPUnit\\Framework\\Constraint\\IsNull' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Type/IsNull.php', 'PHPUnit\\Framework\\Constraint\\IsReadable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsReadable.php', 'PHPUnit\\Framework\\Constraint\\IsTrue' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Boolean/IsTrue.php', 'PHPUnit\\Framework\\Constraint\\IsType' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Type/IsType.php', 'PHPUnit\\Framework\\Constraint\\IsWritable' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Filesystem/IsWritable.php', 'PHPUnit\\Framework\\Constraint\\JsonMatches' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatches.php', 'PHPUnit\\Framework\\Constraint\\JsonMatchesErrorMessageProvider' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php', 'PHPUnit\\Framework\\Constraint\\LessThan' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/LessThan.php', 'PHPUnit\\Framework\\Constraint\\LogicalAnd' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalAnd.php', 'PHPUnit\\Framework\\Constraint\\LogicalNot' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalNot.php', 'PHPUnit\\Framework\\Constraint\\LogicalOr' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalOr.php', 'PHPUnit\\Framework\\Constraint\\LogicalXor' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/LogicalXor.php', 'PHPUnit\\Framework\\Constraint\\ObjectEquals' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectEquals.php', 'PHPUnit\\Framework\\Constraint\\ObjectHasAttribute' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasAttribute.php', 'PHPUnit\\Framework\\Constraint\\ObjectHasProperty' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Object/ObjectHasProperty.php', 'PHPUnit\\Framework\\Constraint\\Operator' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/Operator.php', 'PHPUnit\\Framework\\Constraint\\RegularExpression' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/RegularExpression.php', 'PHPUnit\\Framework\\Constraint\\SameSize' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Cardinality/SameSize.php', 'PHPUnit\\Framework\\Constraint\\StringContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringContains.php', 'PHPUnit\\Framework\\Constraint\\StringEndsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringEndsWith.php', 'PHPUnit\\Framework\\Constraint\\StringMatchesFormatDescription' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringMatchesFormatDescription.php', 'PHPUnit\\Framework\\Constraint\\StringStartsWith' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/String/StringStartsWith.php', 'PHPUnit\\Framework\\Constraint\\TraversableContains' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContains.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsEqual' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsEqual.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsIdentical' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsIdentical.php', 'PHPUnit\\Framework\\Constraint\\TraversableContainsOnly' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Traversable/TraversableContainsOnly.php', 'PHPUnit\\Framework\\Constraint\\UnaryOperator' => $vendorDir . '/phpunit/phpunit/src/Framework/Constraint/Operator/UnaryOperator.php', 'PHPUnit\\Framework\\CoveredCodeNotExecutedException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/CoveredCodeNotExecutedException.php', 'PHPUnit\\Framework\\DataProviderTestSuite' => $vendorDir . '/phpunit/phpunit/src/Framework/DataProviderTestSuite.php', 'PHPUnit\\Framework\\Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/Error.php', 'PHPUnit\\Framework\\ErrorTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/ErrorTestCase.php', 'PHPUnit\\Framework\\Error\\Deprecated' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Deprecated.php', 'PHPUnit\\Framework\\Error\\Error' => $vendorDir . '/phpunit/phpunit/src/Framework/Error/Error.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/Exception.php', 'PHPUnit\\Framework\\ExceptionWrapper' => $vendorDir . '/phpunit/phpunit/src/Framework/ExceptionWrapper.php', 'PHPUnit\\Framework\\ExecutionOrderDependency' => $vendorDir . '/phpunit/phpunit/src/Framework/ExecutionOrderDependency.php', 'PHPUnit\\Framework\\ExpectationFailedException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/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/Exception/IncompleteTestError.php', 'PHPUnit\\Framework\\InvalidArgumentException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/InvalidArgumentException.php', 'PHPUnit\\Framework\\InvalidCoversTargetException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/InvalidCoversTargetException.php', 'PHPUnit\\Framework\\InvalidDataProviderException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/InvalidDataProviderException.php', 'PHPUnit\\Framework\\InvalidParameterGroupException' => $vendorDir . '/phpunit/phpunit/src/Framework/InvalidParameterGroupException.php', 'PHPUnit\\Framework\\MissingCoversAnnotationException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/MissingCoversAnnotationException.php', 'PHPUnit\\Framework\\MockObject\\Api' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Api/Api.php', 'PHPUnit\\Framework\\MockObject\\BadMethodCallException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/BadMethodCallException.php', 'PHPUnit\\Framework\\MockObject\\Builder\\Identity' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/Identity.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationMocker.php', 'PHPUnit\\Framework\\MockObject\\Builder\\InvocationStubber' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/InvocationStubber.php', 'PHPUnit\\Framework\\MockObject\\Builder\\MethodNameMatch' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/MethodNameMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\ParametersMatch' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/ParametersMatch.php', 'PHPUnit\\Framework\\MockObject\\Builder\\Stub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Builder/Stub.php', 'PHPUnit\\Framework\\MockObject\\CannotUseAddMethodsException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseAddMethodsException.php', 'PHPUnit\\Framework\\MockObject\\CannotUseOnlyMethodsException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/CannotUseOnlyMethodsException.php', 'PHPUnit\\Framework\\MockObject\\ClassAlreadyExistsException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassAlreadyExistsException.php', 'PHPUnit\\Framework\\MockObject\\ClassIsFinalException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsFinalException.php', 'PHPUnit\\Framework\\MockObject\\ClassIsReadonlyException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ClassIsReadonlyException.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethod' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/ConfigurableMethod.php', 'PHPUnit\\Framework\\MockObject\\ConfigurableMethodsAlreadyInitializedException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php', 'PHPUnit\\Framework\\MockObject\\DuplicateMethodException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/DuplicateMethodException.php', 'PHPUnit\\Framework\\MockObject\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/Exception.php', 'PHPUnit\\Framework\\MockObject\\Generator' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Generator.php', 'PHPUnit\\Framework\\MockObject\\IncompatibleReturnValueException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php', 'PHPUnit\\Framework\\MockObject\\InvalidMethodNameException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/InvalidMethodNameException.php', 'PHPUnit\\Framework\\MockObject\\Invocation' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Invocation.php', 'PHPUnit\\Framework\\MockObject\\InvocationHandler' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/InvocationHandler.php', 'PHPUnit\\Framework\\MockObject\\MatchBuilderNotFoundException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatchBuilderNotFoundException.php', 'PHPUnit\\Framework\\MockObject\\Matcher' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Matcher.php', 'PHPUnit\\Framework\\MockObject\\MatcherAlreadyRegisteredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MatcherAlreadyRegisteredException.php', 'PHPUnit\\Framework\\MockObject\\Method' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Api/Method.php', 'PHPUnit\\Framework\\MockObject\\MethodCannotBeConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodCannotBeConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameAlreadyConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodNameConstraint' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MethodNameConstraint.php', 'PHPUnit\\Framework\\MockObject\\MethodNameNotConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodNameNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MethodParametersAlreadyConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/MethodParametersAlreadyConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\MockBuilder' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockBuilder.php', 'PHPUnit\\Framework\\MockObject\\MockClass' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockClass.php', 'PHPUnit\\Framework\\MockObject\\MockMethod' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockMethod.php', 'PHPUnit\\Framework\\MockObject\\MockMethodSet' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockMethodSet.php', 'PHPUnit\\Framework\\MockObject\\MockObject' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockObject.php', 'PHPUnit\\Framework\\MockObject\\MockTrait' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockTrait.php', 'PHPUnit\\Framework\\MockObject\\MockType' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/MockType.php', 'PHPUnit\\Framework\\MockObject\\OriginalConstructorInvocationRequiredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/OriginalConstructorInvocationRequiredException.php', 'PHPUnit\\Framework\\MockObject\\ReflectionException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReflectionException.php', 'PHPUnit\\Framework\\MockObject\\ReturnValueNotConfiguredException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/ReturnValueNotConfiguredException.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyInvokedCount' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyInvokedCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\AnyParameters' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/AnyParameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ConsecutiveParameters' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/ConsecutiveParameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvocationOrder' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvocationOrder.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtIndex' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtIndex.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastCount' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtLeastOnce' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtLeastOnce.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedAtMostCount' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedAtMostCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\InvokedCount' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/InvokedCount.php', 'PHPUnit\\Framework\\MockObject\\Rule\\MethodName' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/MethodName.php', 'PHPUnit\\Framework\\MockObject\\Rule\\Parameters' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/Parameters.php', 'PHPUnit\\Framework\\MockObject\\Rule\\ParametersRule' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Rule/ParametersRule.php', 'PHPUnit\\Framework\\MockObject\\RuntimeException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/RuntimeException.php', 'PHPUnit\\Framework\\MockObject\\SoapExtensionNotAvailableException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/SoapExtensionNotAvailableException.php', 'PHPUnit\\Framework\\MockObject\\Stub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ConsecutiveCalls' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ConsecutiveCalls.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Exception' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/Exception.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnArgument' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnArgument.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnCallback' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnCallback.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnReference' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnReference.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnSelf' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnSelf.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnStub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnStub.php', 'PHPUnit\\Framework\\MockObject\\Stub\\ReturnValueMap' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/ReturnValueMap.php', 'PHPUnit\\Framework\\MockObject\\Stub\\Stub' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Stub/Stub.php', 'PHPUnit\\Framework\\MockObject\\UnknownClassException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownClassException.php', 'PHPUnit\\Framework\\MockObject\\UnknownTraitException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTraitException.php', 'PHPUnit\\Framework\\MockObject\\UnknownTypeException' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Exception/UnknownTypeException.php', 'PHPUnit\\Framework\\MockObject\\Verifiable' => $vendorDir . '/phpunit/phpunit/src/Framework/MockObject/Verifiable.php', 'PHPUnit\\Framework\\NoChildTestSuiteException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/NoChildTestSuiteException.php', 'PHPUnit\\Framework\\OutputError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/OutputError.php', 'PHPUnit\\Framework\\PHPTAssertionFailedError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/PHPTAssertionFailedError.php', 'PHPUnit\\Framework\\Reorderable' => $vendorDir . '/phpunit/phpunit/src/Framework/Reorderable.php', 'PHPUnit\\Framework\\RiskyTestError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/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/Exception/SkippedTestError.php', 'PHPUnit\\Framework\\SkippedTestSuiteError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/SkippedTestSuiteError.php', 'PHPUnit\\Framework\\SyntheticError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/SyntheticError.php', 'PHPUnit\\Framework\\SyntheticSkippedError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/SyntheticSkippedError.php', 'PHPUnit\\Framework\\Test' => $vendorDir . '/phpunit/phpunit/src/Framework/Test.php', 'PHPUnit\\Framework\\TestBuilder' => $vendorDir . '/phpunit/phpunit/src/Framework/TestBuilder.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\\TestListenerDefaultImplementation' => $vendorDir . '/phpunit/phpunit/src/Framework/TestListenerDefaultImplementation.php', 'PHPUnit\\Framework\\TestResult' => $vendorDir . '/phpunit/phpunit/src/Framework/TestResult.php', 'PHPUnit\\Framework\\TestSuite' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuite.php', 'PHPUnit\\Framework\\TestSuiteIterator' => $vendorDir . '/phpunit/phpunit/src/Framework/TestSuiteIterator.php', 'PHPUnit\\Framework\\UnintentionallyCoveredCodeError' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/UnintentionallyCoveredCodeError.php', 'PHPUnit\\Framework\\Warning' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/Warning.php', 'PHPUnit\\Framework\\WarningTestCase' => $vendorDir . '/phpunit/phpunit/src/Framework/WarningTestCase.php', 'PHPUnit\\Runner\\AfterIncompleteTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterIncompleteTestHook.php', 'PHPUnit\\Runner\\AfterLastTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterLastTestHook.php', 'PHPUnit\\Runner\\AfterRiskyTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterRiskyTestHook.php', 'PHPUnit\\Runner\\AfterSkippedTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterSkippedTestHook.php', 'PHPUnit\\Runner\\AfterSuccessfulTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterSuccessfulTestHook.php', 'PHPUnit\\Runner\\AfterTestErrorHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterTestErrorHook.php', 'PHPUnit\\Runner\\AfterTestFailureHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterTestFailureHook.php', 'PHPUnit\\Runner\\AfterTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterTestHook.php', 'PHPUnit\\Runner\\AfterTestWarningHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/AfterTestWarningHook.php', 'PHPUnit\\Runner\\BaseTestRunner' => $vendorDir . '/phpunit/phpunit/src/Runner/BaseTestRunner.php', 'PHPUnit\\Runner\\BeforeFirstTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/BeforeFirstTestHook.php', 'PHPUnit\\Runner\\BeforeTestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/BeforeTestHook.php', 'PHPUnit\\Runner\\DefaultTestResultCache' => $vendorDir . '/phpunit/phpunit/src/Runner/DefaultTestResultCache.php', 'PHPUnit\\Runner\\Exception' => $vendorDir . '/phpunit/phpunit/src/Runner/Exception.php', 'PHPUnit\\Runner\\Extension\\ExtensionHandler' => $vendorDir . '/phpunit/phpunit/src/Runner/Extension/ExtensionHandler.php', 'PHPUnit\\Runner\\Extension\\PharLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/Extension/PharLoader.php', 'PHPUnit\\Runner\\Filter\\ExcludeGroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/ExcludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\Factory' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/Factory.php', 'PHPUnit\\Runner\\Filter\\GroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/GroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\IncludeGroupFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/IncludeGroupFilterIterator.php', 'PHPUnit\\Runner\\Filter\\NameFilterIterator' => $vendorDir . '/phpunit/phpunit/src/Runner/Filter/NameFilterIterator.php', 'PHPUnit\\Runner\\Hook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/Hook.php', 'PHPUnit\\Runner\\NullTestResultCache' => $vendorDir . '/phpunit/phpunit/src/Runner/NullTestResultCache.php', 'PHPUnit\\Runner\\PhptTestCase' => $vendorDir . '/phpunit/phpunit/src/Runner/PhptTestCase.php', 'PHPUnit\\Runner\\ResultCacheExtension' => $vendorDir . '/phpunit/phpunit/src/Runner/ResultCacheExtension.php', 'PHPUnit\\Runner\\StandardTestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/StandardTestSuiteLoader.php', 'PHPUnit\\Runner\\TestHook' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/TestHook.php', 'PHPUnit\\Runner\\TestListenerAdapter' => $vendorDir . '/phpunit/phpunit/src/Runner/Hook/TestListenerAdapter.php', 'PHPUnit\\Runner\\TestResultCache' => $vendorDir . '/phpunit/phpunit/src/Runner/TestResultCache.php', 'PHPUnit\\Runner\\TestSuiteLoader' => $vendorDir . '/phpunit/phpunit/src/Runner/TestSuiteLoader.php', 'PHPUnit\\Runner\\TestSuiteSorter' => $vendorDir . '/phpunit/phpunit/src/Runner/TestSuiteSorter.php', 'PHPUnit\\Runner\\Version' => $vendorDir . '/phpunit/phpunit/src/Runner/Version.php', 'PHPUnit\\TextUI\\CliArguments\\Builder' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Builder.php', 'PHPUnit\\TextUI\\CliArguments\\Configuration' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Configuration.php', 'PHPUnit\\TextUI\\CliArguments\\Exception' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Exception.php', 'PHPUnit\\TextUI\\CliArguments\\Mapper' => $vendorDir . '/phpunit/phpunit/src/TextUI/CliArguments/Mapper.php', 'PHPUnit\\TextUI\\Command' => $vendorDir . '/phpunit/phpunit/src/TextUI/Command.php', 'PHPUnit\\TextUI\\DefaultResultPrinter' => $vendorDir . '/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php', 'PHPUnit\\TextUI\\Exception' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/Exception.php', 'PHPUnit\\TextUI\\Help' => $vendorDir . '/phpunit/phpunit/src/TextUI/Help.php', 'PHPUnit\\TextUI\\ReflectionException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/ReflectionException.php', 'PHPUnit\\TextUI\\ResultPrinter' => $vendorDir . '/phpunit/phpunit/src/TextUI/ResultPrinter.php', 'PHPUnit\\TextUI\\RuntimeException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/RuntimeException.php', 'PHPUnit\\TextUI\\TestDirectoryNotFoundException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/TestDirectoryNotFoundException.php', 'PHPUnit\\TextUI\\TestFileNotFoundException' => $vendorDir . '/phpunit/phpunit/src/TextUI/Exception/TestFileNotFoundException.php', 'PHPUnit\\TextUI\\TestRunner' => $vendorDir . '/phpunit/phpunit/src/TextUI/TestRunner.php', 'PHPUnit\\TextUI\\TestSuiteMapper' => $vendorDir . '/phpunit/phpunit/src/TextUI/TestSuiteMapper.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\CodeCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/CodeCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\FilterMapper' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/FilterMapper.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\Directory' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/Directory.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Filter\\DirectoryCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Filter/DirectoryCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Clover' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Clover.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Cobertura' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Cobertura.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Crap4j' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Crap4j.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Html' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Html.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Php' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Php.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Text' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Text.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CodeCoverage\\Report\\Xml' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/CodeCoverage/Report/Xml.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Configuration' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Configuration.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Constant' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Constant.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ConstantCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/ConstantCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ConvertLogTypes' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/ConvertLogTypes.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCloverToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCloverToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageCrap4jToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageCrap4jToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageHtmlToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageHtmlToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoveragePhpToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoveragePhpToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageTextToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageTextToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\CoverageXmlToReport' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/CoverageXmlToReport.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Directory' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/Directory.php', 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\DirectoryCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/DirectoryCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Exception' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Exception.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Extension' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/Extension.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\ExtensionCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/ExtensionCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\File' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/File.php', 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\FileCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Filesystem/FileCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Generator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Generator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Group' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Group.php', 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\GroupCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/GroupCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Groups' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Group/Groups.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IniSetting' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSetting.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IniSettingCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/IniSettingCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\IntroduceCoverageElement' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/IntroduceCoverageElement.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Loader' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Loader.php', 'PHPUnit\\TextUI\\XmlConfiguration\\LogToReportMigration' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/LogToReportMigration.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Junit' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Junit.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Logging' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Logging.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TeamCity' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TeamCity.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Html' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Html.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Text' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Text.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\TestDox\\Xml' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/TestDox/Xml.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Logging\\Text' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Logging/Text.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Migration' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/Migration.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilder' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilder.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationBuilderException' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationBuilderException.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MigrationException' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/MigrationException.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Migrator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromFilterWhitelistToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromFilterWhitelistToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveAttributesFromRootToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveAttributesFromRootToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistExcludesToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistExcludesToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\MoveWhitelistIncludesToCoverage' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/MoveWhitelistIncludesToCoverage.php', 'PHPUnit\\TextUI\\XmlConfiguration\\PHPUnit' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHPUnit/PHPUnit.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Php' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Php.php', 'PHPUnit\\TextUI\\XmlConfiguration\\PhpHandler' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/PhpHandler.php', 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveCacheTokensAttribute' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveCacheTokensAttribute.php', 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveEmptyFilter' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveEmptyFilter.php', 'PHPUnit\\TextUI\\XmlConfiguration\\RemoveLogTypes' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/RemoveLogTypes.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectory' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectory.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestDirectoryCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestDirectoryCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestFile' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFile.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestFileCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestFileCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuite' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuite.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/TestSuite/TestSuiteCollectionIterator.php', 'PHPUnit\\TextUI\\XmlConfiguration\\UpdateSchemaLocationTo93' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/Migration/Migrations/UpdateSchemaLocationTo93.php', 'PHPUnit\\TextUI\\XmlConfiguration\\Variable' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/Variable.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollection' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollection.php', 'PHPUnit\\TextUI\\XmlConfiguration\\VariableCollectionIterator' => $vendorDir . '/phpunit/phpunit/src/TextUI/XmlConfiguration/PHP/VariableCollectionIterator.php', 'PHPUnit\\Util\\Annotation\\DocBlock' => $vendorDir . '/phpunit/phpunit/src/Util/Annotation/DocBlock.php', 'PHPUnit\\Util\\Annotation\\Registry' => $vendorDir . '/phpunit/phpunit/src/Util/Annotation/Registry.php', 'PHPUnit\\Util\\Blacklist' => $vendorDir . '/phpunit/phpunit/src/Util/Blacklist.php', 'PHPUnit\\Util\\Cloner' => $vendorDir . '/phpunit/phpunit/src/Util/Cloner.php', 'PHPUnit\\Util\\Color' => $vendorDir . '/phpunit/phpunit/src/Util/Color.php', 'PHPUnit\\Util\\ErrorHandler' => $vendorDir . '/phpunit/phpunit/src/Util/ErrorHandler.php', 'PHPUnit\\Util\\Exception' => $vendorDir . '/phpunit/phpunit/src/Util/Exception.php', 'PHPUnit\\Util\\ExcludeList' => $vendorDir . '/phpunit/phpunit/src/Util/ExcludeList.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\\GlobalState' => $vendorDir . '/phpunit/phpunit/src/Util/GlobalState.php', 'PHPUnit\\Util\\InvalidDataSetException' => $vendorDir . '/phpunit/phpunit/src/Util/InvalidDataSetException.php', 'PHPUnit\\Util\\Json' => $vendorDir . '/phpunit/phpunit/src/Util/Json.php', 'PHPUnit\\Util\\Log\\JUnit' => $vendorDir . '/phpunit/phpunit/src/Util/Log/JUnit.php', 'PHPUnit\\Util\\Log\\TeamCity' => $vendorDir . '/phpunit/phpunit/src/Util/Log/TeamCity.php', 'PHPUnit\\Util\\PHP\\AbstractPhpProcess' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/AbstractPhpProcess.php', 'PHPUnit\\Util\\PHP\\DefaultPhpProcess' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/DefaultPhpProcess.php', 'PHPUnit\\Util\\PHP\\WindowsPhpProcess' => $vendorDir . '/phpunit/phpunit/src/Util/PHP/WindowsPhpProcess.php', 'PHPUnit\\Util\\Printer' => $vendorDir . '/phpunit/phpunit/src/Util/Printer.php', 'PHPUnit\\Util\\Reflection' => $vendorDir . '/phpunit/phpunit/src/Util/Reflection.php', 'PHPUnit\\Util\\RegularExpression' => $vendorDir . '/phpunit/phpunit/src/Util/RegularExpression.php', 'PHPUnit\\Util\\Test' => $vendorDir . '/phpunit/phpunit/src/Util/Test.php', 'PHPUnit\\Util\\TestDox\\CliTestDoxPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php', 'PHPUnit\\Util\\TestDox\\HtmlResultPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.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\\TestDoxPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/TestDoxPrinter.php', 'PHPUnit\\Util\\TestDox\\TextResultPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/TextResultPrinter.php', 'PHPUnit\\Util\\TestDox\\XmlResultPrinter' => $vendorDir . '/phpunit/phpunit/src/Util/TestDox/XmlResultPrinter.php', 'PHPUnit\\Util\\TextTestListRenderer' => $vendorDir . '/phpunit/phpunit/src/Util/TextTestListRenderer.php', 'PHPUnit\\Util\\Type' => $vendorDir . '/phpunit/phpunit/src/Util/Type.php', 'PHPUnit\\Util\\VersionComparisonOperator' => $vendorDir . '/phpunit/phpunit/src/Util/VersionComparisonOperator.php', 'PHPUnit\\Util\\XdebugFilterScriptGenerator' => $vendorDir . '/phpunit/phpunit/src/Util/XdebugFilterScriptGenerator.php', 'PHPUnit\\Util\\Xml' => $vendorDir . '/phpunit/phpunit/src/Util/Xml.php', 'PHPUnit\\Util\\XmlTestListRenderer' => $vendorDir . '/phpunit/phpunit/src/Util/XmlTestListRenderer.php', 'PHPUnit\\Util\\Xml\\Exception' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/Exception.php', 'PHPUnit\\Util\\Xml\\FailedSchemaDetectionResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/FailedSchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\Loader' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/Loader.php', 'PHPUnit\\Util\\Xml\\SchemaDetectionResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\SchemaDetector' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SchemaDetector.php', 'PHPUnit\\Util\\Xml\\SchemaFinder' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SchemaFinder.php', 'PHPUnit\\Util\\Xml\\SnapshotNodeList' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SnapshotNodeList.php', 'PHPUnit\\Util\\Xml\\SuccessfulSchemaDetectionResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/SuccessfulSchemaDetectionResult.php', 'PHPUnit\\Util\\Xml\\ValidationResult' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/ValidationResult.php', 'PHPUnit\\Util\\Xml\\Validator' => $vendorDir . '/phpunit/phpunit/src/Util/Xml/Validator.php', 'PharIo\\Manifest\\Application' => $vendorDir . '/phar-io/manifest/src/values/Application.php', 'PharIo\\Manifest\\ApplicationName' => $vendorDir . '/phar-io/manifest/src/values/ApplicationName.php', 'PharIo\\Manifest\\Author' => $vendorDir . '/phar-io/manifest/src/values/Author.php', 'PharIo\\Manifest\\AuthorCollection' => $vendorDir . '/phar-io/manifest/src/values/AuthorCollection.php', 'PharIo\\Manifest\\AuthorCollectionIterator' => $vendorDir . '/phar-io/manifest/src/values/AuthorCollectionIterator.php', 'PharIo\\Manifest\\AuthorElement' => $vendorDir . '/phar-io/manifest/src/xml/AuthorElement.php', 'PharIo\\Manifest\\AuthorElementCollection' => $vendorDir . '/phar-io/manifest/src/xml/AuthorElementCollection.php', 'PharIo\\Manifest\\BundledComponent' => $vendorDir . '/phar-io/manifest/src/values/BundledComponent.php', 'PharIo\\Manifest\\BundledComponentCollection' => $vendorDir . '/phar-io/manifest/src/values/BundledComponentCollection.php', 'PharIo\\Manifest\\BundledComponentCollectionIterator' => $vendorDir . '/phar-io/manifest/src/values/BundledComponentCollectionIterator.php', 'PharIo\\Manifest\\BundlesElement' => $vendorDir . '/phar-io/manifest/src/xml/BundlesElement.php', 'PharIo\\Manifest\\ComponentElement' => $vendorDir . '/phar-io/manifest/src/xml/ComponentElement.php', 'PharIo\\Manifest\\ComponentElementCollection' => $vendorDir . '/phar-io/manifest/src/xml/ComponentElementCollection.php', 'PharIo\\Manifest\\ContainsElement' => $vendorDir . '/phar-io/manifest/src/xml/ContainsElement.php', 'PharIo\\Manifest\\CopyrightElement' => $vendorDir . '/phar-io/manifest/src/xml/CopyrightElement.php', 'PharIo\\Manifest\\CopyrightInformation' => $vendorDir . '/phar-io/manifest/src/values/CopyrightInformation.php', 'PharIo\\Manifest\\ElementCollection' => $vendorDir . '/phar-io/manifest/src/xml/ElementCollection.php', 'PharIo\\Manifest\\ElementCollectionException' => $vendorDir . '/phar-io/manifest/src/exceptions/ElementCollectionException.php', 'PharIo\\Manifest\\Email' => $vendorDir . '/phar-io/manifest/src/values/Email.php', 'PharIo\\Manifest\\Exception' => $vendorDir . '/phar-io/manifest/src/exceptions/Exception.php', 'PharIo\\Manifest\\ExtElement' => $vendorDir . '/phar-io/manifest/src/xml/ExtElement.php', 'PharIo\\Manifest\\ExtElementCollection' => $vendorDir . '/phar-io/manifest/src/xml/ExtElementCollection.php', 'PharIo\\Manifest\\Extension' => $vendorDir . '/phar-io/manifest/src/values/Extension.php', 'PharIo\\Manifest\\ExtensionElement' => $vendorDir . '/phar-io/manifest/src/xml/ExtensionElement.php', 'PharIo\\Manifest\\InvalidApplicationNameException' => $vendorDir . '/phar-io/manifest/src/exceptions/InvalidApplicationNameException.php', 'PharIo\\Manifest\\InvalidEmailException' => $vendorDir . '/phar-io/manifest/src/exceptions/InvalidEmailException.php', 'PharIo\\Manifest\\InvalidUrlException' => $vendorDir . '/phar-io/manifest/src/exceptions/InvalidUrlException.php', 'PharIo\\Manifest\\Library' => $vendorDir . '/phar-io/manifest/src/values/Library.php', 'PharIo\\Manifest\\License' => $vendorDir . '/phar-io/manifest/src/values/License.php', 'PharIo\\Manifest\\LicenseElement' => $vendorDir . '/phar-io/manifest/src/xml/LicenseElement.php', 'PharIo\\Manifest\\Manifest' => $vendorDir . '/phar-io/manifest/src/values/Manifest.php', 'PharIo\\Manifest\\ManifestDocument' => $vendorDir . '/phar-io/manifest/src/xml/ManifestDocument.php', 'PharIo\\Manifest\\ManifestDocumentException' => $vendorDir . '/phar-io/manifest/src/exceptions/ManifestDocumentException.php', 'PharIo\\Manifest\\ManifestDocumentLoadingException' => $vendorDir . '/phar-io/manifest/src/exceptions/ManifestDocumentLoadingException.php', 'PharIo\\Manifest\\ManifestDocumentMapper' => $vendorDir . '/phar-io/manifest/src/ManifestDocumentMapper.php', 'PharIo\\Manifest\\ManifestDocumentMapperException' => $vendorDir . '/phar-io/manifest/src/exceptions/ManifestDocumentMapperException.php', 'PharIo\\Manifest\\ManifestElement' => $vendorDir . '/phar-io/manifest/src/xml/ManifestElement.php', 'PharIo\\Manifest\\ManifestElementException' => $vendorDir . '/phar-io/manifest/src/exceptions/ManifestElementException.php', 'PharIo\\Manifest\\ManifestLoader' => $vendorDir . '/phar-io/manifest/src/ManifestLoader.php', 'PharIo\\Manifest\\ManifestLoaderException' => $vendorDir . '/phar-io/manifest/src/exceptions/ManifestLoaderException.php', 'PharIo\\Manifest\\ManifestSerializer' => $vendorDir . '/phar-io/manifest/src/ManifestSerializer.php', 'PharIo\\Manifest\\NoEmailAddressException' => $vendorDir . '/phar-io/manifest/src/exceptions/NoEmailAddressException.php', 'PharIo\\Manifest\\PhpElement' => $vendorDir . '/phar-io/manifest/src/xml/PhpElement.php', 'PharIo\\Manifest\\PhpExtensionRequirement' => $vendorDir . '/phar-io/manifest/src/values/PhpExtensionRequirement.php', 'PharIo\\Manifest\\PhpVersionRequirement' => $vendorDir . '/phar-io/manifest/src/values/PhpVersionRequirement.php', 'PharIo\\Manifest\\Requirement' => $vendorDir . '/phar-io/manifest/src/values/Requirement.php', 'PharIo\\Manifest\\RequirementCollection' => $vendorDir . '/phar-io/manifest/src/values/RequirementCollection.php', 'PharIo\\Manifest\\RequirementCollectionIterator' => $vendorDir . '/phar-io/manifest/src/values/RequirementCollectionIterator.php', 'PharIo\\Manifest\\RequiresElement' => $vendorDir . '/phar-io/manifest/src/xml/RequiresElement.php', 'PharIo\\Manifest\\Type' => $vendorDir . '/phar-io/manifest/src/values/Type.php', 'PharIo\\Manifest\\Url' => $vendorDir . '/phar-io/manifest/src/values/Url.php', 'PharIo\\Version\\AbstractVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/AbstractVersionConstraint.php', 'PharIo\\Version\\AndVersionConstraintGroup' => $vendorDir . '/phar-io/version/src/constraints/AndVersionConstraintGroup.php', 'PharIo\\Version\\AnyVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/AnyVersionConstraint.php', 'PharIo\\Version\\BuildMetaData' => $vendorDir . '/phar-io/version/src/BuildMetaData.php', 'PharIo\\Version\\ExactVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/ExactVersionConstraint.php', 'PharIo\\Version\\Exception' => $vendorDir . '/phar-io/version/src/exceptions/Exception.php', 'PharIo\\Version\\GreaterThanOrEqualToVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/GreaterThanOrEqualToVersionConstraint.php', 'PharIo\\Version\\InvalidPreReleaseSuffixException' => $vendorDir . '/phar-io/version/src/exceptions/InvalidPreReleaseSuffixException.php', 'PharIo\\Version\\InvalidVersionException' => $vendorDir . '/phar-io/version/src/exceptions/InvalidVersionException.php', 'PharIo\\Version\\NoBuildMetaDataException' => $vendorDir . '/phar-io/version/src/exceptions/NoBuildMetaDataException.php', 'PharIo\\Version\\NoPreReleaseSuffixException' => $vendorDir . '/phar-io/version/src/exceptions/NoPreReleaseSuffixException.php', 'PharIo\\Version\\OrVersionConstraintGroup' => $vendorDir . '/phar-io/version/src/constraints/OrVersionConstraintGroup.php', 'PharIo\\Version\\PreReleaseSuffix' => $vendorDir . '/phar-io/version/src/PreReleaseSuffix.php', 'PharIo\\Version\\SpecificMajorAndMinorVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/SpecificMajorAndMinorVersionConstraint.php', 'PharIo\\Version\\SpecificMajorVersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/SpecificMajorVersionConstraint.php', 'PharIo\\Version\\UnsupportedVersionConstraintException' => $vendorDir . '/phar-io/version/src/exceptions/UnsupportedVersionConstraintException.php', 'PharIo\\Version\\Version' => $vendorDir . '/phar-io/version/src/Version.php', 'PharIo\\Version\\VersionConstraint' => $vendorDir . '/phar-io/version/src/constraints/VersionConstraint.php', 'PharIo\\Version\\VersionConstraintParser' => $vendorDir . '/phar-io/version/src/VersionConstraintParser.php', 'PharIo\\Version\\VersionConstraintValue' => $vendorDir . '/phar-io/version/src/VersionConstraintValue.php', 'PharIo\\Version\\VersionNumber' => $vendorDir . '/phar-io/version/src/VersionNumber.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\\Channel\\Frame' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Frame.php', 'PhpAmqpLib\\Channel\\Method' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Channel/Method.php', 'PhpAmqpLib\\Connection\\AMQPConnectionConfig' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionConfig.php', 'PhpAmqpLib\\Connection\\AMQPConnectionFactory' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPConnectionFactory.php', 'PhpAmqpLib\\Connection\\AMQPLazyConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazyConnection.php', 'PhpAmqpLib\\Connection\\AMQPLazySSLConnection' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/AMQPLazySSLConnection.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\\AbstractSignalHeartbeatSender' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/AbstractSignalHeartbeatSender.php', 'PhpAmqpLib\\Connection\\Heartbeat\\PCNTLHeartbeatSender' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/PCNTLHeartbeatSender.php', 'PhpAmqpLib\\Connection\\Heartbeat\\SIGHeartbeatSender' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Connection/Heartbeat/SIGHeartbeatSender.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\\AMQPConnectionBlockedException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionBlockedException.php', 'PhpAmqpLib\\Exception\\AMQPConnectionClosedException' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Exception/AMQPConnectionClosedException.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\\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\\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\\AMQPBufferReader' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPBufferReader.php', 'PhpAmqpLib\\Wire\\AMQPByteStream' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPByteStream.php', 'PhpAmqpLib\\Wire\\AMQPDecimal' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPDecimal.php', 'PhpAmqpLib\\Wire\\AMQPIOReader' => $vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib/Wire/AMQPIOReader.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\\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', 'PhpParser\\Builder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder.php', 'PhpParser\\BuilderFactory' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderFactory.php', 'PhpParser\\BuilderHelpers' => $vendorDir . '/nikic/php-parser/lib/PhpParser/BuilderHelpers.php', 'PhpParser\\Builder\\ClassConst' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php', 'PhpParser\\Builder\\Class_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Class_.php', 'PhpParser\\Builder\\Declaration' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Declaration.php', 'PhpParser\\Builder\\EnumCase' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php', 'PhpParser\\Builder\\Enum_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Enum_.php', 'PhpParser\\Builder\\FunctionLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php', 'PhpParser\\Builder\\Function_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Function_.php', 'PhpParser\\Builder\\Interface_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Interface_.php', 'PhpParser\\Builder\\Method' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Method.php', 'PhpParser\\Builder\\Namespace_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php', 'PhpParser\\Builder\\Param' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Param.php', 'PhpParser\\Builder\\Property' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Property.php', 'PhpParser\\Builder\\TraitUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php', 'PhpParser\\Builder\\TraitUseAdaptation' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php', 'PhpParser\\Builder\\Trait_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Trait_.php', 'PhpParser\\Builder\\Use_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Builder/Use_.php', 'PhpParser\\Comment' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Comment.php', 'PhpParser\\Comment\\Doc' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Comment/Doc.php', 'PhpParser\\ConstExprEvaluationException' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php', 'PhpParser\\ConstExprEvaluator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php', 'PhpParser\\Error' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Error.php', 'PhpParser\\ErrorHandler' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ErrorHandler.php', 'PhpParser\\ErrorHandler\\Collecting' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php', 'PhpParser\\ErrorHandler\\Throwing' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php', 'PhpParser\\Internal\\DiffElem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php', 'PhpParser\\Internal\\Differ' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/Differ.php', 'PhpParser\\Internal\\PrintableNewAnonClassNode' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php', 'PhpParser\\Internal\\TokenPolyfill' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php', 'PhpParser\\Internal\\TokenStream' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php', 'PhpParser\\JsonDecoder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/JsonDecoder.php', 'PhpParser\\Lexer' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer.php', 'PhpParser\\Lexer\\Emulative' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php', 'PhpParser\\Lexer\\TokenEmulator\\AttributeEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\EnumTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ExplicitOctalEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\KeywordEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\MatchTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\NullsafeTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ReadonlyFunctionTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ReadonlyTokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\ReverseEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php', 'PhpParser\\Lexer\\TokenEmulator\\TokenEmulator' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php', 'PhpParser\\Modifiers' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Modifiers.php', 'PhpParser\\NameContext' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NameContext.php', 'PhpParser\\Node' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node.php', 'PhpParser\\NodeAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeAbstract.php', 'PhpParser\\NodeDumper' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeDumper.php', 'PhpParser\\NodeFinder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeFinder.php', 'PhpParser\\NodeTraverser' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeTraverser.php', 'PhpParser\\NodeTraverserInterface' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php', 'PhpParser\\NodeVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor.php', 'PhpParser\\NodeVisitorAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php', 'PhpParser\\NodeVisitor\\CloningVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php', 'PhpParser\\NodeVisitor\\CommentAnnotatingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php', 'PhpParser\\NodeVisitor\\FindingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php', 'PhpParser\\NodeVisitor\\FirstFindingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php', 'PhpParser\\NodeVisitor\\NameResolver' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php', 'PhpParser\\NodeVisitor\\NodeConnectingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php', 'PhpParser\\NodeVisitor\\ParentConnectingVisitor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php', 'PhpParser\\Node\\Arg' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Arg.php', 'PhpParser\\Node\\ArrayItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php', 'PhpParser\\Node\\Attribute' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Attribute.php', 'PhpParser\\Node\\AttributeGroup' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php', 'PhpParser\\Node\\ClosureUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php', 'PhpParser\\Node\\ComplexType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/ComplexType.php', 'PhpParser\\Node\\Const_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Const_.php', 'PhpParser\\Node\\DeclareItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/DeclareItem.php', 'PhpParser\\Node\\Expr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr.php', 'PhpParser\\Node\\Expr\\ArrayDimFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php', 'PhpParser\\Node\\Expr\\Array_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php', 'PhpParser\\Node\\Expr\\ArrowFunction' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php', 'PhpParser\\Node\\Expr\\Assign' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php', 'PhpParser\\Node\\Expr\\AssignOp' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php', 'PhpParser\\Node\\Expr\\AssignOp\\BitwiseAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php', 'PhpParser\\Node\\Expr\\AssignOp\\BitwiseOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php', 'PhpParser\\Node\\Expr\\AssignOp\\BitwiseXor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php', 'PhpParser\\Node\\Expr\\AssignOp\\Coalesce' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php', 'PhpParser\\Node\\Expr\\AssignOp\\Concat' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php', 'PhpParser\\Node\\Expr\\AssignOp\\Div' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php', 'PhpParser\\Node\\Expr\\AssignOp\\Minus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php', 'PhpParser\\Node\\Expr\\AssignOp\\Mod' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php', 'PhpParser\\Node\\Expr\\AssignOp\\Mul' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php', 'PhpParser\\Node\\Expr\\AssignOp\\Plus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php', 'PhpParser\\Node\\Expr\\AssignOp\\Pow' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php', 'PhpParser\\Node\\Expr\\AssignOp\\ShiftLeft' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php', 'PhpParser\\Node\\Expr\\AssignOp\\ShiftRight' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php', 'PhpParser\\Node\\Expr\\AssignRef' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php', 'PhpParser\\Node\\Expr\\BinaryOp' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BitwiseXor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BooleanAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php', 'PhpParser\\Node\\Expr\\BinaryOp\\BooleanOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Coalesce' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Concat' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Div' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Equal' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Greater' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php', 'PhpParser\\Node\\Expr\\BinaryOp\\GreaterOrEqual' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Identical' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php', 'PhpParser\\Node\\Expr\\BinaryOp\\LogicalAnd' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php', 'PhpParser\\Node\\Expr\\BinaryOp\\LogicalOr' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php', 'PhpParser\\Node\\Expr\\BinaryOp\\LogicalXor' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Minus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Mod' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Mul' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php', 'PhpParser\\Node\\Expr\\BinaryOp\\NotEqual' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php', 'PhpParser\\Node\\Expr\\BinaryOp\\NotIdentical' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Plus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Pow' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php', 'PhpParser\\Node\\Expr\\BinaryOp\\ShiftLeft' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php', 'PhpParser\\Node\\Expr\\BinaryOp\\ShiftRight' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Smaller' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php', 'PhpParser\\Node\\Expr\\BinaryOp\\SmallerOrEqual' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php', 'PhpParser\\Node\\Expr\\BinaryOp\\Spaceship' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php', 'PhpParser\\Node\\Expr\\BitwiseNot' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php', 'PhpParser\\Node\\Expr\\BooleanNot' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php', 'PhpParser\\Node\\Expr\\CallLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php', 'PhpParser\\Node\\Expr\\Cast' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php', 'PhpParser\\Node\\Expr\\Cast\\Array_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php', 'PhpParser\\Node\\Expr\\Cast\\Bool_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php', 'PhpParser\\Node\\Expr\\Cast\\Double' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php', 'PhpParser\\Node\\Expr\\Cast\\Int_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php', 'PhpParser\\Node\\Expr\\Cast\\Object_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php', 'PhpParser\\Node\\Expr\\Cast\\String_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php', 'PhpParser\\Node\\Expr\\Cast\\Unset_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php', 'PhpParser\\Node\\Expr\\ClassConstFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.php', 'PhpParser\\Node\\Expr\\Clone_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php', 'PhpParser\\Node\\Expr\\Closure' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php', 'PhpParser\\Node\\Expr\\ConstFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php', 'PhpParser\\Node\\Expr\\Empty_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php', 'PhpParser\\Node\\Expr\\Error' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php', 'PhpParser\\Node\\Expr\\ErrorSuppress' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php', 'PhpParser\\Node\\Expr\\Eval_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php', 'PhpParser\\Node\\Expr\\Exit_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php', 'PhpParser\\Node\\Expr\\FuncCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php', 'PhpParser\\Node\\Expr\\Include_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php', 'PhpParser\\Node\\Expr\\Instanceof_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php', 'PhpParser\\Node\\Expr\\Isset_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php', 'PhpParser\\Node\\Expr\\List_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php', 'PhpParser\\Node\\Expr\\Match_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php', 'PhpParser\\Node\\Expr\\MethodCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php', 'PhpParser\\Node\\Expr\\New_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php', 'PhpParser\\Node\\Expr\\NullsafeMethodCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php', 'PhpParser\\Node\\Expr\\NullsafePropertyFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php', 'PhpParser\\Node\\Expr\\PostDec' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php', 'PhpParser\\Node\\Expr\\PostInc' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php', 'PhpParser\\Node\\Expr\\PreDec' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php', 'PhpParser\\Node\\Expr\\PreInc' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php', 'PhpParser\\Node\\Expr\\Print_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php', 'PhpParser\\Node\\Expr\\PropertyFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php', 'PhpParser\\Node\\Expr\\ShellExec' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php', 'PhpParser\\Node\\Expr\\StaticCall' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php', 'PhpParser\\Node\\Expr\\StaticPropertyFetch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php', 'PhpParser\\Node\\Expr\\Ternary' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php', 'PhpParser\\Node\\Expr\\Throw_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php', 'PhpParser\\Node\\Expr\\UnaryMinus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php', 'PhpParser\\Node\\Expr\\UnaryPlus' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php', 'PhpParser\\Node\\Expr\\Variable' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php', 'PhpParser\\Node\\Expr\\YieldFrom' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php', 'PhpParser\\Node\\Expr\\Yield_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php', 'PhpParser\\Node\\FunctionLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php', 'PhpParser\\Node\\Identifier' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Identifier.php', 'PhpParser\\Node\\InterpolatedStringPart' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php', 'PhpParser\\Node\\IntersectionType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php', 'PhpParser\\Node\\MatchArm' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/MatchArm.php', 'PhpParser\\Node\\Name' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Name.php', 'PhpParser\\Node\\Name\\FullyQualified' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php', 'PhpParser\\Node\\Name\\Relative' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php', 'PhpParser\\Node\\NullableType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/NullableType.php', 'PhpParser\\Node\\Param' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Param.php', 'PhpParser\\Node\\PropertyItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php', 'PhpParser\\Node\\Scalar' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar.php', 'PhpParser\\Node\\Scalar\\Float_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.php', 'PhpParser\\Node\\Scalar\\Int_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.php', 'PhpParser\\Node\\Scalar\\InterpolatedString' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php', 'PhpParser\\Node\\Scalar\\MagicConst' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Class_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Dir' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php', 'PhpParser\\Node\\Scalar\\MagicConst\\File' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Function_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Line' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Method' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Namespace_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php', 'PhpParser\\Node\\Scalar\\MagicConst\\Trait_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php', 'PhpParser\\Node\\Scalar\\String_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php', 'PhpParser\\Node\\StaticVar' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/StaticVar.php', 'PhpParser\\Node\\Stmt' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt.php', 'PhpParser\\Node\\Stmt\\Block' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php', 'PhpParser\\Node\\Stmt\\Break_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php', 'PhpParser\\Node\\Stmt\\Case_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php', 'PhpParser\\Node\\Stmt\\Catch_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php', 'PhpParser\\Node\\Stmt\\ClassConst' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php', 'PhpParser\\Node\\Stmt\\ClassLike' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php', 'PhpParser\\Node\\Stmt\\ClassMethod' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php', 'PhpParser\\Node\\Stmt\\Class_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php', 'PhpParser\\Node\\Stmt\\Const_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php', 'PhpParser\\Node\\Stmt\\Continue_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php', 'PhpParser\\Node\\Stmt\\Declare_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php', 'PhpParser\\Node\\Stmt\\Do_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php', 'PhpParser\\Node\\Stmt\\Echo_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php', 'PhpParser\\Node\\Stmt\\ElseIf_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php', 'PhpParser\\Node\\Stmt\\Else_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php', 'PhpParser\\Node\\Stmt\\EnumCase' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.php', 'PhpParser\\Node\\Stmt\\Enum_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php', 'PhpParser\\Node\\Stmt\\Expression' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php', 'PhpParser\\Node\\Stmt\\Finally_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php', 'PhpParser\\Node\\Stmt\\For_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php', 'PhpParser\\Node\\Stmt\\Foreach_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php', 'PhpParser\\Node\\Stmt\\Function_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php', 'PhpParser\\Node\\Stmt\\Global_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php', 'PhpParser\\Node\\Stmt\\Goto_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php', 'PhpParser\\Node\\Stmt\\GroupUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php', 'PhpParser\\Node\\Stmt\\HaltCompiler' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php', 'PhpParser\\Node\\Stmt\\If_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php', 'PhpParser\\Node\\Stmt\\InlineHTML' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php', 'PhpParser\\Node\\Stmt\\Interface_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php', 'PhpParser\\Node\\Stmt\\Label' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php', 'PhpParser\\Node\\Stmt\\Namespace_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php', 'PhpParser\\Node\\Stmt\\Nop' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php', 'PhpParser\\Node\\Stmt\\Property' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php', 'PhpParser\\Node\\Stmt\\Return_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php', 'PhpParser\\Node\\Stmt\\Static_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php', 'PhpParser\\Node\\Stmt\\Switch_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php', 'PhpParser\\Node\\Stmt\\TraitUse' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php', 'PhpParser\\Node\\Stmt\\TraitUseAdaptation' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php', 'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Alias' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php', 'PhpParser\\Node\\Stmt\\TraitUseAdaptation\\Precedence' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php', 'PhpParser\\Node\\Stmt\\Trait_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php', 'PhpParser\\Node\\Stmt\\TryCatch' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php', 'PhpParser\\Node\\Stmt\\Unset_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php', 'PhpParser\\Node\\Stmt\\Use_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php', 'PhpParser\\Node\\Stmt\\While_' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php', 'PhpParser\\Node\\UnionType' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/UnionType.php', 'PhpParser\\Node\\UseItem' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/UseItem.php', 'PhpParser\\Node\\VarLikeIdentifier' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php', 'PhpParser\\Node\\VariadicPlaceholder' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php', 'PhpParser\\Parser' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Parser.php', 'PhpParser\\ParserAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ParserAbstract.php', 'PhpParser\\ParserFactory' => $vendorDir . '/nikic/php-parser/lib/PhpParser/ParserFactory.php', 'PhpParser\\Parser\\Php7' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Parser/Php7.php', 'PhpParser\\Parser\\Php8' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Parser/Php8.php', 'PhpParser\\PhpVersion' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PhpVersion.php', 'PhpParser\\PrettyPrinter' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PrettyPrinter.php', 'PhpParser\\PrettyPrinterAbstract' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php', 'PhpParser\\PrettyPrinter\\Standard' => $vendorDir . '/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php', 'PhpParser\\Token' => $vendorDir . '/nikic/php-parser/lib/PhpParser/Token.php', 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php', 'Predis\\Autoloader' => $vendorDir . '/predis/predis/src/Autoloader.php', 'Predis\\Client' => $vendorDir . '/predis/predis/src/Client.php', 'Predis\\ClientConfiguration' => $vendorDir . '/predis/predis/src/ClientConfiguration.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\\Hash\\PhpiredisCRC16' => $vendorDir . '/predis/predis/src/Cluster/Hash/PhpiredisCRC16.php', 'Predis\\Cluster\\PredisStrategy' => $vendorDir . '/predis/predis/src/Cluster/PredisStrategy.php', 'Predis\\Cluster\\RedisStrategy' => $vendorDir . '/predis/predis/src/Cluster/RedisStrategy.php', 'Predis\\Cluster\\SlotMap' => $vendorDir . '/predis/predis/src/Cluster/SlotMap.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\\Argument\\ArrayableArgument' => $vendorDir . '/predis/predis/src/Command/Argument/ArrayableArgument.php', 'Predis\\Command\\Argument\\Geospatial\\AbstractBy' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/AbstractBy.php', 'Predis\\Command\\Argument\\Geospatial\\ByBox' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/ByBox.php', 'Predis\\Command\\Argument\\Geospatial\\ByInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/ByInterface.php', 'Predis\\Command\\Argument\\Geospatial\\ByRadius' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/ByRadius.php', 'Predis\\Command\\Argument\\Geospatial\\FromInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/FromInterface.php', 'Predis\\Command\\Argument\\Geospatial\\FromLonLat' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/FromLonLat.php', 'Predis\\Command\\Argument\\Geospatial\\FromMember' => $vendorDir . '/predis/predis/src/Command/Argument/Geospatial/FromMember.php', 'Predis\\Command\\Argument\\Search\\AggregateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/AggregateArguments.php', 'Predis\\Command\\Argument\\Search\\AlterArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/AlterArguments.php', 'Predis\\Command\\Argument\\Search\\CommonArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/CommonArguments.php', 'Predis\\Command\\Argument\\Search\\CreateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/CreateArguments.php', 'Predis\\Command\\Argument\\Search\\CursorArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/CursorArguments.php', 'Predis\\Command\\Argument\\Search\\DropArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/DropArguments.php', 'Predis\\Command\\Argument\\Search\\ExplainArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/ExplainArguments.php', 'Predis\\Command\\Argument\\Search\\ProfileArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/ProfileArguments.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\AbstractField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/AbstractField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\FieldInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/FieldInterface.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\GeoField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/GeoField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\NumericField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/NumericField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\TagField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/TagField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\TextField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/TextField.php', 'Predis\\Command\\Argument\\Search\\SchemaFields\\VectorField' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SchemaFields/VectorField.php', 'Predis\\Command\\Argument\\Search\\SearchArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SearchArguments.php', 'Predis\\Command\\Argument\\Search\\SpellcheckArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SpellcheckArguments.php', 'Predis\\Command\\Argument\\Search\\SugAddArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SugAddArguments.php', 'Predis\\Command\\Argument\\Search\\SugGetArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SugGetArguments.php', 'Predis\\Command\\Argument\\Search\\SynUpdateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/Search/SynUpdateArguments.php', 'Predis\\Command\\Argument\\Server\\LimitInterface' => $vendorDir . '/predis/predis/src/Command/Argument/Server/LimitInterface.php', 'Predis\\Command\\Argument\\Server\\LimitOffsetCount' => $vendorDir . '/predis/predis/src/Command/Argument/Server/LimitOffsetCount.php', 'Predis\\Command\\Argument\\Server\\To' => $vendorDir . '/predis/predis/src/Command/Argument/Server/To.php', 'Predis\\Command\\Argument\\TimeSeries\\AddArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/AddArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\AlterArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/AlterArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\CommonArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/CommonArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\CreateArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/CreateArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\DecrByArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/DecrByArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\GetArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/GetArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\IncrByArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/IncrByArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\InfoArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/InfoArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\MGetArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/MGetArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\MRangeArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/MRangeArguments.php', 'Predis\\Command\\Argument\\TimeSeries\\RangeArguments' => $vendorDir . '/predis/predis/src/Command/Argument/TimeSeries/RangeArguments.php', 'Predis\\Command\\Command' => $vendorDir . '/predis/predis/src/Command/Command.php', 'Predis\\Command\\CommandInterface' => $vendorDir . '/predis/predis/src/Command/CommandInterface.php', 'Predis\\Command\\Factory' => $vendorDir . '/predis/predis/src/Command/Factory.php', 'Predis\\Command\\FactoryInterface' => $vendorDir . '/predis/predis/src/Command/FactoryInterface.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\\RawCommand' => $vendorDir . '/predis/predis/src/Command/RawCommand.php', 'Predis\\Command\\RawFactory' => $vendorDir . '/predis/predis/src/Command/RawFactory.php', 'Predis\\Command\\RedisFactory' => $vendorDir . '/predis/predis/src/Command/RedisFactory.php', 'Predis\\Command\\Redis\\ACL' => $vendorDir . '/predis/predis/src/Command/Redis/ACL.php', 'Predis\\Command\\Redis\\APPEND' => $vendorDir . '/predis/predis/src/Command/Redis/APPEND.php', 'Predis\\Command\\Redis\\AUTH' => $vendorDir . '/predis/predis/src/Command/Redis/AUTH.php', 'Predis\\Command\\Redis\\AbstractCommand\\BZPOPBase' => $vendorDir . '/predis/predis/src/Command/Redis/AbstractCommand/BZPOPBase.php', 'Predis\\Command\\Redis\\BGREWRITEAOF' => $vendorDir . '/predis/predis/src/Command/Redis/BGREWRITEAOF.php', 'Predis\\Command\\Redis\\BGSAVE' => $vendorDir . '/predis/predis/src/Command/Redis/BGSAVE.php', 'Predis\\Command\\Redis\\BITCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/BITCOUNT.php', 'Predis\\Command\\Redis\\BITFIELD' => $vendorDir . '/predis/predis/src/Command/Redis/BITFIELD.php', 'Predis\\Command\\Redis\\BITOP' => $vendorDir . '/predis/predis/src/Command/Redis/BITOP.php', 'Predis\\Command\\Redis\\BITPOS' => $vendorDir . '/predis/predis/src/Command/Redis/BITPOS.php', 'Predis\\Command\\Redis\\BLMOVE' => $vendorDir . '/predis/predis/src/Command/Redis/BLMOVE.php', 'Predis\\Command\\Redis\\BLMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BLMPOP.php', 'Predis\\Command\\Redis\\BLPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BLPOP.php', 'Predis\\Command\\Redis\\BRPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BRPOP.php', 'Predis\\Command\\Redis\\BRPOPLPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/BRPOPLPUSH.php', 'Predis\\Command\\Redis\\BZMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/BZMPOP.php', 'Predis\\Command\\Redis\\BZPOPMAX' => $vendorDir . '/predis/predis/src/Command/Redis/BZPOPMAX.php', 'Predis\\Command\\Redis\\BZPOPMIN' => $vendorDir . '/predis/predis/src/Command/Redis/BZPOPMIN.php', 'Predis\\Command\\Redis\\BloomFilter\\BFADD' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFADD.php', 'Predis\\Command\\Redis\\BloomFilter\\BFEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFEXISTS.php', 'Predis\\Command\\Redis\\BloomFilter\\BFINFO' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFINFO.php', 'Predis\\Command\\Redis\\BloomFilter\\BFINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFINSERT.php', 'Predis\\Command\\Redis\\BloomFilter\\BFLOADCHUNK' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFLOADCHUNK.php', 'Predis\\Command\\Redis\\BloomFilter\\BFMADD' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFMADD.php', 'Predis\\Command\\Redis\\BloomFilter\\BFMEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFMEXISTS.php', 'Predis\\Command\\Redis\\BloomFilter\\BFRESERVE' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFRESERVE.php', 'Predis\\Command\\Redis\\BloomFilter\\BFSCANDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/BloomFilter/BFSCANDUMP.php', 'Predis\\Command\\Redis\\CLIENT' => $vendorDir . '/predis/predis/src/Command/Redis/CLIENT.php', 'Predis\\Command\\Redis\\CLUSTER' => $vendorDir . '/predis/predis/src/Command/Redis/CLUSTER.php', 'Predis\\Command\\Redis\\COMMAND' => $vendorDir . '/predis/predis/src/Command/Redis/COMMAND.php', 'Predis\\Command\\Redis\\CONFIG' => $vendorDir . '/predis/predis/src/Command/Redis/CONFIG.php', 'Predis\\Command\\Redis\\COPY' => $vendorDir . '/predis/predis/src/Command/Redis/COPY.php', 'Predis\\Command\\Redis\\Container\\ACL' => $vendorDir . '/predis/predis/src/Command/Redis/Container/ACL.php', 'Predis\\Command\\Redis\\Container\\AbstractContainer' => $vendorDir . '/predis/predis/src/Command/Redis/Container/AbstractContainer.php', 'Predis\\Command\\Redis\\Container\\CLUSTER' => $vendorDir . '/predis/predis/src/Command/Redis/Container/CLUSTER.php', 'Predis\\Command\\Redis\\Container\\ContainerFactory' => $vendorDir . '/predis/predis/src/Command/Redis/Container/ContainerFactory.php', 'Predis\\Command\\Redis\\Container\\ContainerInterface' => $vendorDir . '/predis/predis/src/Command/Redis/Container/ContainerInterface.php', 'Predis\\Command\\Redis\\Container\\FunctionContainer' => $vendorDir . '/predis/predis/src/Command/Redis/Container/FunctionContainer.php', 'Predis\\Command\\Redis\\Container\\Json\\JSONDEBUG' => $vendorDir . '/predis/predis/src/Command/Redis/Container/Json/JSONDEBUG.php', 'Predis\\Command\\Redis\\Container\\Search\\FTCONFIG' => $vendorDir . '/predis/predis/src/Command/Redis/Container/Search/FTCONFIG.php', 'Predis\\Command\\Redis\\Container\\Search\\FTCURSOR' => $vendorDir . '/predis/predis/src/Command/Redis/Container/Search/FTCURSOR.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINCRBY.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINFO' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINFO.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYDIM' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYDIM.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSINITBYPROB' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSINITBYPROB.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSMERGE.php', 'Predis\\Command\\Redis\\CountMinSketch\\CMSQUERY' => $vendorDir . '/predis/predis/src/Command/Redis/CountMinSketch/CMSQUERY.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFADD' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFADD.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFADDNX' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFADDNX.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFCOUNT.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFDEL' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFDEL.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFEXISTS.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFINFO' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFINFO.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERT.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFINSERTNX' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFINSERTNX.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFLOADCHUNK' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFLOADCHUNK.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFMEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFMEXISTS.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFRESERVE' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFRESERVE.php', 'Predis\\Command\\Redis\\CuckooFilter\\CFSCANDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/CuckooFilter/CFSCANDUMP.php', 'Predis\\Command\\Redis\\DBSIZE' => $vendorDir . '/predis/predis/src/Command/Redis/DBSIZE.php', 'Predis\\Command\\Redis\\DECR' => $vendorDir . '/predis/predis/src/Command/Redis/DECR.php', 'Predis\\Command\\Redis\\DECRBY' => $vendorDir . '/predis/predis/src/Command/Redis/DECRBY.php', 'Predis\\Command\\Redis\\DEL' => $vendorDir . '/predis/predis/src/Command/Redis/DEL.php', 'Predis\\Command\\Redis\\DISCARD' => $vendorDir . '/predis/predis/src/Command/Redis/DISCARD.php', 'Predis\\Command\\Redis\\DUMP' => $vendorDir . '/predis/predis/src/Command/Redis/DUMP.php', 'Predis\\Command\\Redis\\ECHO_' => $vendorDir . '/predis/predis/src/Command/Redis/ECHO_.php', 'Predis\\Command\\Redis\\EVALSHA' => $vendorDir . '/predis/predis/src/Command/Redis/EVALSHA.php', 'Predis\\Command\\Redis\\EVALSHA_RO' => $vendorDir . '/predis/predis/src/Command/Redis/EVALSHA_RO.php', 'Predis\\Command\\Redis\\EVAL_' => $vendorDir . '/predis/predis/src/Command/Redis/EVAL_.php', 'Predis\\Command\\Redis\\EVAL_RO' => $vendorDir . '/predis/predis/src/Command/Redis/EVAL_RO.php', 'Predis\\Command\\Redis\\EXEC' => $vendorDir . '/predis/predis/src/Command/Redis/EXEC.php', 'Predis\\Command\\Redis\\EXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/EXISTS.php', 'Predis\\Command\\Redis\\EXPIRE' => $vendorDir . '/predis/predis/src/Command/Redis/EXPIRE.php', 'Predis\\Command\\Redis\\EXPIREAT' => $vendorDir . '/predis/predis/src/Command/Redis/EXPIREAT.php', 'Predis\\Command\\Redis\\EXPIRETIME' => $vendorDir . '/predis/predis/src/Command/Redis/EXPIRETIME.php', 'Predis\\Command\\Redis\\FAILOVER' => $vendorDir . '/predis/predis/src/Command/Redis/FAILOVER.php', 'Predis\\Command\\Redis\\FCALL' => $vendorDir . '/predis/predis/src/Command/Redis/FCALL.php', 'Predis\\Command\\Redis\\FCALL_RO' => $vendorDir . '/predis/predis/src/Command/Redis/FCALL_RO.php', 'Predis\\Command\\Redis\\FLUSHALL' => $vendorDir . '/predis/predis/src/Command/Redis/FLUSHALL.php', 'Predis\\Command\\Redis\\FLUSHDB' => $vendorDir . '/predis/predis/src/Command/Redis/FLUSHDB.php', 'Predis\\Command\\Redis\\FUNCTIONS' => $vendorDir . '/predis/predis/src/Command/Redis/FUNCTIONS.php', 'Predis\\Command\\Redis\\GEOADD' => $vendorDir . '/predis/predis/src/Command/Redis/GEOADD.php', 'Predis\\Command\\Redis\\GEODIST' => $vendorDir . '/predis/predis/src/Command/Redis/GEODIST.php', 'Predis\\Command\\Redis\\GEOHASH' => $vendorDir . '/predis/predis/src/Command/Redis/GEOHASH.php', 'Predis\\Command\\Redis\\GEOPOS' => $vendorDir . '/predis/predis/src/Command/Redis/GEOPOS.php', 'Predis\\Command\\Redis\\GEORADIUS' => $vendorDir . '/predis/predis/src/Command/Redis/GEORADIUS.php', 'Predis\\Command\\Redis\\GEORADIUSBYMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/GEORADIUSBYMEMBER.php', 'Predis\\Command\\Redis\\GEOSEARCH' => $vendorDir . '/predis/predis/src/Command/Redis/GEOSEARCH.php', 'Predis\\Command\\Redis\\GEOSEARCHSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/GEOSEARCHSTORE.php', 'Predis\\Command\\Redis\\GET' => $vendorDir . '/predis/predis/src/Command/Redis/GET.php', 'Predis\\Command\\Redis\\GETBIT' => $vendorDir . '/predis/predis/src/Command/Redis/GETBIT.php', 'Predis\\Command\\Redis\\GETDEL' => $vendorDir . '/predis/predis/src/Command/Redis/GETDEL.php', 'Predis\\Command\\Redis\\GETEX' => $vendorDir . '/predis/predis/src/Command/Redis/GETEX.php', 'Predis\\Command\\Redis\\GETRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/GETRANGE.php', 'Predis\\Command\\Redis\\GETSET' => $vendorDir . '/predis/predis/src/Command/Redis/GETSET.php', 'Predis\\Command\\Redis\\HDEL' => $vendorDir . '/predis/predis/src/Command/Redis/HDEL.php', 'Predis\\Command\\Redis\\HEXISTS' => $vendorDir . '/predis/predis/src/Command/Redis/HEXISTS.php', 'Predis\\Command\\Redis\\HGET' => $vendorDir . '/predis/predis/src/Command/Redis/HGET.php', 'Predis\\Command\\Redis\\HGETALL' => $vendorDir . '/predis/predis/src/Command/Redis/HGETALL.php', 'Predis\\Command\\Redis\\HINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/HINCRBY.php', 'Predis\\Command\\Redis\\HINCRBYFLOAT' => $vendorDir . '/predis/predis/src/Command/Redis/HINCRBYFLOAT.php', 'Predis\\Command\\Redis\\HKEYS' => $vendorDir . '/predis/predis/src/Command/Redis/HKEYS.php', 'Predis\\Command\\Redis\\HLEN' => $vendorDir . '/predis/predis/src/Command/Redis/HLEN.php', 'Predis\\Command\\Redis\\HMGET' => $vendorDir . '/predis/predis/src/Command/Redis/HMGET.php', 'Predis\\Command\\Redis\\HMSET' => $vendorDir . '/predis/predis/src/Command/Redis/HMSET.php', 'Predis\\Command\\Redis\\HRANDFIELD' => $vendorDir . '/predis/predis/src/Command/Redis/HRANDFIELD.php', 'Predis\\Command\\Redis\\HSCAN' => $vendorDir . '/predis/predis/src/Command/Redis/HSCAN.php', 'Predis\\Command\\Redis\\HSET' => $vendorDir . '/predis/predis/src/Command/Redis/HSET.php', 'Predis\\Command\\Redis\\HSETNX' => $vendorDir . '/predis/predis/src/Command/Redis/HSETNX.php', 'Predis\\Command\\Redis\\HSTRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/HSTRLEN.php', 'Predis\\Command\\Redis\\HVALS' => $vendorDir . '/predis/predis/src/Command/Redis/HVALS.php', 'Predis\\Command\\Redis\\INCR' => $vendorDir . '/predis/predis/src/Command/Redis/INCR.php', 'Predis\\Command\\Redis\\INCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/INCRBY.php', 'Predis\\Command\\Redis\\INCRBYFLOAT' => $vendorDir . '/predis/predis/src/Command/Redis/INCRBYFLOAT.php', 'Predis\\Command\\Redis\\INFO' => $vendorDir . '/predis/predis/src/Command/Redis/INFO.php', 'Predis\\Command\\Redis\\Json\\JSONARRAPPEND' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRAPPEND.php', 'Predis\\Command\\Redis\\Json\\JSONARRINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRINDEX.php', 'Predis\\Command\\Redis\\Json\\JSONARRINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRINSERT.php', 'Predis\\Command\\Redis\\Json\\JSONARRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRLEN.php', 'Predis\\Command\\Redis\\Json\\JSONARRPOP' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRPOP.php', 'Predis\\Command\\Redis\\Json\\JSONARRTRIM' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONARRTRIM.php', 'Predis\\Command\\Redis\\Json\\JSONCLEAR' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONCLEAR.php', 'Predis\\Command\\Redis\\Json\\JSONDEBUG' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONDEBUG.php', 'Predis\\Command\\Redis\\Json\\JSONDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONDEL.php', 'Predis\\Command\\Redis\\Json\\JSONFORGET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONFORGET.php', 'Predis\\Command\\Redis\\Json\\JSONGET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONGET.php', 'Predis\\Command\\Redis\\Json\\JSONMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONMERGE.php', 'Predis\\Command\\Redis\\Json\\JSONMGET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONMGET.php', 'Predis\\Command\\Redis\\Json\\JSONMSET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONMSET.php', 'Predis\\Command\\Redis\\Json\\JSONNUMINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONNUMINCRBY.php', 'Predis\\Command\\Redis\\Json\\JSONOBJKEYS' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONOBJKEYS.php', 'Predis\\Command\\Redis\\Json\\JSONOBJLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONOBJLEN.php', 'Predis\\Command\\Redis\\Json\\JSONRESP' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONRESP.php', 'Predis\\Command\\Redis\\Json\\JSONSET' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONSET.php', 'Predis\\Command\\Redis\\Json\\JSONSTRAPPEND' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONSTRAPPEND.php', 'Predis\\Command\\Redis\\Json\\JSONSTRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONSTRLEN.php', 'Predis\\Command\\Redis\\Json\\JSONTOGGLE' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONTOGGLE.php', 'Predis\\Command\\Redis\\Json\\JSONTYPE' => $vendorDir . '/predis/predis/src/Command/Redis/Json/JSONTYPE.php', 'Predis\\Command\\Redis\\KEYS' => $vendorDir . '/predis/predis/src/Command/Redis/KEYS.php', 'Predis\\Command\\Redis\\LASTSAVE' => $vendorDir . '/predis/predis/src/Command/Redis/LASTSAVE.php', 'Predis\\Command\\Redis\\LCS' => $vendorDir . '/predis/predis/src/Command/Redis/LCS.php', 'Predis\\Command\\Redis\\LINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/LINDEX.php', 'Predis\\Command\\Redis\\LINSERT' => $vendorDir . '/predis/predis/src/Command/Redis/LINSERT.php', 'Predis\\Command\\Redis\\LLEN' => $vendorDir . '/predis/predis/src/Command/Redis/LLEN.php', 'Predis\\Command\\Redis\\LMOVE' => $vendorDir . '/predis/predis/src/Command/Redis/LMOVE.php', 'Predis\\Command\\Redis\\LMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/LMPOP.php', 'Predis\\Command\\Redis\\LPOP' => $vendorDir . '/predis/predis/src/Command/Redis/LPOP.php', 'Predis\\Command\\Redis\\LPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/LPUSH.php', 'Predis\\Command\\Redis\\LPUSHX' => $vendorDir . '/predis/predis/src/Command/Redis/LPUSHX.php', 'Predis\\Command\\Redis\\LRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/LRANGE.php', 'Predis\\Command\\Redis\\LREM' => $vendorDir . '/predis/predis/src/Command/Redis/LREM.php', 'Predis\\Command\\Redis\\LSET' => $vendorDir . '/predis/predis/src/Command/Redis/LSET.php', 'Predis\\Command\\Redis\\LTRIM' => $vendorDir . '/predis/predis/src/Command/Redis/LTRIM.php', 'Predis\\Command\\Redis\\MGET' => $vendorDir . '/predis/predis/src/Command/Redis/MGET.php', 'Predis\\Command\\Redis\\MIGRATE' => $vendorDir . '/predis/predis/src/Command/Redis/MIGRATE.php', 'Predis\\Command\\Redis\\MONITOR' => $vendorDir . '/predis/predis/src/Command/Redis/MONITOR.php', 'Predis\\Command\\Redis\\MOVE' => $vendorDir . '/predis/predis/src/Command/Redis/MOVE.php', 'Predis\\Command\\Redis\\MSET' => $vendorDir . '/predis/predis/src/Command/Redis/MSET.php', 'Predis\\Command\\Redis\\MSETNX' => $vendorDir . '/predis/predis/src/Command/Redis/MSETNX.php', 'Predis\\Command\\Redis\\MULTI' => $vendorDir . '/predis/predis/src/Command/Redis/MULTI.php', 'Predis\\Command\\Redis\\OBJECT_' => $vendorDir . '/predis/predis/src/Command/Redis/OBJECT_.php', 'Predis\\Command\\Redis\\PERSIST' => $vendorDir . '/predis/predis/src/Command/Redis/PERSIST.php', 'Predis\\Command\\Redis\\PEXPIRE' => $vendorDir . '/predis/predis/src/Command/Redis/PEXPIRE.php', 'Predis\\Command\\Redis\\PEXPIREAT' => $vendorDir . '/predis/predis/src/Command/Redis/PEXPIREAT.php', 'Predis\\Command\\Redis\\PEXPIRETIME' => $vendorDir . '/predis/predis/src/Command/Redis/PEXPIRETIME.php', 'Predis\\Command\\Redis\\PFADD' => $vendorDir . '/predis/predis/src/Command/Redis/PFADD.php', 'Predis\\Command\\Redis\\PFCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/PFCOUNT.php', 'Predis\\Command\\Redis\\PFMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/PFMERGE.php', 'Predis\\Command\\Redis\\PING' => $vendorDir . '/predis/predis/src/Command/Redis/PING.php', 'Predis\\Command\\Redis\\PSETEX' => $vendorDir . '/predis/predis/src/Command/Redis/PSETEX.php', 'Predis\\Command\\Redis\\PSUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/PSUBSCRIBE.php', 'Predis\\Command\\Redis\\PTTL' => $vendorDir . '/predis/predis/src/Command/Redis/PTTL.php', 'Predis\\Command\\Redis\\PUBLISH' => $vendorDir . '/predis/predis/src/Command/Redis/PUBLISH.php', 'Predis\\Command\\Redis\\PUBSUB' => $vendorDir . '/predis/predis/src/Command/Redis/PUBSUB.php', 'Predis\\Command\\Redis\\PUNSUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/PUNSUBSCRIBE.php', 'Predis\\Command\\Redis\\QUIT' => $vendorDir . '/predis/predis/src/Command/Redis/QUIT.php', 'Predis\\Command\\Redis\\RANDOMKEY' => $vendorDir . '/predis/predis/src/Command/Redis/RANDOMKEY.php', 'Predis\\Command\\Redis\\RENAME' => $vendorDir . '/predis/predis/src/Command/Redis/RENAME.php', 'Predis\\Command\\Redis\\RENAMENX' => $vendorDir . '/predis/predis/src/Command/Redis/RENAMENX.php', 'Predis\\Command\\Redis\\RESTORE' => $vendorDir . '/predis/predis/src/Command/Redis/RESTORE.php', 'Predis\\Command\\Redis\\RPOP' => $vendorDir . '/predis/predis/src/Command/Redis/RPOP.php', 'Predis\\Command\\Redis\\RPOPLPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/RPOPLPUSH.php', 'Predis\\Command\\Redis\\RPUSH' => $vendorDir . '/predis/predis/src/Command/Redis/RPUSH.php', 'Predis\\Command\\Redis\\RPUSHX' => $vendorDir . '/predis/predis/src/Command/Redis/RPUSHX.php', 'Predis\\Command\\Redis\\SADD' => $vendorDir . '/predis/predis/src/Command/Redis/SADD.php', 'Predis\\Command\\Redis\\SAVE' => $vendorDir . '/predis/predis/src/Command/Redis/SAVE.php', 'Predis\\Command\\Redis\\SCAN' => $vendorDir . '/predis/predis/src/Command/Redis/SCAN.php', 'Predis\\Command\\Redis\\SCARD' => $vendorDir . '/predis/predis/src/Command/Redis/SCARD.php', 'Predis\\Command\\Redis\\SCRIPT' => $vendorDir . '/predis/predis/src/Command/Redis/SCRIPT.php', 'Predis\\Command\\Redis\\SDIFF' => $vendorDir . '/predis/predis/src/Command/Redis/SDIFF.php', 'Predis\\Command\\Redis\\SDIFFSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/SDIFFSTORE.php', 'Predis\\Command\\Redis\\SELECT' => $vendorDir . '/predis/predis/src/Command/Redis/SELECT.php', 'Predis\\Command\\Redis\\SENTINEL' => $vendorDir . '/predis/predis/src/Command/Redis/SENTINEL.php', 'Predis\\Command\\Redis\\SET' => $vendorDir . '/predis/predis/src/Command/Redis/SET.php', 'Predis\\Command\\Redis\\SETBIT' => $vendorDir . '/predis/predis/src/Command/Redis/SETBIT.php', 'Predis\\Command\\Redis\\SETEX' => $vendorDir . '/predis/predis/src/Command/Redis/SETEX.php', 'Predis\\Command\\Redis\\SETNX' => $vendorDir . '/predis/predis/src/Command/Redis/SETNX.php', 'Predis\\Command\\Redis\\SETRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/SETRANGE.php', 'Predis\\Command\\Redis\\SHUTDOWN' => $vendorDir . '/predis/predis/src/Command/Redis/SHUTDOWN.php', 'Predis\\Command\\Redis\\SINTER' => $vendorDir . '/predis/predis/src/Command/Redis/SINTER.php', 'Predis\\Command\\Redis\\SINTERCARD' => $vendorDir . '/predis/predis/src/Command/Redis/SINTERCARD.php', 'Predis\\Command\\Redis\\SINTERSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/SINTERSTORE.php', 'Predis\\Command\\Redis\\SISMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/SISMEMBER.php', 'Predis\\Command\\Redis\\SLAVEOF' => $vendorDir . '/predis/predis/src/Command/Redis/SLAVEOF.php', 'Predis\\Command\\Redis\\SLOWLOG' => $vendorDir . '/predis/predis/src/Command/Redis/SLOWLOG.php', 'Predis\\Command\\Redis\\SMEMBERS' => $vendorDir . '/predis/predis/src/Command/Redis/SMEMBERS.php', 'Predis\\Command\\Redis\\SMISMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/SMISMEMBER.php', 'Predis\\Command\\Redis\\SMOVE' => $vendorDir . '/predis/predis/src/Command/Redis/SMOVE.php', 'Predis\\Command\\Redis\\SORT' => $vendorDir . '/predis/predis/src/Command/Redis/SORT.php', 'Predis\\Command\\Redis\\SORT_RO' => $vendorDir . '/predis/predis/src/Command/Redis/SORT_RO.php', 'Predis\\Command\\Redis\\SPOP' => $vendorDir . '/predis/predis/src/Command/Redis/SPOP.php', 'Predis\\Command\\Redis\\SRANDMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/SRANDMEMBER.php', 'Predis\\Command\\Redis\\SREM' => $vendorDir . '/predis/predis/src/Command/Redis/SREM.php', 'Predis\\Command\\Redis\\SSCAN' => $vendorDir . '/predis/predis/src/Command/Redis/SSCAN.php', 'Predis\\Command\\Redis\\STRLEN' => $vendorDir . '/predis/predis/src/Command/Redis/STRLEN.php', 'Predis\\Command\\Redis\\SUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/SUBSCRIBE.php', 'Predis\\Command\\Redis\\SUBSTR' => $vendorDir . '/predis/predis/src/Command/Redis/SUBSTR.php', 'Predis\\Command\\Redis\\SUNION' => $vendorDir . '/predis/predis/src/Command/Redis/SUNION.php', 'Predis\\Command\\Redis\\SUNIONSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/SUNIONSTORE.php', 'Predis\\Command\\Redis\\Search\\FTAGGREGATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTAGGREGATE.php', 'Predis\\Command\\Redis\\Search\\FTALIASADD' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALIASADD.php', 'Predis\\Command\\Redis\\Search\\FTALIASDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALIASDEL.php', 'Predis\\Command\\Redis\\Search\\FTALIASUPDATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALIASUPDATE.php', 'Predis\\Command\\Redis\\Search\\FTALTER' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTALTER.php', 'Predis\\Command\\Redis\\Search\\FTCONFIG' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTCONFIG.php', 'Predis\\Command\\Redis\\Search\\FTCREATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTCREATE.php', 'Predis\\Command\\Redis\\Search\\FTCURSOR' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTCURSOR.php', 'Predis\\Command\\Redis\\Search\\FTDICTADD' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDICTADD.php', 'Predis\\Command\\Redis\\Search\\FTDICTDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDICTDEL.php', 'Predis\\Command\\Redis\\Search\\FTDICTDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDICTDUMP.php', 'Predis\\Command\\Redis\\Search\\FTDROPINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTDROPINDEX.php', 'Predis\\Command\\Redis\\Search\\FTEXPLAIN' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTEXPLAIN.php', 'Predis\\Command\\Redis\\Search\\FTINFO' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTINFO.php', 'Predis\\Command\\Redis\\Search\\FTPROFILE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTPROFILE.php', 'Predis\\Command\\Redis\\Search\\FTSEARCH' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSEARCH.php', 'Predis\\Command\\Redis\\Search\\FTSPELLCHECK' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSPELLCHECK.php', 'Predis\\Command\\Redis\\Search\\FTSUGADD' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGADD.php', 'Predis\\Command\\Redis\\Search\\FTSUGDEL' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGDEL.php', 'Predis\\Command\\Redis\\Search\\FTSUGGET' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGGET.php', 'Predis\\Command\\Redis\\Search\\FTSUGLEN' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSUGLEN.php', 'Predis\\Command\\Redis\\Search\\FTSYNDUMP' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSYNDUMP.php', 'Predis\\Command\\Redis\\Search\\FTSYNUPDATE' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTSYNUPDATE.php', 'Predis\\Command\\Redis\\Search\\FTTAGVALS' => $vendorDir . '/predis/predis/src/Command/Redis/Search/FTTAGVALS.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTADD' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTADD.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTBYRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTBYREVRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTBYREVRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTCDF' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCDF.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTCREATE' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTCREATE.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTINFO' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTINFO.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTMAX' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMAX.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTMERGE' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMERGE.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTMIN' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTMIN.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTQUANTILE' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTQUANTILE.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTRESET' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTRESET.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTREVRANK' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTREVRANK.php', 'Predis\\Command\\Redis\\TDigest\\TDIGESTTRIMMED_MEAN' => $vendorDir . '/predis/predis/src/Command/Redis/TDigest/TDIGESTTRIMMED_MEAN.php', 'Predis\\Command\\Redis\\TIME' => $vendorDir . '/predis/predis/src/Command/Redis/TIME.php', 'Predis\\Command\\Redis\\TOUCH' => $vendorDir . '/predis/predis/src/Command/Redis/TOUCH.php', 'Predis\\Command\\Redis\\TTL' => $vendorDir . '/predis/predis/src/Command/Redis/TTL.php', 'Predis\\Command\\Redis\\TYPE' => $vendorDir . '/predis/predis/src/Command/Redis/TYPE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSADD' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSADD.php', 'Predis\\Command\\Redis\\TimeSeries\\TSALTER' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSALTER.php', 'Predis\\Command\\Redis\\TimeSeries\\TSCREATE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSCREATERULE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSCREATERULE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSDECRBY' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSDECRBY.php', 'Predis\\Command\\Redis\\TimeSeries\\TSDEL' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSDEL.php', 'Predis\\Command\\Redis\\TimeSeries\\TSDELETERULE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSDELETERULE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSGET' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSGET.php', 'Predis\\Command\\Redis\\TimeSeries\\TSINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSINCRBY.php', 'Predis\\Command\\Redis\\TimeSeries\\TSINFO' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSINFO.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMADD' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMADD.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMGET' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMGET.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMRANGE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSMREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSMREVRANGE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSQUERYINDEX' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSQUERYINDEX.php', 'Predis\\Command\\Redis\\TimeSeries\\TSRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSRANGE.php', 'Predis\\Command\\Redis\\TimeSeries\\TSREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/TimeSeries/TSREVRANGE.php', 'Predis\\Command\\Redis\\TopK\\TOPKADD' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKADD.php', 'Predis\\Command\\Redis\\TopK\\TOPKINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKINCRBY.php', 'Predis\\Command\\Redis\\TopK\\TOPKINFO' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKINFO.php', 'Predis\\Command\\Redis\\TopK\\TOPKLIST' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKLIST.php', 'Predis\\Command\\Redis\\TopK\\TOPKQUERY' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKQUERY.php', 'Predis\\Command\\Redis\\TopK\\TOPKRESERVE' => $vendorDir . '/predis/predis/src/Command/Redis/TopK/TOPKRESERVE.php', 'Predis\\Command\\Redis\\UNSUBSCRIBE' => $vendorDir . '/predis/predis/src/Command/Redis/UNSUBSCRIBE.php', 'Predis\\Command\\Redis\\UNWATCH' => $vendorDir . '/predis/predis/src/Command/Redis/UNWATCH.php', 'Predis\\Command\\Redis\\WAITAOF' => $vendorDir . '/predis/predis/src/Command/Redis/WAITAOF.php', 'Predis\\Command\\Redis\\WATCH' => $vendorDir . '/predis/predis/src/Command/Redis/WATCH.php', 'Predis\\Command\\Redis\\XADD' => $vendorDir . '/predis/predis/src/Command/Redis/XADD.php', 'Predis\\Command\\Redis\\XDEL' => $vendorDir . '/predis/predis/src/Command/Redis/XDEL.php', 'Predis\\Command\\Redis\\XLEN' => $vendorDir . '/predis/predis/src/Command/Redis/XLEN.php', 'Predis\\Command\\Redis\\XRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/XRANGE.php', 'Predis\\Command\\Redis\\XREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/XREVRANGE.php', 'Predis\\Command\\Redis\\XTRIM' => $vendorDir . '/predis/predis/src/Command/Redis/XTRIM.php', 'Predis\\Command\\Redis\\ZADD' => $vendorDir . '/predis/predis/src/Command/Redis/ZADD.php', 'Predis\\Command\\Redis\\ZCARD' => $vendorDir . '/predis/predis/src/Command/Redis/ZCARD.php', 'Predis\\Command\\Redis\\ZCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/ZCOUNT.php', 'Predis\\Command\\Redis\\ZDIFF' => $vendorDir . '/predis/predis/src/Command/Redis/ZDIFF.php', 'Predis\\Command\\Redis\\ZDIFFSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZDIFFSTORE.php', 'Predis\\Command\\Redis\\ZINCRBY' => $vendorDir . '/predis/predis/src/Command/Redis/ZINCRBY.php', 'Predis\\Command\\Redis\\ZINTER' => $vendorDir . '/predis/predis/src/Command/Redis/ZINTER.php', 'Predis\\Command\\Redis\\ZINTERCARD' => $vendorDir . '/predis/predis/src/Command/Redis/ZINTERCARD.php', 'Predis\\Command\\Redis\\ZINTERSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZINTERSTORE.php', 'Predis\\Command\\Redis\\ZLEXCOUNT' => $vendorDir . '/predis/predis/src/Command/Redis/ZLEXCOUNT.php', 'Predis\\Command\\Redis\\ZMPOP' => $vendorDir . '/predis/predis/src/Command/Redis/ZMPOP.php', 'Predis\\Command\\Redis\\ZMSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZMSCORE.php', 'Predis\\Command\\Redis\\ZPOPMAX' => $vendorDir . '/predis/predis/src/Command/Redis/ZPOPMAX.php', 'Predis\\Command\\Redis\\ZPOPMIN' => $vendorDir . '/predis/predis/src/Command/Redis/ZPOPMIN.php', 'Predis\\Command\\Redis\\ZRANDMEMBER' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANDMEMBER.php', 'Predis\\Command\\Redis\\ZRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGE.php', 'Predis\\Command\\Redis\\ZRANGEBYLEX' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGEBYLEX.php', 'Predis\\Command\\Redis\\ZRANGEBYSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGEBYSCORE.php', 'Predis\\Command\\Redis\\ZRANGESTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANGESTORE.php', 'Predis\\Command\\Redis\\ZRANK' => $vendorDir . '/predis/predis/src/Command/Redis/ZRANK.php', 'Predis\\Command\\Redis\\ZREM' => $vendorDir . '/predis/predis/src/Command/Redis/ZREM.php', 'Predis\\Command\\Redis\\ZREMRANGEBYLEX' => $vendorDir . '/predis/predis/src/Command/Redis/ZREMRANGEBYLEX.php', 'Predis\\Command\\Redis\\ZREMRANGEBYRANK' => $vendorDir . '/predis/predis/src/Command/Redis/ZREMRANGEBYRANK.php', 'Predis\\Command\\Redis\\ZREMRANGEBYSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZREMRANGEBYSCORE.php', 'Predis\\Command\\Redis\\ZREVRANGE' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANGE.php', 'Predis\\Command\\Redis\\ZREVRANGEBYLEX' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANGEBYLEX.php', 'Predis\\Command\\Redis\\ZREVRANGEBYSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANGEBYSCORE.php', 'Predis\\Command\\Redis\\ZREVRANK' => $vendorDir . '/predis/predis/src/Command/Redis/ZREVRANK.php', 'Predis\\Command\\Redis\\ZSCAN' => $vendorDir . '/predis/predis/src/Command/Redis/ZSCAN.php', 'Predis\\Command\\Redis\\ZSCORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZSCORE.php', 'Predis\\Command\\Redis\\ZUNION' => $vendorDir . '/predis/predis/src/Command/Redis/ZUNION.php', 'Predis\\Command\\Redis\\ZUNIONSTORE' => $vendorDir . '/predis/predis/src/Command/Redis/ZUNIONSTORE.php', 'Predis\\Command\\ScriptCommand' => $vendorDir . '/predis/predis/src/Command/ScriptCommand.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DeleteStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DeleteStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\DumpStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/DumpStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\FlushStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/FlushStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\KillStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/KillStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\ListStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/ListStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\LoadStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/LoadStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\RestoreStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/RestoreStrategy.php', 'Predis\\Command\\Strategy\\ContainerCommands\\Functions\\StatsStrategy' => $vendorDir . '/predis/predis/src/Command/Strategy/ContainerCommands/Functions/StatsStrategy.php', 'Predis\\Command\\Strategy\\StrategyResolverInterface' => $vendorDir . '/predis/predis/src/Command/Strategy/StrategyResolverInterface.php', 'Predis\\Command\\Strategy\\SubcommandStrategyInterface' => $vendorDir . '/predis/predis/src/Command/Strategy/SubcommandStrategyInterface.php', 'Predis\\Command\\Strategy\\SubcommandStrategyResolver' => $vendorDir . '/predis/predis/src/Command/Strategy/SubcommandStrategyResolver.php', 'Predis\\Command\\Traits\\Aggregate' => $vendorDir . '/predis/predis/src/Command/Traits/Aggregate.php', 'Predis\\Command\\Traits\\BitByte' => $vendorDir . '/predis/predis/src/Command/Traits/BitByte.php', 'Predis\\Command\\Traits\\BloomFilters\\BucketSize' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/BucketSize.php', 'Predis\\Command\\Traits\\BloomFilters\\Capacity' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Capacity.php', 'Predis\\Command\\Traits\\BloomFilters\\Error' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Error.php', 'Predis\\Command\\Traits\\BloomFilters\\Expansion' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Expansion.php', 'Predis\\Command\\Traits\\BloomFilters\\Items' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/Items.php', 'Predis\\Command\\Traits\\BloomFilters\\MaxIterations' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/MaxIterations.php', 'Predis\\Command\\Traits\\BloomFilters\\NoCreate' => $vendorDir . '/predis/predis/src/Command/Traits/BloomFilters/NoCreate.php', 'Predis\\Command\\Traits\\By\\ByArgument' => $vendorDir . '/predis/predis/src/Command/Traits/By/ByArgument.php', 'Predis\\Command\\Traits\\By\\ByLexByScore' => $vendorDir . '/predis/predis/src/Command/Traits/By/ByLexByScore.php', 'Predis\\Command\\Traits\\By\\GeoBy' => $vendorDir . '/predis/predis/src/Command/Traits/By/GeoBy.php', 'Predis\\Command\\Traits\\Count' => $vendorDir . '/predis/predis/src/Command/Traits/Count.php', 'Predis\\Command\\Traits\\DB' => $vendorDir . '/predis/predis/src/Command/Traits/DB.php', 'Predis\\Command\\Traits\\Expire\\ExpireOptions' => $vendorDir . '/predis/predis/src/Command/Traits/Expire/ExpireOptions.php', 'Predis\\Command\\Traits\\From\\GeoFrom' => $vendorDir . '/predis/predis/src/Command/Traits/From/GeoFrom.php', 'Predis\\Command\\Traits\\Get\\Get' => $vendorDir . '/predis/predis/src/Command/Traits/Get/Get.php', 'Predis\\Command\\Traits\\Json\\Indent' => $vendorDir . '/predis/predis/src/Command/Traits/Json/Indent.php', 'Predis\\Command\\Traits\\Json\\Newline' => $vendorDir . '/predis/predis/src/Command/Traits/Json/Newline.php', 'Predis\\Command\\Traits\\Json\\NxXxArgument' => $vendorDir . '/predis/predis/src/Command/Traits/Json/NxXxArgument.php', 'Predis\\Command\\Traits\\Json\\Space' => $vendorDir . '/predis/predis/src/Command/Traits/Json/Space.php', 'Predis\\Command\\Traits\\Keys' => $vendorDir . '/predis/predis/src/Command/Traits/Keys.php', 'Predis\\Command\\Traits\\LeftRight' => $vendorDir . '/predis/predis/src/Command/Traits/LeftRight.php', 'Predis\\Command\\Traits\\Limit\\Limit' => $vendorDir . '/predis/predis/src/Command/Traits/Limit/Limit.php', 'Predis\\Command\\Traits\\Limit\\LimitObject' => $vendorDir . '/predis/predis/src/Command/Traits/Limit/LimitObject.php', 'Predis\\Command\\Traits\\MinMaxModifier' => $vendorDir . '/predis/predis/src/Command/Traits/MinMaxModifier.php', 'Predis\\Command\\Traits\\Replace' => $vendorDir . '/predis/predis/src/Command/Traits/Replace.php', 'Predis\\Command\\Traits\\Rev' => $vendorDir . '/predis/predis/src/Command/Traits/Rev.php', 'Predis\\Command\\Traits\\Sorting' => $vendorDir . '/predis/predis/src/Command/Traits/Sorting.php', 'Predis\\Command\\Traits\\Storedist' => $vendorDir . '/predis/predis/src/Command/Traits/Storedist.php', 'Predis\\Command\\Traits\\Timeout' => $vendorDir . '/predis/predis/src/Command/Traits/Timeout.php', 'Predis\\Command\\Traits\\To\\ServerTo' => $vendorDir . '/predis/predis/src/Command/Traits/To/ServerTo.php', 'Predis\\Command\\Traits\\Weights' => $vendorDir . '/predis/predis/src/Command/Traits/Weights.php', 'Predis\\Command\\Traits\\With\\WithCoord' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithCoord.php', 'Predis\\Command\\Traits\\With\\WithDist' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithDist.php', 'Predis\\Command\\Traits\\With\\WithHash' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithHash.php', 'Predis\\Command\\Traits\\With\\WithScores' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithScores.php', 'Predis\\Command\\Traits\\With\\WithValues' => $vendorDir . '/predis/predis/src/Command/Traits/With/WithValues.php', 'Predis\\CommunicationException' => $vendorDir . '/predis/predis/src/CommunicationException.php', 'Predis\\Configuration\\OptionInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionInterface.php', 'Predis\\Configuration\\Option\\Aggregate' => $vendorDir . '/predis/predis/src/Configuration/Option/Aggregate.php', 'Predis\\Configuration\\Option\\CRC16' => $vendorDir . '/predis/predis/src/Configuration/Option/CRC16.php', 'Predis\\Configuration\\Option\\Cluster' => $vendorDir . '/predis/predis/src/Configuration/Option/Cluster.php', 'Predis\\Configuration\\Option\\Commands' => $vendorDir . '/predis/predis/src/Configuration/Option/Commands.php', 'Predis\\Configuration\\Option\\Connections' => $vendorDir . '/predis/predis/src/Configuration/Option/Connections.php', 'Predis\\Configuration\\Option\\Exceptions' => $vendorDir . '/predis/predis/src/Configuration/Option/Exceptions.php', 'Predis\\Configuration\\Option\\Prefix' => $vendorDir . '/predis/predis/src/Configuration/Option/Prefix.php', 'Predis\\Configuration\\Option\\Replication' => $vendorDir . '/predis/predis/src/Configuration/Option/Replication.php', 'Predis\\Configuration\\Options' => $vendorDir . '/predis/predis/src/Configuration/Options.php', 'Predis\\Configuration\\OptionsInterface' => $vendorDir . '/predis/predis/src/Configuration/OptionsInterface.php', 'Predis\\Connection\\AbstractConnection' => $vendorDir . '/predis/predis/src/Connection/AbstractConnection.php', 'Predis\\Connection\\AggregateConnectionInterface' => $vendorDir . '/predis/predis/src/Connection/AggregateConnectionInterface.php', 'Predis\\Connection\\Cluster\\ClusterInterface' => $vendorDir . '/predis/predis/src/Connection/Cluster/ClusterInterface.php', 'Predis\\Connection\\Cluster\\PredisCluster' => $vendorDir . '/predis/predis/src/Connection/Cluster/PredisCluster.php', 'Predis\\Connection\\Cluster\\RedisCluster' => $vendorDir . '/predis/predis/src/Connection/Cluster/RedisCluster.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\\RelayConnection' => $vendorDir . '/predis/predis/src/Connection/RelayConnection.php', 'Predis\\Connection\\RelayMethods' => $vendorDir . '/predis/predis/src/Connection/RelayMethods.php', 'Predis\\Connection\\Replication\\MasterSlaveReplication' => $vendorDir . '/predis/predis/src/Connection/Replication/MasterSlaveReplication.php', 'Predis\\Connection\\Replication\\ReplicationInterface' => $vendorDir . '/predis/predis/src/Connection/Replication/ReplicationInterface.php', 'Predis\\Connection\\Replication\\SentinelReplication' => $vendorDir . '/predis/predis/src/Connection/Replication/SentinelReplication.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\\Pipeline\\RelayAtomic' => $vendorDir . '/predis/predis/src/Pipeline/RelayAtomic.php', 'Predis\\Pipeline\\RelayPipeline' => $vendorDir . '/predis/predis/src/Pipeline/RelayPipeline.php', 'Predis\\PredisException' => $vendorDir . '/predis/predis/src/PredisException.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\\PubSub\\RelayConsumer' => $vendorDir . '/predis/predis/src/PubSub/RelayConsumer.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', 'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php', 'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php', 'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php', 'Psr\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/psr/event-dispatcher/src/EventDispatcherInterface.php', 'Psr\\EventDispatcher\\ListenerProviderInterface' => $vendorDir . '/psr/event-dispatcher/src/ListenerProviderInterface.php', 'Psr\\EventDispatcher\\StoppableEventInterface' => $vendorDir . '/psr/event-dispatcher/src/StoppableEventInterface.php', 'Psr\\Http\\Client\\ClientExceptionInterface' => $vendorDir . '/psr/http-client/src/ClientExceptionInterface.php', 'Psr\\Http\\Client\\ClientInterface' => $vendorDir . '/psr/http-client/src/ClientInterface.php', 'Psr\\Http\\Client\\NetworkExceptionInterface' => $vendorDir . '/psr/http-client/src/NetworkExceptionInterface.php', 'Psr\\Http\\Client\\RequestExceptionInterface' => $vendorDir . '/psr/http-client/src/RequestExceptionInterface.php', 'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php', 'Psr\\Http\\Message\\RequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/RequestFactoryInterface.php', 'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php', 'Psr\\Http\\Message\\ResponseFactoryInterface' => $vendorDir . '/psr/http-factory/src/ResponseFactoryInterface.php', 'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php', 'Psr\\Http\\Message\\ServerRequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/ServerRequestFactoryInterface.php', 'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php', 'Psr\\Http\\Message\\StreamFactoryInterface' => $vendorDir . '/psr/http-factory/src/StreamFactoryInterface.php', 'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php', 'Psr\\Http\\Message\\UploadedFileFactoryInterface' => $vendorDir . '/psr/http-factory/src/UploadedFileFactoryInterface.php', 'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php', 'Psr\\Http\\Message\\UriFactoryInterface' => $vendorDir . '/psr/http-factory/src/UriFactoryInterface.php', 'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php', 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/src/AbstractLogger.php', 'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/src/InvalidArgumentException.php', 'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/src/LogLevel.php', 'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/src/LoggerAwareInterface.php', 'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/src/LoggerAwareTrait.php', 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/src/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/src/LoggerTrait.php', 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/src/NullLogger.php', 'Psy\\CodeCleaner' => $vendorDir . '/psy/psysh/src/CodeCleaner.php', 'Psy\\CodeCleaner\\AbstractClassPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/AbstractClassPass.php', 'Psy\\CodeCleaner\\AssignThisVariablePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/AssignThisVariablePass.php', 'Psy\\CodeCleaner\\CallTimePassByReferencePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/CallTimePassByReferencePass.php', 'Psy\\CodeCleaner\\CalledClassPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/CalledClassPass.php', 'Psy\\CodeCleaner\\CodeCleanerPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/CodeCleanerPass.php', 'Psy\\CodeCleaner\\EmptyArrayDimFetchPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/EmptyArrayDimFetchPass.php', 'Psy\\CodeCleaner\\ExitPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/ExitPass.php', 'Psy\\CodeCleaner\\FinalClassPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/FinalClassPass.php', 'Psy\\CodeCleaner\\FunctionContextPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/FunctionContextPass.php', 'Psy\\CodeCleaner\\FunctionReturnInWriteContextPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/FunctionReturnInWriteContextPass.php', 'Psy\\CodeCleaner\\ImplicitReturnPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/ImplicitReturnPass.php', 'Psy\\CodeCleaner\\IssetPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/IssetPass.php', 'Psy\\CodeCleaner\\LabelContextPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/LabelContextPass.php', 'Psy\\CodeCleaner\\LeavePsyshAlonePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/LeavePsyshAlonePass.php', 'Psy\\CodeCleaner\\ListPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/ListPass.php', 'Psy\\CodeCleaner\\LoopContextPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/LoopContextPass.php', 'Psy\\CodeCleaner\\MagicConstantsPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/MagicConstantsPass.php', 'Psy\\CodeCleaner\\NamespaceAwarePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/NamespaceAwarePass.php', 'Psy\\CodeCleaner\\NamespacePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/NamespacePass.php', 'Psy\\CodeCleaner\\NoReturnValue' => $vendorDir . '/psy/psysh/src/CodeCleaner/NoReturnValue.php', 'Psy\\CodeCleaner\\PassableByReferencePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/PassableByReferencePass.php', 'Psy\\CodeCleaner\\RequirePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/RequirePass.php', 'Psy\\CodeCleaner\\ReturnTypePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/ReturnTypePass.php', 'Psy\\CodeCleaner\\StrictTypesPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/StrictTypesPass.php', 'Psy\\CodeCleaner\\UseStatementPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/UseStatementPass.php', 'Psy\\CodeCleaner\\ValidClassNamePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/ValidClassNamePass.php', 'Psy\\CodeCleaner\\ValidConstructorPass' => $vendorDir . '/psy/psysh/src/CodeCleaner/ValidConstructorPass.php', 'Psy\\CodeCleaner\\ValidFunctionNamePass' => $vendorDir . '/psy/psysh/src/CodeCleaner/ValidFunctionNamePass.php', 'Psy\\Command\\BufferCommand' => $vendorDir . '/psy/psysh/src/Command/BufferCommand.php', 'Psy\\Command\\ClearCommand' => $vendorDir . '/psy/psysh/src/Command/ClearCommand.php', 'Psy\\Command\\CodeArgumentParser' => $vendorDir . '/psy/psysh/src/Command/CodeArgumentParser.php', 'Psy\\Command\\Command' => $vendorDir . '/psy/psysh/src/Command/Command.php', 'Psy\\Command\\DocCommand' => $vendorDir . '/psy/psysh/src/Command/DocCommand.php', 'Psy\\Command\\DumpCommand' => $vendorDir . '/psy/psysh/src/Command/DumpCommand.php', 'Psy\\Command\\EditCommand' => $vendorDir . '/psy/psysh/src/Command/EditCommand.php', 'Psy\\Command\\ExitCommand' => $vendorDir . '/psy/psysh/src/Command/ExitCommand.php', 'Psy\\Command\\HelpCommand' => $vendorDir . '/psy/psysh/src/Command/HelpCommand.php', 'Psy\\Command\\HistoryCommand' => $vendorDir . '/psy/psysh/src/Command/HistoryCommand.php', 'Psy\\Command\\ListCommand' => $vendorDir . '/psy/psysh/src/Command/ListCommand.php', 'Psy\\Command\\ListCommand\\ClassConstantEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/ClassConstantEnumerator.php', 'Psy\\Command\\ListCommand\\ClassEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/ClassEnumerator.php', 'Psy\\Command\\ListCommand\\ConstantEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/ConstantEnumerator.php', 'Psy\\Command\\ListCommand\\Enumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/Enumerator.php', 'Psy\\Command\\ListCommand\\FunctionEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/FunctionEnumerator.php', 'Psy\\Command\\ListCommand\\GlobalVariableEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/GlobalVariableEnumerator.php', 'Psy\\Command\\ListCommand\\MethodEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/MethodEnumerator.php', 'Psy\\Command\\ListCommand\\PropertyEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/PropertyEnumerator.php', 'Psy\\Command\\ListCommand\\VariableEnumerator' => $vendorDir . '/psy/psysh/src/Command/ListCommand/VariableEnumerator.php', 'Psy\\Command\\ParseCommand' => $vendorDir . '/psy/psysh/src/Command/ParseCommand.php', 'Psy\\Command\\PsyVersionCommand' => $vendorDir . '/psy/psysh/src/Command/PsyVersionCommand.php', 'Psy\\Command\\ReflectingCommand' => $vendorDir . '/psy/psysh/src/Command/ReflectingCommand.php', 'Psy\\Command\\ShowCommand' => $vendorDir . '/psy/psysh/src/Command/ShowCommand.php', 'Psy\\Command\\SudoCommand' => $vendorDir . '/psy/psysh/src/Command/SudoCommand.php', 'Psy\\Command\\ThrowUpCommand' => $vendorDir . '/psy/psysh/src/Command/ThrowUpCommand.php', 'Psy\\Command\\TimeitCommand' => $vendorDir . '/psy/psysh/src/Command/TimeitCommand.php', 'Psy\\Command\\TimeitCommand\\TimeitVisitor' => $vendorDir . '/psy/psysh/src/Command/TimeitCommand/TimeitVisitor.php', 'Psy\\Command\\TraceCommand' => $vendorDir . '/psy/psysh/src/Command/TraceCommand.php', 'Psy\\Command\\WhereamiCommand' => $vendorDir . '/psy/psysh/src/Command/WhereamiCommand.php', 'Psy\\Command\\WtfCommand' => $vendorDir . '/psy/psysh/src/Command/WtfCommand.php', 'Psy\\ConfigPaths' => $vendorDir . '/psy/psysh/src/ConfigPaths.php', 'Psy\\Configuration' => $vendorDir . '/psy/psysh/src/Configuration.php', 'Psy\\Context' => $vendorDir . '/psy/psysh/src/Context.php', 'Psy\\ContextAware' => $vendorDir . '/psy/psysh/src/ContextAware.php', 'Psy\\EnvInterface' => $vendorDir . '/psy/psysh/src/EnvInterface.php', 'Psy\\Exception\\BreakException' => $vendorDir . '/psy/psysh/src/Exception/BreakException.php', 'Psy\\Exception\\DeprecatedException' => $vendorDir . '/psy/psysh/src/Exception/DeprecatedException.php', 'Psy\\Exception\\ErrorException' => $vendorDir . '/psy/psysh/src/Exception/ErrorException.php', 'Psy\\Exception\\Exception' => $vendorDir . '/psy/psysh/src/Exception/Exception.php', 'Psy\\Exception\\FatalErrorException' => $vendorDir . '/psy/psysh/src/Exception/FatalErrorException.php', 'Psy\\Exception\\ParseErrorException' => $vendorDir . '/psy/psysh/src/Exception/ParseErrorException.php', 'Psy\\Exception\\RuntimeException' => $vendorDir . '/psy/psysh/src/Exception/RuntimeException.php', 'Psy\\Exception\\ThrowUpException' => $vendorDir . '/psy/psysh/src/Exception/ThrowUpException.php', 'Psy\\Exception\\UnexpectedTargetException' => $vendorDir . '/psy/psysh/src/Exception/UnexpectedTargetException.php', 'Psy\\ExecutionClosure' => $vendorDir . '/psy/psysh/src/ExecutionClosure.php', 'Psy\\ExecutionLoopClosure' => $vendorDir . '/psy/psysh/src/ExecutionLoopClosure.php', 'Psy\\ExecutionLoop\\AbstractListener' => $vendorDir . '/psy/psysh/src/ExecutionLoop/AbstractListener.php', 'Psy\\ExecutionLoop\\Listener' => $vendorDir . '/psy/psysh/src/ExecutionLoop/Listener.php', 'Psy\\ExecutionLoop\\ProcessForker' => $vendorDir . '/psy/psysh/src/ExecutionLoop/ProcessForker.php', 'Psy\\ExecutionLoop\\RunkitReloader' => $vendorDir . '/psy/psysh/src/ExecutionLoop/RunkitReloader.php', 'Psy\\Formatter\\CodeFormatter' => $vendorDir . '/psy/psysh/src/Formatter/CodeFormatter.php', 'Psy\\Formatter\\DocblockFormatter' => $vendorDir . '/psy/psysh/src/Formatter/DocblockFormatter.php', 'Psy\\Formatter\\ReflectorFormatter' => $vendorDir . '/psy/psysh/src/Formatter/ReflectorFormatter.php', 'Psy\\Formatter\\SignatureFormatter' => $vendorDir . '/psy/psysh/src/Formatter/SignatureFormatter.php', 'Psy\\Formatter\\TraceFormatter' => $vendorDir . '/psy/psysh/src/Formatter/TraceFormatter.php', 'Psy\\Input\\CodeArgument' => $vendorDir . '/psy/psysh/src/Input/CodeArgument.php', 'Psy\\Input\\FilterOptions' => $vendorDir . '/psy/psysh/src/Input/FilterOptions.php', 'Psy\\Input\\ShellInput' => $vendorDir . '/psy/psysh/src/Input/ShellInput.php', 'Psy\\Input\\SilentInput' => $vendorDir . '/psy/psysh/src/Input/SilentInput.php', 'Psy\\Output\\OutputPager' => $vendorDir . '/psy/psysh/src/Output/OutputPager.php', 'Psy\\Output\\PassthruPager' => $vendorDir . '/psy/psysh/src/Output/PassthruPager.php', 'Psy\\Output\\ProcOutputPager' => $vendorDir . '/psy/psysh/src/Output/ProcOutputPager.php', 'Psy\\Output\\ShellOutput' => $vendorDir . '/psy/psysh/src/Output/ShellOutput.php', 'Psy\\Output\\Theme' => $vendorDir . '/psy/psysh/src/Output/Theme.php', 'Psy\\ParserFactory' => $vendorDir . '/psy/psysh/src/ParserFactory.php', 'Psy\\Readline\\GNUReadline' => $vendorDir . '/psy/psysh/src/Readline/GNUReadline.php', 'Psy\\Readline\\Hoa\\Autocompleter' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Autocompleter.php', 'Psy\\Readline\\Hoa\\AutocompleterAggregate' => $vendorDir . '/psy/psysh/src/Readline/Hoa/AutocompleterAggregate.php', 'Psy\\Readline\\Hoa\\AutocompleterPath' => $vendorDir . '/psy/psysh/src/Readline/Hoa/AutocompleterPath.php', 'Psy\\Readline\\Hoa\\AutocompleterWord' => $vendorDir . '/psy/psysh/src/Readline/Hoa/AutocompleterWord.php', 'Psy\\Readline\\Hoa\\Console' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Console.php', 'Psy\\Readline\\Hoa\\ConsoleCursor' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ConsoleCursor.php', 'Psy\\Readline\\Hoa\\ConsoleException' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ConsoleException.php', 'Psy\\Readline\\Hoa\\ConsoleInput' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ConsoleInput.php', 'Psy\\Readline\\Hoa\\ConsoleOutput' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ConsoleOutput.php', 'Psy\\Readline\\Hoa\\ConsoleProcessus' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ConsoleProcessus.php', 'Psy\\Readline\\Hoa\\ConsoleTput' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ConsoleTput.php', 'Psy\\Readline\\Hoa\\ConsoleWindow' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ConsoleWindow.php', 'Psy\\Readline\\Hoa\\Event' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Event.php', 'Psy\\Readline\\Hoa\\EventBucket' => $vendorDir . '/psy/psysh/src/Readline/Hoa/EventBucket.php', 'Psy\\Readline\\Hoa\\EventException' => $vendorDir . '/psy/psysh/src/Readline/Hoa/EventException.php', 'Psy\\Readline\\Hoa\\EventListenable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/EventListenable.php', 'Psy\\Readline\\Hoa\\EventListener' => $vendorDir . '/psy/psysh/src/Readline/Hoa/EventListener.php', 'Psy\\Readline\\Hoa\\EventListens' => $vendorDir . '/psy/psysh/src/Readline/Hoa/EventListens.php', 'Psy\\Readline\\Hoa\\EventSource' => $vendorDir . '/psy/psysh/src/Readline/Hoa/EventSource.php', 'Psy\\Readline\\Hoa\\Exception' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Exception.php', 'Psy\\Readline\\Hoa\\ExceptionIdle' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ExceptionIdle.php', 'Psy\\Readline\\Hoa\\File' => $vendorDir . '/psy/psysh/src/Readline/Hoa/File.php', 'Psy\\Readline\\Hoa\\FileDirectory' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileDirectory.php', 'Psy\\Readline\\Hoa\\FileDoesNotExistException' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileDoesNotExistException.php', 'Psy\\Readline\\Hoa\\FileException' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileException.php', 'Psy\\Readline\\Hoa\\FileFinder' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileFinder.php', 'Psy\\Readline\\Hoa\\FileGeneric' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileGeneric.php', 'Psy\\Readline\\Hoa\\FileLink' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileLink.php', 'Psy\\Readline\\Hoa\\FileLinkRead' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileLinkRead.php', 'Psy\\Readline\\Hoa\\FileLinkReadWrite' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileLinkReadWrite.php', 'Psy\\Readline\\Hoa\\FileRead' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileRead.php', 'Psy\\Readline\\Hoa\\FileReadWrite' => $vendorDir . '/psy/psysh/src/Readline/Hoa/FileReadWrite.php', 'Psy\\Readline\\Hoa\\IStream' => $vendorDir . '/psy/psysh/src/Readline/Hoa/IStream.php', 'Psy\\Readline\\Hoa\\IteratorFileSystem' => $vendorDir . '/psy/psysh/src/Readline/Hoa/IteratorFileSystem.php', 'Psy\\Readline\\Hoa\\IteratorRecursiveDirectory' => $vendorDir . '/psy/psysh/src/Readline/Hoa/IteratorRecursiveDirectory.php', 'Psy\\Readline\\Hoa\\IteratorSplFileInfo' => $vendorDir . '/psy/psysh/src/Readline/Hoa/IteratorSplFileInfo.php', 'Psy\\Readline\\Hoa\\Protocol' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Protocol.php', 'Psy\\Readline\\Hoa\\ProtocolException' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ProtocolException.php', 'Psy\\Readline\\Hoa\\ProtocolNode' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ProtocolNode.php', 'Psy\\Readline\\Hoa\\ProtocolNodeLibrary' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ProtocolNodeLibrary.php', 'Psy\\Readline\\Hoa\\ProtocolWrapper' => $vendorDir . '/psy/psysh/src/Readline/Hoa/ProtocolWrapper.php', 'Psy\\Readline\\Hoa\\Readline' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Readline.php', 'Psy\\Readline\\Hoa\\Stream' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Stream.php', 'Psy\\Readline\\Hoa\\StreamBufferable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamBufferable.php', 'Psy\\Readline\\Hoa\\StreamContext' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamContext.php', 'Psy\\Readline\\Hoa\\StreamException' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamException.php', 'Psy\\Readline\\Hoa\\StreamIn' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamIn.php', 'Psy\\Readline\\Hoa\\StreamLockable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamLockable.php', 'Psy\\Readline\\Hoa\\StreamOut' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamOut.php', 'Psy\\Readline\\Hoa\\StreamPathable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamPathable.php', 'Psy\\Readline\\Hoa\\StreamPointable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamPointable.php', 'Psy\\Readline\\Hoa\\StreamStatable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamStatable.php', 'Psy\\Readline\\Hoa\\StreamTouchable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/StreamTouchable.php', 'Psy\\Readline\\Hoa\\Ustring' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Ustring.php', 'Psy\\Readline\\Hoa\\Xcallable' => $vendorDir . '/psy/psysh/src/Readline/Hoa/Xcallable.php', 'Psy\\Readline\\Libedit' => $vendorDir . '/psy/psysh/src/Readline/Libedit.php', 'Psy\\Readline\\Readline' => $vendorDir . '/psy/psysh/src/Readline/Readline.php', 'Psy\\Readline\\Transient' => $vendorDir . '/psy/psysh/src/Readline/Transient.php', 'Psy\\Readline\\Userland' => $vendorDir . '/psy/psysh/src/Readline/Userland.php', 'Psy\\Reflection\\ReflectionConstant' => $vendorDir . '/psy/psysh/src/Reflection/ReflectionConstant.php', 'Psy\\Reflection\\ReflectionLanguageConstruct' => $vendorDir . '/psy/psysh/src/Reflection/ReflectionLanguageConstruct.php', 'Psy\\Reflection\\ReflectionLanguageConstructParameter' => $vendorDir . '/psy/psysh/src/Reflection/ReflectionLanguageConstructParameter.php', 'Psy\\Reflection\\ReflectionNamespace' => $vendorDir . '/psy/psysh/src/Reflection/ReflectionNamespace.php', 'Psy\\Shell' => $vendorDir . '/psy/psysh/src/Shell.php', 'Psy\\Sudo' => $vendorDir . '/psy/psysh/src/Sudo.php', 'Psy\\Sudo\\SudoVisitor' => $vendorDir . '/psy/psysh/src/Sudo/SudoVisitor.php', 'Psy\\SuperglobalsEnv' => $vendorDir . '/psy/psysh/src/SuperglobalsEnv.php', 'Psy\\SystemEnv' => $vendorDir . '/psy/psysh/src/SystemEnv.php', 'Psy\\TabCompletion\\AutoCompleter' => $vendorDir . '/psy/psysh/src/TabCompletion/AutoCompleter.php', 'Psy\\TabCompletion\\Matcher\\AbstractContextAwareMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/AbstractContextAwareMatcher.php', 'Psy\\TabCompletion\\Matcher\\AbstractDefaultParametersMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/AbstractDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\AbstractMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/AbstractMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassAttributesMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ClassAttributesMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassMethodDefaultParametersMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ClassMethodDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassMethodsMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ClassMethodsMatcher.php', 'Psy\\TabCompletion\\Matcher\\ClassNamesMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ClassNamesMatcher.php', 'Psy\\TabCompletion\\Matcher\\CommandsMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/CommandsMatcher.php', 'Psy\\TabCompletion\\Matcher\\ConstantsMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ConstantsMatcher.php', 'Psy\\TabCompletion\\Matcher\\FunctionDefaultParametersMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/FunctionDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\FunctionsMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/FunctionsMatcher.php', 'Psy\\TabCompletion\\Matcher\\KeywordsMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/KeywordsMatcher.php', 'Psy\\TabCompletion\\Matcher\\MongoClientMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/MongoClientMatcher.php', 'Psy\\TabCompletion\\Matcher\\MongoDatabaseMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/MongoDatabaseMatcher.php', 'Psy\\TabCompletion\\Matcher\\ObjectAttributesMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ObjectAttributesMatcher.php', 'Psy\\TabCompletion\\Matcher\\ObjectMethodDefaultParametersMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ObjectMethodDefaultParametersMatcher.php', 'Psy\\TabCompletion\\Matcher\\ObjectMethodsMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/ObjectMethodsMatcher.php', 'Psy\\TabCompletion\\Matcher\\VariablesMatcher' => $vendorDir . '/psy/psysh/src/TabCompletion/Matcher/VariablesMatcher.php', 'Psy\\Util\\Docblock' => $vendorDir . '/psy/psysh/src/Util/Docblock.php', 'Psy\\Util\\Json' => $vendorDir . '/psy/psysh/src/Util/Json.php', 'Psy\\Util\\Mirror' => $vendorDir . '/psy/psysh/src/Util/Mirror.php', 'Psy\\Util\\Str' => $vendorDir . '/psy/psysh/src/Util/Str.php', 'Psy\\VarDumper\\Cloner' => $vendorDir . '/psy/psysh/src/VarDumper/Cloner.php', 'Psy\\VarDumper\\Dumper' => $vendorDir . '/psy/psysh/src/VarDumper/Dumper.php', 'Psy\\VarDumper\\Presenter' => $vendorDir . '/psy/psysh/src/VarDumper/Presenter.php', 'Psy\\VarDumper\\PresenterAware' => $vendorDir . '/psy/psysh/src/VarDumper/PresenterAware.php', 'Psy\\VersionUpdater\\Checker' => $vendorDir . '/psy/psysh/src/VersionUpdater/Checker.php', 'Psy\\VersionUpdater\\Downloader' => $vendorDir . '/psy/psysh/src/VersionUpdater/Downloader.php', 'Psy\\VersionUpdater\\Downloader\\CurlDownloader' => $vendorDir . '/psy/psysh/src/VersionUpdater/Downloader/CurlDownloader.php', 'Psy\\VersionUpdater\\Downloader\\Factory' => $vendorDir . '/psy/psysh/src/VersionUpdater/Downloader/Factory.php', 'Psy\\VersionUpdater\\Downloader\\FileDownloader' => $vendorDir . '/psy/psysh/src/VersionUpdater/Downloader/FileDownloader.php', 'Psy\\VersionUpdater\\GitHubChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/GitHubChecker.php', 'Psy\\VersionUpdater\\Installer' => $vendorDir . '/psy/psysh/src/VersionUpdater/Installer.php', 'Psy\\VersionUpdater\\IntervalChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/IntervalChecker.php', 'Psy\\VersionUpdater\\NoopChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/NoopChecker.php', 'Psy\\VersionUpdater\\SelfUpdate' => $vendorDir . '/psy/psysh/src/VersionUpdater/SelfUpdate.php', 'ReturnTypeWillChange' => $vendorDir . '/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php', 'SebastianBergmann\\CliParser\\AmbiguousOptionException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/AmbiguousOptionException.php', 'SebastianBergmann\\CliParser\\Exception' => $vendorDir . '/sebastian/cli-parser/src/exceptions/Exception.php', 'SebastianBergmann\\CliParser\\OptionDoesNotAllowArgumentException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/OptionDoesNotAllowArgumentException.php', 'SebastianBergmann\\CliParser\\Parser' => $vendorDir . '/sebastian/cli-parser/src/Parser.php', 'SebastianBergmann\\CliParser\\RequiredOptionArgumentMissingException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/RequiredOptionArgumentMissingException.php', 'SebastianBergmann\\CliParser\\UnknownOptionException' => $vendorDir . '/sebastian/cli-parser/src/exceptions/UnknownOptionException.php', 'SebastianBergmann\\CodeCoverage\\BranchAndPathCoverageNotSupportedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/BranchAndPathCoverageNotSupportedException.php', 'SebastianBergmann\\CodeCoverage\\CodeCoverage' => $vendorDir . '/phpunit/php-code-coverage/src/CodeCoverage.php', 'SebastianBergmann\\CodeCoverage\\DeadCodeDetectionNotSupportedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/DeadCodeDetectionNotSupportedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PathExistsButIsNotDirectoryException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PcovDriver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PcovDriver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PcovNotAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgDriver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/PhpdbgDriver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\PhpdbgNotAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Selector' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Selector.php', 'SebastianBergmann\\CodeCoverage\\Driver\\WriteOperationFailedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\WrongXdebugVersionException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug2NotEnabledException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3Driver' => $vendorDir . '/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php', 'SebastianBergmann\\CodeCoverage\\Driver\\Xdebug3NotEnabledException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php', 'SebastianBergmann\\CodeCoverage\\Driver\\XdebugNotAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.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\\NoCodeCoverageDriverAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php', 'SebastianBergmann\\CodeCoverage\\NoCodeCoverageDriverWithPathCoverageSupportAvailableException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverWithPathCoverageSupportAvailableException.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\\CrapIndex' => $vendorDir . '/phpunit/php-code-coverage/src/Node/CrapIndex.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\\ParserException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/ParserException.php', 'SebastianBergmann\\CodeCoverage\\ProcessedCodeCoverageData' => $vendorDir . '/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php', 'SebastianBergmann\\CodeCoverage\\RawCodeCoverageData' => $vendorDir . '/phpunit/php-code-coverage/src/RawCodeCoverageData.php', 'SebastianBergmann\\CodeCoverage\\ReflectionException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/ReflectionException.php', 'SebastianBergmann\\CodeCoverage\\ReportAlreadyFinalizedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php', 'SebastianBergmann\\CodeCoverage\\Report\\Clover' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Clover.php', 'SebastianBergmann\\CodeCoverage\\Report\\Cobertura' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Cobertura.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\\BuildInformation' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/BuildInformation.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\\Source' => $vendorDir . '/phpunit/php-code-coverage/src/Report/Xml/Source.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\\StaticAnalysisCacheNotConfiguredException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CacheWarmer' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CachingFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\CodeUnitFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ExecutableLinesFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\FileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\IgnoredLinesFindingVisitor' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php', 'SebastianBergmann\\CodeCoverage\\StaticAnalysis\\ParsingFileAnalyser' => $vendorDir . '/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.php', 'SebastianBergmann\\CodeCoverage\\TestIdMissingException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php', 'SebastianBergmann\\CodeCoverage\\UnintentionallyCoveredCodeException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/UnintentionallyCoveredCodeException.php', 'SebastianBergmann\\CodeCoverage\\Util\\DirectoryCouldNotBeCreatedException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php', 'SebastianBergmann\\CodeCoverage\\Util\\Filesystem' => $vendorDir . '/phpunit/php-code-coverage/src/Util/Filesystem.php', 'SebastianBergmann\\CodeCoverage\\Util\\Percentage' => $vendorDir . '/phpunit/php-code-coverage/src/Util/Percentage.php', 'SebastianBergmann\\CodeCoverage\\Version' => $vendorDir . '/phpunit/php-code-coverage/src/Version.php', 'SebastianBergmann\\CodeCoverage\\XmlException' => $vendorDir . '/phpunit/php-code-coverage/src/Exception/XmlException.php', 'SebastianBergmann\\CodeUnitReverseLookup\\Wizard' => $vendorDir . '/sebastian/code-unit-reverse-lookup/src/Wizard.php', 'SebastianBergmann\\CodeUnit\\ClassMethodUnit' => $vendorDir . '/sebastian/code-unit/src/ClassMethodUnit.php', 'SebastianBergmann\\CodeUnit\\ClassUnit' => $vendorDir . '/sebastian/code-unit/src/ClassUnit.php', 'SebastianBergmann\\CodeUnit\\CodeUnit' => $vendorDir . '/sebastian/code-unit/src/CodeUnit.php', 'SebastianBergmann\\CodeUnit\\CodeUnitCollection' => $vendorDir . '/sebastian/code-unit/src/CodeUnitCollection.php', 'SebastianBergmann\\CodeUnit\\CodeUnitCollectionIterator' => $vendorDir . '/sebastian/code-unit/src/CodeUnitCollectionIterator.php', 'SebastianBergmann\\CodeUnit\\Exception' => $vendorDir . '/sebastian/code-unit/src/exceptions/Exception.php', 'SebastianBergmann\\CodeUnit\\FunctionUnit' => $vendorDir . '/sebastian/code-unit/src/FunctionUnit.php', 'SebastianBergmann\\CodeUnit\\InterfaceMethodUnit' => $vendorDir . '/sebastian/code-unit/src/InterfaceMethodUnit.php', 'SebastianBergmann\\CodeUnit\\InterfaceUnit' => $vendorDir . '/sebastian/code-unit/src/InterfaceUnit.php', 'SebastianBergmann\\CodeUnit\\InvalidCodeUnitException' => $vendorDir . '/sebastian/code-unit/src/exceptions/InvalidCodeUnitException.php', 'SebastianBergmann\\CodeUnit\\Mapper' => $vendorDir . '/sebastian/code-unit/src/Mapper.php', 'SebastianBergmann\\CodeUnit\\NoTraitException' => $vendorDir . '/sebastian/code-unit/src/exceptions/NoTraitException.php', 'SebastianBergmann\\CodeUnit\\ReflectionException' => $vendorDir . '/sebastian/code-unit/src/exceptions/ReflectionException.php', 'SebastianBergmann\\CodeUnit\\TraitMethodUnit' => $vendorDir . '/sebastian/code-unit/src/TraitMethodUnit.php', 'SebastianBergmann\\CodeUnit\\TraitUnit' => $vendorDir . '/sebastian/code-unit/src/TraitUnit.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\\Exception' => $vendorDir . '/sebastian/comparator/src/exceptions/Exception.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\\RuntimeException' => $vendorDir . '/sebastian/comparator/src/exceptions/RuntimeException.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\\Complexity\\Calculator' => $vendorDir . '/sebastian/complexity/src/Calculator.php', 'SebastianBergmann\\Complexity\\Complexity' => $vendorDir . '/sebastian/complexity/src/Complexity/Complexity.php', 'SebastianBergmann\\Complexity\\ComplexityCalculatingVisitor' => $vendorDir . '/sebastian/complexity/src/Visitor/ComplexityCalculatingVisitor.php', 'SebastianBergmann\\Complexity\\ComplexityCollection' => $vendorDir . '/sebastian/complexity/src/Complexity/ComplexityCollection.php', 'SebastianBergmann\\Complexity\\ComplexityCollectionIterator' => $vendorDir . '/sebastian/complexity/src/Complexity/ComplexityCollectionIterator.php', 'SebastianBergmann\\Complexity\\CyclomaticComplexityCalculatingVisitor' => $vendorDir . '/sebastian/complexity/src/Visitor/CyclomaticComplexityCalculatingVisitor.php', 'SebastianBergmann\\Complexity\\Exception' => $vendorDir . '/sebastian/complexity/src/Exception/Exception.php', 'SebastianBergmann\\Complexity\\RuntimeException' => $vendorDir . '/sebastian/complexity/src/Exception/RuntimeException.php', 'SebastianBergmann\\Diff\\Chunk' => $vendorDir . '/sebastian/diff/src/Chunk.php', 'SebastianBergmann\\Diff\\ConfigurationException' => $vendorDir . '/sebastian/diff/src/Exception/ConfigurationException.php', 'SebastianBergmann\\Diff\\Diff' => $vendorDir . '/sebastian/diff/src/Diff.php', 'SebastianBergmann\\Diff\\Differ' => $vendorDir . '/sebastian/diff/src/Differ.php', 'SebastianBergmann\\Diff\\Exception' => $vendorDir . '/sebastian/diff/src/Exception/Exception.php', 'SebastianBergmann\\Diff\\InvalidArgumentException' => $vendorDir . '/sebastian/diff/src/Exception/InvalidArgumentException.php', 'SebastianBergmann\\Diff\\Line' => $vendorDir . '/sebastian/diff/src/Line.php', 'SebastianBergmann\\Diff\\LongestCommonSubsequenceCalculator' => $vendorDir . '/sebastian/diff/src/LongestCommonSubsequenceCalculator.php', 'SebastianBergmann\\Diff\\MemoryEfficientLongestCommonSubsequenceCalculator' => $vendorDir . '/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php', 'SebastianBergmann\\Diff\\Output\\AbstractChunkOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/AbstractChunkOutputBuilder.php', 'SebastianBergmann\\Diff\\Output\\DiffOnlyOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/DiffOnlyOutputBuilder.php', 'SebastianBergmann\\Diff\\Output\\DiffOutputBuilderInterface' => $vendorDir . '/sebastian/diff/src/Output/DiffOutputBuilderInterface.php', 'SebastianBergmann\\Diff\\Output\\StrictUnifiedDiffOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php', 'SebastianBergmann\\Diff\\Output\\UnifiedDiffOutputBuilder' => $vendorDir . '/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php', 'SebastianBergmann\\Diff\\Parser' => $vendorDir . '/sebastian/diff/src/Parser.php', 'SebastianBergmann\\Diff\\TimeEfficientLongestCommonSubsequenceCalculator' => $vendorDir . '/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php', 'SebastianBergmann\\Environment\\Console' => $vendorDir . '/sebastian/environment/src/Console.php', 'SebastianBergmann\\Environment\\OperatingSystem' => $vendorDir . '/sebastian/environment/src/OperatingSystem.php', 'SebastianBergmann\\Environment\\Runtime' => $vendorDir . '/sebastian/environment/src/Runtime.php', 'SebastianBergmann\\Exporter\\Exporter' => $vendorDir . '/sebastian/exporter/src/Exporter.php', 'SebastianBergmann\\FileIterator\\Facade' => $vendorDir . '/phpunit/php-file-iterator/src/Facade.php', 'SebastianBergmann\\FileIterator\\Factory' => $vendorDir . '/phpunit/php-file-iterator/src/Factory.php', 'SebastianBergmann\\FileIterator\\Iterator' => $vendorDir . '/phpunit/php-file-iterator/src/Iterator.php', 'SebastianBergmann\\GlobalState\\CodeExporter' => $vendorDir . '/sebastian/global-state/src/CodeExporter.php', 'SebastianBergmann\\GlobalState\\Exception' => $vendorDir . '/sebastian/global-state/src/exceptions/Exception.php', 'SebastianBergmann\\GlobalState\\ExcludeList' => $vendorDir . '/sebastian/global-state/src/ExcludeList.php', 'SebastianBergmann\\GlobalState\\Restorer' => $vendorDir . '/sebastian/global-state/src/Restorer.php', 'SebastianBergmann\\GlobalState\\RuntimeException' => $vendorDir . '/sebastian/global-state/src/exceptions/RuntimeException.php', 'SebastianBergmann\\GlobalState\\Snapshot' => $vendorDir . '/sebastian/global-state/src/Snapshot.php', 'SebastianBergmann\\Invoker\\Exception' => $vendorDir . '/phpunit/php-invoker/src/exceptions/Exception.php', 'SebastianBergmann\\Invoker\\Invoker' => $vendorDir . '/phpunit/php-invoker/src/Invoker.php', 'SebastianBergmann\\Invoker\\ProcessControlExtensionNotLoadedException' => $vendorDir . '/phpunit/php-invoker/src/exceptions/ProcessControlExtensionNotLoadedException.php', 'SebastianBergmann\\Invoker\\TimeoutException' => $vendorDir . '/phpunit/php-invoker/src/exceptions/TimeoutException.php', 'SebastianBergmann\\LinesOfCode\\Counter' => $vendorDir . '/sebastian/lines-of-code/src/Counter.php', 'SebastianBergmann\\LinesOfCode\\Exception' => $vendorDir . '/sebastian/lines-of-code/src/Exception/Exception.php', 'SebastianBergmann\\LinesOfCode\\IllogicalValuesException' => $vendorDir . '/sebastian/lines-of-code/src/Exception/IllogicalValuesException.php', 'SebastianBergmann\\LinesOfCode\\LineCountingVisitor' => $vendorDir . '/sebastian/lines-of-code/src/LineCountingVisitor.php', 'SebastianBergmann\\LinesOfCode\\LinesOfCode' => $vendorDir . '/sebastian/lines-of-code/src/LinesOfCode.php', 'SebastianBergmann\\LinesOfCode\\NegativeValueException' => $vendorDir . '/sebastian/lines-of-code/src/Exception/NegativeValueException.php', 'SebastianBergmann\\LinesOfCode\\RuntimeException' => $vendorDir . '/sebastian/lines-of-code/src/Exception/RuntimeException.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\\ObjectReflector\\Exception' => $vendorDir . '/sebastian/object-reflector/src/Exception.php', 'SebastianBergmann\\ObjectReflector\\InvalidArgumentException' => $vendorDir . '/sebastian/object-reflector/src/InvalidArgumentException.php', 'SebastianBergmann\\ObjectReflector\\ObjectReflector' => $vendorDir . '/sebastian/object-reflector/src/ObjectReflector.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\\Template\\Exception' => $vendorDir . '/phpunit/php-text-template/src/exceptions/Exception.php', 'SebastianBergmann\\Template\\InvalidArgumentException' => $vendorDir . '/phpunit/php-text-template/src/exceptions/InvalidArgumentException.php', 'SebastianBergmann\\Template\\RuntimeException' => $vendorDir . '/phpunit/php-text-template/src/exceptions/RuntimeException.php', 'SebastianBergmann\\Template\\Template' => $vendorDir . '/phpunit/php-text-template/src/Template.php', 'SebastianBergmann\\Timer\\Duration' => $vendorDir . '/phpunit/php-timer/src/Duration.php', 'SebastianBergmann\\Timer\\Exception' => $vendorDir . '/phpunit/php-timer/src/exceptions/Exception.php', 'SebastianBergmann\\Timer\\NoActiveTimerException' => $vendorDir . '/phpunit/php-timer/src/exceptions/NoActiveTimerException.php', 'SebastianBergmann\\Timer\\ResourceUsageFormatter' => $vendorDir . '/phpunit/php-timer/src/ResourceUsageFormatter.php', 'SebastianBergmann\\Timer\\TimeSinceStartOfRequestNotAvailableException' => $vendorDir . '/phpunit/php-timer/src/exceptions/TimeSinceStartOfRequestNotAvailableException.php', 'SebastianBergmann\\Timer\\Timer' => $vendorDir . '/phpunit/php-timer/src/Timer.php', 'SebastianBergmann\\Type\\CallableType' => $vendorDir . '/sebastian/type/src/type/CallableType.php', 'SebastianBergmann\\Type\\Exception' => $vendorDir . '/sebastian/type/src/exception/Exception.php', 'SebastianBergmann\\Type\\FalseType' => $vendorDir . '/sebastian/type/src/type/FalseType.php', 'SebastianBergmann\\Type\\GenericObjectType' => $vendorDir . '/sebastian/type/src/type/GenericObjectType.php', 'SebastianBergmann\\Type\\IntersectionType' => $vendorDir . '/sebastian/type/src/type/IntersectionType.php', 'SebastianBergmann\\Type\\IterableType' => $vendorDir . '/sebastian/type/src/type/IterableType.php', 'SebastianBergmann\\Type\\MixedType' => $vendorDir . '/sebastian/type/src/type/MixedType.php', 'SebastianBergmann\\Type\\NeverType' => $vendorDir . '/sebastian/type/src/type/NeverType.php', 'SebastianBergmann\\Type\\NullType' => $vendorDir . '/sebastian/type/src/type/NullType.php', 'SebastianBergmann\\Type\\ObjectType' => $vendorDir . '/sebastian/type/src/type/ObjectType.php', 'SebastianBergmann\\Type\\Parameter' => $vendorDir . '/sebastian/type/src/Parameter.php', 'SebastianBergmann\\Type\\ReflectionMapper' => $vendorDir . '/sebastian/type/src/ReflectionMapper.php', 'SebastianBergmann\\Type\\RuntimeException' => $vendorDir . '/sebastian/type/src/exception/RuntimeException.php', 'SebastianBergmann\\Type\\SimpleType' => $vendorDir . '/sebastian/type/src/type/SimpleType.php', 'SebastianBergmann\\Type\\StaticType' => $vendorDir . '/sebastian/type/src/type/StaticType.php', 'SebastianBergmann\\Type\\TrueType' => $vendorDir . '/sebastian/type/src/type/TrueType.php', 'SebastianBergmann\\Type\\Type' => $vendorDir . '/sebastian/type/src/type/Type.php', 'SebastianBergmann\\Type\\TypeName' => $vendorDir . '/sebastian/type/src/TypeName.php', 'SebastianBergmann\\Type\\UnionType' => $vendorDir . '/sebastian/type/src/type/UnionType.php', 'SebastianBergmann\\Type\\UnknownType' => $vendorDir . '/sebastian/type/src/type/UnknownType.php', 'SebastianBergmann\\Type\\VoidType' => $vendorDir . '/sebastian/type/src/type/VoidType.php', 'SebastianBergmann\\Version' => $vendorDir . '/sebastian/version/src/Version.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', 'Symfony\\Component\\BrowserKit\\AbstractBrowser' => $vendorDir . '/symfony/browser-kit/AbstractBrowser.php', 'Symfony\\Component\\BrowserKit\\Cookie' => $vendorDir . '/symfony/browser-kit/Cookie.php', 'Symfony\\Component\\BrowserKit\\CookieJar' => $vendorDir . '/symfony/browser-kit/CookieJar.php', 'Symfony\\Component\\BrowserKit\\Exception\\BadMethodCallException' => $vendorDir . '/symfony/browser-kit/Exception/BadMethodCallException.php', 'Symfony\\Component\\BrowserKit\\History' => $vendorDir . '/symfony/browser-kit/History.php', 'Symfony\\Component\\BrowserKit\\HttpBrowser' => $vendorDir . '/symfony/browser-kit/HttpBrowser.php', 'Symfony\\Component\\BrowserKit\\Request' => $vendorDir . '/symfony/browser-kit/Request.php', 'Symfony\\Component\\BrowserKit\\Response' => $vendorDir . '/symfony/browser-kit/Response.php', 'Symfony\\Component\\BrowserKit\\Test\\Constraint\\BrowserCookieValueSame' => $vendorDir . '/symfony/browser-kit/Test/Constraint/BrowserCookieValueSame.php', 'Symfony\\Component\\BrowserKit\\Test\\Constraint\\BrowserHasCookie' => $vendorDir . '/symfony/browser-kit/Test/Constraint/BrowserHasCookie.php', 'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Application.php', 'Symfony\\Component\\Console\\Attribute\\AsCommand' => $vendorDir . '/symfony/console/Attribute/AsCommand.php', 'Symfony\\Component\\Console\\CI\\GithubActionReporter' => $vendorDir . '/symfony/console/CI/GithubActionReporter.php', 'Symfony\\Component\\Console\\Color' => $vendorDir . '/symfony/console/Color.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\\CompleteCommand' => $vendorDir . '/symfony/console/Command/CompleteCommand.php', 'Symfony\\Component\\Console\\Command\\DumpCompletionCommand' => $vendorDir . '/symfony/console/Command/DumpCompletionCommand.php', 'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Command/HelpCommand.php', 'Symfony\\Component\\Console\\Command\\LazyCommand' => $vendorDir . '/symfony/console/Command/LazyCommand.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\\Command\\SignalableCommandInterface' => $vendorDir . '/symfony/console/Command/SignalableCommandInterface.php', 'Symfony\\Component\\Console\\Completion\\CompletionInput' => $vendorDir . '/symfony/console/Completion/CompletionInput.php', 'Symfony\\Component\\Console\\Completion\\CompletionSuggestions' => $vendorDir . '/symfony/console/Completion/CompletionSuggestions.php', 'Symfony\\Component\\Console\\Completion\\Output\\BashCompletionOutput' => $vendorDir . '/symfony/console/Completion/Output/BashCompletionOutput.php', 'Symfony\\Component\\Console\\Completion\\Output\\CompletionOutputInterface' => $vendorDir . '/symfony/console/Completion/Output/CompletionOutputInterface.php', 'Symfony\\Component\\Console\\Completion\\Suggestion' => $vendorDir . '/symfony/console/Completion/Suggestion.php', 'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/ConsoleEvents.php', 'Symfony\\Component\\Console\\Cursor' => $vendorDir . '/symfony/console/Cursor.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\\ConsoleSignalEvent' => $vendorDir . '/symfony/console/Event/ConsoleSignalEvent.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\\MissingInputException' => $vendorDir . '/symfony/console/Exception/MissingInputException.php', 'Symfony\\Component\\Console\\Exception\\NamespaceNotFoundException' => $vendorDir . '/symfony/console/Exception/NamespaceNotFoundException.php', 'Symfony\\Component\\Console\\Exception\\RuntimeException' => $vendorDir . '/symfony/console/Exception/RuntimeException.php', 'Symfony\\Component\\Console\\Formatter\\NullOutputFormatter' => $vendorDir . '/symfony/console/Formatter/NullOutputFormatter.php', 'Symfony\\Component\\Console\\Formatter\\NullOutputFormatterStyle' => $vendorDir . '/symfony/console/Formatter/NullOutputFormatterStyle.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\\Formatter\\WrappableOutputFormatterInterface' => $vendorDir . '/symfony/console/Formatter/WrappableOutputFormatterInterface.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\\Dumper' => $vendorDir . '/symfony/console/Helper/Dumper.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\\TableCellStyle' => $vendorDir . '/symfony/console/Helper/TableCellStyle.php', 'Symfony\\Component\\Console\\Helper\\TableRows' => $vendorDir . '/symfony/console/Helper/TableRows.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\\ConsoleSectionOutput' => $vendorDir . '/symfony/console/Output/ConsoleSectionOutput.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\\Output\\TrimmedBufferOutput' => $vendorDir . '/symfony/console/Output/TrimmedBufferOutput.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\\SignalRegistry\\SignalRegistry' => $vendorDir . '/symfony/console/SignalRegistry/SignalRegistry.php', 'Symfony\\Component\\Console\\SingleCommandApplication' => $vendorDir . '/symfony/console/SingleCommandApplication.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\\CommandCompletionTester' => $vendorDir . '/symfony/console/Tester/CommandCompletionTester.php', 'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Tester/CommandTester.php', 'Symfony\\Component\\Console\\Tester\\Constraint\\CommandIsSuccessful' => $vendorDir . '/symfony/console/Tester/Constraint/CommandIsSuccessful.php', 'Symfony\\Component\\Console\\Tester\\TesterTrait' => $vendorDir . '/symfony/console/Tester/TesterTrait.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\\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\\DomCrawler\\Test\\Constraint\\CrawlerSelectorAttributeValueSame' => $vendorDir . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorAttributeValueSame.php', 'Symfony\\Component\\DomCrawler\\Test\\Constraint\\CrawlerSelectorExists' => $vendorDir . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorExists.php', 'Symfony\\Component\\DomCrawler\\Test\\Constraint\\CrawlerSelectorTextContains' => $vendorDir . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextContains.php', 'Symfony\\Component\\DomCrawler\\Test\\Constraint\\CrawlerSelectorTextSame' => $vendorDir . '/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextSame.php', 'Symfony\\Component\\DomCrawler\\UriResolver' => $vendorDir . '/symfony/dom-crawler/UriResolver.php', 'Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener' => $vendorDir . '/symfony/event-dispatcher/Attribute/AsEventListener.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Debug/WrappedListener.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\AddEventAliasesPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.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\\DirectoryNotFoundException' => $vendorDir . '/symfony/finder/Exception/DirectoryNotFoundException.php', 'Symfony\\Component\\Finder\\Finder' => $vendorDir . '/symfony/finder/Finder.php', 'Symfony\\Component\\Finder\\Gitignore' => $vendorDir . '/symfony/finder/Gitignore.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\\LazyIterator' => $vendorDir . '/symfony/finder/Iterator/LazyIterator.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\\Iterator\\VcsIgnoredFilterIterator' => $vendorDir . '/symfony/finder/Iterator/VcsIgnoredFilterIterator.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\\ProcessSignaledException' => $vendorDir . '/symfony/process/Exception/ProcessSignaledException.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\\ProcessUtils' => $vendorDir . '/symfony/process/ProcessUtils.php', 'Symfony\\Component\\String\\AbstractString' => $vendorDir . '/symfony/string/AbstractString.php', 'Symfony\\Component\\String\\AbstractUnicodeString' => $vendorDir . '/symfony/string/AbstractUnicodeString.php', 'Symfony\\Component\\String\\ByteString' => $vendorDir . '/symfony/string/ByteString.php', 'Symfony\\Component\\String\\CodePointString' => $vendorDir . '/symfony/string/CodePointString.php', 'Symfony\\Component\\String\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/string/Exception/ExceptionInterface.php', 'Symfony\\Component\\String\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/string/Exception/InvalidArgumentException.php', 'Symfony\\Component\\String\\Exception\\RuntimeException' => $vendorDir . '/symfony/string/Exception/RuntimeException.php', 'Symfony\\Component\\String\\Inflector\\EnglishInflector' => $vendorDir . '/symfony/string/Inflector/EnglishInflector.php', 'Symfony\\Component\\String\\Inflector\\FrenchInflector' => $vendorDir . '/symfony/string/Inflector/FrenchInflector.php', 'Symfony\\Component\\String\\Inflector\\InflectorInterface' => $vendorDir . '/symfony/string/Inflector/InflectorInterface.php', 'Symfony\\Component\\String\\LazyString' => $vendorDir . '/symfony/string/LazyString.php', 'Symfony\\Component\\String\\Slugger\\AsciiSlugger' => $vendorDir . '/symfony/string/Slugger/AsciiSlugger.php', 'Symfony\\Component\\String\\Slugger\\SluggerInterface' => $vendorDir . '/symfony/string/Slugger/SluggerInterface.php', 'Symfony\\Component\\String\\UnicodeString' => $vendorDir . '/symfony/string/UnicodeString.php', 'Symfony\\Component\\VarDumper\\Caster\\AmqpCaster' => $vendorDir . '/symfony/var-dumper/Caster/AmqpCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ArgsStub' => $vendorDir . '/symfony/var-dumper/Caster/ArgsStub.php', 'Symfony\\Component\\VarDumper\\Caster\\Caster' => $vendorDir . '/symfony/var-dumper/Caster/Caster.php', 'Symfony\\Component\\VarDumper\\Caster\\ClassStub' => $vendorDir . '/symfony/var-dumper/Caster/ClassStub.php', 'Symfony\\Component\\VarDumper\\Caster\\ConstStub' => $vendorDir . '/symfony/var-dumper/Caster/ConstStub.php', 'Symfony\\Component\\VarDumper\\Caster\\CutArrayStub' => $vendorDir . '/symfony/var-dumper/Caster/CutArrayStub.php', 'Symfony\\Component\\VarDumper\\Caster\\CutStub' => $vendorDir . '/symfony/var-dumper/Caster/CutStub.php', 'Symfony\\Component\\VarDumper\\Caster\\DOMCaster' => $vendorDir . '/symfony/var-dumper/Caster/DOMCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DateCaster' => $vendorDir . '/symfony/var-dumper/Caster/DateCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DoctrineCaster' => $vendorDir . '/symfony/var-dumper/Caster/DoctrineCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DsCaster' => $vendorDir . '/symfony/var-dumper/Caster/DsCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DsPairStub' => $vendorDir . '/symfony/var-dumper/Caster/DsPairStub.php', 'Symfony\\Component\\VarDumper\\Caster\\EnumStub' => $vendorDir . '/symfony/var-dumper/Caster/EnumStub.php', 'Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster' => $vendorDir . '/symfony/var-dumper/Caster/ExceptionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\FiberCaster' => $vendorDir . '/symfony/var-dumper/Caster/FiberCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\FrameStub' => $vendorDir . '/symfony/var-dumper/Caster/FrameStub.php', 'Symfony\\Component\\VarDumper\\Caster\\GmpCaster' => $vendorDir . '/symfony/var-dumper/Caster/GmpCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ImagineCaster' => $vendorDir . '/symfony/var-dumper/Caster/ImagineCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ImgStub' => $vendorDir . '/symfony/var-dumper/Caster/ImgStub.php', 'Symfony\\Component\\VarDumper\\Caster\\IntlCaster' => $vendorDir . '/symfony/var-dumper/Caster/IntlCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\LinkStub' => $vendorDir . '/symfony/var-dumper/Caster/LinkStub.php', 'Symfony\\Component\\VarDumper\\Caster\\MemcachedCaster' => $vendorDir . '/symfony/var-dumper/Caster/MemcachedCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\MysqliCaster' => $vendorDir . '/symfony/var-dumper/Caster/MysqliCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PdoCaster' => $vendorDir . '/symfony/var-dumper/Caster/PdoCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PgSqlCaster' => $vendorDir . '/symfony/var-dumper/Caster/PgSqlCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ProxyManagerCaster' => $vendorDir . '/symfony/var-dumper/Caster/ProxyManagerCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\RdKafkaCaster' => $vendorDir . '/symfony/var-dumper/Caster/RdKafkaCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\RedisCaster' => $vendorDir . '/symfony/var-dumper/Caster/RedisCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ReflectionCaster' => $vendorDir . '/symfony/var-dumper/Caster/ReflectionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ResourceCaster' => $vendorDir . '/symfony/var-dumper/Caster/ResourceCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\SplCaster' => $vendorDir . '/symfony/var-dumper/Caster/SplCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\StubCaster' => $vendorDir . '/symfony/var-dumper/Caster/StubCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\SymfonyCaster' => $vendorDir . '/symfony/var-dumper/Caster/SymfonyCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\TraceStub' => $vendorDir . '/symfony/var-dumper/Caster/TraceStub.php', 'Symfony\\Component\\VarDumper\\Caster\\UuidCaster' => $vendorDir . '/symfony/var-dumper/Caster/UuidCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlReaderCaster' => $vendorDir . '/symfony/var-dumper/Caster/XmlReaderCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlResourceCaster' => $vendorDir . '/symfony/var-dumper/Caster/XmlResourceCaster.php', 'Symfony\\Component\\VarDumper\\Cloner\\AbstractCloner' => $vendorDir . '/symfony/var-dumper/Cloner/AbstractCloner.php', 'Symfony\\Component\\VarDumper\\Cloner\\ClonerInterface' => $vendorDir . '/symfony/var-dumper/Cloner/ClonerInterface.php', 'Symfony\\Component\\VarDumper\\Cloner\\Cursor' => $vendorDir . '/symfony/var-dumper/Cloner/Cursor.php', 'Symfony\\Component\\VarDumper\\Cloner\\Data' => $vendorDir . '/symfony/var-dumper/Cloner/Data.php', 'Symfony\\Component\\VarDumper\\Cloner\\DumperInterface' => $vendorDir . '/symfony/var-dumper/Cloner/DumperInterface.php', 'Symfony\\Component\\VarDumper\\Cloner\\Stub' => $vendorDir . '/symfony/var-dumper/Cloner/Stub.php', 'Symfony\\Component\\VarDumper\\Cloner\\VarCloner' => $vendorDir . '/symfony/var-dumper/Cloner/VarCloner.php', 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\CliDescriptor' => $vendorDir . '/symfony/var-dumper/Command/Descriptor/CliDescriptor.php', 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\DumpDescriptorInterface' => $vendorDir . '/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php', 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\HtmlDescriptor' => $vendorDir . '/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php', 'Symfony\\Component\\VarDumper\\Command\\ServerDumpCommand' => $vendorDir . '/symfony/var-dumper/Command/ServerDumpCommand.php', 'Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper' => $vendorDir . '/symfony/var-dumper/Dumper/AbstractDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\CliDumper' => $vendorDir . '/symfony/var-dumper/Dumper/CliDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\CliContextProvider' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\ContextProviderInterface' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\RequestContextProvider' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\SourceContextProvider' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php', 'Symfony\\Component\\VarDumper\\Dumper\\ContextualizedDumper' => $vendorDir . '/symfony/var-dumper/Dumper/ContextualizedDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => $vendorDir . '/symfony/var-dumper/Dumper/DataDumperInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => $vendorDir . '/symfony/var-dumper/Dumper/HtmlDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\ServerDumper' => $vendorDir . '/symfony/var-dumper/Dumper/ServerDumper.php', 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => $vendorDir . '/symfony/var-dumper/Exception/ThrowingCasterException.php', 'Symfony\\Component\\VarDumper\\Server\\Connection' => $vendorDir . '/symfony/var-dumper/Server/Connection.php', 'Symfony\\Component\\VarDumper\\Server\\DumpServer' => $vendorDir . '/symfony/var-dumper/Server/DumpServer.php', 'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => $vendorDir . '/symfony/var-dumper/Test/VarDumperTestTrait.php', 'Symfony\\Component\\VarDumper\\VarDumper' => $vendorDir . '/symfony/var-dumper/VarDumper.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\\Contracts\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher-contracts/Event.php', 'Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher-contracts/EventDispatcherInterface.php', 'Symfony\\Contracts\\Service\\Attribute\\Required' => $vendorDir . '/symfony/service-contracts/Attribute/Required.php', 'Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => $vendorDir . '/symfony/service-contracts/Attribute/SubscribedService.php', 'Symfony\\Contracts\\Service\\ResetInterface' => $vendorDir . '/symfony/service-contracts/ResetInterface.php', 'Symfony\\Contracts\\Service\\ServiceLocatorTrait' => $vendorDir . '/symfony/service-contracts/ServiceLocatorTrait.php', 'Symfony\\Contracts\\Service\\ServiceProviderInterface' => $vendorDir . '/symfony/service-contracts/ServiceProviderInterface.php', 'Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberInterface.php', 'Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberTrait.php', 'Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => $vendorDir . '/symfony/service-contracts/Test/ServiceLocatorTest.php', 'Symfony\\Polyfill\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php', 'Symfony\\Polyfill\\Intl\\Grapheme\\Grapheme' => $vendorDir . '/symfony/polyfill-intl-grapheme/Grapheme.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\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php', 'Symfony\\Polyfill\\Php80\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/PhpToken.php', 'Symfony\\Polyfill\\Php81\\Php81' => $vendorDir . '/symfony/polyfill-php81/Php81.php', 'TheSeer\\Tokenizer\\Exception' => $vendorDir . '/theseer/tokenizer/src/Exception.php', 'TheSeer\\Tokenizer\\NamespaceUri' => $vendorDir . '/theseer/tokenizer/src/NamespaceUri.php', 'TheSeer\\Tokenizer\\NamespaceUriException' => $vendorDir . '/theseer/tokenizer/src/NamespaceUriException.php', 'TheSeer\\Tokenizer\\Token' => $vendorDir . '/theseer/tokenizer/src/Token.php', 'TheSeer\\Tokenizer\\TokenCollection' => $vendorDir . '/theseer/tokenizer/src/TokenCollection.php', 'TheSeer\\Tokenizer\\TokenCollectionException' => $vendorDir . '/theseer/tokenizer/src/TokenCollectionException.php', 'TheSeer\\Tokenizer\\Tokenizer' => $vendorDir . '/theseer/tokenizer/src/Tokenizer.php', 'TheSeer\\Tokenizer\\XMLSerializer' => $vendorDir . '/theseer/tokenizer/src/XMLSerializer.php', 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php', 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.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; self::initializeIncludeClosure(); } 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) { $paths = (array) $paths; if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, $paths ); } return; } $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { $this->prefixesPsr0[$first][$prefix] = $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], $paths ); } } public function addPsr4($prefix, $paths, $prepend = false) { $paths = (array) $paths; if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr4 = array_merge( $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, $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] = $paths; } elseif ($prepend) { $this->prefixDirsPsr4[$prefix] = array_merge( $paths, $this->prefixDirsPsr4[$prefix] ); } else { $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], $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 = self::$includeFile; $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; } private static function initializeIncludeClosure() { if (self::$includeFile !== null) { return; } self::$includeFile = \Closure::bind(static function($file) { include $file; }, null, null); } } $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', 'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php', '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', '801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php', 'e3b2795a8a512b6083af088fb53afe6c' => $vendorDir . '/codeception/codeception/functions.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '3a37ebac017bc098e9a86b35401e7a68' => $vendorDir . '/mongodb/mongodb/src/functions.php', '2a3c2110e8e0295330dc3d11a4cbc4cb' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php', 'a3b8fb13a112e3be232ab3c07b8a884f' => $vendorDir . '/codeception/module-sequence/src/Codeception/Util/sq.php', ); array($vendorDir . '/behat/gherkin/src'), ); request('GET', $uri, $options); } public function head($uri, array $options = []): ResponseInterface { return $this->request('HEAD', $uri, $options); } public function put($uri, array $options = []): ResponseInterface { return $this->request('PUT', $uri, $options); } public function post($uri, array $options = []): ResponseInterface { return $this->request('POST', $uri, $options); } public function patch($uri, array $options = []): ResponseInterface { return $this->request('PATCH', $uri, $options); } public function delete($uri, array $options = []): ResponseInterface { return $this->request('DELETE', $uri, $options); } abstract public function requestAsync(string $method, $uri, array $options = []): PromiseInterface; public function getAsync($uri, array $options = []): PromiseInterface { return $this->requestAsync('GET', $uri, $options); } public function headAsync($uri, array $options = []): PromiseInterface { return $this->requestAsync('HEAD', $uri, $options); } public function putAsync($uri, array $options = []): PromiseInterface { return $this->requestAsync('PUT', $uri, $options); } public function postAsync($uri, array $options = []): PromiseInterface { return $this->requestAsync('POST', $uri, $options); } public function patchAsync($uri, array $options = []): PromiseInterface { return $this->requestAsync('PATCH', $uri, $options); } public function deleteAsync($uri, array $options = []): PromiseInterface { return $this->requestAsync('DELETE', $uri, $options); } } nextHandler = $nextHandler; } public function __invoke(RequestInterface $request, array $options): PromiseInterface { $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 (is_string($uri) && $type = Psr7\MimeType::fromFilename($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\Utils::modifyRequest($request, $modify), $options); } private function addExpectHeader(RequestInterface $request, array $options, array &$modify): void { if ($request->hasHeader('Expect')) { return; } $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'; } } } $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(): PromiseInterface { return $this->each->promise(); } public static function batch(ClientInterface $client, $requests, array $options = []): array { $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, string $name, array &$results): void { if (!isset($options[$name])) { $options[$name] = static function ($v, $k) use (&$results) { $results[$k] = $v; }; } else { $currentFn = $options[$name]; $options[$name] = static function ($v, $k) use (&$results, $currentFn) { $currentFn($v, $k); $results[$k] = $v; }; } } } 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 !== null) { $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): void { $this->handler = $handler; $this->cached = null; } public function hasHandler(): bool { return $this->handler !== null; } public function unshift(callable $middleware, string $name = null): void { \array_unshift($this->stack, [$middleware, $name]); $this->cached = null; } public function push(callable $middleware, string $name = ''): void { $this->stack[] = [$middleware, $name]; $this->cached = null; } public function before(string $findName, callable $middleware, string $withName = ''): void { $this->splice($findName, $withName, $middleware, true); } public function after(string $findName, callable $middleware, string $withName = ''): void { $this->splice($findName, $withName, $middleware, false); } public function remove($remove): void { if (!is_string($remove) && !is_callable($remove)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a callable or string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->cached = null; $idx = \is_callable($remove) ? 0 : 1; $this->stack = \array_values(\array_filter( $this->stack, static function ($tuple) use ($idx, $remove) { return $tuple[$idx] !== $remove; } )); } public function resolve(): callable { if ($this->cached === null) { if (($prev = $this->handler) === null) { 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(string $name): int { foreach ($this->stack as $k => $v) { if ($v[1] === $name) { return $k; } } throw new \InvalidArgumentException("Middleware not found: $name"); } private function splice(string $findName, string $withName, callable $middleware, bool $before): void { $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): string { 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).')'; } } >>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; private $template; public function __construct(?string $template = self::CLF) { $this->template = $template ?: self::CLF; } public function format(RequestInterface $request, ResponseInterface $response = null, \Throwable $error = null): string { $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\Message::toString($request); break; case 'response': $result = $response ? Psr7\Message::toString($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()->__toString(); break; case 'res_body': if (!$response instanceof ResponseInterface) { $result = 'NULL'; break; } $body = $response->getBody(); if (!$body->isSeekable()) { $result = 'RESPONSE_NOT_LOGGEABLE'; break; } $result = $response->getBody()->__toString(); 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()->__toString(); 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): string { $result = ''; foreach ($message->getHeaders() as $name => $values) { $result .= $name.': '.\implode(', ', $values)."\r\n"; } return \trim($result); } } truncateAt = $truncateAt; } public function summarize(MessageInterface $message): ?string { return $this->truncateAt === null ? \GuzzleHttp\Psr7\Message::bodySummary($message) : \GuzzleHttp\Psr7\Message::bodySummary($message, $this->truncateAt); } } getHost()) { $asciiHost = self::idnToAsci($uri->getHost(), $options, $info); if ($asciiHost === false) { $errorBitSet = $info['errors'] ?? 0; $errorConstants = array_filter(array_keys(get_defined_constants()), static function (string $name): bool { 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); } if ($uri->getHost() !== $asciiHost) { $uri = $uri->withHost($asciiHost); } } return $uri; } public static function getenv(string $name): ?string { if (isset($_SERVER[$name])) { return (string) $_SERVER[$name]; } if (\PHP_SAPI === 'cli' && ($value = \getenv($name)) !== false && $value !== null) { return (string) $value; } return null; } private static function idnToAsci(string $domain, int $options, ?array &$info = []) { if (\function_exists('idn_to_ascii') && \defined('INTL_IDNA_VARIANT_UTS46')) { return \idn_to_ascii($domain, $options, \INTL_IDNA_VARIANT_UTS46, $info); } throw new \Error('ext-idn or symfony/polyfill-intl-idn not loaded or too old'); } } decider = $decider; $this->nextHandler = $nextHandler; $this->delay = $delay ?: __CLASS__.'::exponentialDelay'; } public static function exponentialDelay(int $retries): int { return (int) 2 ** ($retries - 1) * 1000; } public function __invoke(RequestInterface $request, array $options): PromiseInterface { 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 $request, array $options): callable { return function ($value) use ($request, $options) { if (!($this->decider)( $options['retries'], $request, $value, null )) { return $value; } return $this->doRetry($request, $options, $value); }; } private function onRejected(RequestInterface $req, array $options): callable { return function ($reason) use ($req, $options) { if (!($this->decider)( $options['retries'], $req, null, $reason )) { return P\Create::rejectionFor($reason); } return $this->doRetry($req, $options); }; } private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null): PromiseInterface { $options['delay'] = ($this->delay)(++$options['retries'], $response, $request); return $this($request, $options); } } request = $request; $this->response = $response; $this->transferTime = $transferTime; $this->handlerErrorData = $handlerErrorData; $this->handlerStats = $handlerStats; } public function getRequest(): RequestInterface { return $this->request; } public function getResponse(): ?ResponseInterface { return $this->response; } public function hasResponse(): bool { return $this->response !== null; } public function getHandlerErrorData() { return $this->handlerErrorData; } public function getEffectiveUri(): UriInterface { return $this->request->getUri(); } public function getTransferTime(): ?float { return $this->transferTime; } public function getHandlerStats(): array { return $this->handlerStats; } public function getHandlerStat(string $stat) { return $this->handlerStats[$stat] ?? null; } } withCookieHeader($request); return $handler($request, $options) ->then( static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface { $cookieJar->extractCookies($request, $response); return $response; } ); }; }; } public static function httpErrors(BodySummarizerInterface $bodySummarizer = null): callable { return static function (callable $handler) use ($bodySummarizer): callable { return static function ($request, array $options) use ($handler, $bodySummarizer) { if (empty($options['http_errors'])) { return $handler($request, $options); } return $handler($request, $options)->then( static function (ResponseInterface $response) use ($request, $bodySummarizer) { $code = $response->getStatusCode(); if ($code < 400) { return $response; } throw RequestException::create($request, $response, null, [], $bodySummarizer); } ); }; }; } public static function history(&$container): callable { if (!\is_array($container) && !$container instanceof \ArrayAccess) { throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess'); } return static function (callable $handler) use (&$container): callable { return static function (RequestInterface $request, array $options) use ($handler, &$container) { return $handler($request, $options)->then( static function ($value) use ($request, &$container, $options) { $container[] = [ 'request' => $request, 'response' => $value, 'error' => null, 'options' => $options, ]; return $value; }, static function ($reason) use ($request, &$container, $options) { $container[] = [ 'request' => $request, 'response' => null, 'error' => $reason, 'options' => $options, ]; return P\Create::rejectionFor($reason); } ); }; }; } public static function tap(callable $before = null, callable $after = null): callable { return static function (callable $handler) use ($before, $after): callable { return static function (RequestInterface $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(): callable { return static function (callable $handler): RedirectMiddleware { return new RedirectMiddleware($handler); }; } public static function retry(callable $decider, callable $delay = null): callable { return static function (callable $handler) use ($decider, $delay): RetryMiddleware { return new RetryMiddleware($decider, $handler, $delay); }; } public static function log(LoggerInterface $logger, $formatter, string $logLevel = 'info'): callable { if (!$formatter instanceof MessageFormatter && !$formatter instanceof MessageFormatterInterface) { throw new \LogicException(sprintf('Argument 2 to %s::log() must be of type %s', self::class, MessageFormatterInterface::class)); } return static function (callable $handler) use ($logger, $formatter, $logLevel): callable { return static function (RequestInterface $request, array $options = []) use ($handler, $logger, $formatter, $logLevel) { return $handler($request, $options)->then( static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface { $message = $formatter->format($request, $response); $logger->log($logLevel, $message); return $response; }, static function ($reason) use ($logger, $request, $formatter): PromiseInterface { $response = $reason instanceof RequestException ? $reason->getResponse() : null; $message = $formatter->format($request, $response, P\Create::exceptionFor($reason)); $logger->error($message); return P\Create::rejectionFor($reason); } ); }; }; } public static function prepareBody(): callable { return static function (callable $handler): PrepareBodyMiddleware { return new PrepareBodyMiddleware($handler); }; } public static function mapRequest(callable $fn): callable { return static function (callable $handler) use ($fn): callable { return static function (RequestInterface $request, array $options) use ($handler, $fn) { return $handler($fn($request), $options); }; }; } public static function mapResponse(callable $fn): callable { return static function (callable $handler) use ($fn): callable { return static function (RequestInterface $request, array $options) use ($handler, $fn) { return $handler($request, $options)->then($fn); }; }; } } 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): PromiseInterface { $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 (\strpos((string) $response->getStatusCode(), '3') !== 0 || !$response->hasHeader('Location') ) { return $response; } $this->guardMax($request, $response, $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'])) { ($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, string $uri, int $statusCode): PromiseInterface { return $promise->then( static 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, (string) $statusCode); return $response->withHeader(self::HISTORY_HEADER, $historyHeader) ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader); } ); } private function guardMax(RequestInterface $request, ResponseInterface $response, array &$options): void { $current = $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, $response); } } public function modifyRequest(RequestInterface $request, array $options, ResponseInterface $response): RequestInterface { $modify = []; $protocols = $options['allow_redirects']['protocols']; $statusCode = $response->getStatusCode(); if ($statusCode == 303 || ($statusCode <= 302 && !$options['allow_redirects']['strict']) ) { $safeMethods = ['GET', 'HEAD', 'OPTIONS']; $requestMethod = $request->getMethod(); $modify['method'] = in_array($requestMethod, $safeMethods) ? $requestMethod : '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\Message::rewindBody($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\Utils::modifyRequest($request, $modify); } private static function redirectUri( RequestInterface $request, ResponseInterface $response, array $protocols ): UriInterface { $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; } } 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 = $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 = []): PromiseInterface { $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 = []): ResponseInterface { $options[RequestOptions::SYNCHRONOUS] = true; return $this->sendAsync($request, $options)->wait(); } public function sendRequest(RequestInterface $request): ResponseInterface { $options[RequestOptions::SYNCHRONOUS] = true; $options[RequestOptions::ALLOW_REDIRECTS] = false; $options[RequestOptions::HTTP_ERRORS] = false; return $this->sendAsync($request, $options)->wait(); } public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface { $options = $this->prepareDefaults($options); $headers = $options['headers'] ?? []; $body = $options['body'] ?? null; $version = $options['version'] ?? '1.1'; $uri = $this->buildUri(Psr7\Utils::uriFor($uri), $options); if (\is_array($body)) { throw $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(string $method, $uri = '', array $options = []): ResponseInterface { $options[RequestOptions::SYNCHRONOUS] = true; return $this->requestAsync($method, $uri, $options)->wait(); } public function getConfig(string $option = null) { return $option === null ? $this->config : ($this->config[$option] ?? null); } private function buildUri(UriInterface $uri, array $config): UriInterface { if (isset($config['base_uri'])) { $uri = Psr7\UriResolver::resolve(Psr7\Utils::uriFor($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): void { $defaults = [ 'allow_redirects' => RedirectMiddleware::$defaultSettings, 'http_errors' => true, 'decode_content' => true, 'verify' => true, 'cookies' => false, 'idn_conversion' => false, ]; if (\PHP_SAPI === 'cli' && ($proxy = Utils::getenv('HTTP_PROXY'))) { $defaults['proxy']['http'] = $proxy; } if ($proxy = Utils::getenv('HTTPS_PROXY')) { $defaults['proxy']['https'] = $proxy; } if ($noProxy = Utils::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' => Utils::defaultUserAgent()]; } else { foreach (\array_keys($this->config['headers']) as $name) { if (\strtolower($name) === 'user-agent') { return; } } $this->config['headers']['User-Agent'] = Utils::defaultUserAgent(); } } private function prepareDefaults(array $options): array { $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): PromiseInterface { $request = $this->applyOptions($request, $options); $handler = $options['handler']; try { return P\Create::promiseFor($handler($request, $options)); } catch (\Exception $e) { return P\Create::rejectionFor($e); } } private function applyOptions(RequestInterface $request, array &$options): RequestInterface { $modify = [ 'set_headers' => [], ]; if (isset($options['headers'])) { if (array_keys($options['headers']) === range(0, count($options['headers']) - 1)) { throw new InvalidArgumentException('The headers array must have header name as keys.'); } $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\Utils::caselessRemove(['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'] = Utils::jsonEncode($options['json']); unset($options['json']); $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/json'; } if (!empty($options['decode_content']) && $options['decode_content'] !== true ) { $options['_conditional'] = Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']); $modify['set_headers']['Accept-Encoding'] = $options['decode_content']; } if (isset($options['body'])) { if (\is_array($options['body'])) { throw $this->invalidBody(); } $modify['body'] = Psr7\Utils::streamFor($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\Utils::caselessRemove(['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, '', '&', \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'); } } if (isset($options['version'])) { $modify['version'] = $options['version']; } $request = Psr7\Utils::modifyRequest($request, $modify); if ($request->getBody() instanceof Psr7\MultipartStream) { $options['_conditional'] = Psr7\Utils::caselessRemove(['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\Utils::modifyRequest($request, $modify); unset($options['_conditional']); } return $request; } private function invalidBody(): InvalidArgumentException { return new InvalidArgumentException('Passing in the "body" request ' .'option as an array to send a request is not supported. ' .'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.'); } } null, 'Value' => null, 'Domain' => null, 'Path' => '/', 'Max-Age' => null, 'Expires' => null, 'Secure' => false, 'Discard' => false, 'HttpOnly' => false, ]; private $data; public static function fromString(string $cookie): self { $data = self::$defaults; $pieces = \array_filter(\array_map('trim', \explode(';', $cookie))); if (!isset($pieces[0]) || \strpos($pieces[0], '=') === false) { 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 (!isset($data['Name'])) { $data['Name'] = $key; $data['Value'] = $value; } else { foreach (\array_keys(self::$defaults) as $search) { if (!\strcasecmp($search, $key)) { if ($search === 'Max-Age') { if (is_numeric($value)) { $data[$search] = (int) $value; } } else { $data[$search] = $value; } continue 2; } } $data[$key] = $value; } } return new self($data); } public function __construct(array $data = []) { $this->data = self::$defaults; if (isset($data['Name'])) { $this->setName($data['Name']); } if (isset($data['Value'])) { $this->setValue($data['Value']); } if (isset($data['Domain'])) { $this->setDomain($data['Domain']); } if (isset($data['Path'])) { $this->setPath($data['Path']); } if (isset($data['Max-Age'])) { $this->setMaxAge($data['Max-Age']); } if (isset($data['Expires'])) { $this->setExpires($data['Expires']); } if (isset($data['Secure'])) { $this->setSecure($data['Secure']); } if (isset($data['Discard'])) { $this->setDiscard($data['Discard']); } if (isset($data['HttpOnly'])) { $this->setHttpOnly($data['HttpOnly']); } foreach (array_diff(array_keys($data), array_keys(self::$defaults)) as $key) { $this->data[$key] = $data[$key]; } if (!$this->getExpires() && $this->getMaxAge()) { $this->setExpires(\time() + $this->getMaxAge()); } elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) { $this->setExpires($expires); } } 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(): array { return $this->data; } public function getName() { return $this->data['Name']; } public function setName($name): void { if (!is_string($name)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Name'] = (string) $name; } public function getValue() { return $this->data['Value']; } public function setValue($value): void { if (!is_string($value)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Value'] = (string) $value; } public function getDomain() { return $this->data['Domain']; } public function setDomain($domain): void { if (!is_string($domain) && null !== $domain) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Domain'] = null === $domain ? null : (string) $domain; } public function getPath() { return $this->data['Path']; } public function setPath($path): void { if (!is_string($path)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Path'] = (string) $path; } public function getMaxAge() { return null === $this->data['Max-Age'] ? null : (int) $this->data['Max-Age']; } public function setMaxAge($maxAge): void { if (!is_int($maxAge) && null !== $maxAge) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Max-Age'] = $maxAge === null ? null : (int) $maxAge; } public function getExpires() { return $this->data['Expires']; } public function setExpires($timestamp): void { if (!is_int($timestamp) && !is_string($timestamp) && null !== $timestamp) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int, string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Expires'] = null === $timestamp ? null : (\is_numeric($timestamp) ? (int) $timestamp : \strtotime((string) $timestamp)); } public function getSecure() { return $this->data['Secure']; } public function setSecure($secure): void { if (!is_bool($secure)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Secure'] = (bool) $secure; } public function getDiscard() { return $this->data['Discard']; } public function setDiscard($discard): void { if (!is_bool($discard)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Discard'] = (bool) $discard; } public function getHttpOnly() { return $this->data['HttpOnly']; } public function setHttpOnly($httpOnly): void { if (!is_bool($httpOnly)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['HttpOnly'] = (bool) $httpOnly; } public function matchesPath(string $requestPath): bool { $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(string $domain): bool { $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(): bool { return $this->getExpires() !== null && \time() > $this->getExpires(); } public function validate() { $name = $this->getName(); if ($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 ($value === null) { return 'The cookie value must not be empty'; } $domain = $this->getDomain(); if ($domain === null || $domain === '') { return 'The cookie domain must not be empty'; } return true; } } sessionKey = $sessionKey; $this->storeSessionCookies = $storeSessionCookies; $this->load(); } public function __destruct() { $this->save(); } public function save(): void { $json = []; foreach ($this as $cookie) { if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } $_SESSION[$this->sessionKey] = \json_encode($json); } protected function load(): void { 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(string $filename): void { $json = []; foreach ($this as $cookie) { if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } $jsonStr = Utils::jsonEncode($json); if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) { throw new \RuntimeException("Unable to save file {$filename}"); } } public function load(string $filename): void { $json = \file_get_contents($filename); if (false === $json) { throw new \RuntimeException("Unable to load file {$filename}"); } if ($json === '') { return; } $data = Utils::jsonDecode($json, true); if (\is_array($data)) { foreach ($data as $cookie) { $this->setCookie(new SetCookie($cookie)); } } elseif (\is_scalar($data) && !empty($data)) { throw new \RuntimeException("Invalid cookie file: {$filename}"); } } } strictMode = $strictMode; foreach ($cookieArray as $cookie) { if (!($cookie instanceof SetCookie)) { $cookie = new SetCookie($cookie); } $this->setCookie($cookie); } } public static function fromArray(array $cookies, string $domain): self { $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 shouldPersist(SetCookie $cookie, bool $allowSessionCookies = false): bool { if ($cookie->getExpires() || $allowSessionCookies) { if (!$cookie->getDiscard()) { return true; } } return false; } public function getCookieByName(string $name): ?SetCookie { foreach ($this->cookies as $cookie) { if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) { return $cookie; } } return null; } public function toArray(): array { return \array_map(static function (SetCookie $cookie): array { return $cookie->toArray(); }, $this->getIterator()->getArrayCopy()); } public function clear(string $domain = null, string $path = null, string $name = null): void { if (!$domain) { $this->cookies = []; return; } elseif (!$path) { $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie) use ($domain): bool { return !$cookie->matchesDomain($domain); } ); } elseif (!$name) { $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie) use ($path, $domain): bool { return !($cookie->matchesPath($path) && $cookie->matchesDomain($domain)); } ); } else { $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie) use ($path, $domain, $name) { return !($cookie->getName() == $name && $cookie->matchesPath($path) && $cookie->matchesDomain($domain)); } ); } } public function clearSessionCookies(): void { $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie): bool { return !$cookie->getDiscard() && $cookie->getExpires(); } ); } public function setCookie(SetCookie $cookie): bool { $name = $cookie->getName(); if (!$name && $name !== '0') { return false; } $result = $cookie->validate(); if ($result !== true) { if ($this->strictMode) { throw new \RuntimeException('Invalid cookie: '.$result); } $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(): int { return \count($this->cookies); } public function getIterator(): \ArrayIterator { return new \ArrayIterator(\array_values($this->cookies)); } public function extractCookies(RequestInterface $request, ResponseInterface $response): void { 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): string { $uriPath = $request->getUri()->getPath(); if ('' === $uriPath) { return '/'; } if (0 !== \strpos($uriPath, '/')) { return '/'; } if ('/' === $uriPath) { return '/'; } $lastSlashPos = \strrpos($uriPath, '/'); if (0 === $lastSlashPos || false === $lastSlashPos) { return '/'; } return \substr($uriPath, 0, $lastSlashPos); } public function withCookieHeader(RequestInterface $request): RequestInterface { $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): void { $cookieValue = $cookie->getValue(); if ($cookieValue === null || $cookieValue === '') { $this->clear( $cookie->getDomain(), $cookie->getPath(), $cookie->getName() ); } } } getStatusCode() : 0; parent::__construct($message, $code, $previous); $this->request = $request; $this->response = $response; $this->handlerContext = $handlerContext; } public static function wrapException(RequestInterface $request, \Throwable $e): RequestException { return $e instanceof RequestException ? $e : new RequestException($e->getMessage(), $request, null, $e); } public static function create( RequestInterface $request, ResponseInterface $response = null, \Throwable $previous = null, array $handlerContext = [], BodySummarizerInterface $bodySummarizer = null ): self { if (!$response) { return new self( 'Error completing request', $request, null, $previous, $handlerContext ); } $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->__toString(), $response->getStatusCode(), $response->getReasonPhrase() ); $summary = ($bodySummarizer ?? new BodySummarizer())->summarize($response); if ($summary !== null) { $message .= ":\n{$summary}\n"; } return new $className($message, $request, $response, $previous, $handlerContext); } private static function obfuscateUri(UriInterface $uri): UriInterface { $userInfo = $uri->getUserInfo(); if (false !== ($pos = \strpos($userInfo, ':'))) { return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***'); } return $uri; } public function getRequest(): RequestInterface { return $this->request; } public function getResponse(): ?ResponseInterface { return $this->response; } public function hasResponse(): bool { return $this->response !== null; } public function getHandlerContext(): array { return $this->handlerContext; } } request = $request; $this->handlerContext = $handlerContext; } public function getRequest(): RequestInterface { return $this->request; } public function getHandlerContext(): array { return $this->handlerContext; } } headers); $normalizedKeys = Utils::normalizeHeaderKeys($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( $status, $headers, $this->sink, $ver, $reason ); } public function __get($name) { $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: '.$name; throw new \BadMethodCallException($msg); } } factory = $options['handle_factory'] ?? new CurlFactory(3); } public function __invoke(RequestInterface $request, array $options): PromiseInterface { 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); } } 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 (false !== \strpos($message, 'getaddrinfo') || false !== \strpos($message, 'Connection refused') || false !== \strpos($message, "couldn't connect to host") || false !== \strpos($message, 'connection attempt failed') ) { $e = new ConnectException($e->getMessage(), $request, $e); } else { $e = RequestException::wrapException($request, $e); } $this->invokeStats($options, $request, $startTime, null, $e); return P\Create::rejectionFor($e); } } private function invokeStats( array $options, RequestInterface $request, ?float $startTime, ResponseInterface $response = null, \Throwable $error = null ): void { if (isset($options['on_stats'])) { $stats = new TransferStats($request, $response, Utils::currentTime() - $startTime, $error, []); ($options['on_stats'])($stats); } } private function createResponse(RequestInterface $request, array $options, $stream, ?float $startTime): PromiseInterface { $hdrs = $this->lastHeaders; $this->lastHeaders = []; try { [$ver, $status, $reason, $headers] = HeaderProcessor::parseHeaders($hdrs); } catch (\Exception $e) { return P\Create::rejectionFor( new RequestException('An error was encountered while creating the response', $request, null, $e) ); } [$stream, $headers] = $this->checkDecode($options, $headers, $stream); $stream = Psr7\Utils::streamFor($stream); $sink = $stream; if (\strcasecmp('HEAD', $request->getMethod())) { $sink = $this->createSink($stream, $options); } try { $response = new Psr7\Response($status, $headers, $sink, $ver, $reason); } catch (\Exception $e) { return P\Create::rejectionFor( new RequestException('An error was encountered while creating the response', $request, null, $e) ); } if (isset($options['on_headers'])) { try { $options['on_headers']($response); } catch (\Exception $e) { return P\Create::rejectionFor( new RequestException('An error was encountered during the on_headers event', $request, $response, $e) ); } } 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): StreamInterface { if (!empty($options['stream'])) { return $stream; } $sink = $options['sink'] ?? Psr7\Utils::tryFopen('php://temp', 'r+'); return \is_string($sink) ? new Psr7\LazyOpenStream($sink, 'w+') : Psr7\Utils::streamFor($sink); } private function checkDecode(array $options, array $headers, $stream): array { if (!empty($options['decode_content'])) { $normalizedKeys = Utils::normalizeHeaderKeys($headers); if (isset($normalizedKeys['content-encoding'])) { $encoding = $headers[$normalizedKeys['content-encoding']]; if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') { $stream = new Psr7\InflateStream(Psr7\Utils::streamFor($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, string $contentLength): StreamInterface { Psr7\Utils::copyToStream( $source, $sink, (\strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1 ); $sink->seek(0); $source->close(); return $sink; } private function createResource(callable $callback) { $errors = []; \set_error_handler(static function ($_, $msg, $file, $line) use (&$errors): bool { $errors[] = [ 'message' => $msg, 'file' => $file, 'line' => $line, ]; return true; }); try { $resource = $callback(); } finally { \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 (!\in_array($request->getUri()->getScheme(), ['http', 'https'])) { throw new RequestException(\sprintf("The scheme '%s' is not supported.", $request->getUri()->getScheme()), $request); } 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'][2]) && 'ntlm' === $options['auth'][2]) { throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler'); } $uri = $this->resolveHost($request, $options); $contextResource = $this->createResource( static function () use ($context, $params) { return \stream_context_create($context, $params); } ); return $this->createResource( function () use ($uri, &$http_response_header, $contextResource, $context, $options, $request) { $resource = @\fopen((string) $uri, 'r', false, $contextResource); $this->lastHeaders = $http_response_header ?? []; if (false === $resource) { throw new ConnectException(sprintf('Connection refused for URI %s', $uri), $request, null, $context); } 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): UriInterface { $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 (false === $records || !isset($records[0]['ip'])) { throw new ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request); } return $uri->withHost($records[0]['ip']); } if ('v6' === $options['force_ip_resolve']) { $records = \dns_get_record($uri->getHost(), \DNS_AAAA); if (false === $records || !isset($records[0]['ipv6'])) { throw new ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request); } return $uri->withHost('['.$records[0]['ipv6'].']'); } } return $uri; } private function getDefaultContext(RequestInterface $request): array { $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, ], 'ssl' => [ 'peer_name' => $request->getUri()->getHost(), ], ]; $body = (string) $request->getBody(); if ('' !== $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, array &$options, $value, array &$params): void { $uri = null; if (!\is_array($value)) { $uri = $value; } else { $scheme = $request->getUri()->getScheme(); if (isset($value[$scheme])) { if (!isset($value['no']) || !Utils::isHostInNoProxy($request->getUri()->getHost(), $value['no'])) { $uri = $value[$scheme]; } } } if (!$uri) { return; } $parsed = $this->parse_proxy($uri); $options['http']['proxy'] = $parsed['proxy']; if ($parsed['auth']) { if (!isset($options['http']['header'])) { $options['http']['header'] = []; } $options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}"; } } private function parse_proxy(string $url): array { $parsed = \parse_url($url); if ($parsed !== false && isset($parsed['scheme']) && $parsed['scheme'] === 'http') { if (isset($parsed['host']) && isset($parsed['port'])) { $auth = null; if (isset($parsed['user']) && isset($parsed['pass'])) { $auth = \base64_encode("{$parsed['user']}:{$parsed['pass']}"); } return [ 'proxy' => "tcp://{$parsed['host']}:{$parsed['port']}", 'auth' => $auth ? "Basic {$auth}" : null, ]; } } return [ 'proxy' => $url, 'auth' => null, ]; } private function add_timeout(RequestInterface $request, array &$options, $value, array &$params): void { if ($value > 0) { $options['http']['timeout'] = $value; } } private function add_crypto_method(RequestInterface $request, array &$options, $value, array &$params): void { if ( $value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT || $value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT || $value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT || (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT) ) { $options['http']['crypto_method'] = $value; return; } throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); } private function add_verify(RequestInterface $request, array &$options, $value, array &$params): void { if ($value === false) { $options['ssl']['verify_peer'] = false; $options['ssl']['verify_peer_name'] = false; return; } if (\is_string($value)) { $options['ssl']['cafile'] = $value; if (!\file_exists($value)) { throw new \RuntimeException("SSL CA bundle not found: $value"); } } elseif ($value !== true) { 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, array &$options, $value, array &$params): void { 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, array &$options, $value, array &$params): void { self::addNotification( $params, static function ($code, $a, $b, $c, $transferred, $total) use ($value) { if ($code == \STREAM_NOTIFY_PROGRESS) { $value($total, $transferred, 0, 0); } } ); } private function add_debug(RequestInterface $request, array &$options, $value, array &$params): void { 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 = Utils::debugResource($value); $ident = $request->getMethod().' '.$request->getUri()->withFragment(''); self::addNotification( $params, static function (int $code, ...$passed) use ($ident, $value, $map, $args): void { \fprintf($value, '<%s> [%s] ', $ident, $map[$code]); foreach (\array_filter($passed) as $i => $v) { \fwrite($value, $args[$i].': "'.$v.'" '); } \fwrite($value, "\n"); } ); } private static function addNotification(array &$params, callable $notify): void { if (!isset($params['notification'])) { $params['notification'] = $notify; } else { $params['notification'] = self::callArray([ $params['notification'], $notify, ]); } } private static function callArray(array $functions): callable { return static function (...$args) use ($functions) { foreach ($functions as $fn) { $fn(...$args); } }; } } factory = $options['handle_factory'] ?? new CurlFactory(50); if (isset($options['select_timeout'])) { $this->selectTimeout = $options['select_timeout']; } elseif ($selectTimeout = Utils::getenv('GUZZLE_CURL_SELECT_TIMEOUT')) { @trigger_error('Since guzzlehttp/guzzle 7.2.0: Using environment variable GUZZLE_CURL_SELECT_TIMEOUT is deprecated. Use option "select_timeout" instead.', \E_USER_DEPRECATED); $this->selectTimeout = (int) $selectTimeout; } else { $this->selectTimeout = 1; } $this->options = $options['options'] ?? []; unset($this->_mh); } public function __get($name) { if ($name !== '_mh') { throw new \BadMethodCallException("Can not get other property as '_mh'."); } $multiHandle = \curl_multi_init(); if (false === $multiHandle) { throw new \RuntimeException('Can not initialize curl multi handle.'); } $this->_mh = $multiHandle; foreach ($this->options as $option => $value) { curl_multi_setopt($this->_mh, $option, $value); } return $this->_mh; } public function __destruct() { if (isset($this->_mh)) { \curl_multi_close($this->_mh); unset($this->_mh); } } public function __invoke(RequestInterface $request, array $options): PromiseInterface { $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(): void { 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\Utils::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(): void { $queue = P\Utils::queue(); while ($this->handles || !$queue->isEmpty()) { if (!$this->active && $this->delays) { \usleep($this->timeToNext()); } $this->tick(); } } private function addRequest(array $entry): void { $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): bool { if (!is_int($id)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an integer to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } 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(): void { while ($done = \curl_multi_info_read($this->_mh)) { if ($done['msg'] !== \CURLMSG_DONE) { continue; } $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(): int { $currentTime = Utils::currentTime(); $nextTime = \PHP_INT_MAX; foreach ($this->delays as $time) { if ($time < $nextTime) { $nextTime = $time; } } return ((int) \max(0, $nextTime - $currentTime)) * 1000000; } } onFulfilled = $onFulfilled; $this->onRejected = $onRejected; if ($queue) { $this->append(...array_values($queue)); } } public function __invoke(RequestInterface $request, array $options): PromiseInterface { if (!$this->queue) { throw new \OutOfBoundsException('Mock queue is empty'); } if (isset($options['delay']) && \is_numeric($options['delay'])) { \usleep((int) $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 = $response($request, $options); } $response = $response instanceof \Throwable ? P\Create::rejectionFor($response) : P\Create::promiseFor($response); return $response->then( function (?ResponseInterface $value) use ($request, $options) { $this->invokeStats($request, $options, $value); if ($this->onFulfilled) { ($this->onFulfilled)($value); } if ($value !== null && 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 StreamInterface) { $sink->write($contents); } } return $value; }, function ($reason) use ($request, $options) { $this->invokeStats($request, $options, null, $reason); if ($this->onRejected) { ($this->onRejected)($reason); } return P\Create::rejectionFor($reason); } ); } public function append(...$values): void { foreach ($values as $value) { if ($value instanceof ResponseInterface || $value instanceof \Throwable || $value instanceof PromiseInterface || \is_callable($value) ) { $this->queue[] = $value; } else { throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found '.Utils::describeType($value)); } } } public function getLastRequest(): ?RequestInterface { return $this->lastRequest; } public function getLastOptions(): array { return $this->lastOptions; } public function count(): int { return \count($this->queue); } public function reset(): void { $this->queue = []; } private function invokeStats( RequestInterface $request, array $options, ResponseInterface $response = null, $reason = null ): void { if (isset($options['on_stats'])) { $transferTime = $options['transfer_time'] ?? 0; $stats = new TransferStats($request, $response, $transferTime, $reason); ($options['on_stats'])($stats); } } } maxHandles = $maxHandles; } public function create(RequestInterface $request, array $options): EasyHandle { 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): void { $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): PromiseInterface { 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): void { $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 ); ($easy->options['on_stats'])($stats); } private static function finishError(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory): PromiseInterface { $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): PromiseInterface { 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->createResponseException) { return P\Create::rejectionFor( new RequestException( 'An error was encountered while creating the response', $easy->request, $easy->response, $easy->createResponseException, $ctx ) ); } if ($easy->onHeadersException) { return P\Create::rejectionFor( new RequestException( 'An error was encountered during the on_headers event', $easy->request, $easy->response, $easy->onHeadersException, $ctx ) ); } $message = \sprintf( 'cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html' ); $uriString = (string) $easy->request->getUri(); if ($uriString !== '' && false === \strpos($ctx['error'], $uriString)) { $message .= \sprintf(' for %s', $uriString); } $error = isset($connectionErrors[$easy->errno]) ? new ConnectException($message, $easy->request, null, $ctx) : new RequestException($message, $easy->request, $easy->response, null, $ctx); return P\Create::rejectionFor($error); } private function getDefaultConf(EasyHandle $easy): array { $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 => 300, ]; 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): void { $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): void { $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] = static 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): void { 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(string $name, array &$options): void { foreach (\array_keys($options['_headers']) as $key) { if (!\strcasecmp($key, $name)) { unset($options['_headers'][$key]); return; } } } private function applyHandlerOptions(EasyHandle $easy, array &$conf): void { $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']) === true && ($verifyLink = \readlink($options['verify'])) !== false && \is_dir($verifyLink) ) ) { $conf[\CURLOPT_CAPATH] = $options['verify']; } else { $conf[\CURLOPT_CAINFO] = $options['verify']; } } } } if (!isset($options['curl'][\CURLOPT_ENCODING]) && !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'])) { $options['sink'] = \GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+'); } $sink = $options['sink']; if (!\is_string($sink)) { $sink = \GuzzleHttp\Psr7\Utils::streamFor($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] = static function ($ch, $write) use ($sink): int { return $sink->write($write); }; $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']) && Utils::isHostInNoProxy($host, $options['proxy']['no'])) { unset($conf[\CURLOPT_PROXY]); } else { $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme]; } } } } if (isset($options['crypto_method'])) { if (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) { if (!defined('CURL_SSLVERSION_TLSv1_0')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.0 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0; } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) { if (!defined('CURL_SSLVERSION_TLSv1_1')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.1 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1; } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) { if (!defined('CURL_SSLVERSION_TLSv1_2')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { if (!defined('CURL_SSLVERSION_TLSv1_3')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; } else { throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); } } 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}"); } $ext = pathinfo($cert, \PATHINFO_EXTENSION); if (preg_match('#^(der|p12)$#i', $ext)) { $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext); } $conf[\CURLOPT_SSLCERT] = $cert; } if (isset($options['ssl_key'])) { if (\is_array($options['ssl_key'])) { if (\count($options['ssl_key']) === 2) { [$sslKey, $conf[\CURLOPT_SSLKEYPASSWD]] = $options['ssl_key']; } else { [$sslKey] = $options['ssl_key']; } } $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] = static function ($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use ($progress) { $progress($downloadSize, $downloaded, $uploadSize, $uploaded); }; } if (!empty($options['debug'])) { $conf[\CURLOPT_STDERR] = Utils::debugResource($options['debug']); $conf[\CURLOPT_VERBOSE] = true; } } private static function retryFailedRewind(callable $handler, EasyHandle $easy, array $ctx): PromiseInterface { 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): callable { 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 static function ($ch, $h) use ( $onHeaders, $easy, &$startingResponse ) { $value = \trim($h); if ($value === '') { $startingResponse = true; try { $easy->createResponse(); } catch (\Exception $e) { $easy->createResponseException = $e; return -1; } 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); }; } public function __destruct() { foreach ($this->handles as $id => $handle) { \curl_close($handle); unset($this->handles[$id]); } } } waitFn = $waitFn; $this->cancelFn = $cancelFn; } public function then( callable $onFulfilled = null, callable $onRejected = null ): PromiseInterface { 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): PromiseInterface { return $this->then(null, $onRejected); } public function wait(bool $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(): string { return $this->state; } public function cancel(): void { 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); } } if ($this->state === self::PENDING) { $this->reject(new CancellationException('Promise has been cancelled')); } } public function resolve($value): void { $this->settle(self::FULFILLED, $value); } public function reject($reason): void { $this->settle(self::REJECTED, $reason); } private function settle(string $state, $value): void { 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): void { 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): void { foreach ($handlers as $handler) { self::callHandler(1, $value, $handler); } }, static function ($reason) use ($handlers): void { foreach ($handlers as $handler) { self::callHandler(2, $reason, $handler); } } ); } } private static function callHandler(int $index, $value, array $handler): void { $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); } } private function waitIfPending(): void { 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(): void { try { $wfn = $this->waitFn; $this->waitFn = null; $wfn(true); } catch (\Throwable $reason) { if ($this->state === self::PENDING) { $this->reject($reason); } else { throw $reason; } } } private function invokeWaitList(): void { $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); } } } } value = $value; } public function then( callable $onFulfilled = null, callable $onRejected = null ): PromiseInterface { if (!$onFulfilled) { return $this; } $queue = Utils::queue(); $p = new Promise([$queue, 'run']); $value = $this->value; $queue->add(static function () use ($p, $value, $onFulfilled): void { if (Is::pending($p)) { try { $p->resolve($onFulfilled($value)); } catch (\Throwable $e) { $p->reject($e); } } }); return $p; } public function otherwise(callable $onRejected): PromiseInterface { return $this->then(null, $onRejected); } public function wait(bool $unwrap = true) { return $unwrap ? $this->value : null; } public function getState(): string { return self::FULFILLED; } public function resolve($value): void { if ($value !== $this->value) { throw new \LogicException('Cannot resolve a fulfilled promise'); } } public function reject($reason): void { throw new \LogicException('Cannot reject a fulfilled promise'); } public function cancel(): void { } } 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; } } add(function () use ($task, $promise): void { try { if (Is::pending($promise)) { $promise->resolve($task()); } } catch (\Throwable $e) { $promise->reject($e); } }); return $promise; } public static function inspect(PromiseInterface $promise): array { 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]; } } public static function inspectAll($promises): array { $results = []; foreach ($promises as $key => $promise) { $results[$key] = self::inspect($promise); } return $results; } public static function unwrap($promises): array { $results = []; foreach ($promises as $key => $promise) { $results[$key] = $promise->wait(); } return $results; } public static function all($promises, bool $recursive = false): PromiseInterface { $results = []; $promise = Each::of( $promises, function ($value, $idx) use (&$results): void { $results[$idx] = $value; }, function ($reason, $idx, Promise $aggregate): void { $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(int $count, $promises): PromiseInterface { $results = []; $rejections = []; return Each::of( $promises, function ($value, $idx, PromiseInterface $p) use (&$results, $count): void { if (Is::settled($p)) { return; } $results[$idx] = $value; if (count($results) >= $count) { $p->resolve(null); } }, function ($reason) use (&$rejections): void { $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): PromiseInterface { return self::some(1, $promises)->then(function ($values) { return $values[0]; }); } public static function settle($promises): PromiseInterface { $results = []; return Each::of( $promises, function ($value, $idx) use (&$results): void { $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value]; }, function ($reason, $idx) use (&$results): void { $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason]; } )->then(function () use (&$results) { ksort($results); return $results; }); } } 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(): PromiseInterface { if ($this->aggregate) { return $this->aggregate; } try { $this->createPromise(); $this->iterable->rewind(); $this->refillPending(); } catch (\Throwable $e) { $this->aggregate->reject($e); } return $this->aggregate; } private function createPromise(): void { $this->mutex = false; $this->aggregate = new Promise(function (): void { 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 (): void { $this->iterable = $this->concurrency = $this->pending = null; $this->onFulfilled = $this->onRejected = null; $this->nextPendingIndex = 0; }; $this->aggregate->then($clearFn, $clearFn); } private function refillPending(): void { if (!$this->concurrency) { while ($this->addPending() && $this->advanceIterator()) { } return; } $concurrency = is_callable($this->concurrency) ? ($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(): bool { 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): void { if ($this->onFulfilled) { ($this->onFulfilled)( $value, $key, $this->aggregate ); } $this->step($idx); }, function ($reason) use ($idx, $key): void { if ($this->onRejected) { ($this->onRejected)( $reason, $key, $this->aggregate ); } $this->step($idx); } ); return true; } private function advanceIterator(): bool { 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; } } private function step(int $idx): void { if (Is::settled($this->aggregate)) { return; } unset($this->pending[$idx]); if ($this->advanceIterator() && !$this->checkIfFinished()) { $this->refillPending(); } } private function checkIfFinished(): bool { if (!$this->pending && !$this->iterable->valid()) { $this->aggregate->resolve(null); return true; } return false; } } generator = $generatorFn(); $this->result = new Promise(function (): void { while (isset($this->currentPromise)) { $this->currentPromise->wait(); } }); try { $this->nextCoroutine($this->generator->current()); } catch (Throwable $throwable) { $this->result->reject($throwable); } } public static function of(callable $generatorFn): self { return new self($generatorFn); } public function then( callable $onFulfilled = null, callable $onRejected = null ): PromiseInterface { return $this->result->then($onFulfilled, $onRejected); } public function otherwise(callable $onRejected): PromiseInterface { return $this->result->otherwise($onRejected); } public function wait(bool $unwrap = true) { return $this->result->wait($unwrap); } public function getState(): string { return $this->result->getState(); } public function resolve($value): void { $this->result->resolve($value); } public function reject($reason): void { $this->result->reject($reason); } public function cancel(): void { $this->currentPromise->cancel(); $this->result->cancel(); } private function nextCoroutine($yielded): void { $this->currentPromise = Create::promiseFor($yielded) ->then([$this, '_handleSuccess'], [$this, '_handleFailure']); } public function _handleSuccess($value): void { unset($this->currentPromise); try { $next = $this->generator->send($value); if ($this->generator->valid()) { $this->nextCoroutine($next); } else { $this->result->resolve($value); } } catch (Throwable $throwable) { $this->result->reject($throwable); } } public function _handleFailure($reason): void { unset($this->currentPromise); try { $nextYield = $this->generator->throw(Create::exceptionFor($reason)); $this->nextCoroutine($nextYield); } catch (Throwable $throwable) { $this->result->reject($throwable); } } } reason = $reason; } public function then( callable $onFulfilled = null, callable $onRejected = null ): PromiseInterface { if (!$onRejected) { return $this; } $queue = Utils::queue(); $reason = $this->reason; $p = new Promise([$queue, 'run']); $queue->add(static function () use ($p, $reason, $onRejected): void { if (Is::pending($p)) { try { $p->resolve($onRejected($reason)); } catch (\Throwable $e) { $p->reject($e); } } }); return $p; } public function otherwise(callable $onRejected): PromiseInterface { return $this->then(null, $onRejected); } public function wait(bool $unwrap = true) { if ($unwrap) { throw Create::exceptionFor($this->reason); } return null; } public function getState(): string { return self::REJECTED; } public function resolve($value): void { throw new \LogicException('Cannot resolve a rejected promise'); } public function reject($reason): void { if ($reason !== $this->reason) { throw new \LogicException('Cannot reject a rejected promise'); } } public function cancel(): void { } } $onFulfilled, 'rejected' => $onRejected, ]))->promise(); } public static function ofLimit( $iterable, $concurrency, callable $onFulfilled = null, callable $onRejected = null ): PromiseInterface { return (new EachPromise($iterable, [ 'fulfilled' => $onFulfilled, 'rejected' => $onRejected, 'concurrency' => $concurrency, ]))->promise(); } public static function ofLimitAll( $iterable, $concurrency, callable $onFulfilled = null ): PromiseInterface { return self::ofLimit( $iterable, $concurrency, $onFulfilled, function ($reason, $idx, PromiseInterface $aggregate): void { $aggregate->reject($reason); } ); } } enableShutdown) { $err = error_get_last(); if (!$err || ($err['type'] ^ E_ERROR)) { $this->run(); } } }); } } public function isEmpty(): bool { return !$this->queue; } public function add(callable $task): void { $this->queue[] = $task; } public function run(): void { while ($task = array_shift($this->queue)) { $task(); } } public function disableShutdown(): void { $this->enableShutdown = false; } } then([$promise, 'resolve'], [$promise, 'reject']); return $promise; } return new FulfilledPromise($value); } public static function rejectionFor($reason): PromiseInterface { if ($reason instanceof PromiseInterface) { return $reason; } return new RejectedPromise($reason); } public static function exceptionFor($reason): \Throwable { if ($reason instanceof \Throwable) { return $reason; } return new RejectionException($reason); } public static function iterFor($value): \Iterator { if ($value instanceof \Iterator) { return $value; } if (is_array($value)) { return new \ArrayIterator($value); } return new \ArrayIterator([$value]); } } getState() === PromiseInterface::PENDING; } public static function settled(PromiseInterface $promise): bool { return $promise->getState() !== PromiseInterface::PENDING; } public static function fulfilled(PromiseInterface $promise): bool { return $promise->getState() === PromiseInterface::FULFILLED; } public static function rejected(PromiseInterface $promise): bool { return $promise->getState() === PromiseInterface::REJECTED; } } 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, false, self::createStreamContext($stream)); } public static function createStreamContext(StreamInterface $stream) { return stream_context_create([ 'guzzle' => ['stream' => $stream], ]); } public static function register(): void { if (!in_array('guzzle', stream_get_wrappers())) { stream_wrapper_register('guzzle', __CLASS__); } } public function stream_open(string $path, string $mode, int $options, string &$opened_path = null): bool { $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(int $count): string { return $this->stream->read($count); } public function stream_write(string $data): int { return $this->stream->write($data); } public function stream_tell(): int { return $this->stream->tell(); } public function stream_eof(): bool { return $this->stream->eof(); } public function stream_seek(int $offset, int $whence): bool { $this->stream->seek($offset, $whence); return true; } public function stream_cast(int $cast_as) { $stream = clone $this->stream; $resource = $stream->detach(); return $resource ?? false; } public function stream_stat(): array { 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(string $path, int $flags): array { 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, ]; } } remoteStream = $stream; $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+')); } public function getSize(): ?int { $remoteSize = $this->remoteStream->getSize(); if (null === $remoteSize) { return null; } return max($this->stream->getSize(), $remoteSize); } public function rewind(): void { $this->seek(0); } public function seek($offset, $whence = SEEK_SET): void { 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): string { $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): int { $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell(); if ($overflow > 0) { $this->skipReadBytes += $overflow; } return $this->stream->write($string); } public function eof(): bool { return $this->stream->eof() && $this->remoteStream->eof(); } public function close(): void { $this->remoteStream->close(); $this->stream->close(); } private function cacheEntireStream(): int { $target = new FnStream(['write' => 'strlen']); Utils::copyToStream($this, $target); return $this->tell(); } } 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, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool { return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations); } private static function capitalizePercentEncoding(UriInterface $uri): UriInterface { $regex = '/(?:%[A-Fa-f0-9]{2})++/'; $callback = function (array $match): string { 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): UriInterface { $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; $callback = function (array $match): string { 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() { } } protocol; } public function withProtocolVersion($version): MessageInterface { if ($this->protocol === $version) { return $this; } $new = clone $this; $new->protocol = $version; return $new; } public function getHeaders(): array { return $this->headers; } public function hasHeader($header): bool { return isset($this->headerNames[strtolower($header)]); } public function getHeader($header): array { $header = strtolower($header); if (!isset($this->headerNames[$header])) { return []; } $header = $this->headerNames[$header]; return $this->headers[$header]; } public function getHeaderLine($header): string { return implode(', ', $this->getHeader($header)); } public function withHeader($header, $value): MessageInterface { $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): MessageInterface { $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): MessageInterface { $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(): StreamInterface { if (!$this->stream) { $this->stream = Utils::streamFor(''); } return $this->stream; } public function withBody(StreamInterface $body): MessageInterface { if ($body === $this->stream) { return $this; } $new = clone $this; $new->stream = $body; return $new; } private function setHeaders(array $headers): void { $this->headerNames = $this->headers = []; foreach ($headers as $header => $value) { $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): array { 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): array { 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): void { 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 (!preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) { throw new \InvalidArgumentException( sprintf('"%s" is not valid header name.', $header) ); } } private function assertValue(string $value): void { if (!preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*$/D', $value)) { throw new \InvalidArgumentException( sprintf('"%s" is not valid header value.', $value) ); } } } source = $source; $this->size = $options['size'] ?? null; $this->metadata = $options['metadata'] ?? []; $this->buffer = new BufferStream(); } public function __toString(): string { try { return Utils::copyToString($this); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } public function close(): void { $this->detach(); } public function detach() { $this->tellPos = 0; $this->source = null; return null; } public function getSize(): ?int { return $this->size; } public function tell(): int { return $this->tellPos; } public function eof(): bool { return $this->source === null; } public function isSeekable(): bool { return false; } public function rewind(): void { $this->seek(0); } public function seek($offset, $whence = SEEK_SET): void { throw new \RuntimeException('Cannot seek a PumpStream'); } public function isWritable(): bool { return false; } public function write($string): int { throw new \RuntimeException('Cannot write to a PumpStream'); } public function isReadable(): bool { return true; } public function read($length): string { $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(): string { $result = ''; while (!$this->eof()) { $result .= $this->read(1000000); } return $result; } public function getMetadata($key = null) { if (!$key) { return $this->metadata; } return $this->metadata[$key] ?? null; } private function pump(int $length): void { if ($this->source !== null) { do { $data = ($this->source)($length); if ($data === false || $data === null) { $this->source = null; return; } $this->buffer->write($data); $length -= strlen($data); } while ($length > 0); } } } size = $options['size']; } $this->customMetadata = $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(): string { try { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } public function getContents(): string { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->readable) { throw new \RuntimeException('Cannot read from non-readable stream'); } return Utils::tryGetContents($this->stream); } public function close(): void { 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(): ?int { 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 (is_array($stats) && isset($stats['size'])) { $this->size = $stats['size']; return $this->size; } return null; } public function isReadable(): bool { return $this->readable; } public function isWritable(): bool { return $this->writable; } public function isSeekable(): bool { return $this->seekable; } public function eof(): bool { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } return feof($this->stream); } public function tell(): int { 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(): void { $this->seek(0); } public function seek($offset, $whence = SEEK_SET): void { $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): string { 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 ''; } try { $string = fread($this->stream, $length); } catch (\Exception $e) { throw new \RuntimeException('Unable to read from stream', 0, $e); } if (false === $string) { throw new \RuntimeException('Unable to read from stream'); } return $string; } public function write($string): int { 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 $meta[$key] ?? null; } } getSize(); } return new UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType); } public function createStream(string $content = ''): StreamInterface { return Utils::streamFor($content); } public function createStreamFromFile(string $file, string $mode = 'r'): StreamInterface { try { $resource = Utils::tryFopen($file, $mode); } catch (\RuntimeException $e) { if ('' === $mode || false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'], true)) { throw new \InvalidArgumentException(sprintf('Invalid file opening mode "%s"', $mode), 0, $e); } throw $e; } return Utils::streamFor($resource); } public function createStreamFromResource($resource): StreamInterface { return Utils::streamFor($resource); } public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface { if (empty($method)) { if (!empty($serverParams['REQUEST_METHOD'])) { $method = $serverParams['REQUEST_METHOD']; } else { throw new \InvalidArgumentException('Cannot determine HTTP method'); } } return new ServerRequest($method, $uri, [], null, '1.1', $serverParams); } public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface { return new Response($code, [], null, '1.1', $reasonPhrase); } public function createRequest(string $method, $uri): RequestInterface { return new Request($method, $uri); } public function createUri(string $uri = ''): UriInterface { return new Uri($uri); } } 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): UriInterface { 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): string { $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() { } } ]+>|[^=]+/', $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): array { $result = []; foreach ((array) $header as $value) { foreach (self::splitList($value) as $parsed) { $result[] = $parsed; } } return $result; } public static function splitList($values): array { if (!\is_array($values)) { $values = [$values]; } $result = []; foreach ($values as $value) { if (!\is_string($value)) { throw new \TypeError('$header must either be a string or an array containing strings.'); } $v = ''; $isQuoted = false; $isEscaped = false; for ($i = 0, $max = \strlen($value); $i < $max; ++$i) { if ($isEscaped) { $v .= $value[$i]; $isEscaped = false; continue; } if (!$isQuoted && $value[$i] === ',') { $v = \trim($v); if ($v !== '') { $result[] = $v; } $v = ''; continue; } if ($isQuoted && $value[$i] === '\\') { $isEscaped = true; $v .= $value[$i]; continue; } if ($value[$i] === '"') { $isQuoted = !$isQuoted; $v .= $value[$i]; continue; } $v .= $value[$i]; } $v = \trim($v); if ($v !== '') { $result[] = $v; } } return $result; } } 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(): string { 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): RequestInterface { 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(): string { return $this->method; } public function withMethod($method): RequestInterface { $this->assertMethod($method); $new = clone $this; $new->method = strtoupper($method); return $new; } public function getUri(): UriInterface { return $this->uri; } public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface { 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(): void { $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): void { if (!is_string($method) || $method === '') { throw new InvalidArgumentException('Method must be a non-empty string.'); } } } 80, 'https' => 443, 'ftp' => 21, 'gopher' => 70, 'nntp' => 119, 'news' => 119, 'telnet' => 23, 'tn3270' => 23, 'imap' => 143, 'pop' => 110, 'ldap' => 389, ]; private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~'; private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;='; private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26']; private $scheme = ''; private $userInfo = ''; private $host = ''; private $port; private $path = ''; private $query = ''; private $fragment = ''; private $composedComponents; public function __construct(string $uri = '') { if ($uri !== '') { $parts = self::parse($uri); if ($parts === false) { throw new MalformedUriException("Unable to parse URI: $uri"); } $this->applyParts($parts); } } private static function parse(string $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(): string { if ($this->composedComponents === null) { $this->composedComponents = self::composeComponents( $this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment ); } return $this->composedComponents; } public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment): string { $uri = ''; if ($scheme != '') { $uri .= $scheme.':'; } if ($authority != '' || $scheme === 'file') { $uri .= '//'.$authority; } if ($authority != '' && $path != '' && $path[0] != '/') { $path = '/'.$path; } $uri .= $path; if ($query != '') { $uri .= '?'.$query; } if ($fragment != '') { $uri .= '#'.$fragment; } return $uri; } public static function isDefaultPort(UriInterface $uri): bool { return $uri->getPort() === null || (isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$uri->getScheme()]); } public static function isAbsolute(UriInterface $uri): bool { return $uri->getScheme() !== ''; } public static function isNetworkPathReference(UriInterface $uri): bool { return $uri->getScheme() === '' && $uri->getAuthority() !== ''; } public static function isAbsolutePathReference(UriInterface $uri): bool { return $uri->getScheme() === '' && $uri->getAuthority() === '' && isset($uri->getPath()[0]) && $uri->getPath()[0] === '/'; } public static function isRelativePathReference(UriInterface $uri): bool { return $uri->getScheme() === '' && $uri->getAuthority() === '' && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/'); } public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool { 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 withoutQueryValue(UriInterface $uri, string $key): UriInterface { $result = self::getFilteredQueryString($uri, [$key]); return $uri->withQuery(implode('&', $result)); } public static function withQueryValue(UriInterface $uri, string $key, ?string $value): UriInterface { $result = self::getFilteredQueryString($uri, [$key]); $result[] = self::generateQueryString($key, $value); return $uri->withQuery(implode('&', $result)); } public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface { $result = self::getFilteredQueryString($uri, array_keys($keyValueArray)); foreach ($keyValueArray as $key => $value) { $result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null); } return $uri->withQuery(implode('&', $result)); } public static function fromParts(array $parts): UriInterface { $uri = new self(); $uri->applyParts($parts); $uri->validateState(); return $uri; } public function getScheme(): string { return $this->scheme; } public function getAuthority(): string { $authority = $this->host; if ($this->userInfo !== '') { $authority = $this->userInfo.'@'.$authority; } if ($this->port !== null) { $authority .= ':'.$this->port; } return $authority; } public function getUserInfo(): string { return $this->userInfo; } public function getHost(): string { return $this->host; } public function getPort(): ?int { return $this->port; } public function getPath(): string { return $this->path; } public function getQuery(): string { return $this->query; } public function getFragment(): string { return $this->fragment; } public function withScheme($scheme): UriInterface { $scheme = $this->filterScheme($scheme); if ($this->scheme === $scheme) { return $this; } $new = clone $this; $new->scheme = $scheme; $new->composedComponents = null; $new->removeDefaultPort(); $new->validateState(); return $new; } public function withUserInfo($user, $password = null): UriInterface { $info = $this->filterUserInfoComponent($user); if ($password !== null) { $info .= ':'.$this->filterUserInfoComponent($password); } if ($this->userInfo === $info) { return $this; } $new = clone $this; $new->userInfo = $info; $new->composedComponents = null; $new->validateState(); return $new; } public function withHost($host): UriInterface { $host = $this->filterHost($host); if ($this->host === $host) { return $this; } $new = clone $this; $new->host = $host; $new->composedComponents = null; $new->validateState(); return $new; } public function withPort($port): UriInterface { $port = $this->filterPort($port); if ($this->port === $port) { return $this; } $new = clone $this; $new->port = $port; $new->composedComponents = null; $new->removeDefaultPort(); $new->validateState(); return $new; } public function withPath($path): UriInterface { $path = $this->filterPath($path); if ($this->path === $path) { return $this; } $new = clone $this; $new->path = $path; $new->composedComponents = null; $new->validateState(); return $new; } public function withQuery($query): UriInterface { $query = $this->filterQueryAndFragment($query); if ($this->query === $query) { return $this; } $new = clone $this; $new->query = $query; $new->composedComponents = null; return $new; } public function withFragment($fragment): UriInterface { $fragment = $this->filterQueryAndFragment($fragment); if ($this->fragment === $fragment) { return $this; } $new = clone $this; $new->fragment = $fragment; $new->composedComponents = null; return $new; } public function jsonSerialize(): string { return $this->__toString(); } private function applyParts(array $parts): void { $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): string { if (!is_string($scheme)) { throw new \InvalidArgumentException('Scheme must be a string'); } return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } private function filterUserInfoComponent($component): string { if (!is_string($component)) { throw new \InvalidArgumentException('User info must be a string'); } return preg_replace_callback( '/(?:[^%'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $component ); } private function filterHost($host): string { if (!is_string($host)) { throw new \InvalidArgumentException('Host must be a string'); } return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } private function filterPort($port): ?int { 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): array { $current = $uri->getQuery(); if ($current === '') { return []; } $decodedKeys = array_map(function ($k): string { return rawurldecode((string) $k); }, $keys); return array_filter(explode('&', $current), function ($part) use ($decodedKeys) { return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true); }); } private static function generateQueryString(string $key, ?string $value): string { $queryString = strtr($key, self::QUERY_SEPARATORS_REPLACEMENT); if ($value !== null) { $queryString .= '='.strtr($value, self::QUERY_SEPARATORS_REPLACEMENT); } return $queryString; } private function removeDefaultPort(): void { if ($this->port !== null && self::isDefaultPort($this)) { $this->port = null; } } private function filterPath($path): string { if (!is_string($path)) { throw new \InvalidArgumentException('Path must be a string'); } return preg_replace_callback( '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $path ); } private function filterQueryAndFragment($str): string { if (!is_string($str)) { throw new \InvalidArgumentException('Query and fragment must be a string'); } return preg_replace_callback( '/(?:[^'.self::CHAR_UNRESERVED.self::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $str ); } private function rawurlencodeMatchZero(array $match): string { return rawurlencode($match[0]); } private function validateState(): void { 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 MalformedUriException('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 MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon'); } } } } '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', 308 => 'Permanent Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request 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', 510 => 'Not Extended', 511 => 'Network Authentication Required', ]; private $reasonPhrase; private $statusCode; public function __construct( int $status = 200, array $headers = [], $body = null, string $version = '1.1', string $reason = null ) { $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(): int { return $this->statusCode; } public function getReasonPhrase(): string { return $this->reasonPhrase; } public function withStatus($code, $reasonPhrase = ''): ResponseInterface { $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): void { if (filter_var($statusCode, FILTER_VALIDATE_INT) === false) { throw new \InvalidArgumentException('Status code must be an integer value.'); } } private function assertStatusCodeRange(int $statusCode): void { if ($statusCode < 100 || $statusCode >= 600) { throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.'); } } } 'application/vnd.1000minds.decision-model+xml', '3dml' => 'text/vnd.in3d.3dml', '3ds' => 'image/x-3ds', '3g2' => 'video/3gpp2', '3gp' => 'video/3gp', '3gpp' => 'video/3gpp', '3mf' => 'model/3mf', '7z' => 'application/x-7z-compressed', '7zip' => 'application/x-7z-compressed', '123' => 'application/vnd.lotus-1-2-3', 'aab' => 'application/x-authorware-bin', 'aac' => 'audio/aac', 'aam' => 'application/x-authorware-map', 'aas' => 'application/x-authorware-seg', 'abw' => 'application/x-abiword', 'ac' => 'application/vnd.nokia.n-gage.ac+xml', 'ac3' => 'audio/ac3', 'acc' => 'application/vnd.americandynamics.acc', 'ace' => 'application/x-ace-compressed', 'acu' => 'application/vnd.acucobol', 'acutc' => 'application/vnd.acucorp', 'adp' => 'audio/adpcm', 'adts' => 'audio/aac', 'aep' => 'application/vnd.audiograph', 'afm' => 'application/x-font-type1', 'afp' => 'application/vnd.ibm.modcap', 'age' => 'application/vnd.age', 'ahead' => 'application/vnd.ahead.space', 'ai' => 'application/pdf', 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', 'air' => 'application/vnd.adobe.air-application-installer-package+zip', 'ait' => 'application/vnd.dvb.ait', 'ami' => 'application/vnd.amiga.ami', 'aml' => 'application/automationml-aml+xml', 'amlx' => 'application/automationml-amlx+zip', 'amr' => 'audio/amr', 'apk' => 'application/vnd.android.package-archive', 'apng' => 'image/apng', 'appcache' => 'text/cache-manifest', 'appinstaller' => 'application/appinstaller', 'application' => 'application/x-ms-application', 'appx' => 'application/appx', 'appxbundle' => 'application/appxbundle', 'apr' => 'application/vnd.lotus-approach', 'arc' => 'application/x-freearc', 'arj' => 'application/x-arj', 'asc' => 'application/pgp-signature', 'asf' => 'video/x-ms-asf', 'asm' => 'text/x-asm', 'aso' => 'application/vnd.accpac.simply.aso', 'asx' => 'video/x-ms-asf', 'atc' => 'application/vnd.acucorp', 'atom' => 'application/atom+xml', 'atomcat' => 'application/atomcat+xml', 'atomdeleted' => 'application/atomdeleted+xml', 'atomsvc' => 'application/atomsvc+xml', 'atx' => 'application/vnd.antix.game-component', 'au' => 'audio/x-au', 'avci' => 'image/avci', 'avcs' => 'image/avcs', 'avi' => 'video/x-msvideo', 'avif' => 'image/avif', 'aw' => 'application/applixware', 'azf' => 'application/vnd.airzip.filesecure.azf', 'azs' => 'application/vnd.airzip.filesecure.azs', 'azv' => 'image/vnd.airzip.accelerator.azv', 'azw' => 'application/vnd.amazon.ebook', 'b16' => 'image/vnd.pco.b16', 'bat' => 'application/x-msdownload', 'bcpio' => 'application/x-bcpio', 'bdf' => 'application/x-font-bdf', 'bdm' => 'application/vnd.syncml.dm+wbxml', 'bdoc' => 'application/x-bdoc', 'bed' => 'application/vnd.realvnc.bed', 'bh2' => 'application/vnd.fujitsu.oasysprs', 'bin' => 'application/octet-stream', 'blb' => 'application/x-blorb', 'blorb' => 'application/x-blorb', 'bmi' => 'application/vnd.bmi', 'bmml' => 'application/vnd.balsamiq.bmml+xml', 'bmp' => 'image/bmp', 'book' => 'application/vnd.framemaker', 'box' => 'application/vnd.previewsystems.box', 'boz' => 'application/x-bzip2', 'bpk' => 'application/octet-stream', 'bpmn' => 'application/octet-stream', 'bsp' => 'model/vnd.valve.source.compiled-map', 'btf' => 'image/prs.btif', 'btif' => 'image/prs.btif', 'buffer' => 'application/octet-stream', 'bz' => 'application/x-bzip', 'bz2' => 'application/x-bzip2', 'c' => 'text/x-c', 'c4d' => 'application/vnd.clonk.c4group', 'c4f' => 'application/vnd.clonk.c4group', 'c4g' => 'application/vnd.clonk.c4group', 'c4p' => 'application/vnd.clonk.c4group', 'c4u' => 'application/vnd.clonk.c4group', 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', 'cab' => 'application/vnd.ms-cab-compressed', 'caf' => 'audio/x-caf', 'cap' => 'application/vnd.tcpdump.pcap', 'car' => 'application/vnd.curl.car', 'cat' => 'application/vnd.ms-pki.seccat', 'cb7' => 'application/x-cbr', 'cba' => 'application/x-cbr', 'cbr' => 'application/x-cbr', 'cbt' => 'application/x-cbr', 'cbz' => 'application/x-cbr', 'cc' => 'text/x-c', 'cco' => 'application/x-cocoa', 'cct' => 'application/x-director', 'ccxml' => 'application/ccxml+xml', 'cdbcmsg' => 'application/vnd.contact.cmsg', 'cdf' => 'application/x-netcdf', 'cdfx' => 'application/cdfx+xml', 'cdkey' => 'application/vnd.mediastation.cdkey', 'cdmia' => 'application/cdmi-capability', 'cdmic' => 'application/cdmi-container', 'cdmid' => 'application/cdmi-domain', 'cdmio' => 'application/cdmi-object', 'cdmiq' => 'application/cdmi-queue', 'cdr' => 'application/cdr', 'cdx' => 'chemical/x-cdx', 'cdxml' => 'application/vnd.chemdraw+xml', 'cdy' => 'application/vnd.cinderella', 'cer' => 'application/pkix-cert', 'cfs' => 'application/x-cfs-compressed', 'cgm' => 'image/cgm', 'chat' => 'application/x-chat', 'chm' => 'application/vnd.ms-htmlhelp', 'chrt' => 'application/vnd.kde.kchart', 'cif' => 'chemical/x-cif', 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', 'cil' => 'application/vnd.ms-artgalry', 'cjs' => 'application/node', 'cla' => 'application/vnd.claymore', 'class' => 'application/octet-stream', 'cld' => 'model/vnd.cld', 'clkk' => 'application/vnd.crick.clicker.keyboard', 'clkp' => 'application/vnd.crick.clicker.palette', 'clkt' => 'application/vnd.crick.clicker.template', 'clkw' => 'application/vnd.crick.clicker.wordbank', 'clkx' => 'application/vnd.crick.clicker', 'clp' => 'application/x-msclip', 'cmc' => 'application/vnd.cosmocaller', 'cmdf' => 'chemical/x-cmdf', 'cml' => 'chemical/x-cml', 'cmp' => 'application/vnd.yellowriver-custom-menu', 'cmx' => 'image/x-cmx', 'cod' => 'application/vnd.rim.cod', 'coffee' => 'text/coffeescript', 'com' => 'application/x-msdownload', 'conf' => 'text/plain', 'cpio' => 'application/x-cpio', 'cpl' => 'application/cpl+xml', 'cpp' => 'text/x-c', 'cpt' => 'application/mac-compactpro', 'crd' => 'application/x-mscardfile', 'crl' => 'application/pkix-crl', 'crt' => 'application/x-x509-ca-cert', 'crx' => 'application/x-chrome-extension', 'cryptonote' => 'application/vnd.rig.cryptonote', 'csh' => 'application/x-csh', 'csl' => 'application/vnd.citationstyles.style+xml', 'csml' => 'chemical/x-csml', 'csp' => 'application/vnd.commonspace', 'csr' => 'application/octet-stream', 'css' => 'text/css', 'cst' => 'application/x-director', 'csv' => 'text/csv', 'cu' => 'application/cu-seeme', 'curl' => 'text/vnd.curl', 'cwl' => 'application/cwl', 'cww' => 'application/prs.cww', 'cxt' => 'application/x-director', 'cxx' => 'text/x-c', 'dae' => 'model/vnd.collada+xml', 'daf' => 'application/vnd.mobius.daf', 'dart' => 'application/vnd.dart', 'dataless' => 'application/vnd.fdsn.seed', 'davmount' => 'application/davmount+xml', 'dbf' => 'application/vnd.dbf', 'dbk' => 'application/docbook+xml', 'dcr' => 'application/x-director', 'dcurl' => 'text/vnd.curl.dcurl', 'dd2' => 'application/vnd.oma.dd2+xml', 'ddd' => 'application/vnd.fujixerox.ddd', 'ddf' => 'application/vnd.syncml.dmddf+xml', 'dds' => 'image/vnd.ms-dds', 'deb' => 'application/x-debian-package', 'def' => 'text/plain', 'deploy' => 'application/octet-stream', 'der' => 'application/x-x509-ca-cert', 'dfac' => 'application/vnd.dreamfactory', 'dgc' => 'application/x-dgc-compressed', 'dib' => 'image/bmp', 'dic' => 'text/x-c', 'dir' => 'application/x-director', 'dis' => 'application/vnd.mobius.dis', 'disposition-notification' => 'message/disposition-notification', 'dist' => 'application/octet-stream', 'distz' => 'application/octet-stream', 'djv' => 'image/vnd.djvu', 'djvu' => 'image/vnd.djvu', 'dll' => 'application/octet-stream', 'dmg' => 'application/x-apple-diskimage', 'dmn' => 'application/octet-stream', 'dmp' => 'application/vnd.tcpdump.pcap', 'dms' => 'application/octet-stream', 'dna' => 'application/vnd.dna', 'doc' => 'application/msword', 'docm' => 'application/vnd.ms-word.template.macroEnabled.12', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dot' => 'application/msword', 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'dp' => 'application/vnd.osgi.dp', 'dpg' => 'application/vnd.dpgraph', 'dpx' => 'image/dpx', 'dra' => 'audio/vnd.dra', 'drle' => 'image/dicom-rle', 'dsc' => 'text/prs.lines.tag', 'dssc' => 'application/dssc+der', 'dtb' => 'application/x-dtbook+xml', 'dtd' => 'application/xml-dtd', 'dts' => 'audio/vnd.dts', 'dtshd' => 'audio/vnd.dts.hd', 'dump' => 'application/octet-stream', 'dvb' => 'video/vnd.dvb.file', 'dvi' => 'application/x-dvi', 'dwd' => 'application/atsc-dwd+xml', 'dwf' => 'model/vnd.dwf', 'dwg' => 'image/vnd.dwg', 'dxf' => 'image/vnd.dxf', 'dxp' => 'application/vnd.spotfire.dxp', 'dxr' => 'application/x-director', 'ear' => 'application/java-archive', 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', 'ecma' => 'application/ecmascript', 'edm' => 'application/vnd.novadigm.edm', 'edx' => 'application/vnd.novadigm.edx', 'efif' => 'application/vnd.picsel', 'ei6' => 'application/vnd.pg.osasli', 'elc' => 'application/octet-stream', 'emf' => 'image/emf', 'eml' => 'message/rfc822', 'emma' => 'application/emma+xml', 'emotionml' => 'application/emotionml+xml', 'emz' => 'application/x-msmetafile', 'eol' => 'audio/vnd.digital-winds', 'eot' => 'application/vnd.ms-fontobject', 'eps' => 'application/postscript', 'epub' => 'application/epub+zip', 'es3' => 'application/vnd.eszigno3+xml', 'esa' => 'application/vnd.osgi.subsystem', 'esf' => 'application/vnd.epson.esf', 'et3' => 'application/vnd.eszigno3+xml', 'etx' => 'text/x-setext', 'eva' => 'application/x-eva', 'evy' => 'application/x-envoy', 'exe' => 'application/octet-stream', 'exi' => 'application/exi', 'exp' => 'application/express', 'exr' => 'image/aces', 'ext' => 'application/vnd.novadigm.ext', 'ez' => 'application/andrew-inset', 'ez2' => 'application/vnd.ezpix-album', 'ez3' => 'application/vnd.ezpix-package', 'f' => 'text/x-fortran', 'f4v' => 'video/mp4', 'f77' => 'text/x-fortran', 'f90' => 'text/x-fortran', 'fbs' => 'image/vnd.fastbidsheet', 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', 'fcs' => 'application/vnd.isac.fcs', 'fdf' => 'application/vnd.fdf', 'fdt' => 'application/fdt+xml', 'fe_launch' => 'application/vnd.denovo.fcselayout-link', 'fg5' => 'application/vnd.fujitsu.oasysgp', 'fgd' => 'application/x-director', 'fh' => 'image/x-freehand', 'fh4' => 'image/x-freehand', 'fh5' => 'image/x-freehand', 'fh7' => 'image/x-freehand', 'fhc' => 'image/x-freehand', 'fig' => 'application/x-xfig', 'fits' => 'image/fits', 'flac' => 'audio/x-flac', 'fli' => 'video/x-fli', 'flo' => 'application/vnd.micrografx.flo', 'flv' => 'video/x-flv', 'flw' => 'application/vnd.kde.kivio', 'flx' => 'text/vnd.fmi.flexstor', 'fly' => 'text/vnd.fly', 'fm' => 'application/vnd.framemaker', 'fnc' => 'application/vnd.frogans.fnc', 'fo' => 'application/vnd.software602.filler.form+xml', 'for' => 'text/x-fortran', 'fpx' => 'image/vnd.fpx', 'frame' => 'application/vnd.framemaker', 'fsc' => 'application/vnd.fsc.weblaunch', 'fst' => 'image/vnd.fst', 'ftc' => 'application/vnd.fluxtime.clip', 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', 'fvt' => 'video/vnd.fvt', 'fxp' => 'application/vnd.adobe.fxp', 'fxpl' => 'application/vnd.adobe.fxp', 'fzs' => 'application/vnd.fuzzysheet', 'g2w' => 'application/vnd.geoplan', 'g3' => 'image/g3fax', 'g3w' => 'application/vnd.geospace', 'gac' => 'application/vnd.groove-account', 'gam' => 'application/x-tads', 'gbr' => 'application/rpki-ghostbusters', 'gca' => 'application/x-gca-compressed', 'gdl' => 'model/vnd.gdl', 'gdoc' => 'application/vnd.google-apps.document', 'ged' => 'text/vnd.familysearch.gedcom', 'geo' => 'application/vnd.dynageo', 'geojson' => 'application/geo+json', 'gex' => 'application/vnd.geometry-explorer', 'ggb' => 'application/vnd.geogebra.file', 'ggt' => 'application/vnd.geogebra.tool', 'ghf' => 'application/vnd.groove-help', 'gif' => 'image/gif', 'gim' => 'application/vnd.groove-identity-message', 'glb' => 'model/gltf-binary', 'gltf' => 'model/gltf+json', 'gml' => 'application/gml+xml', 'gmx' => 'application/vnd.gmx', 'gnumeric' => 'application/x-gnumeric', 'gpg' => 'application/gpg-keys', 'gph' => 'application/vnd.flographit', 'gpx' => 'application/gpx+xml', 'gqf' => 'application/vnd.grafeq', 'gqs' => 'application/vnd.grafeq', 'gram' => 'application/srgs', 'gramps' => 'application/x-gramps-xml', 'gre' => 'application/vnd.geometry-explorer', 'grv' => 'application/vnd.groove-injector', 'grxml' => 'application/srgs+xml', 'gsf' => 'application/x-font-ghostscript', 'gsheet' => 'application/vnd.google-apps.spreadsheet', 'gslides' => 'application/vnd.google-apps.presentation', 'gtar' => 'application/x-gtar', 'gtm' => 'application/vnd.groove-tool-message', 'gtw' => 'model/vnd.gtw', 'gv' => 'text/vnd.graphviz', 'gxf' => 'application/gxf', 'gxt' => 'application/vnd.geonext', 'gz' => 'application/gzip', 'gzip' => 'application/gzip', 'h' => 'text/x-c', 'h261' => 'video/h261', 'h263' => 'video/h263', 'h264' => 'video/h264', 'hal' => 'application/vnd.hal+xml', 'hbci' => 'application/vnd.hbci', 'hbs' => 'text/x-handlebars-template', 'hdd' => 'application/x-virtualbox-hdd', 'hdf' => 'application/x-hdf', 'heic' => 'image/heic', 'heics' => 'image/heic-sequence', 'heif' => 'image/heif', 'heifs' => 'image/heif-sequence', 'hej2' => 'image/hej2k', 'held' => 'application/atsc-held+xml', 'hh' => 'text/x-c', 'hjson' => 'application/hjson', 'hlp' => 'application/winhlp', 'hpgl' => 'application/vnd.hp-hpgl', 'hpid' => 'application/vnd.hp-hpid', 'hps' => 'application/vnd.hp-hps', 'hqx' => 'application/mac-binhex40', 'hsj2' => 'image/hsj2', 'htc' => 'text/x-component', 'htke' => 'application/vnd.kenameaapp', 'htm' => 'text/html', 'html' => 'text/html', 'hvd' => 'application/vnd.yamaha.hv-dic', 'hvp' => 'application/vnd.yamaha.hv-voice', 'hvs' => 'application/vnd.yamaha.hv-script', 'i2g' => 'application/vnd.intergeo', 'icc' => 'application/vnd.iccprofile', 'ice' => 'x-conference/x-cooltalk', 'icm' => 'application/vnd.iccprofile', 'ico' => 'image/x-icon', 'ics' => 'text/calendar', 'ief' => 'image/ief', 'ifb' => 'text/calendar', 'ifm' => 'application/vnd.shana.informed.formdata', 'iges' => 'model/iges', 'igl' => 'application/vnd.igloader', 'igm' => 'application/vnd.insors.igm', 'igs' => 'model/iges', 'igx' => 'application/vnd.micrografx.igx', 'iif' => 'application/vnd.shana.informed.interchange', 'img' => 'application/octet-stream', 'imp' => 'application/vnd.accpac.simply.imp', 'ims' => 'application/vnd.ms-ims', 'in' => 'text/plain', 'ini' => 'text/plain', 'ink' => 'application/inkml+xml', 'inkml' => 'application/inkml+xml', 'install' => 'application/x-install-instructions', 'iota' => 'application/vnd.astraea-software.iota', 'ipfix' => 'application/ipfix', 'ipk' => 'application/vnd.shana.informed.package', 'irm' => 'application/vnd.ibm.rights-management', 'irp' => 'application/vnd.irepository.package+xml', 'iso' => 'application/x-iso9660-image', 'itp' => 'application/vnd.shana.informed.formtemplate', 'its' => 'application/its+xml', 'ivp' => 'application/vnd.immervision-ivp', 'ivu' => 'application/vnd.immervision-ivu', 'jad' => 'text/vnd.sun.j2me.app-descriptor', 'jade' => 'text/jade', 'jam' => 'application/vnd.jam', 'jar' => 'application/java-archive', 'jardiff' => 'application/x-java-archive-diff', 'java' => 'text/x-java-source', 'jhc' => 'image/jphc', 'jisp' => 'application/vnd.jisp', 'jls' => 'image/jls', 'jlt' => 'application/vnd.hp-jlyt', 'jng' => 'image/x-jng', 'jnlp' => 'application/x-java-jnlp-file', 'joda' => 'application/vnd.joost.joda-archive', 'jp2' => 'image/jp2', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpf' => 'image/jpx', 'jpg' => 'image/jpeg', 'jpg2' => 'image/jp2', 'jpgm' => 'video/jpm', 'jpgv' => 'video/jpeg', 'jph' => 'image/jph', 'jpm' => 'video/jpm', 'jpx' => 'image/jpx', 'js' => 'application/javascript', 'json' => 'application/json', 'json5' => 'application/json5', 'jsonld' => 'application/ld+json', 'jsonml' => 'application/jsonml+json', 'jsx' => 'text/jsx', 'jt' => 'model/jt', 'jxr' => 'image/jxr', 'jxra' => 'image/jxra', 'jxrs' => 'image/jxrs', 'jxs' => 'image/jxs', 'jxsc' => 'image/jxsc', 'jxsi' => 'image/jxsi', 'jxss' => 'image/jxss', 'kar' => 'audio/midi', 'karbon' => 'application/vnd.kde.karbon', 'kdb' => 'application/octet-stream', 'kdbx' => 'application/x-keepass2', 'key' => 'application/x-iwork-keynote-sffkey', 'kfo' => 'application/vnd.kde.kformula', 'kia' => 'application/vnd.kidspiration', 'kml' => 'application/vnd.google-earth.kml+xml', 'kmz' => 'application/vnd.google-earth.kmz', 'kne' => 'application/vnd.kinar', 'knp' => 'application/vnd.kinar', 'kon' => 'application/vnd.kde.kontour', 'kpr' => 'application/vnd.kde.kpresenter', 'kpt' => 'application/vnd.kde.kpresenter', 'kpxx' => 'application/vnd.ds-keypoint', 'ksp' => 'application/vnd.kde.kspread', 'ktr' => 'application/vnd.kahootz', 'ktx' => 'image/ktx', 'ktx2' => 'image/ktx2', 'ktz' => 'application/vnd.kahootz', 'kwd' => 'application/vnd.kde.kword', 'kwt' => 'application/vnd.kde.kword', 'lasxml' => 'application/vnd.las.las+xml', 'latex' => 'application/x-latex', 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', 'les' => 'application/vnd.hhe.lesson-player', 'less' => 'text/less', 'lgr' => 'application/lgr+xml', 'lha' => 'application/octet-stream', 'link66' => 'application/vnd.route66.link66+xml', 'list' => 'text/plain', 'list3820' => 'application/vnd.ibm.modcap', 'listafp' => 'application/vnd.ibm.modcap', 'litcoffee' => 'text/coffeescript', 'lnk' => 'application/x-ms-shortcut', 'log' => 'text/plain', 'lostxml' => 'application/lost+xml', 'lrf' => 'application/octet-stream', 'lrm' => 'application/vnd.ms-lrm', 'ltf' => 'application/vnd.frogans.ltf', 'lua' => 'text/x-lua', 'luac' => 'application/x-lua-bytecode', 'lvp' => 'audio/vnd.lucent.voice', 'lwp' => 'application/vnd.lotus-wordpro', 'lzh' => 'application/octet-stream', 'm1v' => 'video/mpeg', 'm2a' => 'audio/mpeg', 'm2v' => 'video/mpeg', 'm3a' => 'audio/mpeg', 'm3u' => 'text/plain', 'm3u8' => 'application/vnd.apple.mpegurl', 'm4a' => 'audio/x-m4a', 'm4p' => 'application/mp4', 'm4s' => 'video/iso.segment', 'm4u' => 'application/vnd.mpegurl', 'm4v' => 'video/x-m4v', 'm13' => 'application/x-msmediaview', 'm14' => 'application/x-msmediaview', 'm21' => 'application/mp21', 'ma' => 'application/mathematica', 'mads' => 'application/mads+xml', 'maei' => 'application/mmt-aei+xml', 'mag' => 'application/vnd.ecowin.chart', 'maker' => 'application/vnd.framemaker', 'man' => 'text/troff', 'manifest' => 'text/cache-manifest', 'map' => 'application/json', 'mar' => 'application/octet-stream', 'markdown' => 'text/markdown', 'mathml' => 'application/mathml+xml', 'mb' => 'application/mathematica', 'mbk' => 'application/vnd.mobius.mbk', 'mbox' => 'application/mbox', 'mc1' => 'application/vnd.medcalcdata', 'mcd' => 'application/vnd.mcd', 'mcurl' => 'text/vnd.curl.mcurl', 'md' => 'text/markdown', 'mdb' => 'application/x-msaccess', 'mdi' => 'image/vnd.ms-modi', 'mdx' => 'text/mdx', 'me' => 'text/troff', 'mesh' => 'model/mesh', 'meta4' => 'application/metalink4+xml', 'metalink' => 'application/metalink+xml', 'mets' => 'application/mets+xml', 'mfm' => 'application/vnd.mfmp', 'mft' => 'application/rpki-manifest', 'mgp' => 'application/vnd.osgeo.mapguide.package', 'mgz' => 'application/vnd.proteus.magazine', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mie' => 'application/x-mie', 'mif' => 'application/vnd.mif', 'mime' => 'message/rfc822', 'mj2' => 'video/mj2', 'mjp2' => 'video/mj2', 'mjs' => 'text/javascript', 'mk3d' => 'video/x-matroska', 'mka' => 'audio/x-matroska', 'mkd' => 'text/x-markdown', 'mks' => 'video/x-matroska', 'mkv' => 'video/x-matroska', 'mlp' => 'application/vnd.dolby.mlp', 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', 'mmf' => 'application/vnd.smaf', 'mml' => 'text/mathml', 'mmr' => 'image/vnd.fujixerox.edmics-mmr', 'mng' => 'video/x-mng', 'mny' => 'application/x-msmoney', 'mobi' => 'application/x-mobipocket-ebook', 'mods' => 'application/mods+xml', 'mov' => 'video/quicktime', 'movie' => 'video/x-sgi-movie', 'mp2' => 'audio/mpeg', 'mp2a' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 'mp4a' => 'audio/mp4', 'mp4s' => 'application/mp4', 'mp4v' => 'video/mp4', 'mp21' => 'application/mp21', 'mpc' => 'application/vnd.mophun.certificate', 'mpd' => 'application/dash+xml', 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mpf' => 'application/media-policy-dataset+xml', 'mpg' => 'video/mpeg', 'mpg4' => 'video/mp4', 'mpga' => 'audio/mpeg', 'mpkg' => 'application/vnd.apple.installer+xml', 'mpm' => 'application/vnd.blueice.multipass', 'mpn' => 'application/vnd.mophun.application', 'mpp' => 'application/vnd.ms-project', 'mpt' => 'application/vnd.ms-project', 'mpy' => 'application/vnd.ibm.minipay', 'mqy' => 'application/vnd.mobius.mqy', 'mrc' => 'application/marc', 'mrcx' => 'application/marcxml+xml', 'ms' => 'text/troff', 'mscml' => 'application/mediaservercontrol+xml', 'mseed' => 'application/vnd.fdsn.mseed', 'mseq' => 'application/vnd.mseq', 'msf' => 'application/vnd.epson.msf', 'msg' => 'application/vnd.ms-outlook', 'msh' => 'model/mesh', 'msi' => 'application/x-msdownload', 'msix' => 'application/msix', 'msixbundle' => 'application/msixbundle', 'msl' => 'application/vnd.mobius.msl', 'msm' => 'application/octet-stream', 'msp' => 'application/octet-stream', 'msty' => 'application/vnd.muvee.style', 'mtl' => 'model/mtl', 'mts' => 'model/vnd.mts', 'mus' => 'application/vnd.musician', 'musd' => 'application/mmt-usd+xml', 'musicxml' => 'application/vnd.recordare.musicxml+xml', 'mvb' => 'application/x-msmediaview', 'mvt' => 'application/vnd.mapbox-vector-tile', 'mwf' => 'application/vnd.mfer', 'mxf' => 'application/mxf', 'mxl' => 'application/vnd.recordare.musicxml', 'mxmf' => 'audio/mobile-xmf', 'mxml' => 'application/xv+xml', 'mxs' => 'application/vnd.triscape.mxs', 'mxu' => 'video/vnd.mpegurl', 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', 'n3' => 'text/n3', 'nb' => 'application/mathematica', 'nbp' => 'application/vnd.wolfram.player', 'nc' => 'application/x-netcdf', 'ncx' => 'application/x-dtbncx+xml', 'nfo' => 'text/x-nfo', 'ngdat' => 'application/vnd.nokia.n-gage.data', 'nitf' => 'application/vnd.nitf', 'nlu' => 'application/vnd.neurolanguage.nlu', 'nml' => 'application/vnd.enliven', 'nnd' => 'application/vnd.noblenet-directory', 'nns' => 'application/vnd.noblenet-sealer', 'nnw' => 'application/vnd.noblenet-web', 'npx' => 'image/vnd.net-fpx', 'nq' => 'application/n-quads', 'nsc' => 'application/x-conference', 'nsf' => 'application/vnd.lotus-notes', 'nt' => 'application/n-triples', 'ntf' => 'application/vnd.nitf', 'numbers' => 'application/x-iwork-numbers-sffnumbers', 'nzb' => 'application/x-nzb', 'oa2' => 'application/vnd.fujitsu.oasys2', 'oa3' => 'application/vnd.fujitsu.oasys3', 'oas' => 'application/vnd.fujitsu.oasys', 'obd' => 'application/x-msbinder', 'obgx' => 'application/vnd.openblox.game+xml', 'obj' => 'model/obj', 'oda' => 'application/oda', 'odb' => 'application/vnd.oasis.opendocument.database', 'odc' => 'application/vnd.oasis.opendocument.chart', 'odf' => 'application/vnd.oasis.opendocument.formula', 'odft' => 'application/vnd.oasis.opendocument.formula-template', 'odg' => 'application/vnd.oasis.opendocument.graphics', 'odi' => 'application/vnd.oasis.opendocument.image', 'odm' => 'application/vnd.oasis.opendocument.text-master', 'odp' => 'application/vnd.oasis.opendocument.presentation', 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', 'odt' => 'application/vnd.oasis.opendocument.text', 'oga' => 'audio/ogg', 'ogex' => 'model/vnd.opengex', 'ogg' => 'audio/ogg', 'ogv' => 'video/ogg', 'ogx' => 'application/ogg', 'omdoc' => 'application/omdoc+xml', 'onepkg' => 'application/onenote', 'onetmp' => 'application/onenote', 'onetoc' => 'application/onenote', 'onetoc2' => 'application/onenote', 'opf' => 'application/oebps-package+xml', 'opml' => 'text/x-opml', 'oprc' => 'application/vnd.palm', 'opus' => 'audio/ogg', 'org' => 'text/x-org', 'osf' => 'application/vnd.yamaha.openscoreformat', 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', 'osm' => 'application/vnd.openstreetmap.data+xml', 'otc' => 'application/vnd.oasis.opendocument.chart-template', 'otf' => 'font/otf', 'otg' => 'application/vnd.oasis.opendocument.graphics-template', 'oth' => 'application/vnd.oasis.opendocument.text-web', 'oti' => 'application/vnd.oasis.opendocument.image-template', 'otp' => 'application/vnd.oasis.opendocument.presentation-template', 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', 'ott' => 'application/vnd.oasis.opendocument.text-template', 'ova' => 'application/x-virtualbox-ova', 'ovf' => 'application/x-virtualbox-ovf', 'owl' => 'application/rdf+xml', 'oxps' => 'application/oxps', 'oxt' => 'application/vnd.openofficeorg.extension', 'p' => 'text/x-pascal', 'p7a' => 'application/x-pkcs7-signature', 'p7b' => 'application/x-pkcs7-certificates', 'p7c' => 'application/pkcs7-mime', 'p7m' => 'application/pkcs7-mime', 'p7r' => 'application/x-pkcs7-certreqresp', 'p7s' => 'application/pkcs7-signature', 'p8' => 'application/pkcs8', 'p10' => 'application/x-pkcs10', 'p12' => 'application/x-pkcs12', 'pac' => 'application/x-ns-proxy-autoconfig', 'pages' => 'application/x-iwork-pages-sffpages', 'pas' => 'text/x-pascal', 'paw' => 'application/vnd.pawaafile', 'pbd' => 'application/vnd.powerbuilder6', 'pbm' => 'image/x-portable-bitmap', 'pcap' => 'application/vnd.tcpdump.pcap', 'pcf' => 'application/x-font-pcf', 'pcl' => 'application/vnd.hp-pcl', 'pclxl' => 'application/vnd.hp-pclxl', 'pct' => 'image/x-pict', 'pcurl' => 'application/vnd.curl.pcurl', 'pcx' => 'image/x-pcx', 'pdb' => 'application/x-pilot', 'pde' => 'text/x-processing', 'pdf' => 'application/pdf', 'pem' => 'application/x-x509-user-cert', 'pfa' => 'application/x-font-type1', 'pfb' => 'application/x-font-type1', 'pfm' => 'application/x-font-type1', 'pfr' => 'application/font-tdpfr', 'pfx' => 'application/x-pkcs12', 'pgm' => 'image/x-portable-graymap', 'pgn' => 'application/x-chess-pgn', 'pgp' => 'application/pgp', 'phar' => 'application/octet-stream', 'php' => 'application/x-httpd-php', 'php3' => 'application/x-httpd-php', 'php4' => 'application/x-httpd-php', 'phps' => 'application/x-httpd-php-source', 'phtml' => 'application/x-httpd-php', 'pic' => 'image/x-pict', 'pkg' => 'application/octet-stream', 'pki' => 'application/pkixcmp', 'pkipath' => 'application/pkix-pkipath', 'pkpass' => 'application/vnd.apple.pkpass', 'pl' => 'application/x-perl', 'plb' => 'application/vnd.3gpp.pic-bw-large', 'plc' => 'application/vnd.mobius.plc', 'plf' => 'application/vnd.pocketlearn', 'pls' => 'application/pls+xml', 'pm' => 'application/x-perl', 'pml' => 'application/vnd.ctc-posml', 'png' => 'image/png', 'pnm' => 'image/x-portable-anymap', 'portpkg' => 'application/vnd.macports.portpkg', 'pot' => 'application/vnd.ms-powerpoint', 'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 'ppa' => 'application/vnd.ms-powerpoint', 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'ppd' => 'application/vnd.cups-ppd', 'ppm' => 'image/x-portable-pixmap', 'pps' => 'application/vnd.ms-powerpoint', 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'ppt' => 'application/powerpoint', 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'pqa' => 'application/vnd.palm', 'prc' => 'model/prc', 'pre' => 'application/vnd.lotus-freelance', 'prf' => 'application/pics-rules', 'provx' => 'application/provenance+xml', 'ps' => 'application/postscript', 'psb' => 'application/vnd.3gpp.pic-bw-small', 'psd' => 'application/x-photoshop', 'psf' => 'application/x-font-linux-psf', 'pskcxml' => 'application/pskc+xml', 'pti' => 'image/prs.pti', 'ptid' => 'application/vnd.pvi.ptid1', 'pub' => 'application/x-mspublisher', 'pvb' => 'application/vnd.3gpp.pic-bw-var', 'pwn' => 'application/vnd.3m.post-it-notes', 'pya' => 'audio/vnd.ms-playready.media.pya', 'pyo' => 'model/vnd.pytha.pyox', 'pyox' => 'model/vnd.pytha.pyox', 'pyv' => 'video/vnd.ms-playready.media.pyv', 'qam' => 'application/vnd.epson.quickanime', 'qbo' => 'application/vnd.intu.qbo', 'qfx' => 'application/vnd.intu.qfx', 'qps' => 'application/vnd.publishare-delta-tree', 'qt' => 'video/quicktime', 'qwd' => 'application/vnd.quark.quarkxpress', 'qwt' => 'application/vnd.quark.quarkxpress', 'qxb' => 'application/vnd.quark.quarkxpress', 'qxd' => 'application/vnd.quark.quarkxpress', 'qxl' => 'application/vnd.quark.quarkxpress', 'qxt' => 'application/vnd.quark.quarkxpress', 'ra' => 'audio/x-realaudio', 'ram' => 'audio/x-pn-realaudio', 'raml' => 'application/raml+yaml', 'rapd' => 'application/route-apd+xml', 'rar' => 'application/x-rar', 'ras' => 'image/x-cmu-raster', 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', 'rdf' => 'application/rdf+xml', 'rdz' => 'application/vnd.data-vision.rdz', 'relo' => 'application/p2p-overlay+xml', 'rep' => 'application/vnd.businessobjects', 'res' => 'application/x-dtbresource+xml', 'rgb' => 'image/x-rgb', 'rif' => 'application/reginfo+xml', 'rip' => 'audio/vnd.rip', 'ris' => 'application/x-research-info-systems', 'rl' => 'application/resource-lists+xml', 'rlc' => 'image/vnd.fujixerox.edmics-rlc', 'rld' => 'application/resource-lists-diff+xml', 'rm' => 'audio/x-pn-realaudio', 'rmi' => 'audio/midi', 'rmp' => 'audio/x-pn-realaudio-plugin', 'rms' => 'application/vnd.jcp.javame.midlet-rms', 'rmvb' => 'application/vnd.rn-realmedia-vbr', 'rnc' => 'application/relax-ng-compact-syntax', 'rng' => 'application/xml', 'roa' => 'application/rpki-roa', 'roff' => 'text/troff', 'rp9' => 'application/vnd.cloanto.rp9', 'rpm' => 'audio/x-pn-realaudio-plugin', 'rpss' => 'application/vnd.nokia.radio-presets', 'rpst' => 'application/vnd.nokia.radio-preset', 'rq' => 'application/sparql-query', 'rs' => 'application/rls-services+xml', 'rsa' => 'application/x-pkcs7', 'rsat' => 'application/atsc-rsat+xml', 'rsd' => 'application/rsd+xml', 'rsheet' => 'application/urc-ressheet+xml', 'rss' => 'application/rss+xml', 'rtf' => 'text/rtf', 'rtx' => 'text/richtext', 'run' => 'application/x-makeself', 'rusd' => 'application/route-usd+xml', 'rv' => 'video/vnd.rn-realvideo', 's' => 'text/x-asm', 's3m' => 'audio/s3m', 'saf' => 'application/vnd.yamaha.smaf-audio', 'sass' => 'text/x-sass', 'sbml' => 'application/sbml+xml', 'sc' => 'application/vnd.ibm.secure-container', 'scd' => 'application/x-msschedule', 'scm' => 'application/vnd.lotus-screencam', 'scq' => 'application/scvp-cv-request', 'scs' => 'application/scvp-cv-response', 'scss' => 'text/x-scss', 'scurl' => 'text/vnd.curl.scurl', 'sda' => 'application/vnd.stardivision.draw', 'sdc' => 'application/vnd.stardivision.calc', 'sdd' => 'application/vnd.stardivision.impress', 'sdkd' => 'application/vnd.solent.sdkm+xml', 'sdkm' => 'application/vnd.solent.sdkm+xml', 'sdp' => 'application/sdp', 'sdw' => 'application/vnd.stardivision.writer', 'sea' => 'application/octet-stream', 'see' => 'application/vnd.seemail', 'seed' => 'application/vnd.fdsn.seed', 'sema' => 'application/vnd.sema', 'semd' => 'application/vnd.semd', 'semf' => 'application/vnd.semf', 'senmlx' => 'application/senml+xml', 'sensmlx' => 'application/sensml+xml', 'ser' => 'application/java-serialized-object', 'setpay' => 'application/set-payment-initiation', 'setreg' => 'application/set-registration-initiation', 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', 'sfs' => 'application/vnd.spotfire.sfs', 'sfv' => 'text/x-sfv', 'sgi' => 'image/sgi', 'sgl' => 'application/vnd.stardivision.writer-global', 'sgm' => 'text/sgml', 'sgml' => 'text/sgml', 'sh' => 'application/x-sh', 'shar' => 'application/x-shar', 'shex' => 'text/shex', 'shf' => 'application/shf+xml', 'shtml' => 'text/html', 'sid' => 'image/x-mrsid-image', 'sieve' => 'application/sieve', 'sig' => 'application/pgp-signature', 'sil' => 'audio/silk', 'silo' => 'model/mesh', 'sis' => 'application/vnd.symbian.install', 'sisx' => 'application/vnd.symbian.install', 'sit' => 'application/x-stuffit', 'sitx' => 'application/x-stuffitx', 'siv' => 'application/sieve', 'skd' => 'application/vnd.koan', 'skm' => 'application/vnd.koan', 'skp' => 'application/vnd.koan', 'skt' => 'application/vnd.koan', 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 'slim' => 'text/slim', 'slm' => 'text/slim', 'sls' => 'application/route-s-tsid+xml', 'slt' => 'application/vnd.epson.salt', 'sm' => 'application/vnd.stepmania.stepchart', 'smf' => 'application/vnd.stardivision.math', 'smi' => 'application/smil', 'smil' => 'application/smil', 'smv' => 'video/x-smv', 'smzip' => 'application/vnd.stepmania.package', 'snd' => 'audio/basic', 'snf' => 'application/x-font-snf', 'so' => 'application/octet-stream', 'spc' => 'application/x-pkcs7-certificates', 'spdx' => 'text/spdx', 'spf' => 'application/vnd.yamaha.smaf-phrase', 'spl' => 'application/x-futuresplash', 'spot' => 'text/vnd.in3d.spot', 'spp' => 'application/scvp-vp-response', 'spq' => 'application/scvp-vp-request', 'spx' => 'audio/ogg', 'sql' => 'application/x-sql', 'src' => 'application/x-wais-source', 'srt' => 'application/x-subrip', 'sru' => 'application/sru+xml', 'srx' => 'application/sparql-results+xml', 'ssdl' => 'application/ssdl+xml', 'sse' => 'application/vnd.kodak-descriptor', 'ssf' => 'application/vnd.epson.ssf', 'ssml' => 'application/ssml+xml', 'sst' => 'application/octet-stream', 'st' => 'application/vnd.sailingtracker.track', 'stc' => 'application/vnd.sun.xml.calc.template', 'std' => 'application/vnd.sun.xml.draw.template', 'step' => 'application/STEP', 'stf' => 'application/vnd.wt.stf', 'sti' => 'application/vnd.sun.xml.impress.template', 'stk' => 'application/hyperstudio', 'stl' => 'model/stl', 'stp' => 'application/STEP', 'stpx' => 'model/step+xml', 'stpxz' => 'model/step-xml+zip', 'stpz' => 'model/step+zip', 'str' => 'application/vnd.pg.format', 'stw' => 'application/vnd.sun.xml.writer.template', 'styl' => 'text/stylus', 'stylus' => 'text/stylus', 'sub' => 'text/vnd.dvb.subtitle', 'sus' => 'application/vnd.sus-calendar', 'susp' => 'application/vnd.sus-calendar', 'sv4cpio' => 'application/x-sv4cpio', 'sv4crc' => 'application/x-sv4crc', 'svc' => 'application/vnd.dvb.service', 'svd' => 'application/vnd.svd', 'svg' => 'image/svg+xml', 'svgz' => 'image/svg+xml', 'swa' => 'application/x-director', 'swf' => 'application/x-shockwave-flash', 'swi' => 'application/vnd.aristanetworks.swi', 'swidtag' => 'application/swid+xml', 'sxc' => 'application/vnd.sun.xml.calc', 'sxd' => 'application/vnd.sun.xml.draw', 'sxg' => 'application/vnd.sun.xml.writer.global', 'sxi' => 'application/vnd.sun.xml.impress', 'sxm' => 'application/vnd.sun.xml.math', 'sxw' => 'application/vnd.sun.xml.writer', 't' => 'text/troff', 't3' => 'application/x-t3vm-image', 't38' => 'image/t38', 'taglet' => 'application/vnd.mynfc', 'tao' => 'application/vnd.tao.intent-module-archive', 'tap' => 'image/vnd.tencent.tap', 'tar' => 'application/x-tar', 'tcap' => 'application/vnd.3gpp2.tcap', 'tcl' => 'application/x-tcl', 'td' => 'application/urc-targetdesc+xml', 'teacher' => 'application/vnd.smart.teacher', 'tei' => 'application/tei+xml', 'teicorpus' => 'application/tei+xml', 'tex' => 'application/x-tex', 'texi' => 'application/x-texinfo', 'texinfo' => 'application/x-texinfo', 'text' => 'text/plain', 'tfi' => 'application/thraud+xml', 'tfm' => 'application/x-tex-tfm', 'tfx' => 'image/tiff-fx', 'tga' => 'image/x-tga', 'tgz' => 'application/x-tar', 'thmx' => 'application/vnd.ms-officetheme', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'tk' => 'application/x-tcl', 'tmo' => 'application/vnd.tmobile-livetv', 'toml' => 'application/toml', 'torrent' => 'application/x-bittorrent', 'tpl' => 'application/vnd.groove-tool-template', 'tpt' => 'application/vnd.trid.tpt', 'tr' => 'text/troff', 'tra' => 'application/vnd.trueapp', 'trig' => 'application/trig', 'trm' => 'application/x-msterminal', 'ts' => 'video/mp2t', 'tsd' => 'application/timestamped-data', 'tsv' => 'text/tab-separated-values', 'ttc' => 'font/collection', 'ttf' => 'font/ttf', 'ttl' => 'text/turtle', 'ttml' => 'application/ttml+xml', 'twd' => 'application/vnd.simtech-mindmapper', 'twds' => 'application/vnd.simtech-mindmapper', 'txd' => 'application/vnd.genomatix.tuxedo', 'txf' => 'application/vnd.mobius.txf', 'txt' => 'text/plain', 'u3d' => 'model/u3d', 'u8dsn' => 'message/global-delivery-status', 'u8hdr' => 'message/global-headers', 'u8mdn' => 'message/global-disposition-notification', 'u8msg' => 'message/global', 'u32' => 'application/x-authorware-bin', 'ubj' => 'application/ubjson', 'udeb' => 'application/x-debian-package', 'ufd' => 'application/vnd.ufdl', 'ufdl' => 'application/vnd.ufdl', 'ulx' => 'application/x-glulx', 'umj' => 'application/vnd.umajin', 'unityweb' => 'application/vnd.unity', 'uo' => 'application/vnd.uoml+xml', 'uoml' => 'application/vnd.uoml+xml', 'uri' => 'text/uri-list', 'uris' => 'text/uri-list', 'urls' => 'text/uri-list', 'usda' => 'model/vnd.usda', 'usdz' => 'model/vnd.usdz+zip', 'ustar' => 'application/x-ustar', 'utz' => 'application/vnd.uiq.theme', 'uu' => 'text/x-uuencode', 'uva' => 'audio/vnd.dece.audio', 'uvd' => 'application/vnd.dece.data', 'uvf' => 'application/vnd.dece.data', 'uvg' => 'image/vnd.dece.graphic', 'uvh' => 'video/vnd.dece.hd', 'uvi' => 'image/vnd.dece.graphic', 'uvm' => 'video/vnd.dece.mobile', 'uvp' => 'video/vnd.dece.pd', 'uvs' => 'video/vnd.dece.sd', 'uvt' => 'application/vnd.dece.ttml+xml', 'uvu' => 'video/vnd.uvvu.mp4', 'uvv' => 'video/vnd.dece.video', 'uvva' => 'audio/vnd.dece.audio', 'uvvd' => 'application/vnd.dece.data', 'uvvf' => 'application/vnd.dece.data', 'uvvg' => 'image/vnd.dece.graphic', 'uvvh' => 'video/vnd.dece.hd', 'uvvi' => 'image/vnd.dece.graphic', 'uvvm' => 'video/vnd.dece.mobile', 'uvvp' => 'video/vnd.dece.pd', 'uvvs' => 'video/vnd.dece.sd', 'uvvt' => 'application/vnd.dece.ttml+xml', 'uvvu' => 'video/vnd.uvvu.mp4', 'uvvv' => 'video/vnd.dece.video', 'uvvx' => 'application/vnd.dece.unspecified', 'uvvz' => 'application/vnd.dece.zip', 'uvx' => 'application/vnd.dece.unspecified', 'uvz' => 'application/vnd.dece.zip', 'vbox' => 'application/x-virtualbox-vbox', 'vbox-extpack' => 'application/x-virtualbox-vbox-extpack', 'vcard' => 'text/vcard', 'vcd' => 'application/x-cdlink', 'vcf' => 'text/x-vcard', 'vcg' => 'application/vnd.groove-vcard', 'vcs' => 'text/x-vcalendar', 'vcx' => 'application/vnd.vcx', 'vdi' => 'application/x-virtualbox-vdi', 'vds' => 'model/vnd.sap.vds', 'vhd' => 'application/x-virtualbox-vhd', 'vis' => 'application/vnd.visionary', 'viv' => 'video/vnd.vivo', 'vlc' => 'application/videolan', 'vmdk' => 'application/x-virtualbox-vmdk', 'vob' => 'video/x-ms-vob', 'vor' => 'application/vnd.stardivision.writer', 'vox' => 'application/x-authorware-bin', 'vrml' => 'model/vrml', 'vsd' => 'application/vnd.visio', 'vsf' => 'application/vnd.vsf', 'vss' => 'application/vnd.visio', 'vst' => 'application/vnd.visio', 'vsw' => 'application/vnd.visio', 'vtf' => 'image/vnd.valve.source.texture', 'vtt' => 'text/vtt', 'vtu' => 'model/vnd.vtu', 'vxml' => 'application/voicexml+xml', 'w3d' => 'application/x-director', 'wad' => 'application/x-doom', 'wadl' => 'application/vnd.sun.wadl+xml', 'war' => 'application/java-archive', 'wasm' => 'application/wasm', 'wav' => 'audio/x-wav', 'wax' => 'audio/x-ms-wax', 'wbmp' => 'image/vnd.wap.wbmp', 'wbs' => 'application/vnd.criticaltools.wbs+xml', 'wbxml' => 'application/wbxml', 'wcm' => 'application/vnd.ms-works', 'wdb' => 'application/vnd.ms-works', 'wdp' => 'image/vnd.ms-photo', 'weba' => 'audio/webm', 'webapp' => 'application/x-web-app-manifest+json', 'webm' => 'video/webm', 'webmanifest' => 'application/manifest+json', 'webp' => 'image/webp', 'wg' => 'application/vnd.pmi.widget', 'wgsl' => 'text/wgsl', 'wgt' => 'application/widget', 'wif' => 'application/watcherinfo+xml', 'wks' => 'application/vnd.ms-works', 'wm' => 'video/x-ms-wm', 'wma' => 'audio/x-ms-wma', 'wmd' => 'application/x-ms-wmd', 'wmf' => 'image/wmf', 'wml' => 'text/vnd.wap.wml', 'wmlc' => 'application/wmlc', 'wmls' => 'text/vnd.wap.wmlscript', 'wmlsc' => 'application/vnd.wap.wmlscriptc', 'wmv' => 'video/x-ms-wmv', 'wmx' => 'video/x-ms-wmx', 'wmz' => 'application/x-msmetafile', 'woff' => 'font/woff', 'woff2' => 'font/woff2', 'word' => 'application/msword', 'wpd' => 'application/vnd.wordperfect', 'wpl' => 'application/vnd.ms-wpl', 'wps' => 'application/vnd.ms-works', 'wqd' => 'application/vnd.wqd', 'wri' => 'application/x-mswrite', 'wrl' => 'model/vrml', 'wsc' => 'message/vnd.wfa.wsc', 'wsdl' => 'application/wsdl+xml', 'wspolicy' => 'application/wspolicy+xml', 'wtb' => 'application/vnd.webturbo', 'wvx' => 'video/x-ms-wvx', 'x3d' => 'model/x3d+xml', 'x3db' => 'model/x3d+fastinfoset', 'x3dbz' => 'model/x3d+binary', 'x3dv' => 'model/x3d-vrml', 'x3dvz' => 'model/x3d+vrml', 'x3dz' => 'model/x3d+xml', 'x32' => 'application/x-authorware-bin', 'x_b' => 'model/vnd.parasolid.transmit.binary', 'x_t' => 'model/vnd.parasolid.transmit.text', 'xaml' => 'application/xaml+xml', 'xap' => 'application/x-silverlight-app', 'xar' => 'application/vnd.xara', 'xav' => 'application/xcap-att+xml', 'xbap' => 'application/x-ms-xbap', 'xbd' => 'application/vnd.fujixerox.docuworks.binder', 'xbm' => 'image/x-xbitmap', 'xca' => 'application/xcap-caps+xml', 'xcs' => 'application/calendar+xml', 'xdf' => 'application/xcap-diff+xml', 'xdm' => 'application/vnd.syncml.dm+xml', 'xdp' => 'application/vnd.adobe.xdp+xml', 'xdssc' => 'application/dssc+xml', 'xdw' => 'application/vnd.fujixerox.docuworks', 'xel' => 'application/xcap-el+xml', 'xenc' => 'application/xenc+xml', 'xer' => 'application/patch-ops-error+xml', 'xfdf' => 'application/xfdf', 'xfdl' => 'application/vnd.xfdl', 'xht' => 'application/xhtml+xml', 'xhtm' => 'application/vnd.pwg-xhtml-print+xml', 'xhtml' => 'application/xhtml+xml', 'xhvml' => 'application/xv+xml', 'xif' => 'image/vnd.xiff', 'xl' => 'application/excel', 'xla' => 'application/vnd.ms-excel', 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'xlc' => 'application/vnd.ms-excel', 'xlf' => 'application/xliff+xml', 'xlm' => 'application/vnd.ms-excel', 'xls' => 'application/vnd.ms-excel', 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlt' => 'application/vnd.ms-excel', 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'xlw' => 'application/vnd.ms-excel', 'xm' => 'audio/xm', 'xml' => 'application/xml', 'xns' => 'application/xcap-ns+xml', 'xo' => 'application/vnd.olpc-sugar', 'xop' => 'application/xop+xml', 'xpi' => 'application/x-xpinstall', 'xpl' => 'application/xproc+xml', 'xpm' => 'image/x-xpixmap', 'xpr' => 'application/vnd.is-xpr', 'xps' => 'application/vnd.ms-xpsdocument', 'xpw' => 'application/vnd.intercon.formnet', 'xpx' => 'application/vnd.intercon.formnet', 'xsd' => 'application/xml', 'xsf' => 'application/prs.xsf+xml', 'xsl' => 'application/xml', 'xslt' => 'application/xslt+xml', 'xsm' => 'application/vnd.syncml+xml', 'xspf' => 'application/xspf+xml', 'xul' => 'application/vnd.mozilla.xul+xml', 'xvm' => 'application/xv+xml', 'xvml' => 'application/xv+xml', 'xwd' => 'image/x-xwindowdump', 'xyz' => 'chemical/x-xyz', 'xz' => 'application/x-xz', 'yaml' => 'text/yaml', 'yang' => 'application/yang', 'yin' => 'application/yin+xml', 'yml' => 'text/yaml', 'ymp' => 'text/x-suse-ymp', 'z' => 'application/x-compress', 'z1' => 'application/x-zmachine', 'z2' => 'application/x-zmachine', 'z3' => 'application/x-zmachine', 'z4' => 'application/x-zmachine', 'z5' => 'application/x-zmachine', 'z6' => 'application/x-zmachine', 'z7' => 'application/x-zmachine', 'z8' => 'application/x-zmachine', 'zaz' => 'application/vnd.zzazz.deck+xml', 'zip' => 'application/zip', 'zir' => 'application/vnd.zul', 'zirz' => 'application/vnd.zul', 'zmm' => 'application/vnd.handheld-entertainment+xml', 'zsh' => 'text/x-scriptzsh', ]; public static function fromFilename(string $filename): ?string { return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); } public static function fromExtension(string $extension): ?string { return self::MIME_TYPES[strtolower($extension)] ?? null; } } 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 (is_string($name) && 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, int $truncateAt = 120): ?string { $body = $message->getBody(); if (!$body->isSeekable() || !$body->isReadable()) { return null; } $size = $body->getSize(); if ($size === 0) { return null; } $body->rewind(); $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) !== 0) { return null; } return $summary; } public static function rewindBody(MessageInterface $message): void { $body = $message->getBody(); if ($body->tell()) { $body->rewind(); } } public static function parseMessage(string $message): array { 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'); } [$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'); } [$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(string $path, array $headers): string { $hostKey = array_filter(array_keys($headers), function ($k) { $k = (string) $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(string $message): RequestInterface { $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(string $message): ResponseInterface { $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], $parts[2] ?? null ); } } @,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m"; public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; } $v) { if (!in_array(strtolower((string) $k), $keys)) { $result[$k] = $v; } } return $result; } public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void { $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, int $maxLen = -1): string { $buffer = ''; if ($maxLen === -1) { while (!$stream->eof()) { $buf = $stream->read(1048576); if ($buf === '') { break; } $buffer .= $buf; } return $buffer; } $len = 0; while (!$stream->eof() && $len < $maxLen) { $buf = $stream->read($maxLen - $len); if ($buf === '') { break; } $buffer .= $buf; $len = strlen($buffer); } return $buffer; } public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string { $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, $rawOutput); $stream->seek($pos); return $out; } public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface { 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( $changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $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( $changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion() ); } public static function readLine(StreamInterface $stream, int $maxLength = null): string { $buffer = ''; $size = 0; while (!$stream->eof()) { if ('' === ($byte = $stream->read(1))) { return $buffer; } $buffer .= $byte; if ($byte === "\n" || ++$size === $maxLength - 1) { break; } } return $buffer; } public static function streamFor($resource = '', array $options = []): StreamInterface { if (is_scalar($resource)) { $stream = self::tryFopen('php://temp', 'r+'); if ($resource !== '') { fwrite($stream, (string) $resource); fseek($stream, 0); } return new Stream($stream, $options); } switch (gettype($resource)) { case 'resource': if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') { $stream = self::tryFopen('php://temp', 'w+'); stream_copy_to_stream($resource, $stream); 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 self::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(string $filename, string $mode) { $ex = null; set_error_handler(static function (int $errno, string $errstr) use ($filename, $mode, &$ex): bool { $ex = new \RuntimeException(sprintf( 'Unable to open "%s" using mode "%s": %s', $filename, $mode, $errstr )); 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 tryGetContents($stream): string { $ex = null; set_error_handler(static function (int $errno, string $errstr) use (&$ex): bool { $ex = new \RuntimeException(sprintf( 'Unable to read stream contents: %s', $errstr )); return true; }); try { $contents = stream_get_contents($stream); if ($contents === false) { $ex = new \RuntimeException('Unable to read stream contents'); } } catch (\Throwable $e) { $ex = new \RuntimeException(sprintf( 'Unable to read stream contents: %s', $e->getMessage() ), 0, $e); } restore_error_handler(); if ($ex) { throw $ex; } return $contents; } public static function uriFor($uri): UriInterface { if ($uri instanceof UriInterface) { return $uri; } if (is_string($uri)) { return new Uri($uri); } throw new \InvalidArgumentException('URI must be a string or UriInterface'); } } stream = $stream; $this->maxLength = $maxLength; } public function write($string): int { $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)); } } $v) { $k = $encoder((string) $k); if (!is_array($v)) { $qs .= $k; $v = is_bool($v) ? (int) $v : $v; if ($v !== null) { $qs .= '='.$encoder((string) $v); } $qs .= '&'; } else { foreach ($v as $vv) { $qs .= $k; $vv = is_bool($vv) ? (int) $vv : $vv; if ($vv !== null) { $qs .= '='.$encoder((string) $vv); } $qs .= '&'; } } } return $qs ? (string) substr($qs, 0, -1) : ''; } } 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): int { $port = $uri->getPort(); if (null !== $port) { return $port; } return 'https' === $uri->getScheme() ? 443 : 80; } private function __construct() { } } boundary = $boundary ?: bin2hex(random_bytes(20)); $this->stream = $this->createStream($elements); } public function getBoundary(): string { return $this->boundary; } public function isWritable(): bool { return false; } private function getHeaders(array $headers): string { $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 = []): StreamInterface { $stream = new AppendStream(); foreach ($elements as $element) { if (!is_array($element)) { throw new \UnexpectedValueException('An array is expected'); } $this->addElement($stream, $element); } $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n")); return $stream; } private function addElement(AppendStream $stream, array $element): void { 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 ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') { $element['filename'] = $uri; } } [$body, $headers] = $this->createElement( $element['name'], $element['contents'], $element['filename'] ?? null, $element['headers'] ?? [] ); $stream->addStream(Utils::streamFor($this->getHeaders($headers))); $stream->addStream($body); $stream->addStream(Utils::streamFor("\r\n")); } private function createElement(string $name, StreamInterface $stream, ?string $filename, array $headers): array { $disposition = self::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 = self::getHeader($headers, 'content-length'); if (!$length) { if ($length = $stream->getSize()) { $headers['Content-Length'] = (string) $length; } } $type = self::getHeader($headers, 'content-type'); if (!$type && ($filename === '0' || $filename)) { $headers['Content-Type'] = MimeType::fromFilename($filename) ?? 'application/octet-stream'; } return [$stream, $headers]; } private static function getHeader(array $headers, string $key): ?string { $lowercaseHeader = strtolower($key); foreach ($headers as $k => $v) { if (strtolower((string) $k) === $lowercaseHeader) { return $v; } } return null; } } stream = $stream; $this->setLimit($limit); $this->setOffset($offset); } public function eof(): bool { if ($this->stream->eof()) { return true; } if ($this->limit === -1) { return false; } return $this->stream->tell() >= $this->offset + $this->limit; } public function getSize(): ?int { 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): void { 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(): int { return $this->stream->tell() - $this->offset; } public function setOffset(int $offset): void { $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(int $limit): void { $this->limit = $limit; } public function read($length): string { 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 ''; } } 15 + 32]); $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource)); } } serverParams = $serverParams; parent::__construct($method, $uri, $headers, $body, $version); } public static function normalizeFiles(array $files): array { $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 = []): array { $normalizedFiles = []; foreach (array_keys($files['tmp_name']) as $key) { $spec = [ 'tmp_name' => $files['tmp_name'][$key], 'size' => $files['size'][$key] ?? null, 'error' => $files['error'][$key] ?? null, 'name' => $files['name'][$key] ?? null, 'type' => $files['type'][$key] ?? null, ]; $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); } return $normalizedFiles; } public static function fromGlobals(): ServerRequestInterface { $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(string $authority): array { $uri = 'http://'.$authority; $parts = parse_url($uri); if (false === $parts) { return [null, null]; } $host = $parts['host'] ?? null; $port = $parts['port'] ?? null; return [$host, $port]; } public static function getUriFromGlobals(): UriInterface { $uri = new Uri(''); $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); $hasPort = false; if (isset($_SERVER['HTTP_HOST'])) { [$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(): array { return $this->serverParams; } public function getUploadedFiles(): array { return $this->uploadedFiles; } public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface { $new = clone $this; $new->uploadedFiles = $uploadedFiles; return $new; } public function getCookieParams(): array { return $this->cookieParams; } public function withCookieParams(array $cookies): ServerRequestInterface { $new = clone $this; $new->cookieParams = $cookies; return $new; } public function getQueryParams(): array { return $this->queryParams; } public function withQueryParams(array $query): ServerRequestInterface { $new = clone $this; $new->queryParams = $query; return $new; } public function getParsedBody() { return $this->parsedBody; } public function withParsedBody($data): ServerRequestInterface { $new = clone $this; $new->parsedBody = $data; return $new; } public function getAttributes(): array { 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): ServerRequestInterface { $new = clone $this; $new->attributes[$attribute] = $value; return $new; } public function withoutAttribute($attribute): ServerRequestInterface { if (false === array_key_exists($attribute, $this->attributes)) { return $this; } $new = clone $this; unset($new->attributes[$attribute]); return $new; } } stream = $stream; } public function __get(string $name) { if ($name === 'stream') { $this->stream = $this->createStream(); return $this->stream; } throw new \UnexpectedValueException("$name not found on class"); } public function __toString(): string { try { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } public function getContents(): string { return Utils::copyToString($this); } public function __call(string $method, array $args) { $callable = [$this->stream, $method]; $result = ($callable)(...$args); return $result === $this->stream ? $this : $result; } public function close(): void { $this->stream->close(); } public function getMetadata($key = null) { return $this->stream->getMetadata($key); } public function detach() { return $this->stream->detach(); } public function getSize(): ?int { return $this->stream->getSize(); } public function eof(): bool { return $this->stream->eof(); } public function tell(): int { return $this->stream->tell(); } public function isReadable(): bool { return $this->stream->isReadable(); } public function isWritable(): bool { return $this->stream->isWritable(); } public function isSeekable(): bool { return $this->stream->isSeekable(); } public function rewind(): void { $this->seek(0); } public function seek($offset, $whence = SEEK_SET): void { $this->stream->seek($offset, $whence); } public function read($length): string { return $this->stream->read($length); } public function write($string): int { return $this->stream->write($string); } protected function createStream(): StreamInterface { throw new \BadMethodCallException('Not implemented'); } } filename = $filename; $this->mode = $mode; unset($this->stream); } protected function createStream(): StreamInterface { return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode)); } } methods = $methods; foreach ($methods as $name => $fn) { $this->{'_fn_'.$name} = $fn; } } public function __get(string $name): void { throw new \BadMethodCallException(str_replace('_fn_', '', $name) .'() is not implemented in the FnStream'); } public function __destruct() { if (isset($this->_fn_close)) { ($this->_fn_close)(); } } public function __wakeup(): void { 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) { $callable = [$stream, $diff]; $methods[$diff] = $callable; } return new self($methods); } public function __toString(): string { try { return ($this->_fn___toString)(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } public function close(): void { ($this->_fn_close)(); } public function detach() { return ($this->_fn_detach)(); } public function getSize(): ?int { return ($this->_fn_getSize)(); } public function tell(): int { return ($this->_fn_tell)(); } public function eof(): bool { return ($this->_fn_eof)(); } public function isSeekable(): bool { return ($this->_fn_isSeekable)(); } public function rewind(): void { ($this->_fn_rewind)(); } public function seek($offset, $whence = SEEK_SET): void { ($this->_fn_seek)($offset, $whence); } public function isWritable(): bool { return ($this->_fn_isWritable)(); } public function write($string): int { return ($this->_fn_write)($string); } public function isReadable(): bool { return ($this->_fn_isReadable)(); } public function read($length): string { return ($this->_fn_read)($length); } public function getContents(): string { return ($this->_fn_getContents)(); } public function getMetadata($key = null) { return ($this->_fn_getMetadata)($key); } } addStream($stream); } } public function __toString(): string { try { $this->rewind(); return $this->getContents(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); return ''; } } public function addStream(StreamInterface $stream): void { if (!$stream->isReadable()) { throw new \InvalidArgumentException('Each stream must be readable'); } if (!$stream->isSeekable()) { $this->seekable = false; } $this->streams[] = $stream; } public function getContents(): string { return Utils::copyToString($this); } public function close(): void { $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(): int { return $this->pos; } public function getSize(): ?int { $size = 0; foreach ($this->streams as $stream) { $s = $stream->getSize(); if ($s === null) { return null; } $size += $s; } return $size; } public function eof(): bool { return !$this->streams || ($this->current >= count($this->streams) - 1 && $this->streams[$this->current]->eof()); } public function rewind(): void { $this->seek(0); } public function seek($offset, $whence = SEEK_SET): void { 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): string { $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 === '') { $progressToNext = true; continue; } $buffer .= $result; $remaining = $length - strlen($buffer); } $this->pos += strlen($buffer); return $buffer; } public function isReadable(): bool { return true; } public function isWritable(): bool { return false; } public function isSeekable(): bool { return $this->seekable; } public function write($string): int { throw new \RuntimeException('Cannot write to an AppendStream'); } public function getMetadata($key = null) { return $key ? null : []; } } setError($errorStatus); $this->size = $size; $this->clientFilename = $clientFilename; $this->clientMediaType = $clientMediaType; if ($this->isOk()) { $this->setStreamOrFile($streamOrFile); } } private function setStreamOrFile($streamOrFile): void { 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(int $error): void { if (false === in_array($error, UploadedFile::ERRORS, true)) { throw new InvalidArgumentException( 'Invalid error status for UploadedFile' ); } $this->error = $error; } private static function isStringNotEmpty($param): bool { return is_string($param) && false === empty($param); } private function isOk(): bool { return $this->error === UPLOAD_ERR_OK; } public function isMoved(): bool { return $this->moved; } private function validateActive(): void { 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(): StreamInterface { $this->validateActive(); if ($this->stream instanceof StreamInterface) { return $this->stream; } $file = $this->file; return new LazyOpenStream($file, 'r+'); } public function moveTo($targetPath): void { $this->validateActive(); if (false === self::isStringNotEmpty($targetPath)) { throw new InvalidArgumentException( 'Invalid path provided for move operation; must be a non-empty string' ); } if ($this->file) { $this->moved = PHP_SAPI === '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(): ?int { return $this->size; } public function getError(): int { return $this->error; } public function getClientFilename(): ?string { return $this->clientFilename; } public function getClientMediaType(): ?string { return $this->clientMediaType; } } hwm = $hwm; } public function __toString(): string { return $this->getContents(); } public function getContents(): string { $buffer = $this->buffer; $this->buffer = ''; return $buffer; } public function close(): void { $this->buffer = ''; } public function detach() { $this->close(); return null; } public function getSize(): ?int { return strlen($this->buffer); } public function isReadable(): bool { return true; } public function isWritable(): bool { return true; } public function isSeekable(): bool { return false; } public function rewind(): void { $this->seek(0); } public function seek($offset, $whence = SEEK_SET): void { throw new \RuntimeException('Cannot seek a BufferStream'); } public function eof(): bool { return strlen($this->buffer) === 0; } public function tell(): int { throw new \RuntimeException('Cannot determine the position of a BufferStream'); } public function read($length): string { $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): int { $this->buffer .= $string; if (strlen($this->buffer) >= $this->hwm) { return 0; } return strlen($string); } public function getMetadata($key = null) { if ($key === 'hwm') { return $this->hwm; } return $key ? null : []; } } $read_write_timeout) { throw new \InvalidArgumentException('channel RPC timeout must not be greater than I/O read-write timeout'); } $io = new StreamIO( $host, $port, $connection_timeout, $read_write_timeout, $context, $keepalive, $heartbeat ); parent::__construct( $user, $password, $vhost, $insist, $login_method, $login_response, $locale, $io, $heartbeat, $connection_timeout, $channel_rpc_timeout, $config ); $this->construct_params = func_get_args(); } protected static function try_create_connection($host, $port, $user, $password, $vhost, $options) { $insist = isset($options['insist']) ? $options['insist'] : false; $login_method = isset($options['login_method']) ? $options['login_method'] : 'AMQPLAIN'; $login_response = isset($options['login_response']) ? $options['login_response'] : null; $locale = isset($options['locale']) ? $options['locale'] : 'en_US'; $connection_timeout = isset($options['connection_timeout']) ? $options['connection_timeout'] : 3.0; $read_write_timeout = isset($options['read_write_timeout']) ? $options['read_write_timeout'] : 3.0; $context = isset($options['context']) ? $options['context'] : null; $keepalive = isset($options['keepalive']) ? $options['keepalive'] : false; $heartbeat = isset($options['heartbeat']) ? $options['heartbeat'] : 60; $channel_rpc_timeout = isset($options['channel_rpc_timeout']) ? $options['channel_rpc_timeout'] : 0.0; return new static( $host, $port, $user, $password, $vhost, $insist, $login_method, $login_response, $locale, $connection_timeout, $read_write_timeout, $context, $keepalive, $heartbeat, $channel_rpc_timeout ); } } getIoType() === AMQPConnectionConfig::IO_TYPE_STREAM) { if ($config->isSecure()) { $connection = new AMQPSSLConnection( $config->getHost(), $config->getPort(), $config->getUser(), $config->getPassword(), $config->getVhost(), self::getSslOptions($config), [ 'insist' => $config->isInsist(), 'login_method' => $config->getLoginMethod(), 'login_response' => $config->getLoginResponse(), 'locale' => $config->getLocale(), 'connection_timeout' => $config->getConnectionTimeout(), 'read_write_timeout' => self::getReadWriteTimeout($config), 'keepalive' => $config->isKeepalive(), 'heartbeat' => $config->getHeartbeat(), ], $config ); } else { $connection = new AMQPStreamConnection( $config->getHost(), $config->getPort(), $config->getUser(), $config->getPassword(), $config->getVhost(), $config->isInsist(), $config->getLoginMethod(), $config->getLoginResponse(), $config->getLocale(), $config->getConnectionTimeout(), self::getReadWriteTimeout($config), $config->getStreamContext(), $config->isKeepalive(), $config->getHeartbeat(), $config->getChannelRPCTimeout(), $config ); } } else { if ($config->isSecure()) { throw new LogicException('The socket connection implementation does not support secure connections.'); } $connection = new AMQPSocketConnection( $config->getHost(), $config->getPort(), $config->getUser(), $config->getPassword(), $config->getVhost(), $config->isInsist(), $config->getLoginMethod(), $config->getLoginResponse(), $config->getLocale(), $config->getReadTimeout(), $config->isKeepalive(), $config->getWriteTimeout(), $config->getHeartbeat(), $config->getChannelRPCTimeout(), $config ); } return $connection; } private static function getReadWriteTimeout(AMQPConnectionConfig $config): float { return min($config->getReadTimeout(), $config->getWriteTimeout()); } private static function getSslOptions(AMQPConnectionConfig $config): array { return array_filter([ 'cafile' => $config->getSslCaCert(), 'capath' => $config->getSslCaPath(), 'local_cert' => $config->getSslCert(), 'local_pk' => $config->getSslKey(), 'verify_peer' => $config->getSslVerify(), 'verify_peer_name' => $config->getSslVerifyName(), 'passphrase' => $config->getSslPassPhrase(), 'ciphers' => $config->getSslCiphers(), 'security_level' => $config->getSslSecurityLevel(), 'crypto_method' => $config->getSslCryptoMethod(), ], static function ($value) { return null !== $value; }); } } ioType; } public function setIoType(string $ioType): void { if ($ioType !== self::IO_TYPE_STREAM && $ioType !== self::IO_TYPE_SOCKET) { throw new InvalidArgumentException('IO type can be either "stream" or "socket"'); } $this->ioType = $ioType; } public function isLazy(): bool { return $this->isLazy; } public function setIsLazy(bool $isLazy): void { $this->isLazy = $isLazy; } public function getHost(): string { return $this->host; } public function setHost(string $host): void { $this->host = $host; } public function getPort(): int { return $this->port; } public function setPort(int $port): void { if ($port <= 0) { throw new InvalidArgumentException('Port number must be greater than 0'); } $this->port = $port; } public function getUser(): string { return $this->user; } public function setUser(string $user): void { $this->user = $user; } public function getPassword(): string { return $this->password; } public function setPassword(string $password): void { $this->password = $password; } public function getVhost(): string { return $this->vhost; } public function setVhost(string $vhost): void { self::assertStringNotEmpty($vhost, 'vhost'); $this->vhost = $vhost; } public function isInsist(): bool { return $this->insist; } public function setInsist(bool $insist): void { $this->insist = $insist; } public function getLoginMethod(): string { return $this->loginMethod; } public function setLoginMethod(string $loginMethod): void { if ( $loginMethod !== self::AUTH_PLAIN && $loginMethod !== self::AUTH_AMQPPLAIN && $loginMethod !== self::AUTH_EXTERNAL ) { throw new InvalidArgumentException('Unknown login method: ' . $loginMethod); } if ($loginMethod === self::AUTH_EXTERNAL && (!empty($this->user) || !empty($this->password))) { throw new InvalidArgumentException('External auth method cannot be used together with user credentials.'); } $this->loginMethod = $loginMethod; } public function getLoginResponse(): ?string { return $this->loginResponse; } public function setLoginResponse(string $loginResponse): void { $this->loginResponse = $loginResponse; } public function getLocale(): string { return $this->locale; } public function setLocale(string $locale): void { self::assertStringNotEmpty($locale, 'locale'); $this->locale = $locale; } public function getConnectionTimeout(): float { return $this->connectionTimeout; } public function setConnectionTimeout(float $connectionTimeout): void { $this->connectionTimeout = $connectionTimeout; } public function getReadTimeout(): float { return $this->readTimeout; } public function setReadTimeout(float $readTimeout): void { self::assertGreaterOrEq($readTimeout, 0, 'read timeout'); $this->readTimeout = $readTimeout; } public function getWriteTimeout(): float { return $this->writeTimeout; } public function setWriteTimeout(float $writeTimeout): void { self::assertGreaterOrEq($writeTimeout, 0, 'write timeout'); $this->writeTimeout = $writeTimeout; } public function getChannelRPCTimeout(): float { return $this->channelRPCTimeout; } public function setChannelRPCTimeout(float $channelRPCTimeout): void { self::assertGreaterOrEq($channelRPCTimeout, 0, 'channel RPC timeout'); $this->channelRPCTimeout = $channelRPCTimeout; } public function getHeartbeat(): int { return $this->heartbeat; } public function setHeartbeat(int $heartbeat): void { self::assertGreaterOrEq($heartbeat, 0, 'heartbeat'); $this->heartbeat = $heartbeat; } public function isKeepalive(): bool { return $this->keepalive; } public function setKeepalive(bool $keepalive): void { $this->keepalive = $keepalive; } public function isSecure(): bool { return $this->isSecure; } public function setIsSecure(bool $isSecure): void { $this->isSecure = $isSecure; if ($this->isSecure) { $this->networkProtocol = 'tls'; $this->sslCryptoMethod = STREAM_CRYPTO_METHOD_ANY_CLIENT; } else { $this->networkProtocol = 'tcp'; $this->sslCryptoMethod = null; } } public function getNetworkProtocol(): string { return $this->networkProtocol; } public function setNetworkProtocol(string $networkProtocol): void { self::assertStringNotEmpty($networkProtocol, 'network protocol'); $this->networkProtocol = $networkProtocol; } public function getStreamContext() { return $this->streamContext; } public function setStreamContext($streamContext): void { if ($streamContext === null) { $this->streamContext = null; return; } if (!is_resource($streamContext) || get_resource_type($streamContext) !== 'stream-context') { throw new InvalidArgumentException('Resource must be valid stream context'); } $this->streamContext = $streamContext; } public function getSendBufferSize(): int { return $this->sendBufferSize; } public function setSendBufferSize(int $sendBufferSize): void { self::assertGreaterOrEq($sendBufferSize, 0, 'sendBufferSize'); $this->sendBufferSize = $sendBufferSize; } public function isSignalsDispatchEnabled(): bool { return $this->dispatchSignals; } public function enableSignalDispatch(bool $dispatchSignals): void { $this->dispatchSignals = $dispatchSignals; } public function getAMQPProtocol(): string { return $this->amqpProtocol; } public function setAMQPProtocol(string $protocol): void { if ($protocol !== Wire\Constants091::VERSION && $protocol !== Wire\Constants080::VERSION) { throw new InvalidArgumentException('AMQP protocol can be either "0.9.1" or "8.0"'); } $this->amqpProtocol = $protocol; } public function isProtocolStrictFieldsEnabled(): bool { return $this->protocolStrictFields; } public function setProtocolStrictFields(bool $protocolStrictFields): void { $this->protocolStrictFields = $protocolStrictFields; } public function getSslCaCert(): ?string { return $this->sslCaCert; } public function setSslCaCert(?string $sslCaCert): void { $this->sslCaCert = $sslCaCert; } public function getSslCaPath(): ?string { return $this->sslCaPath; } public function setSslCaPath(?string $sslCaPath): void { $this->sslCaPath = $sslCaPath; } public function getSslCert(): ?string { return $this->sslCert; } public function setSslCert(?string $sslCert): void { $this->sslCert = $sslCert; } public function getSslKey(): ?string { return $this->sslKey; } public function setSslKey(?string $sslKey): void { $this->sslKey = $sslKey; } public function getSslVerify(): ?bool { return $this->sslVerify; } public function setSslVerify(?bool $sslVerify): void { $this->sslVerify = $sslVerify; if (!$this->sslVerify) { $this->setSslVerifyName(false); } } public function getSslVerifyName(): ?bool { return $this->sslVerifyName; } public function setSslVerifyName(?bool $sslVerifyName): void { $this->sslVerifyName = $sslVerifyName; } public function getSslPassPhrase(): ?string { return $this->sslPassPhrase; } public function setSslPassPhrase(?string $sslPassPhrase): void { $this->sslPassPhrase = $sslPassPhrase; } public function getSslCiphers(): ?string { return $this->sslCiphers; } public function setSslCiphers(?string $sslCiphers): void { $this->sslCiphers = $sslCiphers; } public function getSslSecurityLevel(): ?int { return $this->sslSecurityLevel; } public function setSslSecurityLevel(?int $sslSecurityLevel): void { $this->sslSecurityLevel = $sslSecurityLevel; } public function getSslCryptoMethod(): ?int { return $this->sslCryptoMethod; } public function setSslCryptoMethod(?int $sslCryptoMethod): void { $this->sslCryptoMethod = $sslCryptoMethod; } public function isDebugPackets(): bool { return $this->debugPackets; } public function setDebugPackets(bool $debugPackets): void { $this->debugPackets = $debugPackets; } private static function assertStringNotEmpty($value, string $param): void { $value = trim($value); if (empty($value)) { throw new InvalidArgumentException(sprintf('Parameter "%s" must be non empty string', $param)); } } private static function assertGreaterOrEq($value, int $limit, string $param): void { if ($value < $limit) { throw new InvalidArgumentException(sprintf('Parameter "%s" must be greater than zero', $param)); } } public function getConnectionName(): string { return $this->connectionName; } public function setConnectionName(string $connectionName): void { $this->connectionName = $connectionName; } } 1) { throw new \RuntimeException('Lazy connection does not support multiple hosts'); } return parent::create_connection($hosts, $options); } } 1) { throw new \RuntimeException('Lazy connection does not support multiple hosts'); } return parent::create_connection($hosts, $options); } } isSupported()) { throw new AMQPRuntimeException('Signal-based heartbeat sender is unsupported'); } $this->connection = $connection; } public function __destruct() { $this->unregister(); } protected function isSupported(): bool { return extension_loaded('pcntl') && function_exists('pcntl_async_signals') && (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true); } abstract public function register(): void; abstract public function unregister(): void; protected function handleSignal(int $interval): void { if (!$this->connection) { return; } if (!$this->wasActive && $this->connection->isConnected()) { $this->wasActive = true; } if (!$this->wasActive) { return; } if (!$this->connection->isConnected()) { $this->unregister(); return; } if ($this->connection->isWriting()) { return; } if (time() > ($this->connection->getLastActivity() + $interval)) { $this->connection->checkHeartBeat(); } } } connection) { throw new AMQPRuntimeException('Unable to re-register heartbeat sender'); } $timeout = $this->connection->getHeartbeat(); if ($timeout > 0) { $interval = (int)ceil($timeout / 2); pcntl_async_signals(true); $this->registerListener($interval); pcntl_alarm($interval); } } public function unregister(): void { $this->connection = null; pcntl_signal(SIGALRM, SIG_IGN); } private function registerListener(int $interval): void { pcntl_signal(SIGALRM, function () use ($interval) { $this->handleSignal($interval); if ($this->connection) { pcntl_alarm($interval); } }, true); } } signal = $signal; } public function register(): void { if (!$this->connection) { throw new AMQPRuntimeException('Unable to re-register heartbeat sender'); } $timeout = $this->connection->getHeartbeat(); if ($timeout > 0) { $interval = (int)ceil($timeout / 2); $this->registerListener($interval); } } public function unregister(): void { $this->connection = null; pcntl_signal($this->signal, SIG_IGN); if ($this->childPid > 0) { posix_kill($this->childPid, SIGKILL); } $this->childPid = null; } private function registerListener(int $interval): void { pcntl_async_signals(true); $this->periodicAlarm($interval); pcntl_signal($this->signal, function () use ($interval) { $this->handleSignal($interval); }); } private function periodicAlarm(int $interval): void { $parent = getmypid(); $pid = pcntl_fork(); if(!$pid) { while (true){ sleep($interval); posix_kill($parent, $this->signal); } } else { $this->childPid = $pid; } } } 1) { throw new \RuntimeException('Lazy connection does not support multiple hosts'); } return parent::create_connection($hosts, $options); } } createSslContext($ssl_options); } parent::__construct( $host, $port, $user, $password, $vhost, isset($options['insist']) ? $options['insist'] : false, isset($options['login_method']) ? $options['login_method'] : 'AMQPLAIN', isset($options['login_response']) ? $options['login_response'] : null, isset($options['locale']) ? $options['locale'] : 'en_US', isset($options['connection_timeout']) ? $options['connection_timeout'] : 3, isset($options['read_write_timeout']) ? $options['read_write_timeout'] : 130, $ssl_context, isset($options['keepalive']) ? $options['keepalive'] : false, isset($options['heartbeat']) ? $options['heartbeat'] : 0, isset($options['channel_rpc_timeout']) ? $options['channel_rpc_timeout'] : 0.0, $config ); } public static function try_create_connection($host, $port, $user, $password, $vhost, $options) { $ssl_options = isset($options['ssl_options']) ? $options['ssl_options'] : []; return new static($host, $port, $user, $password, $vhost, $ssl_options, $options); } private function createSslContext($options) { $ssl_context = stream_context_create(); foreach ($options as $k => $v) { stream_context_set_option($ssl_context, 'ssl', $k, $v); } return $ssl_context; } } array('S', Package::NAME), 'platform' => array('S', 'PHP'), 'version' => array('S', Package::VERSION), 'information' => array('S', ''), 'copyright' => array('S', ''), 'capabilities' => array( 'F', array( 'authentication_failure_close' => array('t', true), 'publisher_confirms' => array('t', true), 'consumer_cancel_notify' => array('t', true), 'exchange_exchange_bindings' => array('t', true), 'basic.nack' => array('t', true), 'connection.blocked' => array('t', true) ) ) ); public $channels = array(); protected $version_major; protected $version_minor; protected $server_properties; protected $mechanisms; protected $locales; protected $wait_tune_ok; protected $known_hosts; protected $input; protected $vhost; protected $insist; protected $login_method; protected $login_response; protected $locale; protected $heartbeat; protected $last_frame; protected $channel_max = 65535; protected $frame_max = 131072; protected $construct_params; protected $close_on_destruct = true; protected $is_connected = false; protected $io; private $connection_block_handler; private $connection_unblock_handler; protected $connection_timeout; protected $config; private $prepare_content_cache = array(); private $prepare_content_cache_max_size = 100; private $channel_rpc_timeout; protected $blocked = false; protected $writing = false; public function __construct( $user, $password, $vhost = '/', $insist = false, $login_method = 'AMQPLAIN', $login_response = null, $locale = 'en_US', AbstractIO $io = null, $heartbeat = 0, $connection_timeout = 0, $channel_rpc_timeout = 0.0, ?AMQPConnectionConfig $config = null ) { if (is_null($io)) { throw new \InvalidArgumentException('Argument $io cannot be null'); } if ($config) { $this->config = clone $config; } $this->construct_params = func_get_args(); $this->vhost = $vhost; $this->insist = $insist; $this->login_method = $login_method; $this->locale = $locale; $this->io = $io; $this->heartbeat = max(0, (int)$heartbeat); $this->connection_timeout = $connection_timeout; $this->channel_rpc_timeout = $channel_rpc_timeout; if ($user && $password) { if ($login_method === 'PLAIN') { $this->login_response = sprintf("\0%s\0%s", $user, $password); } elseif ($login_method === 'AMQPLAIN') { $login_response = new AMQPWriter(); $login_response->write_table(array( 'LOGIN' => array('S', $user), 'PASSWORD' => array('S', $password) )); $responseValue = $login_response->getvalue(); $this->login_response = mb_substr($responseValue, 4, mb_strlen($responseValue, 'ASCII') - 4, 'ASCII'); } else { throw new \InvalidArgumentException('Unknown login method: ' . $login_method); } } elseif ($login_method === 'EXTERNAL') { $this->login_response = $login_response; } else { $this->login_response = null; } if ($this->connectOnConstruct()) { $this->connect(); } } protected function connect() { $this->blocked = false; try { while (!$this->isConnected()) { $this->setIsConnected(true); $this->io->connect(); $this->channels = array(); parent::__construct($this, 0); $this->input = new Wire\AMQPIOReader($this->io); $this->write($this->constants->getHeader()); $this->last_frame = microtime(true); $this->wait(array($this->waitHelper->get_wait('connection.start')), false, $this->connection_timeout); $this->x_start_ok( $this->getLibraryProperties(), $this->login_method, $this->login_response, $this->locale ); $this->wait_tune_ok = true; while ($this->wait_tune_ok) { $this->wait(array( $this->waitHelper->get_wait('connection.secure'), $this->waitHelper->get_wait('connection.tune') ), false, $this->connection_timeout); } $host = $this->x_open($this->vhost, '', $this->insist); if (!$host) { $this->io->reenableHeartbeat(); return null; } $this->setIsConnected(false); $this->closeChannels(); $this->close_socket(); } } catch (\Exception $e) { $this->setIsConnected(false); $this->closeChannels(); $this->close_input(); $this->close_socket(); throw $e; } } public function reconnect() { $this->safeClose(); $this->io->close(); $this->setIsConnected(false); $this->connect(); } public function __clone() { if ($this->config) { $this->config = clone $this->config; } call_user_func_array(array($this, '__construct'), $this->construct_params); } public function __destruct() { if ($this->close_on_destruct) { $this->safeClose(); } } protected function safeClose() { try { if (null !== $this->input) { $this->close(); } } catch (\Exception $e) { } } public function select(?int $sec, int $usec = 0): int { try { return $this->io->select($sec, $usec); } catch (AMQPConnectionClosedException $e) { $this->do_close(); throw $e; } catch (AMQPRuntimeException $e) { $this->setIsConnected(false); throw $e; } } public function set_close_on_destruct($close = true) { $this->close_on_destruct = (bool) $close; } protected function close_input() { $this->debug && $this->debug->debug_msg('closing input'); if (null !== $this->input) { $this->input->close(); $this->input = null; } } protected function close_socket() { $this->debug && $this->debug->debug_msg('closing socket'); $this->io->close(); } public function write($data) { $this->debug->debug_hexdump($data); try { $this->writing = true; $this->io->write($data); } catch (AMQPConnectionClosedException $e) { $this->do_close(); throw $e; } catch (AMQPRuntimeException $e) { $this->setIsConnected(false); throw $e; } finally { $this->writing = false; } } protected function do_close() { $this->frame_queue = new \SplQueue(); $this->method_queue = []; $this->setIsConnected(false); $this->close_input(); $this->close_socket(); } public function get_free_channel_id() { for ($i = 1; $i <= $this->channel_max; $i++) { if (!isset($this->channels[$i])) { return $i; } } throw new AMQPRuntimeException('No free channel ids'); } public function send_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt) { $this->prepare_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt); $this->write($pkt->getvalue()); } public function prepare_content($channel, $class_id, $weight, $body_size, $packed_properties, $body, $pkt) { $pkt = $pkt ?: new AMQPWriter(); $key_cache = sprintf( '%s|%s|%s|%s', $channel, $packed_properties, $class_id, $weight ); if (!isset($this->prepare_content_cache[$key_cache])) { $w = new AMQPWriter(); $w->write_octet(2); $w->write_short($channel); $w->write_long(mb_strlen($packed_properties, 'ASCII') + 12); $w->write_short($class_id); $w->write_short($weight); $this->prepare_content_cache[$key_cache] = $w->getvalue(); if (count($this->prepare_content_cache) > $this->prepare_content_cache_max_size) { reset($this->prepare_content_cache); $old_key = key($this->prepare_content_cache); unset($this->prepare_content_cache[$old_key]); } } $pkt->write($this->prepare_content_cache[$key_cache]); $pkt->write_longlong($body_size); $pkt->write($packed_properties); $pkt->write_octet(0xCE); $position = 0; $bodyLength = mb_strlen($body, 'ASCII'); while ($position < $bodyLength) { $payload = mb_substr($body, $position, $this->frame_max - 8, 'ASCII'); $position += $this->frame_max - 8; $pkt->write_octet(3); $pkt->write_short($channel); $pkt->write_long(mb_strlen($payload, 'ASCII')); $pkt->write($payload); $pkt->write_octet(0xCE); } return $pkt; } protected function send_channel_method_frame($channel, $method_sig, $args = '', $pkt = null) { $pkt = $this->prepare_channel_method_frame($channel, $method_sig, $args, $pkt); $this->write($pkt->getvalue()); $this->debug->debug_method_signature1($method_sig); } protected function prepare_channel_method_frame($channel, $method_sig, $args = '', $pkt = null) { if ($args instanceof AMQPWriter) { $args = $args->getvalue(); } $pkt = $pkt ?: new AMQPWriter(); $pkt->write_octet(1); $pkt->write_short($channel); $pkt->write_long(mb_strlen($args, 'ASCII') + 4); $pkt->write_short($method_sig[0]); $pkt->write_short($method_sig[1]); $pkt->write($args); $pkt->write_octet(0xCE); $this->debug->debug_method_signature1($method_sig); return $pkt; } protected function wait_frame($timeout = 0): Frame { if (null === $this->input) { $this->setIsConnected(false); throw new AMQPConnectionClosedException('Broken pipe or closed connection'); } $currentTimeout = $this->input->getTimeout(); $this->input->setTimeout($timeout); try { $header = $this->input->readFrameHeader(); $frame_type = $header['type']; if (!$this->constants->isFrameType($frame_type)) { throw new AMQPInvalidFrameException('Invalid frame type ' . $frame_type); } $size = $header['size']; $result = unpack('a' . $size . 'payload/Cch', $this->input->read(AMQPReader::OCTET + $size)); $ch = $result['ch']; $frame = new Frame($frame_type, $header['channel'], $size, $result['payload']); } catch (AMQPTimeoutException $e) { if ($this->input) { $this->input->setTimeout($currentTimeout); } throw $e; } catch (AMQPNoDataException $e) { if ($this->input) { $this->input->setTimeout($currentTimeout); } throw $e; } catch (AMQPConnectionClosedException $exception) { $this->do_close(); throw $exception; } finally { if ($this->input) { $this->input->setTimeout($currentTimeout); } } $this->input->setTimeout($currentTimeout); if ($ch !== Frame::END) { throw new AMQPInvalidFrameException(sprintf( 'Framing error, unexpected byte: %x', $ch )); } return $frame; } protected function wait_channel(int $channel_id, $timeout = 0): Frame { $_timeout = $timeout; while (true) { $start = microtime(true); try { $frame = $this->wait_frame($_timeout); } catch (AMQPTimeoutException $e) { if ( $this->heartbeat && $this->last_frame && microtime(true) - ($this->heartbeat * 2) > $this->last_frame ) { $this->debug->debug_msg('missed server heartbeat (at threshold * 2)'); $this->setIsConnected(false); throw new AMQPHeartbeatMissedException('Missed server heartbeat'); } throw $e; } $this->last_frame = microtime(true); $frame_channel = $frame->getChannel(); if ($frame_channel === 0 && $frame->isHeartbeat()) { $this->debug->debug_msg('received server heartbeat'); if ($_timeout > 0) { $_timeout -= $this->last_frame - $start; if ($_timeout <= 0) { throw new AMQPTimeoutException('Timeout waiting on channel'); } } continue; } if ($frame_channel === $channel_id) { return $frame; } if (isset($this->channels[$frame_channel])) { $this->channels[$frame_channel]->frame_queue->enqueue($frame); } if ($frame_channel === 0 && $frame->isMethod()) { $this->wait(); } } } public function channel($channel_id = null) { if (!$this->is_connected) { $this->connect(); } if (isset($this->channels[$channel_id])) { return $this->channels[$channel_id]; } $channel_id = $channel_id ?: $this->get_free_channel_id(); $ch = new AMQPChannel($this, $channel_id, true, $this->channel_rpc_timeout); $this->channels[$channel_id] = $ch; return $ch; } public function close($reply_code = 0, $reply_text = '', $method_sig = array(0, 0)) { $this->io->disableHeartbeat(); if (empty($this->protocolWriter) || !$this->isConnected()) { return null; } $result = null; try { $this->closeChannels(); list($class_id, $method_id, $args) = $this->protocolWriter->connectionClose( $reply_code, $reply_text, $method_sig[0], $method_sig[1] ); $this->send_method_frame(array($class_id, $method_id), $args); $result = $this->wait( array($this->waitHelper->get_wait('connection.close_ok')), false, $this->connection_timeout ); } catch (\Exception $exception) { $this->do_close(); throw $exception; } $this->setIsConnected(false); return $result; } protected function connection_close(AMQPReader $reader) { $code = (int)$reader->read_short(); $reason = $reader->read_shortstr(); $class = $reader->read_short(); $method = $reader->read_short(); $reason .= sprintf('(%s, %s)', $class, $method); $this->x_close_ok(); throw new AMQPConnectionClosedException($reason, $code); } protected function x_close_ok() { $this->send_method_frame( explode(',', $this->waitHelper->get_wait('connection.close_ok')) ); $this->do_close(); } protected function connection_close_ok() { $this->do_close(); } protected function x_open($virtual_host, $capabilities = '', $insist = false) { $args = new AMQPWriter(); $args->write_shortstr($virtual_host); $args->write_shortstr($capabilities); $args->write_bits(array($insist)); $this->send_method_frame(array(10, 40), $args); $wait = array( $this->waitHelper->get_wait('connection.open_ok') ); if ($this->protocolVersion === Wire\Constants080::VERSION) { $wait[] = $this->waitHelper->get_wait('connection.redirect'); } return $this->wait($wait, false, $this->connection_timeout); } protected function connection_open_ok($args) { $this->known_hosts = $args->read_shortstr(); $this->debug->debug_msg('Open OK! known_hosts: ' . $this->known_hosts); } protected function connection_redirect($args) { $host = $args->read_shortstr(); $this->known_hosts = $args->read_shortstr(); $this->debug->debug_msg(sprintf( 'Redirected to [%s], known_hosts [%s]', $host, $this->known_hosts )); return $host; } protected function connection_secure($args) { $args->read_longstr(); } protected function x_secure_ok($response) { $args = new AMQPWriter(); $args->write_longstr($response); $this->send_method_frame(array(10, 21), $args); } protected function connection_start($args) { $this->version_major = $args->read_octet(); $this->version_minor = $args->read_octet(); $this->server_properties = $args->read_table(); $this->mechanisms = explode(' ', $args->read_longstr()); $this->locales = explode(' ', $args->read_longstr()); $this->debug->debug_connection_start( $this->version_major, $this->version_minor, $this->server_properties, $this->mechanisms, $this->locales ); } protected function x_start_ok($clientProperties, $mechanism, $response, $locale) { $args = new AMQPWriter(); $args->write_table($clientProperties); $args->write_shortstr($mechanism); $args->write_longstr($response); $args->write_shortstr($locale); $this->send_method_frame(array(10, 11), $args); } protected function connection_tune($args) { $v = $args->read_short(); if ($v) { $this->channel_max = $v; } $v = $args->read_long(); if ($v) { $this->frame_max = (int)$v; } $v = $args->read_short(); if ($this->heartbeat > 0 && $v > 0) { $this->heartbeat = min($this->heartbeat, $v); } $this->x_tune_ok($this->channel_max, $this->frame_max, $this->heartbeat); $this->io->afterTune($this->heartbeat); } protected function x_tune_ok($channel_max, $frame_max, $heartbeat) { $args = new AMQPWriter(); $args->write_short($channel_max); $args->write_long($frame_max); $args->write_short($heartbeat); $this->send_method_frame(array(10, 31), $args); $this->wait_tune_ok = false; } public function getIO() { return $this->io; } public function checkHeartBeat() { $this->io->check_heartbeat(); } public function getLastActivity() { return $this->io->getLastActivity(); } public function getReadTimeout(): float { return $this->io->getReadTimeout(); } protected function connection_blocked(AMQPReader $args) { $this->blocked = true; $this->dispatch_to_handler($this->connection_block_handler, array($args->read_shortstr())); } protected function connection_unblocked() { $this->blocked = false; $this->dispatch_to_handler($this->connection_unblock_handler); } public function set_connection_block_handler($callback) { Assert::isCallable($callback); $this->connection_block_handler = $callback; } public function set_connection_unblock_handler($callback) { Assert::isCallable($callback); $this->connection_unblock_handler = $callback; } public function isConnected() { return $this->is_connected; } public function isBlocked() { return $this->blocked; } public function isWriting() { return $this->writing; } protected function setIsConnected($is_connected) { $this->is_connected = (bool) $is_connected; } protected function closeChannels() { foreach ($this->channels as $key => $channel) { if ($key === 0) { continue; } try { $channel->close(); } catch (\Exception $e) { } } } public function connectOnConstruct(): bool { if ($this->config) { return !$this->config->isLazy(); } return true; } public function getServerProperties() { return $this->server_properties; } public function getHeartbeat() { return $this->heartbeat; } public function getLibraryProperties() { $config = self::$LIBRARY_PROPERTIES; if ($this->config !== null) { $connectionName = $this->config->getConnectionName(); if ($connectionName !== '') { $config['connection_name'] = ['S', $connectionName]; } } return $config; } public static function create_connection($hosts, $options = array()) { if (!is_array($hosts) || count($hosts) < 1) { throw new \InvalidArgumentException( 'An array of hosts are required when attempting to create a connection' ); } foreach ($hosts as $hostdef) { self::validate_host($hostdef); $host = $hostdef['host']; $port = $hostdef['port']; $user = $hostdef['user']; $password = $hostdef['password']; $vhost = isset($hostdef['vhost']) ? $hostdef['vhost'] : '/'; try { $conn = static::try_create_connection($host, $port, $user, $password, $vhost, $options); return $conn; } catch (\Exception $e) { $latest_exception = $e; } } throw $latest_exception; } public static function validate_host($host) { if (!isset($host['host'])) { throw new \InvalidArgumentException("'host' key is required."); } if (!isset($host['port'])) { throw new \InvalidArgumentException("'port' key is required."); } if (!isset($host['user'])) { throw new \InvalidArgumentException("'user' key is required."); } if (!isset($host['password'])) { throw new \InvalidArgumentException("'password' key is required."); } } } $read_timeout) { throw new \InvalidArgumentException('channel RPC timeout must not be greater than I/O read timeout'); } $io = new SocketIO($host, $port, $read_timeout, $keepalive, $write_timeout, $heartbeat, $config); parent::__construct( $user, $password, $vhost, $insist, $login_method, $login_response, $locale, $io, $heartbeat, max($read_timeout, $write_timeout), $channel_rpc_timeout, $config ); } protected static function try_create_connection($host, $port, $user, $password, $vhost, $options) { $insist = isset($options['insist']) ? $options['insist'] : false; $login_method = isset($options['login_method']) ? $options['login_method'] : 'AMQPLAIN'; $login_response = isset($options['login_response']) ? $options['login_response'] : null; $locale = isset($options['locale']) ? $options['locale'] : 'en_US'; $read_timeout = isset($options['read_timeout']) ? $options['read_timeout'] : 3; $keepalive = isset($options['keepalive']) ? $options['keepalive'] : false; $write_timeout = isset($options['write_timeout']) ? $options['write_timeout'] : 3; $heartbeat = isset($options['heartbeat']) ? $options['heartbeat'] : 0; $channel_rpc_timeout = isset($options['channel_rpc_timeout']) ? $options['channel_rpc_timeout'] : 0.0; return new static( $host, $port, $user, $password, $vhost, $insist, $login_method, $login_response, $locale, $read_timeout, $keepalive, $write_timeout, $heartbeat, $channel_rpc_timeout ); } } 'shortstr', 'content_encoding' => 'shortstr', 'application_headers' => 'table_object', 'delivery_mode' => 'octet', 'priority' => 'octet', 'correlation_id' => 'shortstr', 'reply_to' => 'shortstr', 'expiration' => 'shortstr', 'message_id' => 'shortstr', 'timestamp' => 'timestamp', 'type' => 'shortstr', 'user_id' => 'shortstr', 'app_id' => 'shortstr', 'cluster_id' => 'shortstr', ); public function __construct($body = '', $properties = array()) { $this->setBody($body); if (!empty($properties) && is_array($properties)) { $this->properties = array_intersect_key($properties, self::$propertyDefinitions); } } public function ack($multiple = false) { $this->assertUnacked(); $this->channel->basic_ack($this->deliveryTag, $multiple); $this->onResponse(); } public function nack($requeue = false, $multiple = false) { $this->assertUnacked(); $this->channel->basic_nack($this->deliveryTag, $multiple, $requeue); $this->onResponse(); } public function reject($requeue = true) { $this->assertUnacked(); $this->channel->basic_reject($this->deliveryTag, $requeue); $this->onResponse(); } protected function assertUnacked() { if (!$this->channel || $this->responded) { throw new \LogicException('Message is not published or response was already sent'); } } protected function onResponse() { $this->responded = true; } public function getChannel() { return $this->channel; } public function setChannel($channel) { if ($this->channel) { throw new \RuntimeException('A message is already assigned to channel'); } $this->channel = $channel; $this->delivery_info['channel'] = $channel; return $this; } public function setDeliveryInfo($deliveryTag, $redelivered, $exchange, $routingKey) { $this->deliveryTag = $this->delivery_info['delivery_tag'] = $deliveryTag; $this->redelivered = $this->delivery_info['redelivered'] = $redelivered; $this->exchange = $this->delivery_info['exchange'] = $exchange; $this->routingKey = $this->delivery_info['routing_key'] = $routingKey; return $this; } public function isRedelivered() { return $this->redelivered; } public function getExchange() { return $this->exchange; } public function getRoutingKey() { return $this->routingKey; } public function getConsumerTag() { return $this->consumerTag; } public function setConsumerTag($consumerTag) { $this->consumerTag = $consumerTag; $this->delivery_info['consumer_tag'] = $consumerTag; return $this; } public function getMessageCount() { return $this->messageCount; } public function setMessageCount($messageCount) { $this->messageCount = (int)$messageCount; $this->delivery_info['message_count'] = $this->messageCount; return $this; } public function getBody() { return $this->body; } public function setBody($body) { $this->body = $body; return $this; } public function getContentEncoding() { return $this->content_encoding; } public function getBodySize() { return $this->body_size; } public function setBodySize($body_size) { $this->body_size = (int)$body_size; return $this; } public function isTruncated() { return $this->is_truncated; } public function setIsTruncated($is_truncated) { $this->is_truncated = (bool)$is_truncated; return $this; } public function setDeliveryTag($deliveryTag) { if (!empty($this->deliveryTag)) { throw new \LogicException('Delivery tag cannot be changed'); } $this->deliveryTag = $deliveryTag; $this->delivery_info['delivery_tag'] = $deliveryTag; return $this; } public function getDeliveryTag() { if (empty($this->deliveryTag)) { throw new AMQPEmptyDeliveryTagException('This message was not delivered yet'); } return $this->deliveryTag; } public function has($name) { return isset($this->properties[$name]) || isset($this->delivery_info[$name]); } public function get($name) { if (isset($this->properties[$name])) { return $this->properties[$name]; } if (isset($this->delivery_info[$name])) { return $this->delivery_info[$name]; } throw new \OutOfBoundsException(sprintf( 'No "%s" property', $name )); } public function get_properties() { return $this->properties; } public function set($name, $value) { if (!array_key_exists($name, self::$propertyDefinitions)) { throw new \OutOfBoundsException(sprintf( 'No "%s" property', $name )); } if (isset($this->properties[$name]) && $this->properties[$name] === $value) { return; } $this->properties[$name] = $value; $this->serialized_properties = null; } public function load_properties(AMQPReader $reader) { $flags = array(); while (true) { $flag_bits = $reader->read_short(); $flags[] = $flag_bits; if (($flag_bits & 1) === 0) { break; } } $shift = 0; $data = array(); foreach (self::$propertyDefinitions as $key => $proptype) { if ($shift === 0) { if (!$flags) { break; } $flag_bits = array_shift($flags); $shift = 15; } if ($flag_bits & (1 << $shift)) { $data[$key] = $reader->{'read_' . $proptype}(); } $shift -= 1; } $this->properties = $data; return $this; } public function serialize_properties() { if (!empty($this->serialized_properties)) { return $this->serialized_properties; } $shift = 15; $flag_bits = 0; $flags = array(); $raw_bytes = new AMQPWriter(); foreach (self::$propertyDefinitions as $key => $prototype) { $val = isset($this->properties[$key]) ? $this->properties[$key] : null; if ($val === null) { $shift -= 1; continue; } if ($shift === 0) { $flags[] = $flag_bits; $flag_bits = 0; $shift = 15; } $flag_bits |= (1 << $shift); if ($prototype !== 'bit') { $raw_bytes->{'write_' . $prototype}($val); } $shift -= 1; } $flags[] = $flag_bits; $result = new AMQPWriter(); foreach ($flags as $flag_bits) { $result->write_short($flag_bits); } $result->write($raw_bytes->getvalue()); $this->serialized_properties = $result->getvalue(); return $this->serialized_properties; } } debug = defined('AMQP_DEBUG') ? AMQP_DEBUG : false; if (defined('AMQP_DEBUG_OUTPUT')) { $this->debug_output = AMQP_DEBUG_OUTPUT; } else { $this->debug_output = fopen('php://output', 'wb'); } $this->constants = $constants; } public function debug_msg($msg) { if ($this->debug) { $this->print_msg($msg); } } public function debug_allowed_methods($allowed_methods) { if ($this->debug) { if ($allowed_methods) { $msg = 'waiting for ' . implode(', ', $allowed_methods); } else { $msg = 'waiting for any method'; } $this->debug_msg($msg); } } public function debug_method_signature1($method_sig) { $this->debug_method_signature('< %s:', $method_sig); } public function debug_method_signature($msg, $method_sig) { if ($this->debug) { $constants = $this->constants; $methods = $constants::$GLOBAL_METHOD_NAMES; $key = MiscHelper::methodSig($method_sig); $this->debug_msg(sprintf($msg . ': %s', $key, $methods[$key])); } } public function debug_hexdump($data) { if ($this->debug) { $this->debug_msg( sprintf( '< [hex]: %s%s', PHP_EOL, MiscHelper::hexdump($data, $htmloutput = false, $uppercase = true, $return = true) ) ); } } public function debug_connection_start($version_major, $version_minor, $server_properties, $mechanisms, $locales) { if ($this->debug) { $this->debug_msg( sprintf( 'Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s', $version_major, $version_minor, MiscHelper::dump_table($server_properties), implode(', ', $mechanisms), implode(', ', $locales) ) ); } } protected function print_msg($s) { fwrite($this->debug_output, $s . PHP_EOL); } } write_octet($version_major); $writer->write_octet($version_minor); $writer->write_table(empty($server_properties) ? array() : $server_properties); $writer->write_longstr($mechanisms); $writer->write_longstr($locales); return array(10, 10, $writer); } public static function connectionStartOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_table(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_longstr(); $response[] = $reader->read_shortstr(); return $response; } public function connectionSecure($challenge) { $writer = new AMQPWriter(); $writer->write_longstr($challenge); return array(10, 20, $writer); } public static function connectionSecureOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longstr(); return $response; } public function connectionTune($channel_max = 0, $frame_max = 0, $heartbeat = 0) { $writer = new AMQPWriter(); $writer->write_short($channel_max); $writer->write_long($frame_max); $writer->write_short($heartbeat); return array(10, 30, $writer); } public static function connectionTuneOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_short(); $response[] = $reader->read_long(); $response[] = $reader->read_short(); return $response; } public function connectionOpen($virtual_host = '/', $capabilities = '', $insist = false) { $writer = new AMQPWriter(); $writer->write_shortstr($virtual_host); $writer->write_shortstr($capabilities); $writer->write_bits(array($insist)); return array(10, 40, $writer); } public static function connectionOpenOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function connectionClose($reply_code, $reply_text, $class_id, $method_id) { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_short($class_id); $writer->write_short($method_id); return array(10, 50, $writer); } public static function connectionCloseOk(AMQPReader $reader) { $response = array(); return $response; } public function connectionBlocked($reason = '') { $writer = new AMQPWriter(); $writer->write_shortstr($reason); return array(10, 60, $writer); } public static function connectionUnblocked(AMQPReader $reader) { $response = array(); return $response; } public function channelOpen($out_of_band = '') { $writer = new AMQPWriter(); $writer->write_shortstr($out_of_band); return array(20, 10, $writer); } public static function channelOpenOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longstr(); return $response; } public function channelFlow($active) { $writer = new AMQPWriter(); $writer->write_bits(array($active)); return array(20, 20, $writer); } public static function channelFlowOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_bit(); return $response; } public function channelClose($reply_code, $reply_text, $class_id, $method_id) { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_short($class_id); $writer->write_short($method_id); return array(20, 40, $writer); } public static function channelCloseOk(AMQPReader $reader) { $response = array(); return $response; } public function accessRequest( $realm = '/data', $exclusive = false, $passive = true, $active = true, $write = true, $read = true ) { $writer = new AMQPWriter(); $writer->write_shortstr($realm); $writer->write_bits(array($exclusive, $passive, $active, $write, $read)); return array(30, 10, $writer); } public static function accessRequestOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_short(); return $response; } public function exchangeDeclare( $ticket = 0, $exchange = '', $type = 'direct', $passive = false, $durable = false, $auto_delete = false, $internal = false, $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_shortstr($type); $writer->write_bits(array($passive, $durable, $auto_delete, $internal, $nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(40, 10, $writer); } public static function exchangeDeclareOk(AMQPReader $reader) { $response = array(); return $response; } public function exchangeDelete($ticket = 0, $exchange = '', $if_unused = false, $nowait = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_bits(array($if_unused, $nowait)); return array(40, 20, $writer); } public static function exchangeDeleteOk(AMQPReader $reader) { $response = array(); return $response; } public function exchangeBind( $ticket = 0, $destination = '', $source = '', $routing_key = '', $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($destination); $writer->write_shortstr($source); $writer->write_shortstr($routing_key); $writer->write_bits(array($nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(40, 30, $writer); } public static function exchangeBindOk(AMQPReader $reader) { $response = array(); return $response; } public function exchangeUnbind( $ticket = 0, $destination = '', $source = '', $routing_key = '', $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($destination); $writer->write_shortstr($source); $writer->write_shortstr($routing_key); $writer->write_bits(array($nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(40, 40, $writer); } public static function exchangeUnbindOk(AMQPReader $reader) { $response = array(); return $response; } public function queueDeclare( $ticket = 0, $queue = '', $passive = false, $durable = false, $exclusive = false, $auto_delete = false, $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($passive, $durable, $exclusive, $auto_delete, $nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(50, 10, $writer); } public static function queueDeclareOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_long(); $response[] = $reader->read_long(); return $response; } public function queueBind( $ticket = 0, $queue = '', $exchange = '', $routing_key = '', $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_bits(array($nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(50, 20, $writer); } public static function queueBindOk(AMQPReader $reader) { $response = array(); return $response; } public function queuePurge($ticket = 0, $queue = '', $nowait = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($nowait)); return array(50, 30, $writer); } public static function queuePurgeOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_long(); return $response; } public function queueDelete($ticket = 0, $queue = '', $if_unused = false, $if_empty = false, $nowait = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($if_unused, $if_empty, $nowait)); return array(50, 40, $writer); } public static function queueDeleteOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_long(); return $response; } public function queueUnbind($ticket = 0, $queue = '', $exchange = '', $routing_key = '', $arguments = array()) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_table(empty($arguments) ? array() : $arguments); return array(50, 50, $writer); } public static function queueUnbindOk(AMQPReader $reader) { $response = array(); return $response; } public function basicQos($prefetch_size = 0, $prefetch_count = 0, $global = false) { $writer = new AMQPWriter(); $writer->write_long($prefetch_size); $writer->write_short($prefetch_count); $writer->write_bits(array($global)); return array(60, 10, $writer); } public static function basicQosOk(AMQPReader $reader) { $response = array(); return $response; } public function basicConsume( $ticket = 0, $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($no_local, $no_ack, $exclusive, $nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(60, 20, $writer); } public static function basicConsumeOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function basicCancel($consumer_tag, $nowait = false) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($nowait)); return array(60, 30, $writer); } public static function basicCancelOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function basicPublish( $ticket = 0, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_bits(array($mandatory, $immediate)); return array(60, 40, $writer); } public function basicReturn($reply_code, $reply_text, $exchange, $routing_key) { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); return array(60, 50, $writer); } public function basicDeliver($consumer_tag, $delivery_tag, $redelivered, $exchange, $routing_key) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_longlong($delivery_tag); $writer->write_bits(array($redelivered)); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); return array(60, 60, $writer); } public function basicGet($ticket = 0, $queue = '', $no_ack = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($no_ack)); return array(60, 70, $writer); } public static function basicGetOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longlong(); $response[] = $reader->read_bit(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_long(); return $response; } public static function basicGetEmpty(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function basicAck($delivery_tag = 0, $multiple = false) { $writer = new AMQPWriter(); $writer->write_longlong($delivery_tag); $writer->write_bits(array($multiple)); return array(60, 80, $writer); } public function basicReject($delivery_tag, $requeue = true) { $writer = new AMQPWriter(); $writer->write_longlong($delivery_tag); $writer->write_bits(array($requeue)); return array(60, 90, $writer); } public function basicRecoverAsync($requeue = false) { $writer = new AMQPWriter(); $writer->write_bits(array($requeue)); return array(60, 100, $writer); } public function basicRecover($requeue = false) { $writer = new AMQPWriter(); $writer->write_bits(array($requeue)); return array(60, 110, $writer); } public static function basicRecoverOk(AMQPReader $reader) { $response = array(); return $response; } public function basicNack($delivery_tag = 0, $multiple = false, $requeue = true) { $writer = new AMQPWriter(); $writer->write_longlong($delivery_tag); $writer->write_bits(array($multiple, $requeue)); return array(60, 120, $writer); } public function txSelect() { $writer = new AMQPWriter(); return array(90, 10, $writer); } public static function txSelectOk(AMQPReader $reader) { $response = array(); return $response; } public function txCommit() { $writer = new AMQPWriter(); return array(90, 20, $writer); } public static function txCommitOk(AMQPReader $reader) { $response = array(); return $response; } public function txRollback() { $writer = new AMQPWriter(); return array(90, 30, $writer); } public static function txRollbackOk(AMQPReader $reader) { $response = array(); return $response; } public function confirmSelect($nowait = false) { $writer = new AMQPWriter(); $writer->write_bits(array($nowait)); return array(85, 10, $writer); } public static function confirmSelectOk(AMQPReader $reader) { $response = array(); return $response; } } write_octet($version_major); $writer->write_octet($version_minor); $writer->write_table(empty($server_properties) ? array() : $server_properties); $writer->write_longstr($mechanisms); $writer->write_longstr($locales); return array(10, 10, $writer); } public static function connectionStartOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_table(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_longstr(); $response[] = $reader->read_shortstr(); return $response; } public function connectionSecure($challenge) { $writer = new AMQPWriter(); $writer->write_longstr($challenge); return array(10, 20, $writer); } public static function connectionSecureOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longstr(); return $response; } public function connectionTune($channel_max = 0, $frame_max = 0, $heartbeat = 0) { $writer = new AMQPWriter(); $writer->write_short($channel_max); $writer->write_long($frame_max); $writer->write_short($heartbeat); return array(10, 30, $writer); } public static function connectionTuneOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_short(); $response[] = $reader->read_long(); $response[] = $reader->read_short(); return $response; } public function connectionOpen($virtual_host = '/', $capabilities = '', $insist = false) { $writer = new AMQPWriter(); $writer->write_shortstr($virtual_host); $writer->write_shortstr($capabilities); $writer->write_bits(array($insist)); return array(10, 40, $writer); } public static function connectionOpenOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function connectionRedirect($host, $known_hosts = '') { $writer = new AMQPWriter(); $writer->write_shortstr($host); $writer->write_shortstr($known_hosts); return array(10, 50, $writer); } public function connectionClose($reply_code, $reply_text, $class_id, $method_id) { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_short($class_id); $writer->write_short($method_id); return array(10, 60, $writer); } public static function connectionCloseOk(AMQPReader $reader) { $response = array(); return $response; } public function channelOpen($out_of_band = '') { $writer = new AMQPWriter(); $writer->write_shortstr($out_of_band); return array(20, 10, $writer); } public static function channelOpenOk(AMQPReader $reader) { $response = array(); return $response; } public function channelFlow($active) { $writer = new AMQPWriter(); $writer->write_bits(array($active)); return array(20, 20, $writer); } public static function channelFlowOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_bit(); return $response; } public function channelAlert($reply_code, $reply_text = '', $details = array()) { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_table(empty($details) ? array() : $details); return array(20, 30, $writer); } public function channelClose($reply_code, $reply_text, $class_id, $method_id) { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_short($class_id); $writer->write_short($method_id); return array(20, 40, $writer); } public static function channelCloseOk(AMQPReader $reader) { $response = array(); return $response; } public function accessRequest( $realm = '/data', $exclusive = false, $passive = true, $active = true, $write = true, $read = true ) { $writer = new AMQPWriter(); $writer->write_shortstr($realm); $writer->write_bits(array($exclusive, $passive, $active, $write, $read)); return array(30, 10, $writer); } public static function accessRequestOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_short(); return $response; } public function exchangeDeclare( $ticket = 1, $exchange = '', $type = 'direct', $passive = false, $durable = false, $auto_delete = false, $internal = false, $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_shortstr($type); $writer->write_bits(array($passive, $durable, $auto_delete, $internal, $nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(40, 10, $writer); } public static function exchangeDeclareOk(AMQPReader $reader) { $response = array(); return $response; } public function exchangeDelete($ticket = 1, $exchange = '', $if_unused = false, $nowait = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_bits(array($if_unused, $nowait)); return array(40, 20, $writer); } public static function exchangeDeleteOk(AMQPReader $reader) { $response = array(); return $response; } public function queueDeclare( $ticket = 1, $queue = '', $passive = false, $durable = false, $exclusive = false, $auto_delete = false, $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($passive, $durable, $exclusive, $auto_delete, $nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(50, 10, $writer); } public static function queueDeclareOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_long(); $response[] = $reader->read_long(); return $response; } public function queueBind( $ticket = 1, $queue = '', $exchange = '', $routing_key = '', $nowait = false, $arguments = array() ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_bits(array($nowait)); $writer->write_table(empty($arguments) ? array() : $arguments); return array(50, 20, $writer); } public static function queueBindOk(AMQPReader $reader) { $response = array(); return $response; } public function queuePurge($ticket = 1, $queue = '', $nowait = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($nowait)); return array(50, 30, $writer); } public static function queuePurgeOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_long(); return $response; } public function queueDelete($ticket = 1, $queue = '', $if_unused = false, $if_empty = false, $nowait = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($if_unused, $if_empty, $nowait)); return array(50, 40, $writer); } public static function queueDeleteOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_long(); return $response; } public function queueUnbind($ticket = 1, $queue = '', $exchange = '', $routing_key = '', $arguments = array()) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_table(empty($arguments) ? array() : $arguments); return array(50, 50, $writer); } public static function queueUnbindOk(AMQPReader $reader) { $response = array(); return $response; } public function basicQos($prefetch_size = 0, $prefetch_count = 0, $global = false) { $writer = new AMQPWriter(); $writer->write_long($prefetch_size); $writer->write_short($prefetch_count); $writer->write_bits(array($global)); return array(60, 10, $writer); } public static function basicQosOk(AMQPReader $reader) { $response = array(); return $response; } public function basicConsume( $ticket = 1, $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($no_local, $no_ack, $exclusive, $nowait)); return array(60, 20, $writer); } public static function basicConsumeOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function basicCancel($consumer_tag, $nowait = false) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($nowait)); return array(60, 30, $writer); } public static function basicCancelOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function basicPublish($ticket = 1, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_bits(array($mandatory, $immediate)); return array(60, 40, $writer); } public function basicReturn($reply_code, $reply_text, $exchange, $routing_key) { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); return array(60, 50, $writer); } public function basicDeliver($consumer_tag, $delivery_tag, $redelivered, $exchange, $routing_key) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_longlong($delivery_tag); $writer->write_bits(array($redelivered)); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); return array(60, 60, $writer); } public function basicGet($ticket = 1, $queue = '', $no_ack = false) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_bits(array($no_ack)); return array(60, 70, $writer); } public static function basicGetOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longlong(); $response[] = $reader->read_bit(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_shortstr(); $response[] = $reader->read_long(); return $response; } public static function basicGetEmpty(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function basicAck($delivery_tag = 0, $multiple = false) { $writer = new AMQPWriter(); $writer->write_longlong($delivery_tag); $writer->write_bits(array($multiple)); return array(60, 80, $writer); } public function basicReject($delivery_tag, $requeue = true) { $writer = new AMQPWriter(); $writer->write_longlong($delivery_tag); $writer->write_bits(array($requeue)); return array(60, 90, $writer); } public function basicRecoverAsync($requeue = false) { $writer = new AMQPWriter(); $writer->write_bits(array($requeue)); return array(60, 100, $writer); } public function basicRecover($requeue = false) { $writer = new AMQPWriter(); $writer->write_bits(array($requeue)); return array(60, 110, $writer); } public static function basicRecoverOk(AMQPReader $reader) { $response = array(); return $response; } public function fileQos($prefetch_size = 0, $prefetch_count = 0, $global = false) { $writer = new AMQPWriter(); $writer->write_long($prefetch_size); $writer->write_short($prefetch_count); $writer->write_bits(array($global)); return array(70, 10, $writer); } public static function fileQosOk(AMQPReader $reader) { $response = array(); return $response; } public function fileConsume( $ticket = 1, $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($no_local, $no_ack, $exclusive, $nowait)); return array(70, 20, $writer); } public static function fileConsumeOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function fileCancel($consumer_tag, $nowait = false) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($nowait)); return array(70, 30, $writer); } public static function fileCancelOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function fileOpen($identifier, $content_size) { $writer = new AMQPWriter(); $writer->write_shortstr($identifier); $writer->write_longlong($content_size); return array(70, 40, $writer); } public static function fileOpenOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longlong(); return $response; } public function fileStage() { $writer = new AMQPWriter(); return array(70, 50, $writer); } public function filePublish( $ticket = 1, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false, $identifier = '' ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_bits(array($mandatory, $immediate)); $writer->write_shortstr($identifier); return array(70, 60, $writer); } public function fileReturn($reply_code = 200, $reply_text = '', $exchange = '', $routing_key = '') { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); return array(70, 70, $writer); } public function fileDeliver( $consumer_tag, $delivery_tag, $redelivered, $exchange, $routing_key, $identifier ) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_longlong($delivery_tag); $writer->write_bits(array($redelivered)); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_shortstr($identifier); return array(70, 80, $writer); } public function fileAck($delivery_tag = 0, $multiple = false) { $writer = new AMQPWriter(); $writer->write_longlong($delivery_tag); $writer->write_bits(array($multiple)); return array(70, 90, $writer); } public function fileReject($delivery_tag, $requeue = true) { $writer = new AMQPWriter(); $writer->write_longlong($delivery_tag); $writer->write_bits(array($requeue)); return array(70, 100, $writer); } public function streamQos($prefetch_size = 0, $prefetch_count = 0, $consume_rate = 0, $global = false) { $writer = new AMQPWriter(); $writer->write_long($prefetch_size); $writer->write_short($prefetch_count); $writer->write_long($consume_rate); $writer->write_bits(array($global)); return array(80, 10, $writer); } public static function streamQosOk(AMQPReader $reader) { $response = array(); return $response; } public function streamConsume( $ticket = 1, $queue = '', $consumer_tag = '', $no_local = false, $exclusive = false, $nowait = false ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($queue); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($no_local, $exclusive, $nowait)); return array(80, 20, $writer); } public static function streamConsumeOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function streamCancel($consumer_tag, $nowait = false) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_bits(array($nowait)); return array(80, 30, $writer); } public static function streamCancelOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_shortstr(); return $response; } public function streamPublish( $ticket = 1, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false ) { $writer = new AMQPWriter(); $writer->write_short($ticket); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); $writer->write_bits(array($mandatory, $immediate)); return array(80, 40, $writer); } public function streamReturn($reply_code = 200, $reply_text = '', $exchange = '', $routing_key = '') { $writer = new AMQPWriter(); $writer->write_short($reply_code); $writer->write_shortstr($reply_text); $writer->write_shortstr($exchange); $writer->write_shortstr($routing_key); return array(80, 50, $writer); } public function streamDeliver($consumer_tag, $delivery_tag, $exchange, $queue) { $writer = new AMQPWriter(); $writer->write_shortstr($consumer_tag); $writer->write_longlong($delivery_tag); $writer->write_shortstr($exchange); $writer->write_shortstr($queue); return array(80, 60, $writer); } public function txSelect() { $writer = new AMQPWriter(); return array(90, 10, $writer); } public static function txSelectOk(AMQPReader $reader) { $response = array(); return $response; } public function txCommit() { $writer = new AMQPWriter(); return array(90, 20, $writer); } public static function txCommitOk(AMQPReader $reader) { $response = array(); return $response; } public function txRollback() { $writer = new AMQPWriter(); return array(90, 30, $writer); } public static function txRollbackOk(AMQPReader $reader) { $response = array(); return $response; } public function dtxSelect() { $writer = new AMQPWriter(); return array(100, 10, $writer); } public static function dtxSelectOk(AMQPReader $reader) { $response = array(); return $response; } public function dtxStart($dtx_identifier) { $writer = new AMQPWriter(); $writer->write_shortstr($dtx_identifier); return array(100, 20, $writer); } public static function dtxStartOk(AMQPReader $reader) { $response = array(); return $response; } public function tunnelRequest($meta_data) { $writer = new AMQPWriter(); $writer->write_table(empty($meta_data) ? array() : $meta_data); return array(110, 10, $writer); } public function testInteger($integer_1, $integer_2, $integer_3, $integer_4, $operation) { $writer = new AMQPWriter(); $writer->write_octet($integer_1); $writer->write_short($integer_2); $writer->write_long($integer_3); $writer->write_longlong($integer_4); $writer->write_octet($operation); return array(120, 10, $writer); } public static function testIntegerOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longlong(); return $response; } public function testString($string_1, $string_2, $operation) { $writer = new AMQPWriter(); $writer->write_shortstr($string_1); $writer->write_longstr($string_2); $writer->write_octet($operation); return array(120, 20, $writer); } public static function testStringOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longstr(); return $response; } public function testTable($table, $integer_op, $string_op) { $writer = new AMQPWriter(); $writer->write_table(empty($table) ? array() : $table); $writer->write_octet($integer_op); $writer->write_octet($string_op); return array(120, 30, $writer); } public static function testTableOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_longlong(); $response[] = $reader->read_longstr(); return $response; } public function testContent() { $writer = new AMQPWriter(); return array(120, 40, $writer); } public static function testContentOk(AMQPReader $reader) { $response = array(); $response[] = $reader->read_long(); return $response; } } '10,10', 'connection.start_ok' => '10,11', 'connection.secure' => '10,20', 'connection.secure_ok' => '10,21', 'connection.tune' => '10,30', 'connection.tune_ok' => '10,31', 'connection.open' => '10,40', 'connection.open_ok' => '10,41', 'connection.close' => '10,50', 'connection.close_ok' => '10,51', 'connection.blocked' => '10,60', 'connection.unblocked' => '10,61', 'channel.open' => '20,10', 'channel.open_ok' => '20,11', 'channel.flow' => '20,20', 'channel.flow_ok' => '20,21', 'channel.close' => '20,40', 'channel.close_ok' => '20,41', 'access.request' => '30,10', 'access.request_ok' => '30,11', 'exchange.declare' => '40,10', 'exchange.declare_ok' => '40,11', 'exchange.delete' => '40,20', 'exchange.delete_ok' => '40,21', 'exchange.bind' => '40,30', 'exchange.bind_ok' => '40,31', 'exchange.unbind' => '40,40', 'exchange.unbind_ok' => '40,51', 'queue.declare' => '50,10', 'queue.declare_ok' => '50,11', 'queue.bind' => '50,20', 'queue.bind_ok' => '50,21', 'queue.purge' => '50,30', 'queue.purge_ok' => '50,31', 'queue.delete' => '50,40', 'queue.delete_ok' => '50,41', 'queue.unbind' => '50,50', 'queue.unbind_ok' => '50,51', 'basic.qos' => '60,10', 'basic.qos_ok' => '60,11', 'basic.consume' => '60,20', 'basic.consume_ok' => '60,21', 'basic.cancel' => '60,30', 'basic.cancel_ok' => '60,31', 'basic.publish' => '60,40', 'basic.return' => '60,50', 'basic.deliver' => '60,60', 'basic.get' => '60,70', 'basic.get_ok' => '60,71', 'basic.get_empty' => '60,72', 'basic.ack' => '60,80', 'basic.reject' => '60,90', 'basic.recover_async' => '60,100', 'basic.recover' => '60,110', 'basic.recover_ok' => '60,111', 'basic.nack' => '60,120', 'tx.select' => '90,10', 'tx.select_ok' => '90,11', 'tx.commit' => '90,20', 'tx.commit_ok' => '90,21', 'tx.rollback' => '90,30', 'tx.rollback_ok' => '90,31', 'confirm.select' => '85,10', 'confirm.select_ok' => '85,11', ); public function get_wait($method) { return $this->wait[$method]; } } 'connection_start', '10,11' => 'connection_start_ok', '10,20' => 'connection_secure', '10,21' => 'connection_secure_ok', '10,30' => 'connection_tune', '10,31' => 'connection_tune_ok', '10,40' => 'connection_open', '10,41' => 'connection_open_ok', '10,50' => 'connection_redirect', '10,60' => 'connection_close', '10,61' => 'connection_close_ok', '20,10' => 'channel_open', '20,11' => 'channel_open_ok', '20,20' => 'channel_flow', '20,21' => 'channel_flow_ok', '20,30' => 'channel_alert', '20,40' => 'channel_close', '20,41' => 'channel_close_ok', '30,10' => 'access_request', '30,11' => 'access_request_ok', '40,10' => 'exchange_declare', '40,11' => 'exchange_declare_ok', '40,20' => 'exchange_delete', '40,21' => 'exchange_delete_ok', '50,10' => 'queue_declare', '50,11' => 'queue_declare_ok', '50,20' => 'queue_bind', '50,21' => 'queue_bind_ok', '50,30' => 'queue_purge', '50,31' => 'queue_purge_ok', '50,40' => 'queue_delete', '50,41' => 'queue_delete_ok', '50,50' => 'queue_unbind', '50,51' => 'queue_unbind_ok', '60,10' => 'basic_qos', '60,11' => 'basic_qos_ok', '60,20' => 'basic_consume', '60,21' => 'basic_consume_ok', '60,30' => 'basic_cancel', '60,31' => 'basic_cancel_ok', '60,40' => 'basic_publish', '60,50' => 'basic_return', '60,60' => 'basic_deliver', '60,70' => 'basic_get', '60,71' => 'basic_get_ok', '60,72' => 'basic_get_empty', '60,80' => 'basic_ack', '60,90' => 'basic_reject', '60,100' => 'basic_recover_async', '60,110' => 'basic_recover', '60,111' => 'basic_recover_ok', '70,10' => 'file_qos', '70,11' => 'file_qos_ok', '70,20' => 'file_consume', '70,21' => 'file_consume_ok', '70,30' => 'file_cancel', '70,31' => 'file_cancel_ok', '70,40' => 'file_open', '70,41' => 'file_open_ok', '70,50' => 'file_stage', '70,60' => 'file_publish', '70,70' => 'file_return', '70,80' => 'file_deliver', '70,90' => 'file_ack', '70,100' => 'file_reject', '80,10' => 'stream_qos', '80,11' => 'stream_qos_ok', '80,20' => 'stream_consume', '80,21' => 'stream_consume_ok', '80,30' => 'stream_cancel', '80,31' => 'stream_cancel_ok', '80,40' => 'stream_publish', '80,50' => 'stream_return', '80,60' => 'stream_deliver', '90,10' => 'tx_select', '90,11' => 'tx_select_ok', '90,20' => 'tx_commit', '90,21' => 'tx_commit_ok', '90,30' => 'tx_rollback', '90,31' => 'tx_rollback_ok', '100,10' => 'dtx_select', '100,11' => 'dtx_select_ok', '100,20' => 'dtx_start', '100,21' => 'dtx_start_ok', '110,10' => 'tunnel_request', '120,10' => 'test_integer', '120,11' => 'test_integer_ok', '120,20' => 'test_string', '120,21' => 'test_string_ok', '120,30' => 'test_table', '120,31' => 'test_table_ok', '120,40' => 'test_content', '120,41' => 'test_content_ok', ); public function get_method($method_sig) { return $this->method_map[$method_sig]; } public function valid_method($method_sig) { return array_key_exists($method_sig, $this->method_map); } } '10,10', 'connection.start_ok' => '10,11', 'connection.secure' => '10,20', 'connection.secure_ok' => '10,21', 'connection.tune' => '10,30', 'connection.tune_ok' => '10,31', 'connection.open' => '10,40', 'connection.open_ok' => '10,41', 'connection.redirect' => '10,50', 'connection.close' => '10,60', 'connection.close_ok' => '10,61', 'channel.open' => '20,10', 'channel.open_ok' => '20,11', 'channel.flow' => '20,20', 'channel.flow_ok' => '20,21', 'channel.alert' => '20,30', 'channel.close' => '20,40', 'channel.close_ok' => '20,41', 'access.request' => '30,10', 'access.request_ok' => '30,11', 'exchange.declare' => '40,10', 'exchange.declare_ok' => '40,11', 'exchange.delete' => '40,20', 'exchange.delete_ok' => '40,21', 'queue.declare' => '50,10', 'queue.declare_ok' => '50,11', 'queue.bind' => '50,20', 'queue.bind_ok' => '50,21', 'queue.purge' => '50,30', 'queue.purge_ok' => '50,31', 'queue.delete' => '50,40', 'queue.delete_ok' => '50,41', 'queue.unbind' => '50,50', 'queue.unbind_ok' => '50,51', 'basic.qos' => '60,10', 'basic.qos_ok' => '60,11', 'basic.consume' => '60,20', 'basic.consume_ok' => '60,21', 'basic.cancel' => '60,30', 'basic.cancel_ok' => '60,31', 'basic.publish' => '60,40', 'basic.return' => '60,50', 'basic.deliver' => '60,60', 'basic.get' => '60,70', 'basic.get_ok' => '60,71', 'basic.get_empty' => '60,72', 'basic.ack' => '60,80', 'basic.reject' => '60,90', 'basic.recover_async' => '60,100', 'basic.recover' => '60,110', 'basic.recover_ok' => '60,111', 'file.qos' => '70,10', 'file.qos_ok' => '70,11', 'file.consume' => '70,20', 'file.consume_ok' => '70,21', 'file.cancel' => '70,30', 'file.cancel_ok' => '70,31', 'file.open' => '70,40', 'file.open_ok' => '70,41', 'file.stage' => '70,50', 'file.publish' => '70,60', 'file.return' => '70,70', 'file.deliver' => '70,80', 'file.ack' => '70,90', 'file.reject' => '70,100', 'stream.qos' => '80,10', 'stream.qos_ok' => '80,11', 'stream.consume' => '80,20', 'stream.consume_ok' => '80,21', 'stream.cancel' => '80,30', 'stream.cancel_ok' => '80,31', 'stream.publish' => '80,40', 'stream.return' => '80,50', 'stream.deliver' => '80,60', 'tx.select' => '90,10', 'tx.select_ok' => '90,11', 'tx.commit' => '90,20', 'tx.commit_ok' => '90,21', 'tx.rollback' => '90,30', 'tx.rollback_ok' => '90,31', 'dtx.select' => '100,10', 'dtx.select_ok' => '100,11', 'dtx.start' => '100,20', 'dtx.start_ok' => '100,21', 'tunnel.request' => '110,10', 'test.integer' => '120,10', 'test.integer_ok' => '120,11', 'test.string' => '120,20', 'test.string_ok' => '120,21', 'test.table' => '120,30', 'test.table_ok' => '120,31', 'test.content' => '120,40', 'test.content_ok' => '120,41', ); public function get_wait($method) { return $this->wait[$method]; } } 'connection_start', '10,11' => 'connection_start_ok', '10,20' => 'connection_secure', '10,21' => 'connection_secure_ok', '10,30' => 'connection_tune', '10,31' => 'connection_tune_ok', '10,40' => 'connection_open', '10,41' => 'connection_open_ok', '10,50' => 'connection_close', '10,51' => 'connection_close_ok', '10,60' => 'connection_blocked', '10,61' => 'connection_unblocked', '20,10' => 'channel_open', '20,11' => 'channel_open_ok', '20,20' => 'channel_flow', '20,21' => 'channel_flow_ok', '20,40' => 'channel_close', '20,41' => 'channel_close_ok', '30,10' => 'access_request', '30,11' => 'access_request_ok', '40,10' => 'exchange_declare', '40,11' => 'exchange_declare_ok', '40,20' => 'exchange_delete', '40,21' => 'exchange_delete_ok', '40,30' => 'exchange_bind', '40,31' => 'exchange_bind_ok', '40,40' => 'exchange_unbind', '40,51' => 'exchange_unbind_ok', '50,10' => 'queue_declare', '50,11' => 'queue_declare_ok', '50,20' => 'queue_bind', '50,21' => 'queue_bind_ok', '50,30' => 'queue_purge', '50,31' => 'queue_purge_ok', '50,40' => 'queue_delete', '50,41' => 'queue_delete_ok', '50,50' => 'queue_unbind', '50,51' => 'queue_unbind_ok', '60,10' => 'basic_qos', '60,11' => 'basic_qos_ok', '60,20' => 'basic_consume', '60,21' => 'basic_consume_ok', '60,30' => 'basic_cancel_from_server', '60,31' => 'basic_cancel_ok', '60,40' => 'basic_publish', '60,50' => 'basic_return', '60,60' => 'basic_deliver', '60,70' => 'basic_get', '60,71' => 'basic_get_ok', '60,72' => 'basic_get_empty', '60,80' => 'basic_ack_from_server', '60,90' => 'basic_reject', '60,100' => 'basic_recover_async', '60,110' => 'basic_recover', '60,111' => 'basic_recover_ok', '60,120' => 'basic_nack_from_server', '90,10' => 'tx_select', '90,11' => 'tx_select_ok', '90,20' => 'tx_commit', '90,21' => 'tx_commit_ok', '90,30' => 'tx_rollback', '90,31' => 'tx_rollback_ok', '85,10' => 'confirm_select', '85,11' => 'confirm_select_ok', ); public function get_method($method_sig) { return $this->method_map[$method_sig]; } public function valid_method($method_sig) { return array_key_exists($method_sig, $this->method_map); } } ' : ''; $offset = 0; $len = mb_strlen($data, 'ASCII'); $hexFormat = $uppercase ? 'X' : 'x'; for ($i = $j = 0; $i < $len; $i++) { $hexi .= sprintf('%02' . $hexFormat . ' ', ord($data[$i])); if (ord($data[$i]) >= 32) { $ascii .= $htmloutput ? htmlentities($data[$i]) : $data[$i]; } else { $ascii .= '.'; } if ($j === 7) { $hexi .= ' '; $ascii .= ' '; } if (++$j === 16 || $i === $len - 1) { $dump .= sprintf('%04' . $hexFormat . ' %-49s %s', $offset, $hexi, $ascii); $hexi = $ascii = ''; $offset += 16; $j = 0; if ($i !== $len - 1) { $dump .= PHP_EOL; } } } $dump .= $htmloutput ? '' : ''; $dump .= PHP_EOL; if ($return) { return $dump; } echo $dump; return null; } public static function dump_table($table) { $tokens = array(); foreach ($table as $name => $value) { switch ($value[0]) { case 'D': $val = $value[1]->n . 'E' . $value[1]->e; break; case 'F': $val = '(' . self::dump_table($value[1]) . ')'; break; case 'T': $val = date('Y-m-d H:i:s', $value[1]); break; default: $val = $value[1]; } $tokens[] = $name . '=' . $val; } return implode(', ', $tokens); } } constants = $constants['sockets']; } else { trigger_error('Sockets extension is not enabled', E_USER_WARNING); $this->constants = array(); } } public function __get($name) { return isset($this->constants[$name]) ? $this->constants[$name] : 0; } public function __set($name, $value) { } public function __isset($name) { return isset($this->constants[$name]); } public static function getInstance() { if (!self::$instance) { self::$instance = new self(); } return self::$instance; } } 'FRAME-METHOD', 2 => 'FRAME-HEADER', 3 => 'FRAME-BODY', 8 => 'FRAME-HEARTBEAT', 4096 => 'FRAME-MIN-SIZE', 206 => 'FRAME-END', 501 => 'FRAME-ERROR', ); public static $CONTENT_METHODS = array( 0 => '60,40', 1 => '60,50', 2 => '60,60', 3 => '60,71', ); public static $CLOSE_METHODS = array( 0 => '10,50', 1 => '20,40', ); public static $GLOBAL_METHOD_NAMES = array( '10,10' => 'Connection.start', '10,11' => 'Connection.start_ok', '10,20' => 'Connection.secure', '10,21' => 'Connection.secure_ok', '10,30' => 'Connection.tune', '10,31' => 'Connection.tune_ok', '10,40' => 'Connection.open', '10,41' => 'Connection.open_ok', '10,50' => 'Connection.close', '10,51' => 'Connection.close_ok', '10,60' => 'Connection.blocked', '10,61' => 'Connection.unblocked', '20,10' => 'Channel.open', '20,11' => 'Channel.open_ok', '20,20' => 'Channel.flow', '20,21' => 'Channel.flow_ok', '20,40' => 'Channel.close', '20,41' => 'Channel.close_ok', '30,10' => 'Access.request', '30,11' => 'Access.request_ok', '40,10' => 'Exchange.declare', '40,11' => 'Exchange.declare_ok', '40,20' => 'Exchange.delete', '40,21' => 'Exchange.delete_ok', '40,30' => 'Exchange.bind', '40,31' => 'Exchange.bind_ok', '40,40' => 'Exchange.unbind', '40,51' => 'Exchange.unbind_ok', '50,10' => 'Queue.declare', '50,11' => 'Queue.declare_ok', '50,20' => 'Queue.bind', '50,21' => 'Queue.bind_ok', '50,30' => 'Queue.purge', '50,31' => 'Queue.purge_ok', '50,40' => 'Queue.delete', '50,41' => 'Queue.delete_ok', '50,50' => 'Queue.unbind', '50,51' => 'Queue.unbind_ok', '60,10' => 'Basic.qos', '60,11' => 'Basic.qos_ok', '60,20' => 'Basic.consume', '60,21' => 'Basic.consume_ok', '60,30' => 'Basic.cancel', '60,31' => 'Basic.cancel_ok', '60,40' => 'Basic.publish', '60,50' => 'Basic.return', '60,60' => 'Basic.deliver', '60,70' => 'Basic.get', '60,71' => 'Basic.get_ok', '60,72' => 'Basic.get_empty', '60,80' => 'Basic.ack', '60,90' => 'Basic.reject', '60,100' => 'Basic.recover_async', '60,110' => 'Basic.recover', '60,111' => 'Basic.recover_ok', '60,120' => 'Basic.nack', '90,10' => 'Tx.select', '90,11' => 'Tx.select_ok', '90,20' => 'Tx.commit', '90,21' => 'Tx.commit_ok', '90,30' => 'Tx.rollback', '90,31' => 'Tx.rollback_ok', '85,10' => 'Confirm.select', '85,11' => 'Confirm.select_ok', ); } n = $n; $this->e = $e; } public function asBCvalue() { $n = new BigInteger($this->n); $e = new BigInteger('1' . str_repeat('0', $this->e)); list($q) = $n->divide($e); return $q->toString(); } public function getE() { return $this->e; } public function getN() { return $this->n; } } io = $io; $this->timeout = $timeout; } public function close(): void { $this->io->close(); } public function getTimeout() { return $this->timeout; } public function setTimeout($timeout) { $this->timeout = $timeout; } protected function rawread(int $n): string { $res = ''; while (true) { $this->wait(); try { $res = $this->io->read($n); break; } catch (AMQPTimeoutException $e) { if ($this->getTimeout() > 0) { throw $e; } } } $this->offset += $n; return $res; } protected function wait(): void { $timeout = $this->timeout; if (null === $timeout) { $result = $this->io->select(0); if ($result === 0) { throw new AMQPNoDataException('No data is ready to read'); } return; } if (!($timeout > 0)) { $result = $this->io->select(null); if ($result === 0) { throw new AMQPNoDataException('No data is ready to read'); } return; } $leftTime = $timeout; $started = microtime(true); do { [$sec, $usec] = MiscHelper::splitSecondsMicroseconds($leftTime); $result = $this->io->select($sec, $usec); if ($result > 0) { return; } $leftTime = $timeout - (microtime(true) - $started); } while ($leftTime > 0); throw new AMQPTimeoutException(sprintf( 'The connection timed out after %s sec while awaiting incoming data', $timeout )); } } buffer = $buffer; $this->length = mb_strlen($buffer, 'ASCII'); } public function close(): void { } public function reset(string $str): void { $this->buffer = $str; $this->length = mb_strlen($this->buffer, 'ASCII'); $this->offset = 0; $this->resetCounters(); } protected function rawread(int $n): string { if ($this->length < $n) { throw new AMQPDataReadException(sprintf( 'Error reading data. Requested %s bytes while string buffer has only %s', $n, $this->length )); } $res = mb_substr($this->buffer, 0, $n, 'ASCII'); $this->buffer = mb_substr($this->buffer, $n, null, 'ASCII'); $this->length -= $n; $this->offset += $n; return $res; } } 128)) { throw new Exception\AMQPInvalidArgumentException( 'Table key must be non-empty string up to 128 chars in length' ); } $this->setValue($val, $type, $key); } } 'I', self::T_DECIMAL => 'D', self::T_TIMESTAMP => 'T', self::T_STRING_LONG => 'S', self::T_TABLE => 'F' ); private static $types_091 = array( self::T_INT_SHORTSHORT => 'b', self::T_INT_SHORTSHORT_U => 'B', self::T_INT_SHORT => 'U', self::T_INT_SHORT_U => 'u', self::T_INT_LONG => 'I', self::T_INT_LONG_U => 'i', self::T_INT_LONGLONG => 'L', self::T_INT_LONGLONG_U => 'l', self::T_FLOAT => 'f', self::T_DOUBLE => 'd', self::T_DECIMAL => 'D', self::T_TIMESTAMP => 'T', self::T_VOID => 'V', self::T_BOOL => 't', self::T_STRING_SHORT => 's', self::T_STRING_LONG => 'S', self::T_ARRAY => 'A', self::T_TABLE => 'F', self::T_BYTES => 'x', ); private static $types_rabbit = array( self::T_INT_SHORTSHORT => 'b', self::T_INT_SHORTSHORT_U => 'B', self::T_INT_SHORT => 's', self::T_INT_SHORT_U => 'u', self::T_INT_LONG => 'I', self::T_INT_LONG_U => 'i', self::T_INT_LONGLONG => 'l', self::T_FLOAT => 'f', self::T_DOUBLE => 'd', self::T_DECIMAL => 'D', self::T_TIMESTAMP => 'T', self::T_VOID => 'V', self::T_BOOL => 't', self::T_STRING_LONG => 'S', self::T_ARRAY => 'A', self::T_TABLE => 'F', self::T_BYTES => 'x', ); protected $data = array(); public function __construct(array $data = null) { if (!empty($data)) { $this->data = $this->encodeCollection($data); } } abstract public function getType(); final protected function setValue($val, $type = null, $key = null) { if ($val instanceof self) { if ($type && ($type !== $val->getType())) { throw new Exception\AMQPInvalidArgumentException( sprintf( 'Attempted to add instance of %s representing type [%s] as mismatching type [%s]', get_class($val), $val->getType(), $type ) ); } $type = $val->getType(); } elseif ($type) { switch ($type) { case self::T_ARRAY: throw new Exception\AMQPInvalidArgumentException('Arrays must be passed as AMQPArray instance'); case self::T_TABLE: throw new Exception\AMQPInvalidArgumentException('Tables must be passed as AMQPTable instance'); case self::T_DECIMAL: if (!($val instanceof AMQPDecimal)) { throw new Exception\AMQPInvalidArgumentException( 'Decimal values must be instance of AMQPDecimal' ); } break; } } if ($type) { self::checkDataTypeIsSupported($type, false); $val = array($type, $val); } else { $val = $this->encodeValue($val); } if ($key === null) { $this->data[] = $val; } else { $this->data[$key] = $val; } } final public function getNativeData() { return $this->decodeCollection($this->data); } final protected function encodeCollection(array $val) { foreach ($val as $k => $v) { $val[$k] = $this->encodeValue($v); } return $val; } final protected function decodeCollection(array $val) { foreach ($val as $k => $v) { $val[$k] = $this->decodeValue($v[1], $v[0]); } return $val; } public function offsetExists($offset): bool { return isset($this->data[$offset]); } #[\ReturnTypeWillChange] public function offsetGet($offset) { $value = isset($this->data[$offset]) ? $this->data[$offset] : null; return is_array($value) ? $value[1] : $value; } public function offsetSet($offset, $value): void { $this->setValue($value, null, $offset); } public function offsetUnset($offset): void { unset($this->data[$offset]); } protected function encodeValue($val) { if (is_string($val)) { $val = $this->encodeString($val); } elseif (is_float($val)) { $val = $this->encodeFloat($val); } elseif (is_int($val)) { $val = $this->encodeInt($val); } elseif (is_bool($val)) { $val = $this->encodeBool($val); } elseif (is_null($val)) { $val = $this->encodeVoid(); } elseif ($val instanceof \DateTimeInterface) { $val = array(self::T_TIMESTAMP, $val->getTimestamp()); } elseif ($val instanceof AMQPDecimal) { $val = array(self::T_DECIMAL, $val); } elseif ($val instanceof self) { self::checkDataTypeIsSupported($val->getType(), false); $val = array($val->getType(), $val); } elseif (is_array($val)) { if (self::isProtocol(Wire\Constants080::VERSION)) { $val = array(self::T_TABLE, new AMQPTable($val)); } elseif (empty($val) || (array_keys($val) === range(0, count($val) - 1))) { $val = array(self::T_ARRAY, new AMQPArray($val)); } else { $val = array(self::T_TABLE, new AMQPTable($val)); } } else { throw new Exception\AMQPOutOfBoundsException( sprintf('Encountered value of unsupported type: %s', gettype($val)) ); } return $val; } protected function decodeValue($val, $type) { if ($val instanceof self) { $val = $val->getNativeData(); } else { switch ($type) { case self::T_BOOL: $val = (bool) $val; break; case self::T_TIMESTAMP: $val = \DateTime::createFromFormat('U', $val); break; case self::T_VOID: $val = null; break; case self::T_ARRAY: case self::T_TABLE: throw new Exception\AMQPLogicException( sprintf( '%s %s', 'Encountered an array/table struct which is not an instance of AMQPCollection.', 'This is considered a bug and should be fixed, please report' ) ); } } return $val; } protected function encodeString($val) { return array(self::T_STRING_LONG, $val); } protected function encodeInt($val) { if (($val >= -2147483648) && ($val <= 2147483647)) { $ev = array(self::T_INT_LONG, $val); } elseif (self::isProtocol(Wire\Constants080::VERSION)) { $ev = $this->encodeString((string) $val); } else { $ev = array(self::T_INT_LONGLONG, $val); } return $ev; } protected function encodeFloat($val) { return $this->encodeString((string) $val); } protected function encodeBool($val) { $val = (bool) $val; return self::isProtocol(Wire\Constants080::VERSION) ? array(self::T_INT_LONG, (int) $val) : array(self::T_BOOL, $val); } protected function encodeVoid() { return self::isProtocol(Wire\Constants080::VERSION) ? $this->encodeString('') : array(self::T_VOID, null); } final public static function getProtocol() { if (self::$protocol === null) { self::$protocol = defined('AMQP_STRICT_FLD_TYPES') && AMQP_STRICT_FLD_TYPES ? AbstractChannel::getProtocolVersion() : self::PROTOCOL_RBT; } return self::$protocol; } final public static function isProtocol($proto) { return self::getProtocol() === $proto; } final public static function getSupportedDataTypes() { switch ($proto = self::getProtocol()) { case Wire\Constants080::VERSION: $types = self::$types_080; break; case Wire\Constants091::VERSION: $types = self::$types_091; break; case self::PROTOCOL_RBT: $types = self::$types_rabbit; break; default: throw new Exception\AMQPOutOfRangeException(sprintf('Unknown protocol: %s', $proto)); } return $types; } final public static function checkDataTypeIsSupported($type, $return = true) { try { $supported = self::getSupportedDataTypes(); if (!isset($supported[$type])) { throw new Exception\AMQPOutOfRangeException(sprintf( 'AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type )); } return true; } catch (Exception\AMQPOutOfRangeException $ex) { if (!$return) { throw $ex; } return false; } } final public static function getSymbolForDataType($type) { $types = self::getSupportedDataTypes(); if (!isset($types[$type])) { throw new Exception\AMQPOutOfRangeException(sprintf( 'AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type )); } return $types[$type]; } final public static function getDataTypeForSymbol($symbol) { $symbols = array_flip(self::getSupportedDataTypes()); if (!isset($symbols[$symbol])) { throw new Exception\AMQPOutOfRangeException(sprintf( 'AMQP-%s doesn\'t define data of type [%s]', self::getProtocol(), $symbol )); } return $symbols[$symbol]; } #[\ReturnTypeWillChange] public function current() { return current($this->data); } #[\ReturnTypeWillChange] public function key() { return key($this->data); } public function next(): void { next($this->data); } public function rewind(): void { reset($this->data); } public function valid(): bool { return key($this->data) !== null; } } bits)) { $this->out .= implode('', array_map('chr', $this->bits)); $this->bits = array(); $this->bitcount = 0; } } public function getvalue() { if ($this->bitcount) { $this->flushbits(); } return $this->out; } public function write($s) { $this->out .= $s; return $this; } public function write_bit($b) { $b = $b ? 1 : 0; $shift = $this->bitcount % 8; $last = $shift === 0 ? 0 : array_pop($this->bits); $last |= ($b << $shift); $this->bits[] = $last; $this->bitcount++; return $this; } public function write_bits($bits) { $value = 0; foreach ($bits as $n => $bit) { $bit = $bit ? 1 : 0; $value |= ($bit << $n); } $this->out .= chr($value); return $this; } public function write_octet($n) { if ($n < 0 || $n > 255) { throw new AMQPInvalidArgumentException('Octet out of range: ' . $n); } $this->out .= chr($n); return $this; } public function write_signed_octet($n) { if (($n < -128) || ($n > 127)) { throw new AMQPInvalidArgumentException('Signed octet out of range: ' . $n); } $this->out .= pack('c', $n); return $this; } public function write_short($n) { if ($n < 0 || $n > 65535) { throw new AMQPInvalidArgumentException('Short out of range: ' . $n); } $this->out .= pack('n', $n); return $this; } public function write_signed_short($n) { if (($n < -32768) || ($n > 32767)) { throw new AMQPInvalidArgumentException('Signed short out of range: ' . $n); } $this->out .= $this->correctEndianness(pack('s', $n)); return $this; } public function write_long($n) { if (($n < 0) || ($n > 4294967295)) { throw new AMQPInvalidArgumentException('Long out of range: ' . $n); } if (!self::PLATFORM_64BIT && is_string($n)) { $n = (float) $n; } $this->out .= pack('N', $n); return $this; } private function writeSignedLong($n) { if (($n < -2147483648) || ($n > 2147483647)) { throw new AMQPInvalidArgumentException('Signed long out of range: ' . $n); } $this->out .= $this->correctEndianness(pack('l', $n)); return $this; } public function write_longlong($n) { if (is_int($n)) { if ($n < 0) { throw new AMQPOutOfRangeException('Longlong out of range: ' . $n); } if (self::PLATFORM_64BIT) { $res = pack('J', $n); $this->out .= $res; } else { $this->out .= pack('NN', 0, $n); } return $this; } $value = new BigInteger($n); if ( $value->compare(self::getBigInteger('0')) < 0 || $value->compare(self::getBigInteger('FFFFFFFFFFFFFFFF', 16)) > 0 ) { throw new AMQPInvalidArgumentException('Longlong out of range: ' . $n); } $value->setPrecision(64); $this->out .= $value->toBytes(); return $this; } public function write_signed_longlong($n) { if (is_int($n)) { if (self::PLATFORM_64BIT) { $packed = pack('q', $n); if (self::isLittleEndian()) { $packed = $this->convertByteOrder($packed); } $this->out .= $packed; } else { $hi = $n < 0 ? -1 : 0; $lo = $n; $this->out .= pack('NN', $hi, $lo); } return $this; } $value = new BigInteger($n); if ( $value->compare(self::getBigInteger('-8000000000000000', 16)) < 0 || $value->compare(self::getBigInteger('7FFFFFFFFFFFFFFF', 16)) > 0 ) { throw new AMQPInvalidArgumentException('Signed longlong out of range: ' . $n); } $value->setPrecision(64); $this->out .= substr($value->toBytes(true), -8); return $this; } public function write_shortstr($s) { if ($s === null) { $this->write_octet(0); return $this; } $len = mb_strlen($s, 'ASCII'); if ($len > 255) { throw new AMQPInvalidArgumentException('String too long'); } $this->write_octet($len); $this->out .= $s; return $this; } public function write_longstr($s) { if ($s === null) { $this->write_long(0); return $this; } $this->write_long(mb_strlen($s, 'ASCII')); $this->out .= $s; return $this; } public function write_array($a) { if (!($a instanceof AMQPArray)) { $a = new AMQPArray($a); } $data = new self(); foreach ($a as $v) { $data->writeValue($v[0], $v[1]); } $data = $data->getvalue(); $this->write_long(mb_strlen($data, 'ASCII')); $this->write($data); return $this; } public function write_timestamp($v) { $this->write_longlong($v); return $this; } public function write_table($d) { $typeIsSym = !($d instanceof AMQPTable); $table_data = new self(); foreach ($d as $k => $va) { list($ftype, $v) = $va; $table_data->write_shortstr($k); $table_data->writeValue($typeIsSym ? AMQPAbstractCollection::getDataTypeForSymbol($ftype) : $ftype, $v); } $table_data = $table_data->getvalue(); $this->write_long(mb_strlen($table_data, 'ASCII')); $this->write($table_data); return $this; } public function write_table_object($d) { return $this->write_table($d); } private function writeValue($type, $val) { $this->write(AMQPAbstractCollection::getSymbolForDataType($type)); switch ($type) { case AMQPAbstractCollection::T_INT_SHORTSHORT: $this->write_signed_octet($val); break; case AMQPAbstractCollection::T_INT_SHORTSHORT_U: $this->write_octet($val); break; case AMQPAbstractCollection::T_INT_SHORT: $this->write_signed_short($val); break; case AMQPAbstractCollection::T_INT_SHORT_U: $this->write_short($val); break; case AMQPAbstractCollection::T_INT_LONG: $this->writeSignedLong($val); break; case AMQPAbstractCollection::T_INT_LONG_U: $this->write_long($val); break; case AMQPAbstractCollection::T_INT_LONGLONG: $this->write_signed_longlong($val); break; case AMQPAbstractCollection::T_INT_LONGLONG_U: $this->write_longlong($val); break; case AMQPAbstractCollection::T_DECIMAL: $this->write_octet($val->getE()); $this->writeSignedLong($val->getN()); break; case AMQPAbstractCollection::T_TIMESTAMP: $this->write_timestamp($val); break; case AMQPAbstractCollection::T_BOOL: $this->write_octet($val ? 1 : 0); break; case AMQPAbstractCollection::T_STRING_SHORT: $this->write_shortstr($val); break; case AMQPAbstractCollection::T_STRING_LONG: $this->write_longstr($val); break; case AMQPAbstractCollection::T_ARRAY: $this->write_array($val); break; case AMQPAbstractCollection::T_TABLE: $this->write_table($val); break; case AMQPAbstractCollection::T_VOID: break; case AMQPAbstractCollection::T_BYTES: $this->write_longstr($val); break; default: throw new AMQPInvalidArgumentException(sprintf( 'Unsupported type "%s"', $type )); } } } convertByteOrder($bytes) : $bytes; } protected function convertByteOrder($bytes) { return strrev($bytes); } protected function getLongMSB($longInt) { return (bool) ($longInt & 0x80000000); } protected function getMSB($bytes) { return ord($bytes[0]) > 127; } protected static function isLittleEndian() { if (self::$isLittleEndian === null) { $tmp = unpack('S', "\x01\x00"); self::$isLittleEndian = $tmp[1] === 1; } return self::$isLittleEndian; } protected static function getBigInteger($value, $base = 10) { if (!isset(self::$bigIntegers[$base])) { self::$bigIntegers[$base] = array(); } if (isset(self::$bigIntegers[$base][$value])) { return self::$bigIntegers[$base][$value]; } $integer = new BigInteger($value, $base); self::$bigIntegers[$base][$value] = $integer; return $integer; } } host = $host; $this->port = $port; $this->connection_timeout = $connection_timeout; $this->read_timeout = (float)$read_write_timeout; $this->write_timeout = (float)$read_write_timeout; $this->context = $context; $this->keepalive = $keepalive; $this->heartbeat = $heartbeat; $this->initial_heartbeat = $heartbeat; $this->canDispatchPcntlSignal = $this->isPcntlSignalEnabled(); } public function connect() { $errstr = $errno = null; $remote = sprintf( 'tcp://%s:%s', $this->host, $this->port ); $context = $this->setupContext(); $this->setErrorHandler(); try { $this->sock = stream_socket_client( $remote, $errno, $errstr, $this->connection_timeout, STREAM_CLIENT_CONNECT, $context ); $this->throwOnError(); } catch (\ErrorException $e) { throw new AMQPIOException($e->getMessage()); } finally { $this->restoreErrorHandler(); } if (false === $this->sock) { throw new AMQPIOException( sprintf( 'Error Connecting to server(%s): %s ', $errno, $errstr ), $errno ); } if (!stream_socket_get_name($this->sock, true)) { throw new AMQPIOException( sprintf( 'Connection refused: %s ', $remote ) ); } list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds(max($this->read_timeout, $this->write_timeout)); if (!stream_set_timeout($this->sock, $sec, $uSec)) { throw new AMQPIOException('Timeout could not be set'); } if ($this->canDispatchPcntlSignal) { stream_set_blocking($this->sock, false); stream_set_write_buffer($this->sock, 0); if (function_exists('stream_set_read_buffer')) { stream_set_read_buffer($this->sock, 0); } } else { stream_set_blocking($this->sock, true); } if ($this->keepalive) { $this->enable_keepalive(); } $options = stream_context_get_options($context); if (isset($options['ssl']['crypto_method'])) { $this->enableCrypto(); } $this->heartbeat = $this->initial_heartbeat; } private function setupContext() { $context = $this->context; if (!is_resource($context) || get_resource_type($context) !== 'stream-context') { $context = stream_context_create(); } stream_context_set_option($context, 'socket', 'tcp_nodelay', true); $options = stream_context_get_options($context); if (!empty($options['ssl']) && !isset($options['ssl']['crypto_method'])) { if (!stream_context_set_option($context, 'ssl', 'crypto_method', STREAM_CRYPTO_METHOD_ANY_CLIENT)) { throw new AMQPIOException("Can not set ssl.crypto_method stream context option"); } } return $context; } public function read($len) { $this->check_heartbeat(); list($timeout_sec, $timeout_uSec) = MiscHelper::splitSecondsMicroseconds($this->read_timeout); $read_start = microtime(true); $read = 0; $data = ''; while ($read < $len) { if (!is_resource($this->sock) || feof($this->sock)) { $this->close(); throw new AMQPConnectionClosedException('Broken pipe or closed connection'); } $this->setErrorHandler(); try { $buffer = fread($this->sock, ($len - $read)); $this->throwOnError(); } catch (\ErrorException $e) { throw new AMQPDataReadException($e->getMessage(), $e->getCode(), $e); } finally { $this->restoreErrorHandler(); } if ($buffer === false) { throw new AMQPDataReadException('Error receiving data'); } if ($buffer === '') { $read_now = microtime(true); $t_read = $read_now - $read_start; if ($t_read > $this->read_timeout) { throw new AMQPTimeoutException('Too many read attempts detected in StreamIO'); } $this->select($timeout_sec, $timeout_uSec); continue; } $this->last_read = microtime(true); $read_start = $this->last_read; $read += mb_strlen($buffer, 'ASCII'); $data .= $buffer; } if (mb_strlen($data, 'ASCII') !== $len) { throw new AMQPDataReadException( sprintf( 'Error reading data. Received %s instead of expected %s bytes', mb_strlen($data, 'ASCII'), $len ) ); } $this->last_read = microtime(true); return $data; } public function write($data) { $this->checkBrokerHeartbeat(); $written = 0; $len = mb_strlen($data, 'ASCII'); $write_start = microtime(true); while ($written < $len) { if (!is_resource($this->sock) || feof($this->sock)) { $this->close(); $constants = SocketConstants::getInstance(); throw new AMQPConnectionClosedException('Broken pipe or closed connection', $constants->SOCKET_EPIPE); } $result = false; $this->setErrorHandler(); try { $result = 0; if ($this->select_write()) { $buffer = mb_substr($data, $written, self::BUFFER_SIZE, 'ASCII'); $result = fwrite($this->sock, $buffer); } $this->throwOnError(); } catch (\ErrorException $e) { $code = $this->last_error['errno']; $constants = SocketConstants::getInstance(); switch ($code) { case $constants->SOCKET_EPIPE: case $constants->SOCKET_ENETDOWN: case $constants->SOCKET_ENETUNREACH: case $constants->SOCKET_ENETRESET: case $constants->SOCKET_ECONNABORTED: case $constants->SOCKET_ECONNRESET: case $constants->SOCKET_ECONNREFUSED: case $constants->SOCKET_ETIMEDOUT: $this->close(); throw new AMQPConnectionClosedException(socket_strerror($code), $code, $e); default: throw new AMQPRuntimeException($e->getMessage(), $code, $e); } } finally { $this->restoreErrorHandler(); } if ($result === false) { throw new AMQPRuntimeException('Error sending data'); } if ($this->timed_out()) { throw AMQPTimeoutException::writeTimeout($this->write_timeout); } $now = microtime(true); if ($result > 0) { $this->last_write = $write_start = $now; $written += $result; } else { if (feof($this->sock)) { $this->close(); throw new AMQPConnectionClosedException('Broken pipe or closed connection'); } if (($now - $write_start) > $this->write_timeout) { throw AMQPTimeoutException::writeTimeout($this->write_timeout); } } } } public function error_handler($errno, $errstr, $errfile, $errline): void { $code = $this->extract_error_code($errstr); $constants = SocketConstants::getInstance(); switch ($code) { case $constants->SOCKET_EAGAIN: case $constants->SOCKET_EWOULDBLOCK: case $constants->SOCKET_EINTR: return; } parent::error_handler($code > 0 ? $code : $errno, $errstr, $errfile, $errline); } public function close() { $this->disableHeartbeat(); if (is_resource($this->sock)) { fclose($this->sock); } $this->sock = null; $this->last_read = 0; $this->last_write = 0; } public function getSocket() { return $this->sock; } protected function do_select(?int $sec, int $usec) { if ($this->sock === null || !is_resource($this->sock)) { $this->sock = null; throw new AMQPConnectionClosedException('Broken pipe or closed connection', 0); } $read = array($this->sock); $write = null; $except = null; if ($sec === null && PHP_VERSION_ID >= 80100) { $usec = 0; } return stream_select($read, $write, $except, $sec, $usec); } protected function select_write() { $read = $except = null; $write = array($this->sock); return stream_select($read, $write, $except, 0, 100000); } protected function timed_out() { $info = stream_get_meta_data($this->sock); return $info['timed_out']; } protected function enable_keepalive(): void { if (!function_exists('socket_import_stream')) { throw new AMQPIOException('Can not enable keepalive: function socket_import_stream does not exist'); } if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) { throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined'); } $socket = socket_import_stream($this->sock); socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1); } protected function extract_error_code($message) { if (0 === strpos($message, 'stream_select():')) { $pattern = '/\s+\[(\d+)\]:\s+/'; } else { $pattern = '/\s+errno=(\d+)\s+/'; } $matches = array(); $result = preg_match($pattern, $message, $matches); if ($result > 0) { return (int)$matches[1]; } return 0; } private function enableCrypto(): void { $timeout_at = time() + ($this->read_timeout + $this->write_timeout) * 2; try { $this->setErrorHandler(); do { $enabled = stream_socket_enable_crypto($this->sock, true); if ($enabled === true) { return; } $this->throwOnError(); usleep(1e3); } while ($enabled === 0 && time() < $timeout_at); } catch (\ErrorException $exception) { throw new AMQPIOException($exception->getMessage(), $exception->getCode(), $exception); } finally { $this->restoreErrorHandler(); } if ($enabled !== true) { throw new AMQPIOException('Could not enable socket crypto'); } } } config = $config; $this->host = str_replace(['[', ']'], '', $host); $this->port = $port; $this->read_timeout = (float)$read_timeout; $this->write_timeout = (float)($write_timeout ?: $read_timeout); $this->heartbeat = $heartbeat; $this->initial_heartbeat = $heartbeat; $this->keepalive = $keepalive; $this->canDispatchPcntlSignal = $this->isPcntlSignalEnabled(); } public function connect() { $this->sock = socket_create(!$this->isIpv6() ? AF_INET : AF_INET6, SOCK_STREAM, SOL_TCP); list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->write_timeout); socket_set_option($this->sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => $sec, 'usec' => $uSec)); list($sec, $uSec) = MiscHelper::splitSecondsMicroseconds($this->read_timeout); socket_set_option($this->sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $sec, 'usec' => $uSec)); $this->setErrorHandler(); try { $connected = socket_connect($this->sock, $this->host, $this->port); $this->throwOnError(); } catch (\ErrorException $e) { $connected = false; } finally { $this->restoreErrorHandler(); } if (!$connected) { $errno = socket_last_error($this->sock); $errstr = socket_strerror($errno); throw new AMQPIOException(sprintf( 'Error Connecting to server (%s): %s', $errno, $errstr ), $errno); } socket_set_block($this->sock); socket_set_option($this->sock, SOL_TCP, TCP_NODELAY, 1); if ($this->config && $this->config->getSendBufferSize() > 0) { socket_set_option($this->sock, SOL_SOCKET, SO_SNDBUF, $this->config->getSendBufferSize()); } if ($this->keepalive) { $this->enable_keepalive(); } $this->heartbeat = $this->initial_heartbeat; } public function getSocket() { return $this->sock; } public function read($len) { if (is_null($this->sock)) { throw new AMQPSocketException(sprintf( 'Socket was null! Last SocketError was: %s', socket_strerror(socket_last_error()) )); } $this->check_heartbeat(); list($timeout_sec, $timeout_uSec) = MiscHelper::splitSecondsMicroseconds($this->read_timeout); $read_start = microtime(true); $read = 0; $data = ''; while ($read < $len) { $buffer = null; $result = socket_recv($this->sock, $buffer, $len - $read, 0); if ($result === 0) { $this->close(); throw new AMQPConnectionClosedException('Broken pipe or closed connection'); } if (empty($buffer)) { $read_now = microtime(true); $t_read = $read_now - $read_start; if ($t_read > $this->read_timeout) { throw new AMQPTimeoutException('Too many read attempts detected in SocketIO'); } $this->select($timeout_sec, $timeout_uSec); continue; } $read += mb_strlen($buffer, 'ASCII'); $data .= $buffer; } if (mb_strlen($data, 'ASCII') !== $len) { throw new AMQPIOException(sprintf( 'Error reading data. Received %s instead of expected %s bytes', mb_strlen($data, 'ASCII'), $len )); } $this->last_read = microtime(true); return $data; } public function write($data) { if (is_null($this->sock)) { throw new AMQPSocketException(sprintf( 'Socket was null! Last SocketError was: %s', socket_strerror(socket_last_error()) )); } $this->checkBrokerHeartbeat(); $written = 0; $len = mb_strlen($data, 'ASCII'); $write_start = microtime(true); while ($written < $len) { $this->setErrorHandler(); try { $result = 0; if ($this->select_write()) { $buffer = mb_substr($data, $written, self::BUFFER_SIZE, 'ASCII'); $result = socket_write($this->sock, $buffer); } $this->throwOnError(); } catch (\ErrorException $e) { $code = socket_last_error($this->sock); $constants = SocketConstants::getInstance(); switch ($code) { case $constants->SOCKET_EPIPE: case $constants->SOCKET_ENETDOWN: case $constants->SOCKET_ENETUNREACH: case $constants->SOCKET_ENETRESET: case $constants->SOCKET_ECONNABORTED: case $constants->SOCKET_ECONNRESET: case $constants->SOCKET_ECONNREFUSED: case $constants->SOCKET_ETIMEDOUT: $this->close(); throw new AMQPConnectionClosedException(socket_strerror($code), $code, $e); default: throw new AMQPIOException(sprintf( 'Error sending data. Last SocketError: %s', socket_strerror($code) ), $code, $e); } } finally { $this->restoreErrorHandler(); } if ($result === false) { throw new AMQPIOException(sprintf( 'Error sending data. Last SocketError: %s', socket_strerror(socket_last_error($this->sock)) )); } $now = microtime(true); if ($result > 0) { $this->last_write = $write_start = $now; $written += $result; } else { if (($now - $write_start) > $this->write_timeout) { throw AMQPTimeoutException::writeTimeout($this->write_timeout); } } } } public function close() { $this->disableHeartbeat(); if (is_resource($this->sock) || is_a($this->sock, \Socket::class)) { socket_close($this->sock); } $this->sock = null; $this->last_read = 0; $this->last_write = 0; } protected function do_select(?int $sec, int $usec) { if (!is_resource($this->sock) && !is_a($this->sock, \Socket::class)) { $this->sock = null; throw new AMQPConnectionClosedException('Broken pipe or closed connection', 0); } $read = array($this->sock); $write = null; $except = null; return socket_select($read, $write, $except, $sec, $usec); } protected function select_write() { $read = $except = null; $write = array($this->sock); return socket_select($read, $write, $except, 0, 100000); } protected function enable_keepalive(): void { if (!defined('SOL_SOCKET') || !defined('SO_KEEPALIVE')) { throw new AMQPIOException('Can not enable keepalive: SOL_SOCKET or SO_KEEPALIVE is not defined'); } socket_set_option($this->sock, SOL_SOCKET, SO_KEEPALIVE, 1); } public function error_handler($errno, $errstr, $errfile, $errline): void { $constants = SocketConstants::getInstance(); if (isset($constants->SOCKET_EINTR) && false !== strrpos($errstr, socket_strerror($constants->SOCKET_EINTR))) { return; } parent::error_handler($errno, $errstr, $errfile, $errline); } protected function setErrorHandler(): void { parent::setErrorHandler(); socket_clear_error($this->sock); } private function isIpv6(): bool { $ipv6 = filter_var($this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); if ($ipv6 !== false || checkdnsrr($this->host, 'AAAA')) { return true; } return false; } } check_heartbeat(); $this->setErrorHandler(); try { $result = $this->do_select($sec, $usec); $this->throwOnError(); } catch (\ErrorException $e) { throw new AMQPIOWaitException($e->getMessage(), $e->getCode(), $e); } finally { $this->restoreErrorHandler(); } if ($this->canDispatchPcntlSignal) { pcntl_signal_dispatch(); } if ($result === false) { $result = 0; } return $result; } abstract protected function do_select(?int $sec, int $usec); abstract public function connect(); public function afterTune(int $heartbeat): void { $this->heartbeat = $heartbeat; $this->initial_heartbeat = $heartbeat; } public function check_heartbeat() { if ($this->heartbeat !== 0 && $this->last_read > 0 && $this->last_write > 0) { $this->checkBrokerHeartbeat(); $now = microtime(true); if (($this->heartbeat / 2) < $now - $this->last_write) { $this->write_heartbeat(); } } } protected function checkBrokerHeartbeat(): void { if ($this->heartbeat > 0 && ($this->last_read > 0 || $this->last_write > 0)) { $lastActivity = $this->getLastActivity(); $now = microtime(true); if (($now - $lastActivity) > $this->heartbeat * 2 + 1) { $this->close(); throw new AMQPHeartbeatMissedException('Missed server heartbeat'); } } } public function getLastActivity() { return max($this->last_read, $this->last_write); } public function getReadTimeout(): float { return $this->read_timeout; } public function disableHeartbeat() { $this->initial_heartbeat = $this->heartbeat; $this->heartbeat = 0; return $this; } public function reenableHeartbeat() { $this->heartbeat = $this->initial_heartbeat; return $this; } protected function write_heartbeat() { $pkt = new AMQPWriter(); $pkt->write_octet(8); $pkt->write_short(0); $pkt->write_long(0); $pkt->write_octet(0xCE); $this->write($pkt->getvalue()); } protected function setErrorHandler(): void { $this->last_error = null; set_error_handler(array($this, 'error_handler')); } protected function throwOnError(): void { if ($this->last_error !== null) { throw new \ErrorException( $this->last_error['errstr'], 0, $this->last_error['errno'], $this->last_error['errfile'], $this->last_error['errline'] ); } } protected function restoreErrorHandler(): void { restore_error_handler(); } public function error_handler($errno, $errstr, $errfile, $errline): void { $this->last_error = compact('errno', 'errstr', 'errfile', 'errline'); } protected function isPcntlSignalEnabled(): bool { return extension_loaded('pcntl') && function_exists('pcntl_signal_dispatch') && (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true); } } 'FRAME-METHOD', 2 => 'FRAME-HEADER', 3 => 'FRAME-BODY', 4 => 'FRAME-OOB-METHOD', 5 => 'FRAME-OOB-HEADER', 6 => 'FRAME-OOB-BODY', 7 => 'FRAME-TRACE', 8 => 'FRAME-HEARTBEAT', 4096 => 'FRAME-MIN-SIZE', 206 => 'FRAME-END', 501 => 'FRAME-ERROR', ); public static $CONTENT_METHODS = array( 0 => '60,40', 1 => '60,50', 2 => '60,60', 3 => '60,71', 4 => '70,50', 5 => '70,70', 6 => '80,40', 7 => '80,50', 8 => '80,60', 9 => '110,10', 10 => '120,40', 11 => '120,41', ); public static $CLOSE_METHODS = array( 0 => '10,60', 1 => '20,40', ); public static $GLOBAL_METHOD_NAMES = array( '10,10' => 'Connection.start', '10,11' => 'Connection.start_ok', '10,20' => 'Connection.secure', '10,21' => 'Connection.secure_ok', '10,30' => 'Connection.tune', '10,31' => 'Connection.tune_ok', '10,40' => 'Connection.open', '10,41' => 'Connection.open_ok', '10,50' => 'Connection.redirect', '10,60' => 'Connection.close', '10,61' => 'Connection.close_ok', '20,10' => 'Channel.open', '20,11' => 'Channel.open_ok', '20,20' => 'Channel.flow', '20,21' => 'Channel.flow_ok', '20,30' => 'Channel.alert', '20,40' => 'Channel.close', '20,41' => 'Channel.close_ok', '30,10' => 'Access.request', '30,11' => 'Access.request_ok', '40,10' => 'Exchange.declare', '40,11' => 'Exchange.declare_ok', '40,20' => 'Exchange.delete', '40,21' => 'Exchange.delete_ok', '50,10' => 'Queue.declare', '50,11' => 'Queue.declare_ok', '50,20' => 'Queue.bind', '50,21' => 'Queue.bind_ok', '50,30' => 'Queue.purge', '50,31' => 'Queue.purge_ok', '50,40' => 'Queue.delete', '50,41' => 'Queue.delete_ok', '50,50' => 'Queue.unbind', '50,51' => 'Queue.unbind_ok', '60,10' => 'Basic.qos', '60,11' => 'Basic.qos_ok', '60,20' => 'Basic.consume', '60,21' => 'Basic.consume_ok', '60,30' => 'Basic.cancel', '60,31' => 'Basic.cancel_ok', '60,40' => 'Basic.publish', '60,50' => 'Basic.return', '60,60' => 'Basic.deliver', '60,70' => 'Basic.get', '60,71' => 'Basic.get_ok', '60,72' => 'Basic.get_empty', '60,80' => 'Basic.ack', '60,90' => 'Basic.reject', '60,100' => 'Basic.recover_async', '60,110' => 'Basic.recover', '60,111' => 'Basic.recover_ok', '70,10' => 'File.qos', '70,11' => 'File.qos_ok', '70,20' => 'File.consume', '70,21' => 'File.consume_ok', '70,30' => 'File.cancel', '70,31' => 'File.cancel_ok', '70,40' => 'File.open', '70,41' => 'File.open_ok', '70,50' => 'File.stage', '70,60' => 'File.publish', '70,70' => 'File.return', '70,80' => 'File.deliver', '70,90' => 'File.ack', '70,100' => 'File.reject', '80,10' => 'Stream.qos', '80,11' => 'Stream.qos_ok', '80,20' => 'Stream.consume', '80,21' => 'Stream.consume_ok', '80,30' => 'Stream.cancel', '80,31' => 'Stream.cancel_ok', '80,40' => 'Stream.publish', '80,50' => 'Stream.return', '80,60' => 'Stream.deliver', '90,10' => 'Tx.select', '90,11' => 'Tx.select_ok', '90,20' => 'Tx.commit', '90,21' => 'Tx.commit_ok', '90,30' => 'Tx.rollback', '90,31' => 'Tx.rollback_ok', '100,10' => 'Dtx.select', '100,11' => 'Dtx.select_ok', '100,20' => 'Dtx.start', '100,21' => 'Dtx.start_ok', '110,10' => 'Tunnel.request', '120,10' => 'Test.integer', '120,11' => 'Test.integer_ok', '120,20' => 'Test.string', '120,21' => 'Test.string_ok', '120,30' => 'Test.table', '120,31' => 'Test.table_ok', '120,40' => 'Test.content', '120,41' => 'Test.content_ok', ); } setValue($val, $type); return $this; } } resetCounters(); return $this->rawread($n); } public function read_bit(): bool { if (empty($this->bitcount)) { $this->bits = ord($this->rawread(1)); $this->bitcount = 8; } $result = ($this->bits & 1) === 1; $this->bits >>= 1; $this->bitcount--; return $result; } public function read_octet() { $this->resetCounters(); list(, $res) = unpack('C', $this->rawread(1)); return $res; } public function read_signed_octet() { $this->resetCounters(); list(, $res) = unpack('c', $this->rawread(1)); return $res; } public function read_short() { $this->resetCounters(); list(, $res) = unpack('n', $this->rawread(2)); return $res; } public function read_signed_short() { $this->resetCounters(); list(, $res) = unpack('s', $this->correctEndianness($this->rawread(2))); return $res; } public function read_php_int() { list(, $res) = unpack('N', $this->rawread(4)); if (self::PLATFORM_64BIT) { return (int) sprintf('%u', $res); } return $res; } public function read_long() { $this->resetCounters(); list(, $res) = unpack('N', $this->rawread(4)); if (!self::PLATFORM_64BIT && $this->getLongMSB($res)) { return sprintf('%u', $res); } return $res; } private function readSignedLong() { $this->resetCounters(); list(, $res) = unpack('l', $this->correctEndianness($this->rawread(4))); return $res; } public function read_longlong() { $this->resetCounters(); $bytes = $this->rawread(8); if (self::PLATFORM_64BIT) { if (!$this->getMSB($bytes)) { $res = unpack('J', $bytes); return $res[1]; } } else { list(, $hi, $lo) = unpack('N2', $bytes); if ($hi === 0 && $lo > 0) { return $lo; } } $var = new BigInteger($bytes, 256); return $var->toString(); } public function read_signed_longlong() { $this->resetCounters(); $bytes = $this->rawread(8); if (self::PLATFORM_64BIT) { $res = unpack('q', $this->correctEndianness($bytes)); return $res[1]; } else { list(, $hi, $lo) = unpack('N2', $bytes); if ($hi === 0 && $lo > 0) { return $lo; } if ($hi === -1 && $this->getLongMSB($lo)) { return $lo; } } $var = new BigInteger($bytes, -256); return $var->toString(); } public function read_float() { $this->resetCounters(); list(, $res) = unpack('G', $this->rawread(4)); return (float)$res; } public function read_double() { $this->resetCounters(); list(, $res) = unpack('E', $this->rawread(8)); return (float)$res; } public function read_shortstr() { $this->resetCounters(); list(, $slen) = unpack('C', $this->rawread(1)); return $this->rawread($slen); } public function read_longstr() { $this->resetCounters(); $slen = $this->read_php_int(); if ($slen < 0) { throw new AMQPOutOfBoundsException('Strings longer than supported on this platform'); } return $this->rawread($slen); } public function read_timestamp() { return $this->read_longlong(); } public function read_table(bool $returnObject = false) { $this->resetCounters(); $tlen = $this->read_php_int(); if ($tlen < 0) { throw new AMQPOutOfBoundsException('Table is longer than supported'); } $table_data = new AMQPBufferReader($this->rawread($tlen)); $result = $returnObject ? new AMQPTable() : array(); while ($table_data->tell() < $tlen) { $name = $table_data->read_shortstr(); $ftype = AMQPAbstractCollection::getDataTypeForSymbol($ftypeSym = $table_data->rawread(1)); $val = $table_data->read_value($ftype, $returnObject); $returnObject ? $result->set($name, $val, $ftype) : $result[$name] = array($ftypeSym, $val); } return $result; } public function read_table_object() { return $this->read_table(true); } public function read_array($returnObject = false) { $this->resetCounters(); $arrayLength = $this->read_php_int(); $endOffset = $this->offset + $arrayLength; $result = $returnObject ? new AMQPArray() : array(); while ($this->offset < $endOffset) { $fieldType = AMQPAbstractCollection::getDataTypeForSymbol($this->rawread(1)); $fieldValue = $this->read_value($fieldType, $returnObject); $returnObject ? $result->push($fieldValue, $fieldType) : $result[] = $fieldValue; } return $result; } public function read_array_object() { return $this->read_array(true); } public function readFrameHeader(): array { return unpack('Ctype/nchannel/Nsize', $this->rawread(Frame::FRAME_HEADER_SIZE)); } public function read_value(int $fieldType, bool $collectionsAsObjects = false) { $this->resetCounters(); switch ($fieldType) { case AMQPAbstractCollection::T_INT_SHORTSHORT: $val = $this->read_signed_octet(); break; case AMQPAbstractCollection::T_INT_SHORTSHORT_U: case AMQPAbstractCollection::T_BOOL: $val = $this->read_octet(); break; case AMQPAbstractCollection::T_INT_SHORT: $val = $this->read_signed_short(); break; case AMQPAbstractCollection::T_INT_SHORT_U: $val = $this->read_short(); break; case AMQPAbstractCollection::T_INT_LONG: $val = $this->readSignedLong(); break; case AMQPAbstractCollection::T_INT_LONG_U: $val = $this->read_long(); break; case AMQPAbstractCollection::T_INT_LONGLONG: $val = $this->read_signed_longlong(); break; case AMQPAbstractCollection::T_INT_LONGLONG_U: $val = $this->read_longlong(); break; case AMQPAbstractCollection::T_DECIMAL: $e = $this->read_octet(); $n = $this->readSignedLong(); $val = new AMQPDecimal($n, $e); break; case AMQPAbstractCollection::T_TIMESTAMP: $val = $this->read_timestamp(); break; case AMQPAbstractCollection::T_STRING_SHORT: $val = $this->read_shortstr(); break; case AMQPAbstractCollection::T_STRING_LONG: case AMQPAbstractCollection::T_BYTES: $val = $this->read_longstr(); break; case AMQPAbstractCollection::T_ARRAY: $val = $this->read_array($collectionsAsObjects); break; case AMQPAbstractCollection::T_TABLE: $val = $this->read_table($collectionsAsObjects); break; case AMQPAbstractCollection::T_VOID: $val = null; break; case AMQPAbstractCollection::T_FLOAT: $val = $this->read_float(); break; case AMQPAbstractCollection::T_DOUBLE: $val = $this->read_double(); break; default: throw new AMQPInvalidArgumentException(sprintf( 'Unsupported type "%s"', $fieldType )); } return $val; } protected function tell(): int { return $this->offset; } protected function resetCounters(): void { $this->bitcount = $this->bits = 0; } } connection = $connection; $this->channel_id = (int)$channel_id; $connection->channels[$channel_id] = $this; $this->msg_property_reader = new Wire\AMQPBufferReader(''); $this->dispatch_reader = new Wire\AMQPBufferReader(''); $this->protocolVersion = self::getProtocolVersion(); switch ($this->protocolVersion) { case Wire\Constants091::VERSION: $constantClass = Wire\Constants091::class; $this->protocolWriter = new Protocol091(); $this->waitHelper = new Wait091(); $this->methodMap = new MethodMap091(); break; case Wire\Constants080::VERSION: $constantClass = Wire\Constants080::class; $this->protocolWriter = new Protocol080(); $this->waitHelper = new Wait080(); $this->methodMap = new MethodMap080(); break; default: throw new AMQPNotImplementedException(sprintf( 'Protocol: %s not implemented.', $this->protocolVersion )); } $this->constants = new $constantClass(); $this->debug = new DebugHelper($this->constants); $this->frame_queue = new \SplQueue(); } public static function getProtocolVersion() { $protocol = defined('AMQP_PROTOCOL') ? AMQP_PROTOCOL : Wire\Constants091::VERSION; if (!in_array($protocol, array(Wire\Constants080::VERSION, Wire\Constants091::VERSION), true)) { throw new AMQPOutOfRangeException(sprintf('Protocol version %s not implemented.', $protocol)); } return $protocol; } public function getChannelId() { return $this->channel_id; } public function setBodySizeLimit($max_bytes) { $max_bytes = (int) $max_bytes; if ($max_bytes > 0) { $this->maxBodySize = $max_bytes; } return $this; } public function getConnection() { return $this->connection; } public function getMethodQueue() { return $this->method_queue; } public function hasPendingMethods() { return !empty($this->method_queue); } public function dispatch($method_sig, $args, $amqpMessage) { if (!$this->methodMap->valid_method($method_sig)) { throw new AMQPNotImplementedException(sprintf( 'Unknown AMQP method "%s"', $method_sig )); } $amqp_method = $this->methodMap->get_method($method_sig); if (!method_exists($this, $amqp_method)) { throw new AMQPNotImplementedException(sprintf( 'Method: "%s" not implemented by class: %s', $amqp_method, get_class($this) )); } $this->dispatch_reader->reset($args); if ($amqpMessage === null) { return call_user_func(array($this, $amqp_method), $this->dispatch_reader); } return call_user_func(array($this, $amqp_method), $this->dispatch_reader, $amqpMessage); } protected function next_frame($timeout = 0): Frame { $this->debug->debug_msg('waiting for a new frame'); if (!$this->frame_queue->isEmpty()) { return $this->frame_queue->dequeue(); } return $this->connection->wait_channel($this->channel_id, $timeout); } protected function send_method_frame($method_sig, $args = '') { if ($this->connection === null) { throw new AMQPChannelClosedException('Channel connection is closed.'); } $this->connection->send_channel_method_frame($this->channel_id, $method_sig, $args); } protected function prepare_method_frame($method_sig, $args = '', $pkt = null) { return $this->connection->prepare_channel_method_frame($this->channel_id, $method_sig, $args, $pkt); } public function wait_content(): AMQPMessage { $frame = $this->next_frame(); $this->validate_frame($frame, Frame::TYPE_HEADER); $payload = $frame->getPayload(); $this->msg_property_reader->reset(mb_substr($payload, 4, null, 'ASCII')); $size = $this->msg_property_reader->read_longlong(); return $this->createMessage( $this->msg_property_reader, $size ); } protected function createMessage(AMQPReader $propertyReader, int $bodySize): AMQPMessage { $body = ''; $bodyReceivedBytes = 0; $message = new AMQPMessage(); $message ->load_properties($propertyReader) ->setBodySize($bodySize); while ($bodySize > $bodyReceivedBytes) { $frame = $this->next_frame(); $this->validate_frame($frame, Frame::TYPE_BODY); $bodyReceivedBytes += $frame->getSize(); if (is_int($this->maxBodySize) && $bodyReceivedBytes > $this->maxBodySize) { $message->setIsTruncated(true); continue; } $body .= $frame->getPayload(); } $message->setBody($body); return $message; } public function wait($allowed_methods = null, $non_blocking = false, $timeout = 0) { $this->debug->debug_allowed_methods($allowed_methods); $deferred = $this->process_deferred_methods($allowed_methods); if ($deferred['dispatch'] === true) { return $this->dispatch_deferred_method($deferred['queued_method']); } if (true === $non_blocking) { $timeout = null; } while (true) { try { $frame = $this->next_frame($timeout); } catch (AMQPNoDataException $e) { break; } catch (AMQPConnectionClosedException $exception) { if ($this instanceof AMQPChannel) { $this->do_close(); } throw $exception; } $this->validate_method_frame($frame); $this->validate_frame_payload($frame); $payload = $frame->getPayload(); $method = $this->parseMethod($payload); $method_sig = $method->getSignature(); $this->debug->debug_method_signature('> %s', $method_sig); $amqpMessage = $this->maybe_wait_for_content($method_sig); if ($this->should_dispatch_method($allowed_methods, $method_sig)) { return $this->dispatch($method_sig, $method->getArguments(), $amqpMessage); } $this->debug->debug_method_signature('Queueing for later: %s', $method_sig); $this->method_queue[] = array($method_sig, $method->getArguments(), $amqpMessage); if ($non_blocking) { break; } } } protected function process_deferred_methods($allowed_methods) { $dispatch = false; $queued_method = array(); foreach ($this->method_queue as $qk => $qm) { $this->debug->debug_msg('checking queue method ' . $qk); $method_sig = $qm[0]; if ($allowed_methods === null || in_array($method_sig, $allowed_methods, true)) { unset($this->method_queue[$qk]); $dispatch = true; $queued_method = $qm; break; } } return array('dispatch' => $dispatch, 'queued_method' => $queued_method); } protected function dispatch_deferred_method($queued_method) { $this->debug->debug_method_signature('Executing queued method: %s', $queued_method[0]); return $this->dispatch($queued_method[0], $queued_method[1], $queued_method[2]); } protected function validate_method_frame(Frame $frame): void { $this->validate_frame($frame, Frame::TYPE_METHOD); } protected function validate_frame(Frame $frame, int $expectedType): void { if ($frame->getType() !== $expectedType) { throw new AMQPInvalidFrameException(sprintf( 'Expecting %u, received frame type %s (%s)', $expectedType, $frame->getType(), $this->constants->getFrameType($frame->getType()) )); } } protected function validate_frame_payload(Frame $frame): void { $payload = $frame->getPayload(); $payloadSize = mb_strlen($payload, 'ASCII'); if ($payloadSize < 4) { throw new AMQPOutOfBoundsException('Method frame too short'); } if ($payloadSize !== $frame->getSize()) { throw new AMQPInvalidFrameException('Frame size does not match payload'); } } protected function parseMethod(string $payload): Method { $result = unpack('n2method/a*args', $payload); return new Method($result['method1'], $result['method2'], $result['args']); } protected function should_dispatch_method($allowed_methods, $method_sig) { return $allowed_methods === null || in_array($method_sig, $allowed_methods, true) || $this->constants->isCloseMethod($method_sig); } protected function maybe_wait_for_content($method_sig) { $amqpMessage = null; if ($this->constants->isContentMethod($method_sig)) { $amqpMessage = $this->wait_content(); } return $amqpMessage; } protected function dispatch_to_handler($handler, array $arguments = []) { if (is_callable($handler)) { call_user_func_array($handler, $arguments); } } } type = $type; $this->channel = $channel; $this->size = $size; $this->payload = $payload; } public function getType(): int { return $this->type; } public function getChannel(): int { return $this->channel; } public function getSize(): int { return $this->size; } public function getPayload(): ?string { return $this->payload; } public function isMethod(): bool { return $this->type === self::TYPE_METHOD; } public function isHeartbeat(): bool { return $this->type === self::TYPE_HEARTBEAT; } } get_free_channel_id(); } parent::__construct($connection, $channel_id); $this->debug->debug_msg('using channel_id: ' . $channel_id); $this->auto_decode = $auto_decode; $this->channel_rpc_timeout = $channel_rpc_timeout; try { $this->x_open(); } catch (\Exception $e) { $this->close(); throw $e; } } public function is_open() { return $this->is_open; } protected function do_close() { if ($this->channel_id !== null) { unset($this->connection->channels[$this->channel_id]); } $this->channel_id = $this->connection = null; $this->is_open = false; $this->callbacks = array(); } protected function channel_alert(AMQPReader $reader): void { $reply_code = $reader->read_short(); $reply_text = $reader->read_shortstr(); $details = $reader->read_table(); array_push($this->alerts, array($reply_code, $reply_text, $details)); } public function close($reply_code = 0, $reply_text = '', $method_sig = array(0, 0)) { $this->callbacks = array(); if ($this->is_open === false || $this->connection === null) { $this->do_close(); return null; } list($class_id, $method_id, $args) = $this->protocolWriter->channelClose( $reply_code, $reply_text, $method_sig[0], $method_sig[1] ); try { $this->send_method_frame(array($class_id, $method_id), $args); } catch (\Exception $e) { $this->do_close(); throw $e; } return $this->wait(array( $this->waitHelper->get_wait('channel.close_ok') ), false, $this->channel_rpc_timeout); } protected function channel_close(AMQPReader $reader): void { $reply_code = $reader->read_short(); $reply_text = $reader->read_shortstr(); $class_id = $reader->read_short(); $method_id = $reader->read_short(); $this->send_method_frame(array(20, 41)); $this->do_close(); throw new AMQPProtocolChannelException($reply_code, $reply_text, array($class_id, $method_id)); } protected function channel_close_ok() { $this->do_close(); } public function flow($active) { list($class_id, $method_id, $args) = $this->protocolWriter->channelFlow($active); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('channel.flow_ok') ), false, $this->channel_rpc_timeout); } protected function channel_flow(AMQPReader $reader): void { $this->active = $reader->read_bit(); $this->x_flow_ok($this->active); } protected function x_flow_ok($active) { list($class_id, $method_id, $args) = $this->protocolWriter->channelFlow($active); $this->send_method_frame(array($class_id, $method_id), $args); } protected function channel_flow_ok(AMQPReader $reader): bool { return $reader->read_bit(); } protected function x_open($out_of_band = '') { if ($this->is_open) { return null; } list($class_id, $method_id, $args) = $this->protocolWriter->channelOpen($out_of_band); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('channel.open_ok') ), false, $this->channel_rpc_timeout); } protected function channel_open_ok() { $this->is_open = true; $this->debug->debug_msg('Channel open'); } public function access_request( $realm, $exclusive = false, $passive = false, $active = false, $write = false, $read = false ) { list($class_id, $method_id, $args) = $this->protocolWriter->accessRequest( $realm, $exclusive, $passive, $active, $write, $read ); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('access.request_ok') ), false, $this->channel_rpc_timeout); } protected function access_request_ok(AMQPReader $reader): int { $this->default_ticket = $reader->read_short(); return $this->default_ticket; } public function exchange_declare( $exchange, $type, $passive = false, $durable = false, $auto_delete = true, $internal = false, $nowait = false, $arguments = array(), $ticket = null ) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->exchangeDeclare( $ticket, $exchange, $type, $passive, $durable, $auto_delete, $internal, $nowait, $arguments ); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } return $this->wait(array( $this->waitHelper->get_wait('exchange.declare_ok') ), false, $this->channel_rpc_timeout); } protected function exchange_declare_ok() { } public function exchange_delete( $exchange, $if_unused = false, $nowait = false, $ticket = null ) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->exchangeDelete( $ticket, $exchange, $if_unused, $nowait ); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } return $this->wait(array( $this->waitHelper->get_wait('exchange.delete_ok') ), false, $this->channel_rpc_timeout); } protected function exchange_delete_ok() { } public function exchange_bind( $destination, $source, $routing_key = '', $nowait = false, $arguments = array(), $ticket = null ) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->exchangeBind( $ticket, $destination, $source, $routing_key, $nowait, $arguments ); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } return $this->wait(array( $this->waitHelper->get_wait('exchange.bind_ok') ), false, $this->channel_rpc_timeout); } protected function exchange_bind_ok() { } public function exchange_unbind( $destination, $source, $routing_key = '', $nowait = false, $arguments = array(), $ticket = null ) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->exchangeUnbind( $ticket, $destination, $source, $routing_key, $nowait, $arguments ); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('exchange.unbind_ok') ), false, $this->channel_rpc_timeout); } protected function exchange_unbind_ok() { } public function queue_bind( $queue, $exchange, $routing_key = '', $nowait = false, $arguments = array(), $ticket = null ) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->queueBind( $ticket, $queue, $exchange, $routing_key, $nowait, $arguments ); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } return $this->wait(array( $this->waitHelper->get_wait('queue.bind_ok') ), false, $this->channel_rpc_timeout); } protected function queue_bind_ok() { } public function queue_unbind( $queue, $exchange, $routing_key = '', $arguments = array(), $ticket = null ) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->queueUnbind( $ticket, $queue, $exchange, $routing_key, $arguments ); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('queue.unbind_ok') ), false, $this->channel_rpc_timeout); } protected function queue_unbind_ok() { } public function queue_declare( $queue = '', $passive = false, $durable = false, $exclusive = false, $auto_delete = true, $nowait = false, $arguments = array(), $ticket = null ) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->queueDeclare( $ticket, $queue, $passive, $durable, $exclusive, $auto_delete, $nowait, $arguments ); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } return $this->wait(array( $this->waitHelper->get_wait('queue.declare_ok') ), false, $this->channel_rpc_timeout); } protected function queue_declare_ok(AMQPReader $reader) { $queue = $reader->read_shortstr(); $message_count = $reader->read_long(); $consumer_count = $reader->read_long(); return array($queue, $message_count, $consumer_count); } public function queue_delete($queue = '', $if_unused = false, $if_empty = false, $nowait = false, $ticket = null) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->queueDelete( $ticket, $queue, $if_unused, $if_empty, $nowait ); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } return $this->wait(array( $this->waitHelper->get_wait('queue.delete_ok') ), false, $this->channel_rpc_timeout); } protected function queue_delete_ok(AMQPReader $reader) { return $reader->read_long(); } public function queue_purge($queue = '', $nowait = false, $ticket = null) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->queuePurge($ticket, $queue, $nowait); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } return $this->wait(array( $this->waitHelper->get_wait('queue.purge_ok') ), false, $this->channel_rpc_timeout); } protected function queue_purge_ok(AMQPReader $reader) { return $reader->read_long(); } public function basic_ack($delivery_tag, $multiple = false) { list($class_id, $method_id, $args) = $this->protocolWriter->basicAck($delivery_tag, $multiple); $this->send_method_frame(array($class_id, $method_id), $args); } protected function basic_ack_from_server(AMQPReader $reader): void { $delivery_tag = $reader->read_longlong(); $multiple = (bool) $reader->read_bit(); if (!isset($this->published_messages[$delivery_tag])) { throw new AMQPRuntimeException(sprintf( 'Server ack\'ed unknown delivery_tag "%s"', $delivery_tag )); } $this->internal_ack_handler($delivery_tag, $multiple, $this->ack_handler); } protected function basic_nack_from_server(AMQPReader $reader): void { $delivery_tag = $reader->read_longlong(); $multiple = (bool) $reader->read_bit(); if (!isset($this->published_messages[$delivery_tag])) { throw new AMQPRuntimeException(sprintf( 'Server nack\'ed unknown delivery_tag "%s"', $delivery_tag )); } $this->internal_ack_handler($delivery_tag, $multiple, $this->nack_handler); } protected function internal_ack_handler($delivery_tag, $multiple, $handler) { if ($multiple) { $keys = $this->get_keys_less_or_equal($this->published_messages, $delivery_tag); foreach ($keys as $key) { $this->internal_ack_handler($key, false, $handler); } } else { $message = $this->get_and_unset_message($delivery_tag); $this->dispatch_to_handler($handler, array($message)); } } protected function get_keys_less_or_equal(array $messages, $value) { $value = (int) $value; $keys = array_reduce( array_keys($messages), function ($keys, $key) use ($value) { if ($key <= $value) { $keys[] = $key; } return $keys; }, array() ); return $keys; } public function basic_nack($delivery_tag, $multiple = false, $requeue = false) { list($class_id, $method_id, $args) = $this->protocolWriter->basicNack($delivery_tag, $multiple, $requeue); $this->send_method_frame(array($class_id, $method_id), $args); } public function basic_cancel($consumer_tag, $nowait = false, $noreturn = false) { list($class_id, $method_id, $args) = $this->protocolWriter->basicCancel($consumer_tag, $nowait); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait || $noreturn) { unset($this->callbacks[$consumer_tag]); return $consumer_tag; } return $this->wait(array( $this->waitHelper->get_wait('basic.cancel_ok') ), false, $this->channel_rpc_timeout); } protected function basic_cancel_from_server(AMQPReader $reader) { throw new AMQPBasicCancelException($reader->read_shortstr()); } protected function basic_cancel_ok(AMQPReader $reader): string { $consumerTag = $reader->read_shortstr(); unset($this->callbacks[$consumerTag]); return $consumerTag; } public function is_consuming() { return !empty($this->callbacks); } public function basic_consume( $queue = '', $consumer_tag = '', $no_local = false, $no_ack = false, $exclusive = false, $nowait = false, $callback = null, $ticket = null, $arguments = array() ) { if (null !== $callback) { Assert::isCallable($callback); } if ($nowait && empty($consumer_tag)) { throw new \InvalidArgumentException('Cannot start consumer without consumer_tag and no-wait=true'); } if (!empty($consumer_tag) && array_key_exists($consumer_tag, $this->callbacks)) { throw new \InvalidArgumentException('This consumer tag is already registered.'); } $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->basicConsume( $ticket, $queue, $consumer_tag, $no_local, $no_ack, $exclusive, $nowait, $this->protocolVersion === Wire\Constants091::VERSION ? $arguments : null ); $this->send_method_frame(array($class_id, $method_id), $args); if (false === $nowait) { $consumer_tag = $this->wait(array( $this->waitHelper->get_wait('basic.consume_ok') ), false, $this->channel_rpc_timeout); } $this->callbacks[$consumer_tag] = $callback; return $consumer_tag; } protected function basic_consume_ok(AMQPReader $reader): string { return $reader->read_shortstr(); } protected function basic_deliver(AMQPReader $reader, AMQPMessage $message): void { $consumer_tag = $reader->read_shortstr(); $delivery_tag = $reader->read_longlong(); $redelivered = $reader->read_bit(); $exchange = $reader->read_shortstr(); $routing_key = $reader->read_shortstr(); $message ->setChannel($this) ->setDeliveryInfo($delivery_tag, $redelivered, $exchange, $routing_key) ->setConsumerTag($consumer_tag); if (isset($this->callbacks[$consumer_tag])) { call_user_func($this->callbacks[$consumer_tag], $message); } } public function basic_get($queue = '', $no_ack = false, $ticket = null) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->basicGet($ticket, $queue, $no_ack); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('basic.get_ok'), $this->waitHelper->get_wait('basic.get_empty') ), false, $this->channel_rpc_timeout); } protected function basic_get_empty() { } protected function basic_get_ok(AMQPReader $reader, AMQPMessage $message): AMQPMessage { $delivery_tag = $reader->read_longlong(); $redelivered = $reader->read_bit(); $exchange = $reader->read_shortstr(); $routing_key = $reader->read_shortstr(); $message_count = $reader->read_long(); $message ->setChannel($this) ->setDeliveryInfo($delivery_tag, $redelivered, $exchange, $routing_key) ->setMessageCount($message_count); return $message; } private function prePublish($exchange, $routing_key, $mandatory, $immediate, $ticket) { $cache_key = sprintf( '%s|%s|%s|%s|%s', $exchange, $routing_key, $mandatory, $immediate, $ticket ); if (false === isset($this->publish_cache[$cache_key])) { $ticket = $this->getTicket($ticket); list($class_id, $method_id, $args) = $this->protocolWriter->basicPublish( $ticket, $exchange, $routing_key, $mandatory, $immediate ); $pkt = $this->prepare_method_frame(array($class_id, $method_id), $args); $this->publish_cache[$cache_key] = $pkt->getvalue(); if (count($this->publish_cache) > $this->publish_cache_max_size) { reset($this->publish_cache); $old_key = key($this->publish_cache); unset($this->publish_cache[$old_key]); } } return $this->publish_cache[$cache_key]; } public function basic_publish( $msg, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false, $ticket = null ) { $this->checkConnection(); $pkt = new AMQPWriter(); $pkt->write($this->prePublish($exchange, $routing_key, $mandatory, $immediate, $ticket)); try { $this->connection->send_content( $this->channel_id, 60, 0, mb_strlen($msg->body, 'ASCII'), $msg->serialize_properties(), $msg->body, $pkt ); } catch (AMQPConnectionClosedException $e) { $this->do_close(); throw $e; } if ($this->next_delivery_tag > 0) { $this->published_messages[$this->next_delivery_tag] = $msg; $msg->setDeliveryInfo($this->next_delivery_tag, false, $exchange, $routing_key); $this->next_delivery_tag++; } } public function batch_basic_publish( $message, $exchange = '', $routing_key = '', $mandatory = false, $immediate = false, $ticket = null ) { $this->batch_messages[] = [ $message, $exchange, $routing_key, $mandatory, $immediate, $ticket ]; } public function publish_batch() { if (empty($this->batch_messages)) { return; } $this->checkConnection(); $pkt = new AMQPWriter(); foreach ($this->batch_messages as $m) { $msg = $m[0]; $exchange = isset($m[1]) ? $m[1] : ''; $routing_key = isset($m[2]) ? $m[2] : ''; $mandatory = isset($m[3]) ? $m[3] : false; $immediate = isset($m[4]) ? $m[4] : false; $ticket = isset($m[5]) ? $m[5] : null; $pkt->write($this->prePublish($exchange, $routing_key, $mandatory, $immediate, $ticket)); $this->connection->prepare_content( $this->channel_id, 60, 0, mb_strlen($msg->body, 'ASCII'), $msg->serialize_properties(), $msg->body, $pkt ); if ($this->next_delivery_tag > 0) { $this->published_messages[$this->next_delivery_tag] = $msg; $this->next_delivery_tag++; } } $this->connection->write($pkt->getvalue()); $this->batch_messages = array(); } public function basic_qos($prefetch_size, $prefetch_count, $a_global) { list($class_id, $method_id, $args) = $this->protocolWriter->basicQos( $prefetch_size, $prefetch_count, $a_global ); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('basic.qos_ok') ), false, $this->channel_rpc_timeout); } protected function basic_qos_ok() { } public function basic_recover($requeue = false) { list($class_id, $method_id, $args) = $this->protocolWriter->basicRecover($requeue); $this->send_method_frame(array($class_id, $method_id), $args); return $this->wait(array( $this->waitHelper->get_wait('basic.recover_ok') ), false, $this->channel_rpc_timeout); } protected function basic_recover_ok() { } public function basic_reject($delivery_tag, $requeue) { list($class_id, $method_id, $args) = $this->protocolWriter->basicReject($delivery_tag, $requeue); $this->send_method_frame(array($class_id, $method_id), $args); } protected function basic_return(AMQPReader $reader, AMQPMessage $message) { $callback = $this->basic_return_callback; if (!is_callable($callback)) { $this->debug->debug_msg('Skipping unhandled basic_return message'); return null; } $reply_code = $reader->read_short(); $reply_text = $reader->read_shortstr(); $exchange = $reader->read_shortstr(); $routing_key = $reader->read_shortstr(); call_user_func_array($callback, array( $reply_code, $reply_text, $exchange, $routing_key, $message, )); } public function tx_commit() { $this->send_method_frame(array(90, 20)); return $this->wait(array( $this->waitHelper->get_wait('tx.commit_ok') ), false, $this->channel_rpc_timeout); } protected function tx_commit_ok() { } public function tx_rollback() { $this->send_method_frame(array(90, 30)); return $this->wait(array( $this->waitHelper->get_wait('tx.rollback_ok') ), false, $this->channel_rpc_timeout); } protected function tx_rollback_ok() { } public function confirm_select($nowait = false) { list($class_id, $method_id, $args) = $this->protocolWriter->confirmSelect($nowait); $this->send_method_frame(array($class_id, $method_id), $args); if ($nowait) { return null; } $this->wait(array( $this->waitHelper->get_wait('confirm.select_ok') ), false, $this->channel_rpc_timeout); $this->next_delivery_tag = 1; } public function confirm_select_ok() { } public function wait_for_pending_acks($timeout = 0) { $functions = array( $this->waitHelper->get_wait('basic.ack'), $this->waitHelper->get_wait('basic.nack'), ); $timeout = max(0, $timeout); while (!empty($this->published_messages)) { $this->wait($functions, false, $timeout); } } public function wait_for_pending_acks_returns($timeout = 0) { $functions = array( $this->waitHelper->get_wait('basic.ack'), $this->waitHelper->get_wait('basic.nack'), $this->waitHelper->get_wait('basic.return'), ); $timeout = max(0, $timeout); while (!empty($this->published_messages)) { $this->wait($functions, false, $timeout); } } public function tx_select() { $this->send_method_frame(array(90, 10)); return $this->wait(array( $this->waitHelper->get_wait('tx.select_ok') ), false, $this->channel_rpc_timeout); } protected function tx_select_ok() { } protected function getTicket($ticket) { return (null === $ticket) ? $this->default_ticket : $ticket; } protected function get_and_unset_message($index) { $message = $this->published_messages[$index]; unset($this->published_messages[$index]); return $message; } public function set_return_listener($callback) { Assert::isCallable($callback); $this->basic_return_callback = $callback; } public function set_nack_handler($callback) { Assert::isCallable($callback); $this->nack_handler = $callback; } public function set_ack_handler($callback) { Assert::isCallable($callback); $this->ack_handler = $callback; } private function checkConnection() { if ($this->connection === null || !$this->connection->isConnected()) { throw new AMQPChannelClosedException('Channel connection is closed.'); } if ($this->connection->isBlocked()) { throw new AMQPConnectionBlockedException(); } } public function consume(float $maximumPoll = 10.0): void { $this->checkConnection(); if ($this->stopConsume) { $this->stopConsume = false; return; } $timeout = $this->connection->getReadTimeout(); $heartBeat = $this->connection->getHeartbeat(); if ($heartBeat > 2) { $timeout = min($timeout, floor($heartBeat / 2)); } $timeout = max(min($timeout, $maximumPoll), 1); while ($this->is_consuming() || !empty($this->method_queue)) { if ($this->stopConsume) { $this->stopConsume = false; return; } try { $this->wait(null, false, $timeout); } catch (AMQPTimeoutException $exception) { $this->connection->checkHeartBeat(); continue; } catch (AMQPNoDataException $exception) { continue; } } } public function stopConsume() { $this->stopConsume = true; } } class = $class; $this->method = $method; $this->arguments = $arguments; } public function getClass(): int { return $this->class; } public function getMethod(): int { return $this->method; } public function getArguments(): string { return $this->arguments; } public function getSignature(): string { return $this->class . ',' . $this->method; } } amqp_reply_code = $reply_code; $this->amqp_reply_text = $reply_text; $this->amqp_method_sig = $method_sig; $this->args = array($reply_code, $reply_text, $method_sig); } } timeout = $timeout; } public static function writeTimeout($timeout, $code = 0) { return new self('Error sending data. Connection timed out.', $timeout, $code); } public function getTimeout() { return $this->timeout; } } consumerTag = $consumerTag; } public function getConsumerTag() { return $this->consumerTag; } } buildAndCacheFromFactory($className); } private function buildAndCacheFromFactory(string $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(string $className): callable { $reflectionClass = $this->getReflectionClass($className); if ($this->isInstantiableViaReflection($reflectionClass)) { return [$reflectionClass, 'newInstanceWithoutConstructor']; } $serializedString = sprintf( '%s:%d:"%s":0:{}', is_subclass_of($className, Serializable::class) ? self::SERIALIZATION_FORMAT_USE_UNSERIALIZER : self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, strlen($className), $className ); $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); return static function () use ($serializedString) { return unserialize($serializedString); }; } private function getReflectionClass(string $className): ReflectionClass { if (! class_exists($className)) { throw InvalidArgumentException::fromNonExistingClass($className); } if (PHP_VERSION_ID >= 80100 && enum_exists($className, false)) { throw InvalidArgumentException::fromEnum($className); } $reflection = new ReflectionClass($className); if ($reflection->isAbstract()) { throw InvalidArgumentException::fromAbstractClass($reflection); } return $reflection; } private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, string $serializedString): void { set_error_handler(static function (int $code, string $message, string $file, int $line) use ($reflectionClass, &$error): bool { $error = UnexpectedValueException::fromUncleanUnSerialization( $reflectionClass, $message, $code, $file, $line ); return true; }); try { $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); } finally { restore_error_handler(); } if ($error) { throw $error; } } private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, string $serializedString): void { try { unserialize($serializedString); } catch (Exception $exception) { throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); } } private function isInstantiableViaReflection(ReflectionClass $reflectionClass): bool { return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); } private function hasInternalAncestors(ReflectionClass $reflectionClass): bool { do { if ($reflectionClass->isInternal()) { return true; } $reflectionClass = $reflectionClass->getParentClass(); } while ($reflectionClass); return false; } private function isSafeToClone(ReflectionClass $reflectionClass): bool { return $reflectionClass->isCloneable() && ! $reflectionClass->hasMethod('__clone') && ! $reflectionClass->isSubclassOf(ArrayIterator::class); } } getName() )); } public static function fromEnum(string $className): self { return new self(sprintf( 'The provided class "%s" is an enum, and cannot be instantiated', $className )); } } getName() ), 0, $exception ); } public static function fromUncleanUnSerialization( ReflectionClass $reflectionClass, string $errorString, int $errorCode, string $errorFile, int $errorLine ): self { 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) ); } } 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); } } copier = $copier; } public function apply($arrayObject) { $clone = clone $arrayObject; foreach ($arrayObject->getArrayCopy() as $k => $v) { $clone->offsetSet($k, $this->copier->copy($v)); } return $clone; } } callback = $callable; } public function apply($element) { return call_user_func($this->callback, $element); } } $propertyValue) { $copy->{$propertyName} = $propertyValue; } return $copy; } } copy($value); } } 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 ) ); } } propertyType = $propertyType; } public function matches($object, $property) { try { $reflectionProperty = ReflectionHelper::getProperty($object, $property); } catch (ReflectionException $exception) { return false; } $reflectionProperty->setAccessible(true); if (method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) { return false; } return $reflectionProperty->getValue($object) instanceof $this->propertyType; } } class = $class; $this->property = $property; } public function matches($object, $property) { return ($object instanceof $this->class) && $property == $this->property; } } property = $property; } public function matches($object, $property) { return $property == $this->property; } } useCloneMethod = $useCloneMethod; $this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class)); $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 prependFilter(Filter $filter, Matcher $matcher) { array_unshift($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; } if (PHP_VERSION_ID >= 80100 && enum_exists(get_class($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); } ); if ($filter instanceof ChainableFilter) { continue; } return; } } $property->setAccessible(true); if (method_exists($property, 'isInitialized') && !$property->isInitialized($object)) { return; } $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; } } type = $type; } public function matches($element) { return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type; } } 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); } } filter = $filter; } public function apply($object, $property, $objectCopier) { $this->filter->apply($object, $property, $objectCopier); } } setAccessible(true); $reflectionProperty->setValue($object, new ArrayCollection()); } } setAccessible(true); $oldCollection = $reflectionProperty->getValue($object); $newCollection = $oldCollection->map( function ($item) use ($objectCopier) { return $objectCopier($item); } ); $reflectionProperty->setValue($object, $newCollection); } } __load(); } } 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; } } 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; } } handlers = $this->getDefaultHandlers(); } protected function getDefaultHandlers() { return [ '+' => 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 response header'); } $prefix = $header[0]; if (!isset($this->handlers[$prefix])) { $this->onProtocolError($connection, "Unknown response prefix: '$prefix'"); } return $this->handlers[$prefix]->handle($connection, substr($header, 1)); } protected function onProtocolError(CompositeConnectionInterface $connection, $message) { CommunicationException::handle( new ProtocolException($connection, "$message [{$connection->getParameters()}]") ); } } 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 = []; 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' [{$connection->getParameters()}]" )); return; } } public function useIterableMultibulk($value) { $this->mbiterable = (bool) $value; } } getParameters()}]" )); } if ($length >= 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 [{$connection->getParameters()}]" )); return; } } getParameters()}]" )); } return new MultiBulkIterator($connection, $length); } } getParameters()}]" )); } if ($length === -1) { return; } $list = []; if ($length > 0) { $handlersCache = []; $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; } } getParameters()}]" )); } return; } } client->onFlushed($callback); } public function onInvalidated(?callable $callback, string $pattern = null) { return $this->client->onInvalidated($callback, $pattern); } public function dispatchEvents() { return $this->client->dispatchEvents(); } public function addIgnorePatterns(string ...$pattern) { return $this->client->addIgnorePatterns(...$pattern); } public function addAllowPatterns(string ...$pattern) { return $this->client->addAllowPatterns(...$pattern); } public function endpointId() { return $this->client->endpointId(); } public function socketId() { return $this->client->socketId(); } public function license() { return $this->client->license(); } public function stats() { return $this->client->stats(); } public function maxMemory() { return $this->client->maxMemory(); } public function flushMemory(string $endpointId = null, int $db = null) { return $this->client->flushMemory($endpointId, $db); } } '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 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 ?: []; } 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) ? [$parameters->username, $parameters->password] : [$parameters->password]; $connection->addConnectCommand( new RawCommand('AUTH', $cmdAuthArgs) ); } if ($parameters->client_info ?? false && !$connection instanceof RelayConnection) { $connection->addConnectCommand( new RawCommand('CLIENT', ['SETINFO', 'LIB-NAME', 'predis']) ); $connection->addConnectCommand( new RawCommand('CLIENT', ['SETINFO', 'LIB-VER', Client::VERSION]) ); } if (isset($parameters->database) && strlen($parameters->database)) { $connection->addConnectCommand( new RawCommand('SELECT', [$parameters->database]) ); } } } 'tcp', 'host' => '127.0.0.1', 'port' => 6379, ]; protected $parameters; public function __construct(array $parameters = []) { $this->parameters = $this->filter($parameters + static::$defaults); } protected function filter(array $parameters) { return array_filter($parameters, function ($value) { return $value !== null && $value !== ''; }); } public static function create($parameters) { if (is_string($parameters)) { $parameters = static::parse($parameters); } return new static($parameters ?: []); } 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; } public function toArray() { return $this->parameters; } public function __get($parameter) { if (isset($this->parameters[$parameter])) { return $this->parameters[$parameter]; } } public function __isset($parameter) { return isset($this->parameters[$parameter]); } public function __toString() { if ($this->scheme === 'unix') { return "$this->scheme:$this->path"; } if (filter_var($this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return "$this->scheme://[$this->host]:$this->port"; } return "$this->scheme://$this->host:$this->port"; } public function __sleep() { return ['parameters']; } } parameters->persistent) && $this->parameters->persistent) { return; } $this->disconnect(); } protected function assertParameters(ParametersInterface $parameters) { switch ($parameters->scheme) { case 'tcp': case 'redis': case 'unix': case 'tls': case 'rediss': break; default: throw new InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); } return $parameters; } 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); $context = stream_context_create(['socket' => ['tcp_nodelay' => (bool) $parameters->tcp_nodelay]]); if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context)) { $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); } 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}"; } } } return $this->createStreamSocket($parameters, $address, $flags); } 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.' ); } } } return $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags); } protected function tlsStreamInitializer(ParametersInterface $parameters) { $resource = $this->tcpStreamInitializer($parameters); $metadata = stream_get_meta_data($resource); if (isset($metadata['crypto'])) { return $resource; } if (isset($parameters->ssl) && is_array($parameters->ssl)) { $options = $parameters->ssl; } else { $options = []; } if (!isset($options['crypto_type'])) { $options['crypto_type'] = STREAM_CRYPTO_METHOD_TLS_CLIENT; } if (!stream_context_set_option($resource, ['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 && $command->getId() === 'CLIENT') { } elseif ($response instanceof ErrorResponseInterface) { $this->onConnectionError("`{$command->getId()}` failed: {$response->getMessage()}", 0); } } } } public function disconnect() { if ($this->isConnected()) { $resource = $this->getResource(); if (is_resource($resource)) { fclose($resource); } parent::disconnect(); } } protected function write($buffer) { $socket = $this->getResource(); while (($length = strlen($buffer)) > 0) { $written = is_resource($socket) ? @fwrite($socket, $buffer) : false; 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 = is_resource($socket) ? fread($socket, min($bytesLeft, 4096)) : false; 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 = []; 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(); $this->parameters = $this->assertParameters($parameters); $this->client = $this->createClient(); } public function isConnected() { return $this->client->isConnected(); } public function disconnect() { if ($this->client->isConnected()) { $this->client->close(); } } private function assertExtensions() { if (!extension_loaded('relay')) { throw new NotSupportedException( 'The "relay" extension is required by this connection backend.' ); } } protected function assertParameters(ParametersInterface $parameters) { if (!in_array($parameters->scheme, ['tcp', 'tls', 'unix', 'redis', 'rediss'])) { throw new InvalidArgumentException("Invalid scheme: '{$parameters->scheme}'."); } if (!in_array($parameters->serializer, [null, 'php', 'igbinary', 'msgpack', 'json'])) { throw new InvalidArgumentException("Invalid serializer: '{$parameters->serializer}'."); } if (!in_array($parameters->compression, [null, 'lzf', 'lz4', 'zstd'])) { throw new InvalidArgumentException("Invalid compression algorithm: '{$parameters->compression}'."); } return $parameters; } private function createClient() { $client = new Relay(); $client->setOption(Relay::OPT_PHPREDIS_COMPATIBILITY, false); $client->setOption(Relay::OPT_REPLY_LITERAL, true); $client->setOption(Relay::OPT_MAX_RETRIES, 0); $client->setOption(Relay::OPT_USE_CACHE, $this->parameters->cache ?? true); $client->setOption(Relay::OPT_SERIALIZER, constant(sprintf( '%s::SERIALIZER_%s', Relay::class, strtoupper($this->parameters->serializer ?? 'none') ))); $client->setOption(Relay::OPT_COMPRESSION, constant(sprintf( '%s::COMPRESSION_%s', Relay::class, strtoupper($this->parameters->compression ?? 'none') ))); return $client; } public function getClient() { return $this->client; } protected function getIdentifier() { return $this->client->endpointId(); } protected function createStreamSocket(ParametersInterface $parameters, $address, $flags) { $timeout = isset($parameters->timeout) ? (float) $parameters->timeout : 5.0; $retry_interval = 0; $read_timeout = 5.0; if (isset($parameters->read_write_timeout)) { $read_timeout = (float) $parameters->read_write_timeout; $read_timeout = $read_timeout > 0 ? $read_timeout : 0; } try { $this->client->connect( $parameters->path ?? $parameters->host, isset($parameters->path) ? 0 : $parameters->port, $timeout, null, $retry_interval, $read_timeout ); } catch (RelayException $ex) { $this->onConnectionError($ex->getMessage(), $ex->getCode()); } return $this->client; } public function executeCommand(CommandInterface $command) { if (!$this->client->isConnected()) { $this->getResource(); } try { $name = $command->getId(); return in_array($name, $this->atypicalCommands) ? $this->client->{$name}(...$command->getArguments()) : $this->client->rawCommand($name, ...$command->getArguments()); } catch (RelayException $ex) { throw $this->onCommandError($ex, $command); } } public function onCommandError(RelayException $exception, CommandInterface $command) { $code = $exception->getCode(); $message = $exception->getMessage(); if (strpos($message, 'RELAY_ERR_IO')) { return new ConnectionException($this, $message, $code, $exception); } if (strpos($message, 'RELAY_ERR_REDIS')) { return new ServerException($message, $code, $exception); } if (strpos($message, 'RELAY_ERR_WRONGTYPE') && strpos($message, "Got reply-type 'status'")) { $message = 'Operation against a key holding the wrong kind of value'; } return new ClientException($message, $code, $exception); } public function pack($value) { return $this->client->_pack($value); } public function unpack($value) { return $this->client->_unpack($value); } public function writeRequest(CommandInterface $command) { throw new NotSupportedException('The "relay" extension does not support writing requests.'); } public function readResponse(CommandInterface $command) { throw new NotSupportedException('The "relay" extension does not support reading responses.'); } public function __destruct() { $this->disconnect(); } public function __wakeup() { $this->assertExtensions(); $this->client = $this->createClient(); } } 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) { $socket = null; $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); $context = stream_context_create(['socket' => ['tcp_nodelay' => (bool) $parameters->tcp_nodelay]]); if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context)) { $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 = [ '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(), ['protocol']); } } 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 = [ '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 = [$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->getMessage()}", 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(); } } 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 = []; $this->pool = []; } public function add(NodeConnectionInterface $connection) { $parameters = $connection->getParameters(); $role = $parameters->role; if ('master' === $role) { $this->master = $connection; } elseif ('sentinel' === $role) { $this->sentinels[] = $connection; return; } else { $this->slaves[] = $connection; } $this->pool[(string) $connection] = $connection; $this->reset(); } public function remove(NodeConnectionInterface $connection) { if ($connection === $this->master) { $this->master = null; } elseif (false !== $id = array_search($connection, $this->slaves, true)) { unset($this->slaves[$id]); } elseif (false !== $id = array_search($connection, $this->sentinels, true)) { unset($this->sentinels[$id]); return true; } else { return false; } unset($this->pool[(string) $connection]); $this->reset(); return true; } 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; if (!isset($parameters['password'])) { $parameters['password'] = null; } if (!isset($parameters['timeout'])) { $parameters['timeout'] = $this->sentinelTimeout; } } return $this->connectionFactory->create($parameters); } public function getSentinelConnection() { if (!$this->sentinelConnection) { if ($this->sentinelIndex >= count($this->sentinels)) { $this->sentinelIndex = 0; throw new \Predis\ClientException('No sentinel server available for autodiscovery.'); } $sentinel = $this->sentinels[$this->sentinelIndex]; ++$this->sentinelIndex; $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 = []; $this->sentinelIndex = 0; $this->sentinels[] = $sentinel->getParameters()->toArray(); foreach ($payload as $sentinel) { $this->sentinels[] = [ 'host' => $sentinel[3], 'port' => $sentinel[5], 'role' => 'sentinel', ]; } } 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 [ 'host' => $payload[0], 'port' => $payload[1], 'role' => 'master', ]; } protected function querySentinelForSlaves(NodeConnectionInterface $sentinel, $service) { $slaves = []; $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, ['s_down', 'o_down', 'disconnected'])) { continue; } $slaves[] = [ 'host' => $slave[3], 'port' => $slave[5], 'role' => 'slave', ]; } 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); } protected function pickSlave() { $slaves = $this->getSlaves(); return $slaves ? $slaves[rand(1, count($slaves)) - 1] : null; } 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 getConnectionByCommand(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($id) { return $this->pool[$id] ?? null; } public function getConnectionByRole($role) { if ($role === 'master') { return $this->getMaster(); } elseif ($role === 'slave') { return $this->pickSlave(); } elseif ($role === 'sentinel') { return $this->getSentinelConnection(); } else { return null; } } public function switchTo(NodeConnectionInterface $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() { $connection = $this->getConnectionByRole('master'); $this->switchTo($connection); } public function switchToSlave() { $connection = $this->getConnectionByRole('slave'); $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() { foreach ($this->pool as $connection) { $connection->disconnect(); } } private function retryCommandOnFailure(CommandInterface $command, $method) { $retries = 0; while ($retries <= $this->retryLimit) { try { $response = $this->getConnectionByCommand($command)->$method($command); break; } catch (CommunicationException $exception) { $this->wipeServerList(); $exception->getConnection()->disconnect(); if ($retries === $this->retryLimit) { throw $exception; } usleep($this->retryWait * 1000); ++$retries; } } 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 [ 'master', 'slaves', 'pool', 'service', 'sentinels', 'connectionFactory', 'strategy', ]; } } 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) { $parameters = $connection->getParameters(); if ('master' === $parameters->role) { $this->master = $connection; } else { $this->slaves[] = $connection; } if (isset($parameters->alias)) { $this->aliases[$parameters->alias] = $connection; } $this->pool[(string) $connection] = $connection; $this->reset(); } public function remove(NodeConnectionInterface $connection) { if ($connection === $this->master) { $this->master = null; } elseif (false !== $id = array_search($connection, $this->slaves, true)) { unset($this->slaves[$id]); } else { return false; } unset($this->pool[(string) $connection]); if ($this->aliases && $alias = $connection->getParameters()->alias) { unset($this->aliases[$alias]); } $this->reset(); return true; } public function getConnectionByCommand(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($id) { return $this->pool[$id] ?? null; } public function getConnectionByAlias($alias) { return $this->aliases[$alias] ?? null; } public function getConnectionByRole($role) { if ($role === 'master') { return $this->getMaster(); } elseif ($role === 'slave') { return $this->pickSlave(); } return null; } public function switchTo(NodeConnectionInterface $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.'); } $this->current = $connection; } public function switchToMaster() { if (!$connection = $this->getConnectionByRole('master')) { throw new InvalidArgumentException('Invalid connection or connection not found.'); } $this->switchTo($connection); } public function switchToSlave() { if (!$connection = $this->getConnectionByRole('slave')) { throw new InvalidArgumentException('Invalid connection or connection not found.'); } $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 $this->slaves; } public function getReplicationStrategy() { return $this->strategy; } protected function pickSlave() { if (!$this->slaves) { return null; } 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() { foreach ($this->pool as $connection) { $connection->disconnect(); } } private function handleInfoResponse($response) { $info = []; foreach (preg_split('/\r?\n/', $response) as $row) { if (strpos($row, ':') === false) { continue; } [$k, $v] = explode(':', $row, 2); $info[$k] = $v; } return $info; } public function discover() { if (!$this->connectionFactory) { throw new ClientException('Discovery requires a connection factory'); } while (true) { try { if ($connection = $this->getMaster()) { $this->discoverFromMaster($connection, $this->connectionFactory); break; } elseif ($connection = $this->pickSlave()) { $this->discoverFromSlave($connection, $this->connectionFactory); break; } else { throw new ClientException('No connection available for discovery'); } } catch (ConnectionException $exception) { $this->remove($connection); } } } 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 = []; foreach ($replication as $k => $v) { $parameters = null; if (strpos($k, 'slave') === 0 && preg_match('/ip=(?P.*),port=(?P\d+)/', $v, $parameters)) { $slaveConnection = $connectionFactory->create([ 'host' => $parameters['host'], 'port' => $parameters['port'], 'role' => 'slave', ]); $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([ 'host' => $replication['master_host'], 'port' => $replication['master_port'], 'role' => 'master', ]); $this->add($masterConnection); $this->discoverFromMaster($masterConnection, $connectionFactory); } private function retryCommandOnFailure(CommandInterface $command, $method) { while (true) { try { $connection = $this->getConnectionByCommand($command); $response = $connection->$method($command); if ($response instanceof ResponseErrorInterface && $response->getErrorType() === 'LOADING') { throw new ConnectionException($connection, "Redis is loading the dataset in memory [$connection]"); } break; } 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(); } } catch (MissingMasterException $exception) { if ($this->autoDiscovery) { $this->discover(); } else { throw $exception; } } } 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 ['master', 'slaves', 'pool', 'aliases', 'strategy']; } } 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 = [ 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 => [$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 = trim(curl_error($resource)); $errno = curl_errno($resource); throw new ConnectionException($this, "$error{$this->getParameters()}]", $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 ['parameters']; } public function __wakeup() { $this->assertExtensions(); $this->resource = $this->createCurl(); $this->reader = $this->createReader(); } } connections = $connections; $this->strategy = $strategy ?: new RedisClusterStrategy(); $this->slotmap = new SlotMap(); } public function setRetryLimit($retry) { $this->retryLimit = (int) $retry; } public function setRetryInterval($retryInterval) { $this->retryInterval = (int) $retryInterval; } public function getRetryInterval() { return (int) $this->retryInterval; } 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; $this->slotmap->reset(); } public function remove(NodeConnectionInterface $connection) { if (false !== $id = array_search($connection, $this->pool, true)) { $this->slotmap->reset(); $this->slots = array_diff($this->slots, [$connection]); unset($this->pool[$id]); return true; } return false; } public function removeById($connectionID) { if (isset($this->pool[$connectionID])) { $this->slotmap->reset(); $this->slots = array_diff($this->slots, [$connectionID]); unset($this->pool[$connectionID]); return true; } return false; } public function buildSlotMap() { $this->slotmap->reset(); 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->slotmap->setSlots($slots[0], $slots[1], $connectionID); } } } private function queryClusterNodeForSlotMap(NodeConnectionInterface $connection) { $retries = 0; $retryAfter = $this->retryInterval; $command = RawCommand::create('CLUSTER', 'SLOTS'); while ($retries <= $this->retryLimit) { try { $response = $connection->executeCommand($command); break; } 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`'); } usleep($retryAfter * 1000); $retryAfter = $retryAfter * 2; ++$retries; } } return $response; } public function askSlotMap(NodeConnectionInterface $connection = null) { if (!$connection && !$connection = $this->getRandomConnection()) { return; } $this->slotmap->reset(); $response = $this->queryClusterNodeForSlotMap($connection); foreach ($response as $slots) { [$start, $end, $master] = $slots; if ($master[0] === '') { $this->slotmap->setSlots($start, $end, (string) $connection); } else { $this->slotmap->setSlots($start, $end, "{$master[0]}:{$master[1]}"); } } } protected function guessNode($slot) { if (!$this->pool) { throw new ClientException('No connections available in the pool'); } if ($this->slotmap->isEmpty()) { $this->buildSlotMap(); } if ($node = $this->slotmap[$slot]) { return $node; } $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([ 'host' => substr($connectionID, 0, $separator), 'port' => substr($connectionID, $separator + 1), ]); } public function getConnectionByCommand(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 (!SlotMap::isValid($slot)) { 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) { return $this->pool[$connectionID] ?? null; } protected function getRandomConnection() { if (!$this->pool) { return null; } return $this->pool[array_rand($this->pool)]; } protected function move(NodeConnectionInterface $connection, $slot) { $this->pool[(string) $connection] = $connection; $this->slots[(int) $slot] = $connection; $this->slotmap[(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) { [$slot, $connectionID] = explode(' ', $details, 2); if (!$connection = $this->getConnectionById($connectionID)) { $connection = $this->createConnection($connectionID); } if ($this->useClusterSlots) { $this->askSlotMap($connection); } $this->move($connection, $slot); return $this->executeCommand($command); } protected function onAskResponse(CommandInterface $command, $details) { [$slot, $connectionID] = explode(' ', $details, 2); if (!$connection = $this->getConnectionById($connectionID)) { $connection = $this->createConnection($connectionID); } $connection->executeCommand(RawCommand::create('ASKING')); return $connection->executeCommand($command); } private function retryCommandOnFailure(CommandInterface $command, $method) { $retries = 0; $retryAfter = $this->retryInterval; while ($retries <= $this->retryLimit) { try { $response = $this->getConnectionByCommand($command)->$method($command); if ($response instanceof ErrorResponse) { $message = $response->getMessage(); if (strpos($message, 'CLUSTERDOWN') !== false) { throw new ServerException($message); } } break; } catch (Throwable $exception) { usleep($retryAfter * 1000); $retryAfter = $retryAfter * 2; if ($exception instanceof ConnectionException) { $connection = $exception->getConnection(); if ($connection) { $connection->disconnect(); $this->remove($connection); } } if ($retries === $this->retryLimit) { throw $exception; } if ($this->useClusterSlots) { $this->askSlotMap(); } ++$retries; } } 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->slotmap->isEmpty()) { $this->useClusterSlots ? $this->askSlotMap() : $this->buildSlotMap(); } $connections = []; foreach ($this->slotmap->getNodes() as $node) { if (!$connection = $this->getConnectionById($node)) { $this->add($connection = $this->createConnection($node)); } $connections[] = $connection; } return new ArrayIterator($connections); } public function getSlotMap() { return $this->slotmap; } public function getClusterStrategy() { return $this->strategy; } public function getConnectionFactory() { return $this->connections; } public function useClusterSlots($value) { $this->useClusterSlots = (bool) $value; } } 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(); $this->pool[(string) $connection] = $connection; if (isset($parameters->alias)) { $this->aliases[$parameters->alias] = $connection; } $this->distributor->add($connection, $parameters->weight); } public function remove(NodeConnectionInterface $connection) { if (false !== $id = array_search($connection, $this->pool, true)) { unset($this->pool[$id]); $this->distributor->remove($connection); if ($this->aliases && $alias = $connection->getParameters()->alias) { unset($this->aliases[$alias]); } return true; } return false; } public function getConnectionByCommand(CommandInterface $command) { $slot = $this->strategy->getSlot($command); if (!isset($slot)) { throw new NotSupportedException( "Cannot use '{$command->getId()}' over clusters of connections." ); } return $this->distributor->getBySlot($slot); } public function getConnectionById($id) { return $this->pool[$id] ?? null; } public function getConnectionByAlias($alias) { return $this->aliases[$alias] ?? null; } public function getConnectionBySlot($slot) { return $this->distributor->getBySlot($slot); } public function getConnectionByKey($key) { $hash = $this->strategy->getSlotByKey($key); return $this->distributor->getBySlot($hash); } 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->getConnectionByCommand($command)->writeRequest($command); } public function readResponse(CommandInterface $command) { return $this->getConnectionByCommand($command)->readResponse($command); } public function executeCommand(CommandInterface $command) { return $this->getConnectionByCommand($command)->executeCommand($command); } } 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 getInitCommands(): array { return $this->initCommands; } public function executeCommand(CommandInterface $command) { $this->writeRequest($command); return $this->readResponse($command); } public function readResponse(CommandInterface $command) { return $this->read(); } protected function onConnectionError($message, $code = 0) { CommunicationException::handle( new ConnectionException($this, "$message [{$this->getParameters()}]", $code) ); } protected function onProtocolError($message) { CommunicationException::handle( new ProtocolException($this, "$message [{$this->getParameters()}]") ); } 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 ['parameters', 'initCommands']; } } [ ['name' => 'Json', 'commandPrefix' => 'JSON'], ['name' => 'BloomFilter', 'commandPrefix' => 'BF'], ['name' => 'CuckooFilter', 'commandPrefix' => 'CF'], ['name' => 'CountMinSketch', 'commandPrefix' => 'CMS'], ['name' => 'TDigest', 'commandPrefix' => 'TDIGEST'], ['name' => 'TopK', 'commandPrefix' => 'TOPK'], ['name' => 'Search', 'commandPrefix' => 'FT'], ['name' => 'TimeSeries', 'commandPrefix' => 'TS'], ], ]; public static function getModules(): array { return self::$config['modules']; } } directory = $baseDirectory; $this->prefix = __NAMESPACE__ . '\\'; $this->prefixLength = strlen($this->prefix); } public static function register($prepend = false) { spl_autoload_register([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; } } } } requiredCommand($client, 'SCAN'); parent::__construct($client, $match, $count); } protected function executeCommand() { return $this->client->scan($this->cursor, $this->getScanOptions()); } } 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->getCommandFactory()->supports($commandID)) { throw new NotSupportedException("'$commandID' is not supported by the current command factory."); } } protected function reset() { $this->valid = true; $this->fetchmore = true; $this->elements = []; $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]); } } 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, 'SSCAN'); parent::__construct($client, $match, $count); $this->key = $key; } protected function executeCommand() { return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions()); } } client = $client; $this->match = $match; $this->count = $count; $this->reset(); } protected function requiredCommand(ClientInterface $client, $commandID) { if (!$client->getCommandFactory()->supports($commandID)) { throw new NotSupportedException("'$commandID' is not supported by the current command factory."); } } protected function reset() { $this->valid = true; $this->fetchmore = true; $this->elements = []; $this->cursor = 0; $this->position = -1; $this->current = null; } protected function getScanOptions() { $options = []; 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() { [$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; } } message = $message; } public function getMessage() { return $this->message; } public function getErrorType() { [$errorType] = explode(' ', $this->getMessage(), 2); return $errorType; } public function __toString() { return $this->getMessage(); } } 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); } } } getMessage(), 2); return $errorType; } public function toErrorResponse() { return new Error($this->getMessage()); } } 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(); } } 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 [$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(); } client = $client; if (isset($options['gc_maxlifetime'])) { $this->ttl = (int) $options['gc_maxlifetime']; } else { $this->ttl = ini_get('session.gc_maxlifetime'); } } public function register() { session_set_save_handler($this, true); } #[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; } } 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); } } assertClient($client); $this->client = $client; $this->state = new MultiExecState(); $this->configure($client, $options ?: []); $this->reset(); } private function assertClient(ClientInterface $client) { if ($client->getConnection() instanceof ClusterInterface) { throw new NotSupportedException( 'Cannot initialize a MULTI/EXEC transaction over cluster connections.' ); } if (!$client->getCommandFactory()->supports('MULTI', 'EXEC', 'DISCARD')) { throw new NotSupportedException( 'MULTI, EXEC and DISCARD are not supported by the current command factory.' ); } } 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 = []) { try { $response = $this->client->executeCommand( $this->client->createCommand($commandID, $arguments) ); } catch (ServerException $exception) { if (!$this->client->getConnection() instanceof RelayConnection) { throw $exception; } if (strcasecmp($commandID, 'EXEC') != 0) { throw $exception; } if (!strpos($exception->getMessage(), 'RELAY_ERR_REDIS')) { throw $exception; } return null; } 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 Relay) { $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->getCommandFactory()->supports('WATCH')) { throw new NotSupportedException('WATCH is not supported by the current command factory.'); } if ($this->state->isWatchAllowed()) { throw new ClientException('Sending WATCH after MULTI is not allowed.'); } $response = $this->call('WATCH', is_array($keys) ? $keys : [$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->getCommandFactory()->supports('UNWATCH')) { throw new NotSupportedException( 'UNWATCH is not supported by the current command factory.' ); } $this->state->unflag(MultiExecState::WATCH); $this->__call('UNWATCH', []); 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 || $execResponse === false) { if ($attempts === 0) { throw new AbortedMultiExecException( $this, 'The current transaction has been aborted by the server.' ); } $this->reset(); continue; } break; } while ($attempts-- > 0); $response = []; $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 ($this->exceptions && $cmdResponse instanceof ErrorResponseInterface) { throw new ServerException($cmdResponse->getMessage()); } if ($cmdResponse instanceof RelayException) { if ($this->exceptions) { throw new ServerException($cmdResponse->getMessage(), $cmdResponse->getCode(), $cmdResponse); } $commands->dequeue(); $response[$i] = new Error($cmdResponse->getMessage()); continue; } $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; } } 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; } } checkCapabilities($client); $this->options = $options ?: []; $this->client = $client; $this->genericSubscribeInit('subscribe'); $this->genericSubscribeInit('psubscribe'); } public function getClient() { return $this->client; } protected function checkCapabilities(ClientInterface $client) { if ($client->getConnection() instanceof ClusterInterface) { throw new NotSupportedException( 'Cannot initialize a PUB/SUB consumer over cluster connections.' ); } $commands = ['publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe']; if (!$client->getCommandFactory()->supports(...$commands)) { throw new NotSupportedException( 'PUB/SUB commands are not supported by the current command factory.' ); } } protected 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) [ 'kind' => $response[0], 'channel' => $response[1], 'payload' => $response[2], ]; case self::PMESSAGE: return (object) [ 'kind' => $response[0], 'pattern' => $response[1], 'channel' => $response[2], 'payload' => $response[3], ]; case self::PONG: return (object) [ 'kind' => $response[0], 'payload' => $response[1], ]; default: throw new ClientException( "Unknown message type '{$response[0]}' received in the PUB/SUB context." ); } } } callbacks = []; $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, $this); } continue; } if (isset($this->callbacks[$message->channel])) { $callback = $this->callbacks[$message->channel]; call_user_func($callback, $message->payload, $this); } elseif (isset($this->defaultCallback)) { $callback = $this->defaultCallback; call_user_func($callback, $message, $this); } } } public function stop() { $this->pubsub->stop(); } protected function getPrefixKeys() { $options = $this->pubsub->getClient()->getOptions(); if (isset($options->prefix)) { return $options->prefix->getPrefix(); } return ''; } } 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(...$channel) { $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(...$pattern) { $this->writeRequest(self::PUNSUBSCRIBE, func_get_args()); } public function ping($payload = null) { $this->writeRequest('PING', [$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(); } statusFlags |= self::STATUS_SUBSCRIBED; $command = $this->client->createCommand('subscribe', [ $channels, function ($relay, $channel, $message) use ($callback) { $callback((object) [ 'kind' => is_null($message) ? self::SUBSCRIBE : self::MESSAGE, 'channel' => $channel, 'payload' => $message, ], $relay); }, ]); $this->client->getConnection()->executeCommand($command); $this->invalidate(); } public function psubscribe(...$pattern) { $patterns = func_get_args(); $callback = array_pop($patterns); $this->statusFlags |= self::STATUS_PSUBSCRIBED; $command = $this->client->createCommand('psubscribe', [ $patterns, function ($relay, $pattern, $channel, $message) use ($callback) { $callback((object) [ 'kind' => is_null($message) ? self::PSUBSCRIBE : self::PMESSAGE, 'pattern' => $pattern, 'channel' => $channel, 'payload' => $message, ], $relay); }, ]); $this->client->getConnection()->executeCommand($command); $this->invalidate(); } protected function genericSubscribeInit($subscribeAction) { if (isset($this->options[$subscribeAction])) { throw new NotSupportedException('Relay does not support Pub/Sub constructor options.'); } } public function ping($payload = null) { throw new NotSupportedException('Relay does not support PING in Pub/Sub.'); } public function stop($drop = false) { return false; } public function __destruct() { } } assertClient($client); $this->client = $client; $this->start(); } public function __destruct() { $this->stop(); } private function assertClient(ClientInterface $client) { if ($client->getConnection() instanceof ClusterInterface) { throw new NotSupportedException( 'Cannot initialize a monitor consumer over cluster connections.' ); } if (!$client->getCommandFactory()->supports('MONITOR')) { throw new NotSupportedException("'MONITOR' is not supported by the current command factory."); } } 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); @[$timestamp, $command, $arguments] = explode(' ', $event, 3); return (object) [ 'timestamp' => (float) $timestamp, 'database' => $database, 'client' => $client, 'command' => substr($command, 1, -1), 'arguments' => $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( '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); } } prefix = $prefix; $prefixFirst = static::class . '::first'; $prefixAll = static::class . '::all'; $prefixInterleaved = static::class . '::interleaved'; $prefixSkipFirst = static::class . '::skipFirst'; $prefixSkipLast = static::class . '::skipLast'; $prefixSort = static::class . '::sort'; $prefixEvalKeys = static::class . '::evalKeys'; $prefixZsetStore = static::class . '::zsetStore'; $prefixMigrate = static::class . '::migrate'; $prefixGeoradius = static::class . '::georadius'; $this->commands = [ 'EXISTS' => $prefixAll, 'DEL' => $prefixAll, 'TYPE' => $prefixFirst, 'KEYS' => $prefixFirst, 'RENAME' => $prefixAll, 'RENAMENX' => $prefixAll, 'EXPIRE' => $prefixFirst, 'EXPIREAT' => $prefixFirst, 'TTL' => $prefixFirst, 'MOVE' => $prefixFirst, 'SORT' => $prefixSort, 'DUMP' => $prefixFirst, 'RESTORE' => $prefixFirst, 'SET' => $prefixFirst, 'SETNX' => $prefixFirst, 'MSET' => $prefixInterleaved, 'MSETNX' => $prefixInterleaved, 'GET' => $prefixFirst, 'MGET' => $prefixAll, 'GETSET' => $prefixFirst, 'INCR' => $prefixFirst, 'INCRBY' => $prefixFirst, 'DECR' => $prefixFirst, 'DECRBY' => $prefixFirst, 'RPUSH' => $prefixFirst, 'LPUSH' => $prefixFirst, 'LLEN' => $prefixFirst, 'LRANGE' => $prefixFirst, 'LTRIM' => $prefixFirst, 'LINDEX' => $prefixFirst, 'LSET' => $prefixFirst, 'LREM' => $prefixFirst, 'LPOP' => $prefixFirst, 'RPOP' => $prefixFirst, 'RPOPLPUSH' => $prefixAll, 'SADD' => $prefixFirst, 'SREM' => $prefixFirst, 'SPOP' => $prefixFirst, 'SMOVE' => $prefixSkipLast, 'SCARD' => $prefixFirst, 'SISMEMBER' => $prefixFirst, 'SINTER' => $prefixAll, 'SINTERSTORE' => $prefixAll, 'SUNION' => $prefixAll, 'SUNIONSTORE' => $prefixAll, 'SDIFF' => $prefixAll, 'SDIFFSTORE' => $prefixAll, 'SMEMBERS' => $prefixFirst, 'SRANDMEMBER' => $prefixFirst, 'ZADD' => $prefixFirst, 'ZINCRBY' => $prefixFirst, 'ZREM' => $prefixFirst, 'ZRANGE' => $prefixFirst, 'ZREVRANGE' => $prefixFirst, 'ZRANGEBYSCORE' => $prefixFirst, 'ZCARD' => $prefixFirst, 'ZSCORE' => $prefixFirst, 'ZREMRANGEBYSCORE' => $prefixFirst, 'SETEX' => $prefixFirst, 'APPEND' => $prefixFirst, 'SUBSTR' => $prefixFirst, 'BLPOP' => $prefixSkipLast, 'BRPOP' => $prefixSkipLast, 'ZUNIONSTORE' => $prefixZsetStore, 'ZINTERSTORE' => $prefixZsetStore, 'ZCOUNT' => $prefixFirst, 'ZRANK' => $prefixFirst, 'ZREVRANK' => $prefixFirst, 'ZREMRANGEBYRANK' => $prefixFirst, 'HSET' => $prefixFirst, 'HSETNX' => $prefixFirst, 'HMSET' => $prefixFirst, 'HINCRBY' => $prefixFirst, 'HGET' => $prefixFirst, 'HMGET' => $prefixFirst, 'HDEL' => $prefixFirst, 'HEXISTS' => $prefixFirst, 'HLEN' => $prefixFirst, 'HKEYS' => $prefixFirst, 'HVALS' => $prefixFirst, 'HGETALL' => $prefixFirst, 'SUBSCRIBE' => $prefixAll, 'UNSUBSCRIBE' => $prefixAll, 'PSUBSCRIBE' => $prefixAll, 'PUNSUBSCRIBE' => $prefixAll, 'PUBLISH' => $prefixFirst, 'PERSIST' => $prefixFirst, 'STRLEN' => $prefixFirst, 'SETRANGE' => $prefixFirst, 'GETRANGE' => $prefixFirst, 'SETBIT' => $prefixFirst, 'GETBIT' => $prefixFirst, 'RPUSHX' => $prefixFirst, 'LPUSHX' => $prefixFirst, 'LINSERT' => $prefixFirst, 'BRPOPLPUSH' => $prefixSkipLast, 'ZREVRANGEBYSCORE' => $prefixFirst, 'WATCH' => $prefixAll, 'PTTL' => $prefixFirst, 'PEXPIRE' => $prefixFirst, 'PEXPIREAT' => $prefixFirst, 'PSETEX' => $prefixFirst, 'INCRBYFLOAT' => $prefixFirst, 'BITOP' => $prefixSkipFirst, 'BITCOUNT' => $prefixFirst, 'HINCRBYFLOAT' => $prefixFirst, 'EVAL' => $prefixEvalKeys, 'EVALSHA' => $prefixEvalKeys, 'MIGRATE' => $prefixMigrate, 'SSCAN' => $prefixFirst, 'ZSCAN' => $prefixFirst, 'HSCAN' => $prefixFirst, 'PFADD' => $prefixFirst, 'PFCOUNT' => $prefixAll, 'PFMERGE' => $prefixAll, 'ZLEXCOUNT' => $prefixFirst, 'ZRANGEBYLEX' => $prefixFirst, 'ZREMRANGEBYLEX' => $prefixFirst, 'ZREVRANGEBYLEX' => $prefixFirst, 'BITPOS' => $prefixFirst, 'HSTRLEN' => $prefixFirst, 'BITFIELD' => $prefixFirst, 'GEOADD' => $prefixFirst, 'GEOHASH' => $prefixFirst, 'GEOPOS' => $prefixFirst, 'GEODIST' => $prefixFirst, 'GEORADIUS' => $prefixGeoradius, 'GEORADIUSBYMEMBER' => $prefixGeoradius, 'XADD' => $prefixFirst, 'XRANGE' => $prefixFirst, 'XREVRANGE' => $prefixFirst, 'XDEL' => $prefixFirst, 'XLEN' => $prefixFirst, 'XACK' => $prefixFirst, 'XTRIM' => $prefixFirst, 'GETDEL' => $prefixFirst, 'EXPIRETIME' => $prefixFirst, 'JSON.ARRAPPEND' => $prefixFirst, 'JSON.ARRINDEX' => $prefixFirst, 'JSON.ARRINSERT' => $prefixFirst, 'JSON.ARRLEN' => $prefixFirst, 'JSON.ARRPOP' => $prefixFirst, 'JSON.ARRTRIM' => $prefixFirst, 'JSON.CLEAR' => $prefixFirst, 'JSON.DEBUG MEMORY' => $prefixFirst, 'JSON.DEL' => $prefixFirst, 'JSON.FORGET' => $prefixFirst, 'JSON.GET' => $prefixFirst, 'JSON.MGET' => $prefixAll, 'JSON.NUMINCRBY' => $prefixFirst, 'JSON.OBJKEYS' => $prefixFirst, 'JSON.OBJLEN' => $prefixFirst, 'JSON.RESP' => $prefixFirst, 'JSON.SET' => $prefixFirst, 'JSON.STRAPPEND' => $prefixFirst, 'JSON.STRLEN' => $prefixFirst, 'JSON.TOGGLE' => $prefixFirst, 'JSON.TYPE' => $prefixFirst, 'BF.ADD' => $prefixFirst, 'BF.EXISTS' => $prefixFirst, 'BF.INFO' => $prefixFirst, 'BF.INSERT' => $prefixFirst, 'BF.LOADCHUNK' => $prefixFirst, 'BF.MADD' => $prefixFirst, 'BF.MEXISTS' => $prefixFirst, 'BF.RESERVE' => $prefixFirst, 'BF.SCANDUMP' => $prefixFirst, 'CF.ADD' => $prefixFirst, 'CF.ADDNX' => $prefixFirst, 'CF.COUNT' => $prefixFirst, 'CF.DEL' => $prefixFirst, 'CF.EXISTS' => $prefixFirst, 'CF.INFO' => $prefixFirst, 'CF.INSERT' => $prefixFirst, 'CF.INSERTNX' => $prefixFirst, 'CF.LOADCHUNK' => $prefixFirst, 'CF.MEXISTS' => $prefixFirst, 'CF.RESERVE' => $prefixFirst, 'CF.SCANDUMP' => $prefixFirst, 'CMS.INCRBY' => $prefixFirst, 'CMS.INFO' => $prefixFirst, 'CMS.INITBYDIM' => $prefixFirst, 'CMS.INITBYPROB' => $prefixFirst, 'CMS.QUERY' => $prefixFirst, 'TDIGEST.ADD' => $prefixFirst, 'TDIGEST.BYRANK' => $prefixFirst, 'TDIGEST.BYREVRANK' => $prefixFirst, 'TDIGEST.CDF' => $prefixFirst, 'TDIGEST.CREATE' => $prefixFirst, 'TDIGEST.INFO' => $prefixFirst, 'TDIGEST.MAX' => $prefixFirst, 'TDIGEST.MIN' => $prefixFirst, 'TDIGEST.QUANTILE' => $prefixFirst, 'TDIGEST.RANK' => $prefixFirst, 'TDIGEST.RESET' => $prefixFirst, 'TDIGEST.REVRANK' => $prefixFirst, 'TDIGEST.TRIMMED_MEAN' => $prefixFirst, 'TOPK.ADD' => $prefixFirst, 'TOPK.INCRBY' => $prefixFirst, 'TOPK.INFO' => $prefixFirst, 'TOPK.LIST' => $prefixFirst, 'TOPK.QUERY' => $prefixFirst, 'TOPK.RESERVE' => $prefixFirst, 'FT.AGGREGATE' => $prefixFirst, 'FT.ALTER' => $prefixFirst, 'FT.CREATE' => $prefixFirst, 'FT.CURSOR DEL' => $prefixFirst, 'FT.CURSOR READ' => $prefixFirst, 'FT.DROPINDEX' => $prefixFirst, 'FT.EXPLAIN' => $prefixFirst, 'FT.INFO' => $prefixFirst, 'FT.PROFILE' => $prefixFirst, 'FT.SEARCH' => $prefixFirst, 'FT.SPELLCHECK' => $prefixFirst, 'FT.SYNDUMP' => $prefixFirst, 'FT.SYNUPDATE' => $prefixFirst, 'FT.TAGVALS' => $prefixFirst, 'TS.ADD' => $prefixFirst, 'TS.ALTER' => $prefixFirst, 'TS.CREATE' => $prefixFirst, 'TS.DECRBY' => $prefixFirst, 'TS.DEL' => $prefixFirst, 'TS.GET' => $prefixFirst, 'TS.INCRBY' => $prefixFirst, 'TS.INFO' => $prefixFirst, 'TS.MGET' => $prefixFirst, 'TS.MRANGE' => $prefixFirst, 'TS.MREVRANGE' => $prefixFirst, 'TS.QUERYINDEX' => $prefixFirst, 'TS.RANGE' => $prefixFirst, 'TS.REVRANGE' => $prefixFirst, ]; } 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())])) { $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); } } } $v) { $flattenedKVs[] = $k; $flattenedKVs[] = $v; } $arguments = $flattenedKVs; } parent::setArguments($arguments); } } $value) { $modifier = strtoupper($modifier); if ($modifier === 'COPY' && $value == true) { $arguments[] = $modifier; } if ($modifier === 'REPLACE' && $value == true) { $arguments[] = $modifier; } } } parent::setArguments($arguments); } } setExpansion($arguments); $this->filterArguments(); } } 'CAPACITY', 'size' => 'SIZE', 'filters' => 'FILTERS', 'items' => 'ITEMS', 'expansion' => 'EXPANSION', ]; public function getId() { return 'BF.INFO'; } public function setArguments(array $arguments) { if (isset($arguments[1])) { $modifier = array_pop($arguments); if ($modifier === '') { parent::setArguments($arguments); return; } if (!in_array(strtoupper($modifier), $this->modifierEnum)) { $enumValues = implode(', ', array_keys($this->modifierEnum)); throw new UnexpectedValueException("Argument accepts only: {$enumValues} values"); } $arguments[] = $this->modifierEnum[strtolower($modifier)]; } parent::setArguments($arguments); } public function parseResponse($data) { if (count($data) > 1) { $result = []; for ($i = 0, $iMax = count($data); $i < $iMax; ++$i) { if (array_key_exists($i + 1, $data)) { $result[(string) $data[$i]] = $data[++$i]; } } return $result; } return $data; } } setNoCreate($arguments); $arguments = $this->getArguments(); if (array_key_exists(5, $arguments) && $arguments[5]) { $arguments[5] = 'NONSCALING'; } $this->setItems($arguments); $arguments = $this->getArguments(); $this->setExpansion($arguments); $arguments = $this->getArguments(); $this->setErrorRate($arguments); $arguments = $this->getArguments(); $this->setCapacity($arguments); $this->filterArguments(); } } 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 = []; $count = count($node); for ($i = 0; $i < $count; ++$i) { $processed[$node[$i]] = $node[++$i]; } $servers[$idx] = $processed; } return $servers; } } toArray() : []; $schema = array_reduce($schema, static function (array $carry, FieldInterface $field) { return array_merge($carry, $field->toArray()); }, []); array_unshift($schema, 'SCHEMA'); parent::setArguments(array_merge( [$index], $commandArguments, $schema )); } } toArray() : []; $schema = array_reduce($schema, static function (array $carry, FieldInterface $field) { return array_merge($carry, $field->toArray()); }, []); array_unshift($schema, 'SCHEMA', 'ADD'); parent::setArguments(array_merge( [$index], $commandArguments, $schema )); } } toArray(); } $terms = array_slice($arguments, 3); parent::setArguments(array_merge( [$index, $synonymGroupId], $commandArguments, $terms )); } } toArray(); } parent::setArguments(array_merge( [$index, $query], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key, $string, $score], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$subcommand, $index, $cursorId], $commandArguments )); } } toArray() )); } } toArray(); } parent::setArguments(array_merge( [$index], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$index, $query], $commandArguments )); } } toArray(); } parent::setArguments(array_merge( [$index, $query], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$index, $query], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key, $prefix], $commandArguments )); } } setAggregate($arguments); $arguments = $this->getArguments(); $this->setWeights($arguments); $arguments = $this->getArguments(); $this->setKeys($arguments); } } true]; $lastType = 'array'; } if ($lastType === 'array') { $options = $this->prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } } parent::setArguments($arguments); } protected function prepareOptions($options) { $opts = array_change_key_case($options, CASE_UPPER); $finalizedOpts = []; 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 = []; for ($i = 0; $i < count($data); ++$i) { if (is_array($data[$i])) { $result[$data[$i][0]] = $data[$i][1]; } else { $result[$data[$i]] = $data[++$i]; } } return $result; } return $data; } } setKeys($arguments); $arguments = $this->getArguments(); $this->setWithScore($arguments); } } getArgument(0))) { case 'numsub': return self::processNumsub($data); default: return $data; } } protected static function processNumsub(array $channels) { $processed = []; $count = count($channels); for ($i = 0; $i < $count; ++$i) { $processed[$channels[$i]] = $channels[++$i]; } return $processed; } } $v) { $flattenedKVs[] = $k; $flattenedKVs[] = $v; } $arguments = $flattenedKVs; } parent::setArguments($arguments); } } $val) { $args[] = $key; $args[] = $val; } } parent::setArguments($args); } } $score) { $arguments[] = $score; $arguments[] = $member; } } parent::setArguments($arguments); } } getArguments(); for ($i = 3; $i < count($arguments); ++$i) { switch (strtoupper($arguments[$i])) { case 'WITHSCORES': return true; case 'LIMIT': $i += 2; break; } } return false; } } parseNewResponseFormat($lines); } else { return $this->parseOldResponseFormat($lines); } } public function parseNewResponseFormat($lines) { $info = []; $current = null; foreach ($lines as $row) { if ($row === '') { continue; } if (preg_match('/^# (\w+)$/', $row, $matches)) { $info[$matches[1]] = []; $current = &$info[$matches[1]]; continue; } [$k, $v] = $this->parseRow($row); $current[$k] = $v; } return $info; } public function parseOldResponseFormat($lines) { $info = []; foreach ($lines as $row) { if (strpos($row, ':') === false) { continue; } [$k, $v] = $this->parseRow($row); $info[$k] = $v; } return $info; } protected function parseRow($row) { if (preg_match('/^module:name/', $row)) { return $this->parseModuleRow($row); } [$k, $v] = explode(':', $row, 2); if (preg_match('/^db\d+$/', $k)) { $v = $this->parseDatabaseStats($v); } return [$k, $v]; } protected function parseDatabaseStats($str) { $db = []; foreach (explode(',', $str) as $dbvar) { [$dbvk, $dbvv] = explode('=', $dbvar); $db[trim($dbvk)] = $dbvv; } return $db; } protected function parseModuleRow(string $row): array { [$moduleKeyword, $moduleData] = explode(':', $row); $explodedData = explode(',', $moduleData); $parsedData = []; foreach ($explodedData as $moduleDataRow) { [$k, $v] = explode('=', $moduleDataRow); if ($k === 'name') { $parsedData[0] = $v; continue; } $parsedData[1][$k] = $v; } return $parsedData; } } setStoreDist($arguments); $arguments = $this->getArguments(); $this->setCount($arguments, $arguments[6] ?? false); $arguments = $this->getArguments(); $this->setSorting($arguments); $arguments = $this->getArguments(); $this->setFrom($arguments); $arguments = $this->getArguments(); $this->setBy($arguments); $this->filterArguments(); } } getArgument(0); } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } parent::setArguments($arguments); } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = []; 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 = []; for ($i = 0; $i < count($members); ++$i) { $result[$members[$i]] = (float) $members[++$i]; } $data[1] = $result; } return $data; } } 2) { for ($i = 2, $iMax = count($arguments); $i < $iMax; $i++) { $processedArguments[] = $arguments[$i]; } } parent::setArguments($processedArguments); } } filterArguments(); } public function parseResponse($data) { if (is_array($data)) { if ($data !== array_values($data)) { return $data; } return [$data[0] => $data[1], $data[2] => $data[3]]; } return $data; } } 1) { $result = []; for ($i = 0, $iMax = count($data); $i < $iMax; ++$i) { if (array_key_exists($i + 1, $data)) { $result[(string) $data[$i]] = $data[++$i]; } } return $result; } return $data; } } setKeys($arguments, false); } public function parseResponse($data) { $key = array_shift($data); if (null === $key) { return [$key]; } return array_combine([$key], [[$data[0] => $data[1]]]); } } setByLexByScoreArgument($arguments); $arguments = $this->getArguments(); $this->setReversedArgument($arguments); $arguments = $this->getArguments(); $this->setLimitArguments($arguments); $this->filterArguments(); } } getArgument(0)); } } setLimit($arguments); $arguments = $this->getArguments(); $this->setKeys($arguments); } } strategyResolver = new SubcommandStrategyResolver(); } public function getId() { return 'FUNCTION'; } public function setArguments(array $arguments) { $strategy = $this->strategyResolver->resolve('functions', strtolower($arguments[0])); $arguments = $strategy->processArguments($arguments); parent::setArguments($arguments); $this->filterArguments(); } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } parent::setArguments($arguments); } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = []; 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 = []; for ($i = 0; $i < count($fields); ++$i) { $result[$fields[$i]] = $fields[++$i]; } $data[1] = $result; } return $data; } } 'EX', 'px' => 'PX', 'exat' => 'EXAT', 'pxat' => 'PXAT', 'persist' => 'PERSIST', ]; public function getId() { return 'GETEX'; } public function setArguments(array $arguments) { if (!array_key_exists(1, $arguments) || $arguments[1] === '') { parent::setArguments([$arguments[0]]); return; } if (!in_array(strtoupper($arguments[1]), self::$modifierEnum)) { $enumValues = implode(', ', array_keys(self::$modifierEnum)); throw new UnexpectedValueException("Modifier argument accepts only: {$enumValues} values"); } if ($arguments[1] === 'persist') { parent::setArguments([$arguments[0], self::$modifierEnum[$arguments[1]]]); return; } $arguments[1] = self::$modifierEnum[$arguments[1]]; if (!array_key_exists(2, $arguments)) { throw new UnexpectedValueException('You should provide value for current modifier'); } parent::setArguments($arguments); } } setSorting($arguments); $arguments = $this->getArguments(); $this->setWithCoord($arguments); $arguments = $this->getArguments(); $this->setWithDist($arguments); $arguments = $this->getArguments(); $this->setWithHash($arguments); $arguments = $this->getArguments(); $this->setCount($arguments, $arguments[5] ?? false); $arguments = $this->getArguments(); $this->setFrom($arguments); $arguments = $this->getArguments(); $this->setBy($arguments); $this->filterArguments(); } public function parseResponse($data) { $parsedData = []; $itemKey = ''; foreach ($data as $item) { if (!is_array($item)) { $parsedData[] = $item; continue; } foreach ($item as $key => $itemRow) { if ($key === 0) { $itemKey = $itemRow; continue; } if (is_string($itemRow)) { $parsedData[$itemKey]['dist'] = round((float) $itemRow, 5); } elseif (is_int($itemRow)) { $parsedData[$itemKey]['hash'] = $itemRow; } else { $parsedData[$itemKey]['lng'] = round($itemRow[0], 5); $parsedData[$itemKey]['lat'] = round($itemRow[1], 5); } } } return $parsedData; } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } parent::setArguments($arguments); } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = []; if (!empty($options['MATCH'])) { $normalized[] = 'MATCH'; $normalized[] = $options['MATCH']; } if (!empty($options['COUNT'])) { $normalized[] = 'COUNT'; $normalized[] = $options['COUNT']; } return $normalized; } } 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 = []; foreach (explode("\n", $data, -1) as $clientData) { $client = []; foreach (explode(' ', $clientData) as $kv) { @[$k, $v] = explode('=', $kv); $client[$k] = $v; } $clients[] = $client; } return $clients; } } setCount($arguments); $arguments = $this->getArguments(); $this->setLeftRight($arguments); $arguments = $this->getArguments(); $this->setKeys($arguments); $this->filterArguments(); } public function parseResponse($data) { if (null === $data) { return null; } return [$data[0] => $data[1]]; } } prepareOptions(array_pop($arguments)); $arguments = array_merge($arguments, $options); } parent::setArguments($arguments); } protected function prepareOptions($options) { $options = array_change_key_case($options, CASE_UPPER); $normalized = []; if (!empty($options['MATCH'])) { $normalized[] = 'MATCH'; $normalized[] = $options['MATCH']; } if (!empty($options['COUNT'])) { $normalized[] = 'COUNT'; $normalized[] = $options['COUNT']; } return $normalized; } } FunctionContainer::class, ]; public static function create(ClientInterface $client, string $containerCommandID): ContainerInterface { $containerCommandID = strtoupper($containerCommandID); $commandModule = self::resolveCommandModuleByPrefix($containerCommandID); if (null !== $commandModule) { if (class_exists($containerClass = self::CONTAINER_NAMESPACE . '\\' . $commandModule . '\\' . $containerCommandID)) { return new $containerClass($client); } throw new UnexpectedValueException('Given module container command is not supported.'); } if (class_exists($containerClass = self::CONTAINER_NAMESPACE . '\\' . $containerCommandID)) { return new $containerClass($client); } if (array_key_exists($containerCommandID, self::$specialMappings)) { $containerClass = self::$specialMappings[$containerCommandID]; return new $containerClass($client); } throw new UnexpectedValueException('Given container command is not supported.'); } private static function resolveCommandModuleByPrefix(string $commandID): ?string { $modules = ClientConfiguration::getModules(); foreach ($modules as $module) { if (preg_match("/^{$module['commandPrefix']}/", $commandID)) { return $module['name']; } } return null; } } client = $client; } public function __call(string $subcommandID, array $arguments) { array_unshift($arguments, strtoupper($subcommandID)); return $this->client->executeCommand( $this->client->createCommand($this->getContainerCommandId(), $arguments) ); } abstract public function getContainerCommandId(): string; } setSorting($arguments); $arguments = $this->getArguments(); $this->setGetArgument($arguments); $arguments = $this->getArguments(); $this->setLimit($arguments); $arguments = $this->getArguments(); $this->setBy($arguments); $this->filterArguments(); } } setCount($arguments); $arguments = $this->getArguments(); $this->resolveModifier(static::$modifierArgumentPositionOffset, $arguments); $this->setKeys($arguments); $arguments = $this->getArguments(); parent::setArguments($arguments); } public function parseResponse($data) { $key = array_shift($data); if (null === $key) { return [$key]; } $data = $data[0]; $parsedData = []; for ($i = 0, $iMax = count($data); $i < $iMax; $i++) { for ($j = 0, $jMax = count($data[$i]); $j < $jMax; ++$j) { if ($data[$i][$j + 1] ?? false) { $parsedData[$data[$i][$j]] = $data[$i][++$j]; } } } return array_combine([$key], [$parsedData]); } } setSpace($arguments); $arguments = $this->getArguments(); $this->setNewline($arguments); $arguments = $this->getArguments(); $this->setIndent($arguments); $this->filterArguments(); } } setSubcommand($arguments); $this->filterArguments(); } } 1) { $result = []; for ($i = 0, $iMax = count($data); $i < $iMax; ++$i) { if (array_key_exists($i + 1, $data)) { $result[(string) $data[$i]] = $data[++$i]; } } return $result; } return $data; } } setNoCreate($arguments); $arguments = $this->getArguments(); $this->setItems($arguments); $arguments = $this->getArguments(); $this->setCapacity($arguments); $this->filterArguments(); } } setExpansion($arguments); $arguments = $this->getArguments(); $this->setMaxIterations($arguments); $arguments = $this->getArguments(); $this->setBucketSize($arguments); $this->filterArguments(); } } setLimit($arguments); $arguments = $this->getArguments(); $this->setKeys($arguments); } } setDB($arguments); $arguments = $this->getArguments(); $this->setReplace($arguments); } } filterArguments(); } public function parseResponse($data) { if ($this->isWithCountModifier()) { $result = []; for ($i = 0, $iMax = count($data); $i < $iMax; ++$i) { if (array_key_exists($i + 1, $data)) { $result[(string) $data[$i]] = $data[++$i]; } } return $result; } return $data; } private function isWithCountModifier(): bool { $arguments = $this->getArguments(); $lastArgument = (!empty($arguments)) ? $arguments[count($arguments) - 1] : null; return is_string($lastArgument) && strtoupper($lastArgument) === 'WITHCOUNT'; } } $entry) { $log[$index] = [ 'id' => $entry[0], 'timestamp' => $entry[1], 'duration' => $entry[2], 'command' => $entry[3], ]; } return $log; } return $data; } } setTimeout($arguments); $arguments = $this->getArguments(); $this->setTo($arguments); $this->filterArguments(); } } toArray() : []; parent::setArguments(array_merge( [$key], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key, $value], $commandArguments )); } } toArray(); array_push($processedArguments, 'FILTER', ...$arguments); parent::setArguments(array_merge( $commandArguments, $processedArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key, $value], $commandArguments )); } } toArray(); parent::setArguments(array_merge( [$fromTimestamp, $toTimestamp], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key, $timestamp, $value], $commandArguments )); } } toArray() : []; parent::setArguments(array_merge( [$key, $fromTimestamp, $toTimestamp], $commandArguments )); } } commands = [ 'ECHO' => 'Predis\Command\Redis\ECHO_', 'EVAL' => 'Predis\Command\Redis\EVAL_', 'OBJECT' => 'Predis\Command\Redis\OBJECT_', 'FUNCTION' => FUNCTIONS::class, ]; } public function getCommandClass(string $commandID): ?string { $commandID = strtoupper($commandID); if (isset($this->commands[$commandID]) || array_key_exists($commandID, $this->commands)) { return $this->commands[$commandID]; } $commandClass = $this->resolve($commandID); if (null === $commandClass) { return null; } $this->commands[$commandID] = $commandClass; return $commandClass; } public function undefine(string $commandID): void { $this->commands[strtoupper($commandID)] = null; } private function resolve(string $commandID): ?string { if (class_exists($commandClass = self::COMMANDS_NAMESPACE . '\\' . $commandID)) { return $commandClass; } $commandModule = $this->resolveCommandModuleByPrefix($commandID); if (null === $commandModule) { return null; } if (class_exists($commandClass = self::COMMANDS_NAMESPACE . '\\' . $commandModule . '\\' . $commandID)) { return $commandClass; } return null; } private function resolveCommandModuleByPrefix(string $commandID): ?string { foreach (ClientConfiguration::getModules() as $module) { if (preg_match("/^{$module['commandPrefix']}/", $commandID)) { return $module['name']; } } return null; } } getCommandClass($commandID) === null) { return false; } } return true; } public function getCommandClass(string $commandID): ?string { return $this->commands[strtoupper($commandID)] ?? null; } public function create(string $commandID, array $arguments = []): CommandInterface { if (!$commandClass = $this->getCommandClass($commandID)) { $commandID = strtoupper($commandID); throw new ClientException("Command `$commandID` is not a registered Redis command."); } $command = new $commandClass(); $command->setArguments($arguments); if (isset($this->processor)) { $this->processor->process($command); } return $command; } public function define(string $commandID, string $commandClass): void { if (!is_a($commandClass, 'Predis\Command\CommandInterface', true)) { throw new InvalidArgumentException( "Class $commandClass must implement Predis\Command\CommandInterface" ); } $this->commands[strtoupper($commandID)] = $commandClass; } public function undefine(string $commandID): void { unset($this->commands[strtoupper($commandID)]); } public function setProcessor(?ProcessorInterface $processor): void { $this->processor = $processor; } public function getProcessor(): ?ProcessorInterface { return $this->processor; } } separator = $separator; } public function resolve(string $commandId, string $subcommandId): SubcommandStrategyInterface { $subcommandStrategyClass = ucwords($subcommandId) . 'Strategy'; $commandDirectoryName = ucwords($commandId); if (!is_null($this->separator)) { $subcommandStrategyClass = str_replace($this->separator, '', $subcommandStrategyClass); $commandDirectoryName = str_replace($this->separator, '', $commandDirectoryName); } if (class_exists( $containerCommandClass = self::CONTAINER_COMMANDS_NAMESPACE . '\\' . $commandDirectoryName . '\\' . $subcommandStrategyClass )) { return new $containerCommandClass(); } throw new InvalidArgumentException('Non-existing container command given'); } } offset = $offset; $this->count = $count; } public function toArray(): array { return [self::KEYWORD, $this->offset, $this->count]; } } host = $host; $this->port = $port; $this->isForce = $isForce; } public function toArray(): array { $arguments = [self::KEYWORD, $this->host, $this->port]; if ($this->isForce) { $arguments[] = self::FORCE_KEYWORD; } return $arguments; } } 'INCLUDE', 'exclude' => 'EXCLUDE', ]; public function distance(int $distance): self { $this->arguments[] = 'DISTANCE'; $this->arguments[] = $distance; return $this; } public function terms(string $dictionary, string $modifier = 'INCLUDE', string ...$terms): self { if (!in_array(strtoupper($modifier), $this->termsEnum)) { $enumValues = implode(', ', array_values($this->termsEnum)); throw new InvalidArgumentException("Wrong modifier value given. Currently supports: {$enumValues}"); } array_push($this->arguments, 'TERMS', $this->termsEnum[strtolower($modifier)], $dictionary, ...$terms); return $this; } } arguments[] = 'FUZZY'; return $this; } public function max(int $num): self { array_push($this->arguments, 'MAX', $num); return $this; } } 'ASC', 'desc' => 'DESC', ]; public function noContent(): self { $this->arguments[] = 'NOCONTENT'; return $this; } public function withSortKeys(): self { $this->arguments[] = 'WITHSORTKEYS'; return $this; } public function searchFilter(array ...$filter): self { $arguments = func_get_args(); foreach ($arguments as $argument) { array_push($this->arguments, 'FILTER', ...$argument); } return $this; } public function geoFilter(array ...$filter): self { $arguments = func_get_args(); foreach ($arguments as $argument) { array_push($this->arguments, 'GEOFILTER', ...$argument); } return $this; } public function inKeys(array $keys): self { $this->arguments[] = 'INKEYS'; $this->arguments[] = count($keys); $this->arguments = array_merge($this->arguments, $keys); return $this; } public function inFields(array $fields): self { $this->arguments[] = 'INFIELDS'; $this->arguments[] = count($fields); $this->arguments = array_merge($this->arguments, $fields); return $this; } public function addReturn(int $count, ...$identifier): self { $arguments = func_get_args(); $this->arguments[] = 'RETURN'; for ($i = 1, $iMax = count($arguments); $i < $iMax; $i++) { if (true === $arguments[$i]) { $arguments[$i] = 'AS'; } } $this->arguments = array_merge($this->arguments, $arguments); return $this; } public function summarize(array $fields = [], int $frags = 0, int $len = 0, string $separator = ''): self { $this->arguments[] = 'SUMMARIZE'; if (!empty($fields)) { $this->arguments[] = 'FIELDS'; $this->arguments[] = count($fields); $this->arguments = array_merge($this->arguments, $fields); } if ($frags !== 0) { $this->arguments[] = 'FRAGS'; $this->arguments[] = $frags; } if ($len !== 0) { $this->arguments[] = 'LEN'; $this->arguments[] = $len; } if ($separator !== '') { $this->arguments[] = 'SEPARATOR'; $this->arguments[] = $separator; } return $this; } public function highlight(array $fields = [], string $openTag = '', string $closeTag = ''): self { $this->arguments[] = 'HIGHLIGHT'; if (!empty($fields)) { $this->arguments[] = 'FIELDS'; $this->arguments[] = count($fields); $this->arguments = array_merge($this->arguments, $fields); } if ($openTag !== '' && $closeTag !== '') { array_push($this->arguments, 'TAGS', $openTag, $closeTag); } return $this; } public function slop(int $slop): self { $this->arguments[] = 'SLOP'; $this->arguments[] = $slop; return $this; } public function inOrder(): self { $this->arguments[] = 'INORDER'; return $this; } public function expander(string $expander): self { $this->arguments[] = 'EXPANDER'; $this->arguments[] = $expander; return $this; } public function scorer(string $scorer): self { $this->arguments[] = 'SCORER'; $this->arguments[] = $scorer; return $this; } public function explainScore(): self { $this->arguments[] = 'EXPLAINSCORE'; return $this; } public function sortBy(string $sortAttribute, string $orderBy = 'asc'): self { $this->arguments[] = 'SORTBY'; $this->arguments[] = $sortAttribute; if (in_array(strtoupper($orderBy), $this->sortingEnum)) { $this->arguments[] = $this->sortingEnum[strtolower($orderBy)]; } else { $enumValues = implode(', ', array_values($this->sortingEnum)); throw new InvalidArgumentException("Wrong order direction value given. Currently supports: {$enumValues}"); } return $this; } } arguments[] = 'LANGUAGE'; $this->arguments[] = $defaultLanguage; return $this; } public function dialect(string $dialect): self { $this->arguments[] = 'DIALECT'; $this->arguments[] = $dialect; return $this; } public function skipInitialScan(): self { $this->arguments[] = 'SKIPINITIALSCAN'; return $this; } public function payload(string $payload): self { $this->arguments[] = 'PAYLOAD'; $this->arguments[] = $payload; return $this; } public function withScores(): self { $this->arguments[] = 'WITHSCORES'; return $this; } public function withPayloads(): self { $this->arguments[] = 'WITHPAYLOADS'; return $this; } public function verbatim(): self { $this->arguments[] = 'VERBATIM'; return $this; } public function timeout(int $timeout): self { $this->arguments[] = 'TIMEOUT'; $this->arguments[] = $timeout; return $this; } public function limit(int $offset, int $num): self { array_push($this->arguments, 'LIMIT', $offset, $num); return $this; } public function filter(string $filter): self { $this->arguments[] = 'FILTER'; $this->arguments[] = $filter; return $this; } public function params(array $nameValuesDictionary): self { $this->arguments[] = 'PARAMS'; $this->arguments[] = count($nameValuesDictionary); $this->arguments = array_merge($this->arguments, $nameValuesDictionary); return $this; } public function toArray(): array { return $this->arguments; } } arguments[] = 'INCR'; return $this; } } fieldArguments[] = $identifier; if ($alias !== '') { $this->fieldArguments[] = 'AS'; $this->fieldArguments[] = $alias; } $this->fieldArguments[] = $fieldType; if ($sortable === self::SORTABLE) { $this->fieldArguments[] = 'SORTABLE'; } elseif ($sortable === self::SORTABLE_UNF) { $this->fieldArguments[] = 'SORTABLE'; $this->fieldArguments[] = 'UNF'; } if ($noIndex) { $this->fieldArguments[] = 'NOINDEX'; } } public function toArray(): array { return $this->fieldArguments; } } setCommonOptions('NUMERIC', $identifier, $alias, $sortable, $noIndex); } } setCommonOptions('TEXT', $identifier, $alias, $sortable, $noIndex); if ($noStem) { $this->fieldArguments[] = 'NOSTEM'; } if ($phonetic !== '') { $this->fieldArguments[] = 'PHONETIC'; $this->fieldArguments[] = $phonetic; } if ($weight !== 1) { $this->fieldArguments[] = 'WEIGHT'; $this->fieldArguments[] = $weight; } if ($withSuffixTrie) { $this->fieldArguments[] = 'WITHSUFFIXTRIE'; } } } setCommonOptions('GEO', $identifier, $alias, $sortable, $noIndex); } } setCommonOptions('TAG', $identifier, $alias, $sortable, $noIndex); if ($separator !== ',') { $this->fieldArguments[] = 'SEPARATOR'; $this->fieldArguments[] = $separator; } if ($caseSensitive) { $this->fieldArguments[] = 'CASESENSITIVE'; } } } setCommonOptions('VECTOR', $fieldName, $alias); array_push($this->fieldArguments, $algorithm, count($attributeNameValueDictionary)); $this->fieldArguments = array_merge($this->fieldArguments, $attributeNameValueDictionary); } public function toArray(): array { return $this->fieldArguments; } } 'HASH', 'json' => 'JSON', ]; public function on(string $modifier = 'HASH'): self { if (in_array(strtoupper($modifier), $this->supportedDataTypesEnum)) { $this->arguments[] = 'ON'; $this->arguments[] = $this->supportedDataTypesEnum[strtolower($modifier)]; return $this; } $enumValues = implode(', ', array_values($this->supportedDataTypesEnum)); throw new InvalidArgumentException("Wrong modifier value given. Currently supports: {$enumValues}"); } public function prefix(array $prefixes): self { $this->arguments[] = 'PREFIX'; $this->arguments[] = count($prefixes); $this->arguments = array_merge($this->arguments, $prefixes); return $this; } public function languageField(string $languageAttribute): self { $this->arguments[] = 'LANGUAGE_FIELD'; $this->arguments[] = $languageAttribute; return $this; } public function score(float $defaultScore = 1.0): self { $this->arguments[] = 'SCORE'; $this->arguments[] = $defaultScore; return $this; } public function scoreField(string $scoreAttribute): self { $this->arguments[] = 'SCORE_FIELD'; $this->arguments[] = $scoreAttribute; return $this; } public function maxTextFields(): self { $this->arguments[] = 'MAXTEXTFIELDS'; return $this; } public function noOffsets(): self { $this->arguments[] = 'NOOFFSETS'; return $this; } public function temporary(int $seconds): self { $this->arguments[] = 'TEMPORARY'; $this->arguments[] = $seconds; return $this; } public function noHl(): self { $this->arguments[] = 'NOHL'; return $this; } public function noFields(): self { $this->arguments[] = 'NOFIELDS'; return $this; } public function noFreqs(): self { $this->arguments[] = 'NOFREQS'; return $this; } public function stopWords(array $stopWords): self { $this->arguments[] = 'STOPWORDS'; $this->arguments[] = count($stopWords); $this->arguments = array_merge($this->arguments, $stopWords); return $this; } } arguments[] = 'DD'; return $this; } public function toArray(): array { return $this->arguments; } } 'ASC', 'desc' => 'DESC', ]; public function load(string ...$fields): self { $arguments = func_get_args(); $this->arguments[] = 'LOAD'; if ($arguments[0] === '*') { $this->arguments[] = '*'; return $this; } $this->arguments[] = count($arguments); $this->arguments = array_merge($this->arguments, $arguments); return $this; } public function groupBy(string ...$properties): self { $arguments = func_get_args(); array_push($this->arguments, 'GROUPBY', count($arguments)); $this->arguments = array_merge($this->arguments, $arguments); return $this; } public function reduce(string $function, ...$argument): self { $arguments = func_get_args(); $functionValue = array_shift($arguments); $argumentsCounter = 0; for ($i = 0, $iMax = count($arguments); $i < $iMax; $i++) { if (true === $arguments[$i]) { $arguments[$i] = 'AS'; $i++; continue; } $argumentsCounter++; } array_push($this->arguments, 'REDUCE', $functionValue); $this->arguments = array_merge($this->arguments, [$argumentsCounter], $arguments); return $this; } public function sortBy(int $max = 0, ...$properties): self { $arguments = func_get_args(); $maxValue = array_shift($arguments); $this->arguments[] = 'SORTBY'; $this->arguments = array_merge($this->arguments, [count($arguments)], $arguments); if ($maxValue !== 0) { array_push($this->arguments, 'MAX', $maxValue); } return $this; } public function apply(string $expression, string $as = ''): self { array_push($this->arguments, 'APPLY', $expression); if ($as !== '') { array_push($this->arguments, 'AS', $as); } return $this; } public function withCursor(int $readSize = 0, int $idleTime = 0): self { $this->arguments[] = 'WITHCURSOR'; if ($readSize !== 0) { array_push($this->arguments, 'COUNT', $readSize); } if ($idleTime !== 0) { array_push($this->arguments, 'MAXIDLE', $idleTime); } return $this; } } arguments[] = 'SEARCH'; return $this; } public function aggregate(): self { $this->arguments[] = 'AGGREGATE'; return $this; } public function limited(): self { $this->arguments[] = 'LIMITED'; return $this; } public function query(string $query): self { $this->arguments[] = 'QUERY'; $this->arguments[] = $query; return $this; } public function toArray(): array { return $this->arguments; } } arguments, 'COUNT', $readSize); return $this; } public function toArray(): array { return $this->arguments; } } unit = $unit; } } longitude = $longitude; $this->latitude = $latitude; } public function toArray(): array { return [self::KEYWORD, $this->longitude, $this->latitude]; } } width = $width; $this->height = $height; $this->setUnit($unit); } public function toArray(): array { return [self::KEYWORD, $this->width, $this->height, $this->unit]; } } radius = $radius; $this->setUnit($unit); } public function toArray(): array { return [self::KEYWORD, $this->radius, $this->unit]; } } member = $member; } public function toArray(): array { return [self::KEYWORD, $this->member]; } } arguments, 'FILTER_BY_TS', ...$ts); return $this; } public function filterByValue(int $min, int $max): self { array_push($this->arguments, 'FILTER_BY_VALUE', $min, $max); return $this; } public function count(int $count): self { array_push($this->arguments, 'COUNT', $count); return $this; } public function aggregation(string $aggregator, int $bucketDuration, int $align = 0, int $bucketTimestamp = 0, bool $empty = false): self { if ($align > 0) { array_push($this->arguments, 'ALIGN', $align); } array_push($this->arguments, 'AGGREGATION', $aggregator, $bucketDuration); if ($bucketTimestamp > 0) { array_push($this->arguments, 'BUCKETTIMESTAMP', $bucketTimestamp); } if (true === $empty) { $this->arguments[] = 'EMPTY'; } return $this; } } arguments, 'TIMESTAMP', $timeStamp); return $this; } public function uncompressed(): self { $this->arguments[] = 'UNCOMPRESSED'; return $this; } } arguments, 'RETENTION', $retentionPeriod); return $this; } public function chunkSize(int $size): self { array_push($this->arguments, 'CHUNK_SIZE', $size); return $this; } public function duplicatePolicy(string $policy = self::POLICY_BLOCK): self { array_push($this->arguments, 'DUPLICATE_POLICY', $policy); return $this; } public function labels(...$labelValuePair): self { array_push($this->arguments, 'LABELS', ...$labelValuePair); return $this; } public function encoding(string $encoding = self::ENCODING_COMPRESSED): self { array_push($this->arguments, 'ENCODING', $encoding); return $this; } public function latest(): self { $this->arguments[] = 'LATEST'; return $this; } public function withLabels(): self { $this->arguments[] = 'WITHLABELS'; return $this; } public function selectedLabels(string ...$labels): self { array_push($this->arguments, 'SELECTED_LABELS', ...$labels); return $this; } public function toArray(): array { return $this->arguments; } } arguments[] = 'DEBUG'; return $this; } public function toArray(): array { return $this->arguments; } } arguments, 'ON_DUPLICATE', $policy); return $this; } } arguments, 'FILTER', ...$filterExpressions); return $this; } public function groupBy(string $label, string $reducer): self { array_push($this->arguments, 'GROUPBY', $label, 'REDUCE', $reducer); return $this; } } getScript()); } protected function getKeysCount() { return 0; } public function getKeys() { return array_slice($this->getArguments(), 2, $this->getKeysCount()); } public function setArguments(array $arguments) { if (($numkeys = $this->getKeysCount()) && $numkeys < 0) { $numkeys = count($arguments) + $numkeys; } $arguments = array_merge([$this->getScriptHash(), (int) $numkeys], $arguments); parent::setArguments($arguments); } public function getEvalArguments() { $arguments = $this->getArguments(); $arguments[0] = $this->getScript(); return $arguments; } public function getEvalCommand() { return new RawCommand('EVAL', $this->getEvalArguments()); } } arguments = $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() { return $this->slot ?? null; } 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([$arguments[0]], $arguments[1]); } return $arguments; } public function filterArguments(): void { $this->arguments = array_filter($this->arguments, static function ($argument) { return $argument !== false && $argument !== null; }); } } commandID = strtoupper($commandID); $this->setArguments($arguments); } public static function create($commandID, ...$args) { $arguments = func_get_args(); return new static(array_shift($arguments), $arguments); } 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() { return $this->slot ?? null; } public function parseResponse($data) { return $data; } } 'BIT', 'byte' => 'BYTE', ]; public function setArguments(array $arguments) { $value = array_pop($arguments); if (null === $value) { parent::setArguments($arguments); return; } if (in_array(strtoupper($value), self::$argumentEnum, true)) { $arguments[] = self::$argumentEnum[$value]; } else { $arguments[] = $value; } parent::setArguments($arguments); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$errorArgumentPositionOffset] === -1) { array_splice($arguments, static::$errorArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if ($arguments[static::$errorArgumentPositionOffset] < 0) { throw new UnexpectedValueException('Wrong error argument value or position offset'); } $argument = $arguments[static::$errorArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$errorArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$errorArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$errorModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$bucketSizeArgumentPositionOffset] === -1) { array_splice($arguments, static::$bucketSizeArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if ($arguments[static::$bucketSizeArgumentPositionOffset] < 1) { throw new UnexpectedValueException('Wrong bucket size argument value or position offset'); } $argument = $arguments[static::$bucketSizeArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$bucketSizeArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$bucketSizeArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$bucketSizeModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$expansionArgumentPositionOffset] === -1) { array_splice($arguments, static::$expansionArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if ($arguments[static::$expansionArgumentPositionOffset] < 1) { throw new UnexpectedValueException('Wrong expansion argument value or position offset'); } $argument = $arguments[static::$expansionArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$expansionArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$expansionArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$expansionModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } $argument = $arguments[static::$itemsArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$itemsArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$itemsArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$itemsModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$capacityArgumentPositionOffset] === -1) { array_splice($arguments, static::$capacityArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if ($arguments[static::$capacityArgumentPositionOffset] < 1) { throw new UnexpectedValueException('Wrong capacity argument value or position offset'); } $argument = $arguments[static::$capacityArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$capacityArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$capacityArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$capacityModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$maxIterationsArgumentPositionOffset] === -1) { array_splice($arguments, static::$maxIterationsArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if ($arguments[static::$maxIterationsArgumentPositionOffset] < 1) { throw new UnexpectedValueException('Wrong max iterations argument value or position offset'); } $argument = $arguments[static::$maxIterationsArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$maxIterationsArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$maxIterationsArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$maxIterationsModifier], [$argument], $argumentsAfter )); } } = $argumentsLength || false === $arguments[static::$noCreateArgumentPositionOffset] ) { parent::setArguments($arguments); return; } $argument = $arguments[static::$noCreateArgumentPositionOffset]; if (true === $argument) { $argument = 'NOCREATE'; } else { throw new UnexpectedValueException('Wrong NOCREATE argument type'); } $argumentsBefore = array_slice($arguments, 0, static::$noCreateArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$noCreateArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); } } = $argumentsLength) { parent::setArguments($arguments); return; } if (!is_numeric($arguments[static::$dbArgumentPositionOffset])) { throw new UnexpectedValueException('DB argument should be a valid numeric value'); } if ($arguments[static::$dbArgumentPositionOffset] < 0) { array_splice($arguments, static::$dbArgumentPositionOffset, 1); parent::setArguments($arguments); return; } $argument = $arguments[static::$dbArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$dbArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$dbArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [$this->dbModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if (!is_array($arguments[static::$weightsArgumentPositionOffset])) { throw new UnexpectedValueException('Wrong weights argument type'); } $weightsArray = $arguments[static::$weightsArgumentPositionOffset]; if (empty($weightsArray)) { unset($arguments[static::$weightsArgumentPositionOffset]); parent::setArguments($arguments); return; } $argumentsBefore = array_slice($arguments, 0, static::$weightsArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$weightsArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$weightsModifier], $weightsArray, $argumentsAfter )); } } 'LEFT', 'right' => 'RIGHT', ]; public function setArguments(array $arguments) { $argumentsLength = count($arguments); if (static::$leftRightArgumentPositionOffset >= $argumentsLength) { $arguments[] = 'LEFT'; parent::setArguments($arguments); return; } $argument = $arguments[static::$leftRightArgumentPositionOffset]; if (is_string($argument) && in_array(strtoupper($argument), self::$leftRightEnum, true)) { $argument = self::$leftRightEnum[$argument]; } else { $enumValues = implode(', ', array_keys(self::$leftRightEnum)); throw new UnexpectedValueException("Left/Right argument accepts only: {$enumValues} values"); } $argumentsBefore = array_slice($arguments, 0, static::$leftRightArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$leftRightArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [$argument], $argumentsAfter )); } } 'ASC', 'desc' => 'DESC', ]; public function setArguments(array $arguments) { $argumentsLength = count($arguments); if (static::$sortArgumentPositionOffset >= $argumentsLength) { parent::setArguments($arguments); return; } $argument = $arguments[static::$sortArgumentPositionOffset]; if (null === $argument) { array_splice($arguments, static::$sortArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if (!in_array(strtoupper($argument), self::$sortingEnum, true)) { $enumValues = implode(', ', array_keys(self::$sortingEnum)); throw new UnexpectedValueException("Sorting argument accepts only: {$enumValues} values"); } $argumentsBefore = array_slice($arguments, 0, static::$sortArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$sortArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$sortingEnum[$argument]], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } $toArgument = $arguments[static::$toArgumentPositionOffset]; if (null === $toArgument) { array_splice($arguments, static::$toArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } $argumentsBefore = array_slice($arguments, 0, static::$toArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$toArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, $toArgument->toArray(), $argumentsAfter )); } } 'BYLEX', 'byscore' => 'BYSCORE', ]; public function setArguments(array $arguments) { $argument = $arguments[static::$byLexByScoreArgumentPositionOffset]; if (false === $argument) { parent::setArguments($arguments); return; } if (is_string($argument) && in_array(strtoupper($argument), self::$argumentsEnum)) { $argument = self::$argumentsEnum[$argument]; } else { throw new UnexpectedValueException('By argument accepts only "bylex" and "byscore" values'); } $argumentsBefore = array_slice($arguments, 0, static::$byLexByScoreArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$byLexByScoreArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); } } = $argumentsLength || null === $arguments[static::$byArgumentPositionOffset]) { parent::setArguments($arguments); return; } $argument = $arguments[static::$byArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$byArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$byArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBefore, [$this->byModifier, $argument], $argumentsAfter)); } } getByArgumentPositionOffset($arguments); if (null === $argumentPositionOffset) { throw new InvalidArgumentException('Invalid BY argument value given'); } $byArgumentObject = $arguments[$argumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, $argumentPositionOffset); $argumentsAfter = array_slice($arguments, $argumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, $byArgumentObject->toArray(), $argumentsAfter )); } private function getByArgumentPositionOffset(array $arguments): ?int { foreach ($arguments as $i => $value) { if ($value instanceof ByInterface) { return $i; } } return null; } } isWithScoreModifier()) { $result = []; for ($i = 0, $iMax = count($data); $i < $iMax; ++$i) { if (is_array($data[$i])) { $result[$data[$i][0]] = $data[$i][1]; } elseif (array_key_exists($i + 1, $data)) { $result[$data[$i]] = $data[++$i]; } } return $result; } return $data; } } = $argumentsLength || false === $arguments[static::$withCoordArgumentPositionOffset] ) { parent::setArguments($arguments); return; } $argument = $arguments[static::$withCoordArgumentPositionOffset]; if (true === $argument) { $argument = 'WITHCOORD'; } else { throw new UnexpectedValueException('Wrong WITHCOORD argument type'); } $argumentsBefore = array_slice($arguments, 0, static::$withCoordArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$withCoordArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); } } = $argumentsLength || false === $arguments[static::$withHashArgumentPositionOffset] ) { parent::setArguments($arguments); return; } $argument = $arguments[static::$withHashArgumentPositionOffset]; if (true === $argument) { $argument = 'WITHHASH'; } else { throw new UnexpectedValueException('Wrong WITHHASH argument type'); } $argumentsBefore = array_slice($arguments, 0, static::$withHashArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$withHashArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); } } = $argumentsLength || false === $arguments[static::$withDistArgumentPositionOffset] ) { parent::setArguments($arguments); return; } $argument = $arguments[static::$withDistArgumentPositionOffset]; if (true === $argument) { $argument = 'WITHDIST'; } else { throw new UnexpectedValueException('Wrong WITHDIST argument type'); } $argumentsBefore = array_slice($arguments, 0, static::$withDistArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$withDistArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); } } getLimitArgumentPositionOffset($arguments); if (null === $argumentPositionOffset) { parent::setArguments($arguments); return; } $limitObject = $arguments[$argumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, $argumentPositionOffset); $argumentsAfter = array_slice($arguments, $argumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, $limitObject->toArray(), $argumentsAfter )); } private function getLimitArgumentPositionOffset(array $arguments): ?int { foreach ($arguments as $i => $value) { if ($value instanceof LimitInterface) { return $i; } } return null; } } = $argumentsLength || false === $arguments[static::$limitArgumentPositionOffset] ) { parent::setArguments($argumentsBefore); return; } $argument = $arguments[static::$limitArgumentPositionOffset]; $argumentsAfter = array_slice($arguments, static::$limitArgumentPositionOffset + 1); if (true === $argument) { parent::setArguments(array_merge($argumentsBefore, [self::$limitModifier], $argumentsAfter)); return; } if (!is_int($argument)) { throw new UnexpectedValueException('Wrong limit argument type'); } parent::setArguments(array_merge($argumentsBefore, [self::$limitModifier], [$argument], $argumentsAfter)); } } = $argumentsLength || false === $arguments[static::$storeDistArgumentPositionOffset] ) { parent::setArguments($arguments); return; } $argument = $arguments[static::$storeDistArgumentPositionOffset]; if (true === $argument) { $argument = 'STOREDIST'; } else { throw new UnexpectedValueException('Wrong STOREDIST argument type'); } $argumentsBefore = array_slice($arguments, 0, static::$storeDistArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$storeDistArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter)); } } getFromArgumentPositionOffset($arguments); if (null === $argumentPositionOffset) { throw new InvalidArgumentException('Invalid FROM argument value given'); } $fromArgumentObject = $arguments[$argumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, $argumentPositionOffset); $argumentsAfter = array_slice($arguments, $argumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, $fromArgumentObject->toArray(), $argumentsAfter )); } private function getFromArgumentPositionOffset(array $arguments): ?int { foreach ($arguments as $i => $value) { if ($value instanceof FromInterface) { return $i; } } return null; } } 'MIN', 'max' => 'MAX', ]; public function resolveModifier(int $offset, array &$arguments): void { if ($offset >= count($arguments)) { $arguments[$offset] = $this->modifierEnum['min']; return; } if (!is_string($arguments[$offset]) || !array_key_exists($arguments[$offset], $this->modifierEnum)) { throw new UnexpectedValueException('Wrong type of modifier given'); } $arguments[$offset] = $this->modifierEnum[$arguments[$offset]]; } } $argumentsLength || !is_array($arguments[static::$keysArgumentPositionOffset]) ) { throw new UnexpectedValueException('Wrong keys argument type or position offset'); } $keysArgument = $arguments[static::$keysArgumentPositionOffset]; $argumentsBeforeKeys = array_slice($arguments, 0, static::$keysArgumentPositionOffset); $argumentsAfterKeys = array_slice($arguments, static::$keysArgumentPositionOffset + 1); if ($withNumkeys) { $numkeys = count($keysArgument); parent::setArguments(array_merge($argumentsBeforeKeys, [$numkeys], $keysArgument, $argumentsAfterKeys)); return; } parent::setArguments(array_merge($argumentsBeforeKeys, $keysArgument, $argumentsAfterKeys)); } } = $argumentsLength) { parent::setArguments($arguments); return; } if (!is_array($arguments[static::$getArgumentPositionOffset])) { throw new UnexpectedValueException('Wrong get argument type'); } $patterns = []; foreach ($arguments[static::$getArgumentPositionOffset] as $pattern) { $patterns[] = self::$getModifier; $patterns[] = $pattern; } $argumentsBeforeKeys = array_slice($arguments, 0, static::$getArgumentPositionOffset); $argumentsAfterKeys = array_slice($arguments, static::$getArgumentPositionOffset + 1); parent::setArguments(array_merge($argumentsBeforeKeys, $patterns, $argumentsAfterKeys)); } } 'NX', 'xx' => 'XX', 'gt' => 'GT', 'lt' => 'LT', ]; public function setArguments(array $arguments) { $value = array_pop($arguments); if (null === $value) { parent::setArguments($arguments); return; } if (in_array(strtoupper($value), self::$argumentEnum, true)) { $arguments[] = self::$argumentEnum[strtolower($value)]; } else { $arguments[] = $value; } parent::setArguments($arguments); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$spaceArgumentPositionOffset] === '') { array_splice($arguments, static::$spaceArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } $argument = $arguments[static::$spaceArgumentPositionOffset]; if (!is_string($argument)) { throw new UnexpectedValueException('Space argument value should be a string'); } $argumentsBefore = array_slice($arguments, 0, static::$spaceArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$spaceArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$spaceModifier], [$argument], $argumentsAfter )); } } 'NX', 'xx' => 'XX', ]; public function setArguments(array $arguments) { $argumentsLength = count($arguments); if (static::$nxXxArgumentPositionOffset >= $argumentsLength) { parent::setArguments($arguments); return; } if (null === $arguments[static::$nxXxArgumentPositionOffset]) { array_splice($arguments, static::$nxXxArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } $argument = $arguments[static::$nxXxArgumentPositionOffset]; if (!in_array(strtoupper($argument), self::$argumentEnum, true)) { $enumValues = implode(', ', array_keys(self::$argumentEnum)); throw new UnexpectedValueException("Argument accepts only: {$enumValues} values"); } $argumentsBefore = array_slice($arguments, 0, static::$nxXxArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$nxXxArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$argumentEnum[strtolower($argument)]], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$newlineArgumentPositionOffset] === '') { array_splice($arguments, static::$newlineArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } $argument = $arguments[static::$newlineArgumentPositionOffset]; if (!is_string($argument)) { throw new UnexpectedValueException('Newline argument value should be a string'); } $argumentsBefore = array_slice($arguments, 0, static::$newlineArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$newlineArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$newlineModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$indentArgumentPositionOffset] === '') { array_splice($arguments, static::$indentArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } $argument = $arguments[static::$indentArgumentPositionOffset]; if (!is_string($argument)) { throw new UnexpectedValueException('Indent argument value should be a string'); } $argumentsBefore = array_slice($arguments, 0, static::$indentArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$indentArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$indentModifier], [$argument], $argumentsAfter )); } } 'MIN', 'max' => 'MAX', 'sum' => 'SUM', ]; private static $aggregateModifier = 'AGGREGATE'; public function setArguments(array $arguments) { $argumentsLength = count($arguments); if (static::$aggregateArgumentPositionOffset >= $argumentsLength) { parent::setArguments($arguments); return; } $argument = $arguments[static::$aggregateArgumentPositionOffset]; if (is_string($argument) && in_array(strtoupper($argument), self::$aggregateValuesEnum)) { $argument = self::$aggregateValuesEnum[$argument]; } else { $enumValues = implode(', ', array_keys(self::$aggregateValuesEnum)); throw new UnexpectedValueException("Aggregate argument accepts only: {$enumValues} values"); } $argumentsBefore = array_slice($arguments, 0, static::$aggregateArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$aggregateArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$aggregateModifier], [$argument], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$countArgumentPositionOffset] === -1) { array_splice($arguments, static::$countArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if ($arguments[static::$countArgumentPositionOffset] < 1) { throw new UnexpectedValueException('Wrong count argument value or position offset'); } $countArgument = $arguments[static::$countArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$countArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$countArgumentPositionOffset + 2); if (!$any) { $argumentsAfter = array_slice($arguments, static::$countArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [$this->countModifier], [$countArgument], $argumentsAfter )); return; } parent::setArguments(array_merge( $argumentsBefore, [$this->countModifier], [$countArgument], [$this->anyModifier], $argumentsAfter )); } } = $argumentsLength) { parent::setArguments($arguments); return; } if ($arguments[static::$timeoutArgumentPositionOffset] === -1) { array_splice($arguments, static::$timeoutArgumentPositionOffset, 1, [false]); parent::setArguments($arguments); return; } if ($arguments[static::$timeoutArgumentPositionOffset] < 1) { throw new UnexpectedValueException('Wrong timeout argument value or position offset'); } $argument = $arguments[static::$timeoutArgumentPositionOffset]; $argumentsBefore = array_slice($arguments, 0, static::$timeoutArgumentPositionOffset); $argumentsAfter = array_slice($arguments, static::$timeoutArgumentPositionOffset + 1); parent::setArguments(array_merge( $argumentsBefore, [self::$timeoutModifier], [$argument], $argumentsAfter )); } } disallowed = $this->getDisallowedOperations(); $this->readonly = $this->getReadOnlyOperations(); $this->readonlySHA1 = []; } public function isReadOperation(CommandInterface $command) { if (!$this->loadBalancing) { return false; } 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 [ '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 [ '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' => [$this, 'isBitfieldReadOnly'], 'GEOHASH' => true, 'GEOPOS' => true, 'GEODIST' => true, 'GEORADIUS' => [$this, 'isGeoradiusReadOnly'], 'GEORADIUSBYMEMBER' => [$this, 'isGeoradiusReadOnly'], ]; } public function disableLoadBalancing(): self { $this->loadBalancing = false; return $this; } } Option\Aggregate::class, 'cluster' => Option\Cluster::class, 'replication' => Option\Replication::class, 'connections' => Option\Connections::class, 'commands' => Option\Commands::class, 'exceptions' => Option\Exceptions::class, 'prefix' => Option\Prefix::class, 'crc16' => Option\CRC16::class, ]; protected $options = []; protected $input; public function __construct(array $options = null) { $this->input = $options ?? []; } 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 (isset($this->handlers[$option])) { $handler = $this->handlers[$option]; $handler = new $handler(); $value = $handler->filter($this, $value); } elseif (is_object($value) && method_exists($value, '__invoke')) { $value = $value($this); } return $this->options[$option] = $value; } if (isset($this->handlers[$option])) { return $this->options[$option] = $this->getDefault($option); } return; } } getHashGeneratorByDescription($options, $value); } elseif ($value instanceof Hash\HashGeneratorInterface) { return $value; } else { $class = get_class($this); throw new InvalidArgumentException("$class expects a valid hash generator"); } } public function getDefault(OptionsInterface $options) { return function_exists('phpiredis_utils_crc16') ? new Hash\PhpiredisCRC16() : new Hash\CRC16(); } } getConnectionInitializerByString($options, $value); } if (is_callable($value)) { return $this->getConnectionInitializer($options, $value); } else { throw new InvalidArgumentException(sprintf( '%s expects either a string or a callable value, %s given', static::class, is_object($value) ? get_class($value) : gettype($value) )); } } protected function getConnectionInitializerByString(OptionsInterface $options, string $description) { switch ($description) { case 'redis': case 'redis-cluster': return function ($parameters, $options, $option) { return new RedisCluster($options->connections, new RedisStrategy($options->crc16)); }; case 'predis': return $this->getDefaultConnectionInitializer(); default: throw new InvalidArgumentException(sprintf( '%s expects either `predis`, `redis` or `redis-cluster` as valid string values, `%s` given', static::class, $description )); } } protected function getDefaultConnectionInitializer() { return function ($parameters, $options, $option) { return new PredisCluster(); }; } public function getDefault(OptionsInterface $options) { return $this->getConnectionInitializer( $options, $this->getDefaultConnectionInitializer() ); } } createFactoryByArray($options, $value); } elseif (is_string($value)) { return $this->createFactoryByString($options, $value); } else { throw new InvalidArgumentException(sprintf( '%s expects a valid command factory', static::class )); } } protected function createFactoryByArray(OptionsInterface $options, array $value) { $commands = $this->getDefault($options); foreach ($value as $commandID => $commandClass) { if ($commandClass === null) { $commands->undefine($commandID); } else { $commands->define($commandID, $commandClass); } } return $commands; } protected function createFactoryByString(OptionsInterface $options, string $value) { switch (strtolower($value)) { case 'default': case 'predis': return $this->getDefault($options); case 'raw': return $this->createRawFactory($options); default: throw new InvalidArgumentException(sprintf( '%s does not recognize `%s` as a supported configuration string', static::class, $value )); } } protected function createRawFactory(OptionsInterface $options): FactoryInterface { $commands = new RawFactory(); if (isset($options->prefix)) { throw new InvalidArgumentException(sprintf( '%s does not support key prefixing', RawFactory::class )); } return $commands; } public function getDefault(OptionsInterface $options) { $commands = new RedisFactory(); if (isset($options->prefix)) { $commands->setProcessor($options->prefix); } return $commands; } } createFactoryByArray($options, $value); } elseif (is_string($value)) { return $this->createFactoryByString($options, $value); } else { throw new InvalidArgumentException(sprintf( '%s expects a valid connection factory', static::class )); } } protected function createFactoryByArray(OptionsInterface $options, array $value) { $factory = $this->getDefault($options); foreach ($value as $scheme => $initializer) { $factory->define($scheme, $initializer); } return $factory; } protected function createFactoryByString(OptionsInterface $options, string $value) { $factory = $this->getDefault($options); switch (strtolower($value)) { case 'phpiredis': case 'phpiredis-stream': $factory->define('tcp', PhpiredisStreamConnection::class); $factory->define('redis', PhpiredisStreamConnection::class); $factory->define('unix', PhpiredisStreamConnection::class); break; case 'phpiredis-socket': $factory->define('tcp', PhpiredisSocketConnection::class); $factory->define('redis', PhpiredisSocketConnection::class); $factory->define('unix', PhpiredisSocketConnection::class); break; case 'relay': $factory->define('tcp', RelayConnection::class); $factory->define('redis', RelayConnection::class); $factory->define('unix', RelayConnection::class); break; case 'default': return $factory; default: throw new InvalidArgumentException(sprintf( '%s does not recognize `%s` as a supported configuration string', static::class, $value )); } return $factory; } public function getDefault(OptionsInterface $options) { $factory = new Factory(); if ($options->defined('parameters')) { $factory->setDefaultParameters($options->parameters); } return $factory; } } getConnectionInitializerByString($options, $value); } if (is_callable($value)) { return $this->getConnectionInitializer($options, $value); } else { throw new InvalidArgumentException(sprintf( '%s expects either a string or a callable value, %s given', static::class, is_object($value) ? get_class($value) : gettype($value) )); } } protected function getConnectionInitializerByString(OptionsInterface $options, string $description) { switch ($description) { case 'sentinel': case 'redis-sentinel': return function ($parameters, $options) { return new SentinelReplication($options->service, $parameters, $options->connections); }; case 'predis': return $this->getDefaultConnectionInitializer(); default: throw new InvalidArgumentException(sprintf( '%s expects either `predis`, `sentinel` or `redis-sentinel` as valid string values, `%s` given', static::class, $description )); } } protected function getDefaultConnectionInitializer() { return function ($parameters, $options) { $connection = new MasterSlaveReplication(); if ($options->autodiscovery) { $connection->setConnectionFactory($options->connections); $connection->setAutoDiscovery(true); } return $connection; }; } public static function aggregate(OptionsInterface $options, AggregateConnectionInterface $connection, array $nodes) { if (!$connection instanceof SentinelReplication) { parent::aggregate($options, $connection, $nodes); } } public function getDefault(OptionsInterface $options) { return $this->getConnectionInitializer( $options, $this->getDefaultConnectionInitializer() ); } } getConnectionInitializer($options, $value); } protected function getConnectionInitializer(OptionsInterface $options, callable $callable) { return function ($parameters = null, $autoaggregate = false) use ($callable, $options) { $connection = call_user_func_array($callable, [&$parameters, $options, $this]); if (!$connection instanceof AggregateConnectionInterface) { throw new InvalidArgumentException(sprintf( '%s expects the supplied callable to return an instance of %s, but %s was returned', static::class, AggregateConnectionInterface::class, is_object($connection) ? get_class($connection) : gettype($connection) )); } if ($parameters && $autoaggregate) { static::aggregate($options, $connection, $parameters); } return $connection; }; } public static function aggregate(OptionsInterface $options, AggregateConnectionInterface $connection, array $nodes) { $connections = $options->connections; foreach ($nodes as $node) { $connection->add($node instanceof NodeConnectionInterface ? $node : $connections->create($node)); } } public function getDefault(OptionsInterface $options) { return; } } options = static::createOptions($options ?? new Options()); $this->connection = static::createConnection($this->options, $parameters ?? new Parameters()); $this->commands = $this->options->commands; } protected static function createOptions($options) { if (is_array($options)) { return new Options($options); } elseif ($options instanceof OptionsInterface) { return $options; } else { throw new InvalidArgumentException('Invalid type for client options'); } } protected static function createConnection(OptionsInterface $options, $parameters) { if ($parameters instanceof ConnectionInterface) { return $parameters; } if ($parameters instanceof ParametersInterface || is_string($parameters)) { return $options->connections->create($parameters); } if (is_array($parameters)) { if (!isset($parameters[0])) { return $options->connections->create($parameters); } elseif ($options->defined('cluster') && $initializer = $options->cluster) { return $initializer($parameters, true); } elseif ($options->defined('replication') && $initializer = $options->replication) { return $initializer($parameters, true); } elseif ($options->defined('aggregate') && $initializer = $options->aggregate) { return $initializer($parameters, false); } else { throw new InvalidArgumentException( 'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option' ); } } if (is_callable($parameters)) { $connection = call_user_func($parameters, $options); if (!$connection instanceof ConnectionInterface) { throw new InvalidArgumentException('Callable parameters must return a valid connection'); } return $connection; } throw new InvalidArgumentException('Invalid type for connection parameters'); } public function getCommandFactory() { return $this->commands; } public function getOptions() { return $this->options; } public function getClientBy($selector, $value) { $selector = strtolower($selector); if (!in_array($selector, ['id', 'key', 'slot', 'role', 'alias', 'command'])) { throw new InvalidArgumentException("Invalid selector type: `$selector`"); } if (!method_exists($this->connection, $method = "getConnectionBy$selector")) { $class = get_class($this->connection); throw new InvalidArgumentException("Selecting connection by $selector is not supported by $class"); } if (!$connection = $this->connection->$method($value)) { throw new InvalidArgumentException("Cannot find a connection by $selector matching `$value`"); } return new static($connection, $this->getOptions()); } 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 pack($value) { return $this->connection instanceof RelayConnection ? $this->connection->pack($value) : $value; } public function unpack($value) { return $this->connection instanceof RelayConnection ? $this->connection->unpack($value) : $value; } public function executeRaw(array $arguments, &$error = null) { $error = false; $commandID = array_shift($arguments); $response = $this->connection->executeCommand( new RawCommand($commandID, $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 = []) { return $this->commands->create($commandID, $arguments); } public function __get(string $name) { return ContainerFactory::create($this, $name); } public function __set(string $name, $value) { throw new RuntimeException('Not allowed'); } public function __isset(string $name) { throw new RuntimeException('Not allowed'); } 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') { $response = $this->executeCommand($command->getEvalCommand()); 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: [$arg0, $arg1] = $argv; return $this->$initializer($arg0, $arg1); default: return $this->$initializer($this, $argv); } } public function pipeline(...$arguments) { return $this->sharedContextFactory('createPipeline', func_get_args()); } protected function createPipeline(array $options = null, $callable = null) { if (isset($options['atomic']) && $options['atomic']) { $class = Atomic::class; } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) { $class = FireAndForget::class; } else { $class = Pipeline::class; } if ($this->connection instanceof RelayConnection) { if (isset($options['atomic']) && $options['atomic']) { $class = RelayAtomic::class; } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) { throw new NotSupportedException('The "relay" extension does not support fire-and-forget pipelines.'); } else { $class = RelayPipeline::class; } } $pipeline = new $class($this); if (isset($callable)) { return $pipeline->execute($callable); } return $pipeline; } public function transaction(...$arguments) { 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(...$arguments) { return $this->sharedContextFactory('createPubSub', func_get_args()); } protected function createPubSub(array $options = null, $callable = null) { if ($this->connection instanceof RelayConnection) { $pubsub = new RelayPubSubConsumer($this, $options); } else { $pubsub = new PubSubConsumer($this, $options); } if (!isset($callable)) { return $pubsub; } foreach ($pubsub as $message) { if (call_user_func($callable, $pubsub, $message) === false) { $pubsub->stop(); } } return null; } public function monitor() { return new MonitorConsumer($this); } #[ReturnTypeWillChange] public function getIterator() { $clients = []; $connection = $this->getConnection(); if (!$connection instanceof Traversable) { return new ArrayIterator([ (string) $connection => new static($connection, $this->getOptions()), ]); } foreach ($connection as $node) { $clients[(string) $node] = new static($node, $this->getOptions()); } return new ArrayIterator($clients); } } getClient(); $throw = $this->client->getOptions()->exceptions; try { $pipeline = $client->pipeline(); foreach ($commands as $command) { $name = $command->getId(); in_array($name, $connection->atypicalCommands) ? $pipeline->{$name}(...$command->getArguments()) : $pipeline->rawCommand($name, ...$command->getArguments()); } $responses = $pipeline->exec(); if (!is_array($responses)) { return $responses; } foreach ($responses as $key => $response) { if ($response instanceof RelayException) { if ($throw) { throw $response; } $responses[$key] = new Error($response->getMessage()); } } return $responses; } catch (RelayException $ex) { if ($client->getMode() !== $client::ATOMIC) { $client->discard(); } throw new ServerException($ex->getMessage(), $ex->getCode(), $ex); } } } isEmpty()) { $connection->writeRequest($commands->dequeue()); } $connection->disconnect(); return []; } } 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 = []; $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 = []; $sizeOfPipe = count($commands); $exceptions = []; foreach ($commands as $command) { $cmdConnection = $connection->getConnectionByCommand($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->getConnectionByCommand($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; } } 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->switchToMaster(); } return $connection; } protected function executePipeline(ConnectionInterface $connection, SplQueue $commands) { foreach ($commands as $command) { $connection->writeRequest($command); } $responses = []; $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; } } getClient(); $throw = $this->client->getOptions()->exceptions; try { $transaction = $client->multi(); foreach ($commands as $command) { $name = $command->getId(); in_array($name, $connection->atypicalCommands) ? $transaction->{$name}(...$command->getArguments()) : $transaction->rawCommand($name, ...$command->getArguments()); } $responses = $transaction->exec(); if (!is_array($responses)) { return $responses; } foreach ($responses as $key => $response) { if ($response instanceof RelayException) { if ($throw) { throw $response; } $responses[$key] = new Error($response->getMessage()); } } return $responses; } catch (RelayException $ex) { if ($client->getMode() !== $client::ATOMIC) { $client->discard(); } throw new ServerException($ex->getMessage(), $ex->getCode(), $ex); } } } getCommandFactory()->supports('multi', 'exec', 'discard')) { throw new ClientException( "'MULTI', 'EXEC' and 'DISCARD' are not supported by the current command factory." ); } 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) { $commandFactory = $this->getClient()->getCommandFactory(); $connection->executeCommand($commandFactory->create('multi')); foreach ($commands as $command) { $connection->writeRequest($command); } foreach ($commands as $command) { $response = $connection->readResponse($command); if ($response instanceof ErrorResponseInterface) { $connection->executeCommand($commandFactory->create('discard')); throw new ServerException($response->getMessage()); } } $executed = $connection->executeCommand($commandFactory->create('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 = []; $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; } } 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; } } replicas = $replicas; $this->nodeHashCallback = $nodeHashCallback; } public function add($node, $weight = null) { $this->nodes[] = [ '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 = []; $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); return $this->getByHash($hash); } protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) { return $upper >= 0 ? $upper : $ringKeysCount - 1; } public function getHashGenerator() { return $this; } } = 0x0000 && $slot <= 0x3FFF; } public static function isValidRange($first, $last) { return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last; } public function reset() { $this->slots = []; } public function isEmpty() { return empty($this->slots); } public function toArray() { return $this->slots; } public function getNodes() { return array_keys(array_flip($this->slots)); } public function setSlots($first, $last, $connection) { if (!static::isValidRange($first, $last)) { throw new OutOfBoundsException("Invalid slot range $first-$last for `$connection`"); } $this->slots += array_fill($first, $last - $first + 1, (string) $connection); } public function getSlots($first, $last) { if (!static::isValidRange($first, $last)) { throw new OutOfBoundsException("Invalid slot range $first-$last"); } return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null)); } #[ReturnTypeWillChange] public function offsetExists($slot) { return isset($this->slots[$slot]); } #[ReturnTypeWillChange] public function offsetGet($slot) { return $this->slots[$slot] ?? null; } #[ReturnTypeWillChange] public function offsetSet($slot, $connection) { if (!static::isValid($slot)) { throw new OutOfBoundsException("Invalid slot $slot for `$connection`"); } $this->slots[(int) $slot] = (string) $connection; } #[ReturnTypeWillChange] public function offsetUnset($slot) { unset($this->slots[$slot]); } #[ReturnTypeWillChange] public function count() { return count($this->slots); } #[ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->slots); } } hashGenerator = $hashGenerator ?: new CRC16(); } public function getSlotByKey($key) { $key = $this->extractKeyTag($key); return $this->hashGenerator->hash($key) & 0x3FFF; } public function getDistributor() { $class = get_class($this); throw new NotSupportedException("$class does not provide an external distributor"); } } > 8) ^ ord($value[$i])]) & 0xFFFF; } return $crc; } } distributor = $distributor ?: new HashRing(); } public function getSlotByKey($key) { $key = $this->extractKeyTag($key); $hash = $this->distributor->hash($key); return $this->distributor->getSlot($hash); } 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; } } commands = $this->getDefaultCommands(); } protected function getDefaultCommands() { $getKeyFromFirstArgument = [$this, 'getKeyFromFirstArgument']; $getKeyFromAllArguments = [$this, 'getKeyFromAllArguments']; return [ 'EXISTS' => $getKeyFromAllArguments, 'DEL' => $getKeyFromAllArguments, 'TYPE' => $getKeyFromFirstArgument, 'EXPIRE' => $getKeyFromFirstArgument, 'EXPIREAT' => $getKeyFromFirstArgument, 'PERSIST' => $getKeyFromFirstArgument, 'PEXPIRE' => $getKeyFromFirstArgument, 'PEXPIREAT' => $getKeyFromFirstArgument, 'TTL' => $getKeyFromFirstArgument, 'PTTL' => $getKeyFromFirstArgument, 'SORT' => [$this, 'getKeyFromSortCommand'], 'DUMP' => $getKeyFromFirstArgument, 'RESTORE' => $getKeyFromFirstArgument, 'FLUSHDB' => [$this, 'getFakeKey'], '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' => [$this, 'getKeyFromInterleavedArguments'], 'MSETNX' => [$this, 'getKeyFromInterleavedArguments'], 'SETNX' => $getKeyFromFirstArgument, 'SETRANGE' => $getKeyFromFirstArgument, 'STRLEN' => $getKeyFromFirstArgument, 'SUBSTR' => $getKeyFromFirstArgument, 'BITOP' => [$this, 'getKeyFromBitOp'], 'BITCOUNT' => $getKeyFromFirstArgument, 'BITFIELD' => $getKeyFromFirstArgument, 'LINSERT' => $getKeyFromFirstArgument, 'LINDEX' => $getKeyFromFirstArgument, 'LLEN' => $getKeyFromFirstArgument, 'LPOP' => $getKeyFromFirstArgument, 'RPOP' => $getKeyFromFirstArgument, 'RPOPLPUSH' => $getKeyFromAllArguments, 'BLPOP' => [$this, 'getKeyFromBlockingListCommands'], 'BRPOP' => [$this, 'getKeyFromBlockingListCommands'], 'BRPOPLPUSH' => [$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' => [$this, 'getKeyFromZsetAggregationCommands'], 'ZRANGE' => $getKeyFromFirstArgument, 'ZRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZRANK' => $getKeyFromFirstArgument, 'ZREM' => $getKeyFromFirstArgument, 'ZREMRANGEBYRANK' => $getKeyFromFirstArgument, 'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZREVRANGE' => $getKeyFromFirstArgument, 'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZREVRANK' => $getKeyFromFirstArgument, 'ZSCORE' => $getKeyFromFirstArgument, 'ZUNIONSTORE' => [$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' => [$this, 'getKeyFromScriptingCommands'], 'EVALSHA' => [$this, 'getKeyFromScriptingCommands'], 'INFO' => [$this, 'getFakeKey'], 'GEOADD' => $getKeyFromFirstArgument, 'GEOHASH' => $getKeyFromFirstArgument, 'GEOPOS' => $getKeyFromFirstArgument, 'GEODIST' => $getKeyFromFirstArgument, 'GEORADIUS' => [$this, 'getKeyFromGeoradiusCommands'], 'GEORADIUSBYMEMBER' => [$this, 'getKeyFromGeoradiusCommands'], 'CLUSTER' => [$this, 'getFakeKey'], ]; } 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 getFakeKey(): string { return 'key'; } protected function getKeyFromFirstArgument(CommandInterface $command) { return $command->getArgument(0); } protected function getKeyFromAllArguments(CommandInterface $command) { $arguments = $command->getArguments(); if (!$this->checkSameSlotForKeys($arguments)) { return null; } return $arguments[0]; } protected function getKeyFromInterleavedArguments(CommandInterface $command) { $arguments = $command->getArguments(); $keys = []; for ($i = 0; $i < count($arguments); $i += 2) { $keys[] = $arguments[$i]; } if (!$this->checkSameSlotForKeys($keys)) { return null; } return $arguments[0]; } protected function getKeyFromSortCommand(CommandInterface $command) { $arguments = $command->getArguments(); $firstKey = $arguments[0]; if (1 === $argc = count($arguments)) { return $firstKey; } $keys = [$firstKey]; for ($i = 1; $i < $argc; ++$i) { if (strtoupper($arguments[$i]) === 'STORE') { $keys[] = $arguments[++$i]; } } if (!$this->checkSameSlotForKeys($keys)) { return null; } return $firstKey; } protected function getKeyFromBlockingListCommands(CommandInterface $command) { $arguments = $command->getArguments(); if (!$this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) { return null; } return $arguments[0]; } protected function getKeyFromBitOp(CommandInterface $command) { $arguments = $command->getArguments(); if (!$this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) { return null; } return $arguments[1]; } protected function getKeyFromGeoradiusCommands(CommandInterface $command) { $arguments = $command->getArguments(); $argc = count($arguments); $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; if ($argc > $startIndex) { $keys = [$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 null; } } return $arguments[0]; } protected function getKeyFromZsetAggregationCommands(CommandInterface $command) { $arguments = $command->getArguments(); $keys = array_merge([$arguments[0]], array_slice($arguments, 2, $arguments[1])); if (!$this->checkSameSlotForKeys($keys)) { return null; } return $arguments[0]; } protected function getKeyFromScriptingCommands(CommandInterface $command) { $keys = $command instanceof ScriptCommand ? $command->getKeys() : array_slice($args = $command->getArguments(), 2, $args[1]); if (!$keys || !$this->checkSameSlotForKeys($keys)) { return null; } 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; } } className = $className; $this->allowsNull = $allowsNull; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($other instanceof self) { if (0 === strcasecmp($this->className->qualifiedName(), $other->className->qualifiedName())) { return true; } if (is_subclass_of($other->className->qualifiedName(), $this->className->qualifiedName(), true)) { return true; } } return false; } public function name(): string { return $this->className->qualifiedName(); } public function allowsNull(): bool { return $this->allowsNull; } public function className(): TypeName { return $this->className; } public function isObject(): bool { return true; } } allowsNull = $nullable; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if (!$other instanceof ObjectType) { return false; } return true; } public function name(): string { return 'object'; } public function allowsNull(): bool { return $this->allowsNull; } public function isGenericObject(): bool { return true; } } className = $className; $this->allowsNull = $allowsNull; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if (!$other instanceof ObjectType) { return false; } if (0 === strcasecmp($this->className->qualifiedName(), $other->className()->qualifiedName())) { return true; } if (is_subclass_of($other->className()->qualifiedName(), $this->className->qualifiedName(), true)) { return true; } return false; } public function name(): string { return 'static'; } public function allowsNull(): bool { return $this->allowsNull; } public function isStatic(): bool { return true; } } name = $this->normalize($name); $this->allowsNull = $nullable; $this->value = $value; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($this->name === 'bool' && $other->name() === 'true') { return true; } if ($this->name === 'bool' && $other->name() === 'false') { return true; } if ($other instanceof self) { return $this->name === $other->name; } return false; } public function name(): string { return $this->name; } public function allowsNull(): bool { return $this->allowsNull; } public function value() { return $this->value; } public function isSimple(): bool { return true; } private function normalize(string $name): string { $name = strtolower($name); switch ($name) { case 'boolean': return 'bool'; case 'real': case 'double': return 'float'; case 'integer': return 'int'; case '[]': return 'array'; default: return $name; } } } name() === 'bool' && $other->value() === false; } public function name(): string { return 'false'; } public function allowsNull(): bool { return false; } public function isFalse(): bool { return true; } } ensureMinimumOfTwoTypes(...$types); $this->ensureOnlyValidTypes(...$types); $this->ensureNoDuplicateTypes(...$types); $this->types = $types; } public function isAssignable(Type $other): bool { return $other->isObject(); } public function asString(): string { return $this->name(); } public function name(): string { $types = []; foreach ($this->types as $type) { $types[] = $type->name(); } sort($types); return implode('&', $types); } public function allowsNull(): bool { return false; } public function isIntersection(): bool { return true; } public function types(): array { return $this->types; } private function ensureMinimumOfTwoTypes(Type ...$types): void { if (count($types) < 2) { throw new RuntimeException( 'An intersection type must be composed of at least two types' ); } } private function ensureOnlyValidTypes(Type ...$types): void { foreach ($types as $type) { if (!$type->isObject()) { throw new RuntimeException( 'An intersection type can only be composed of interfaces and classes' ); } } } private function ensureNoDuplicateTypes(Type ...$types): void { $names = []; foreach ($types as $type) { assert($type instanceof ObjectType); $classQualifiedName = $type->className()->qualifiedName(); if (in_array($classQualifiedName, $names, true)) { throw new RuntimeException('An intersection type must not contain duplicate types'); } $names[] = $classQualifiedName; } } } allowsNull = $nullable; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($other instanceof self) { return true; } if ($other instanceof SimpleType) { return is_iterable($other->value()); } if ($other instanceof ObjectType) { $className = $other->className()->qualifiedName(); assert(class_exists($className)); try { return (new ReflectionClass($className))->isIterable(); } catch (ReflectionException $e) { throw new RuntimeException( $e->getMessage(), (int) $e->getCode(), $e ); } } return false; } public function name(): string { return 'iterable'; } public function allowsNull(): bool { return $this->allowsNull; } public function isIterable(): bool { return true; } } name() === 'bool' && $other->value() === true; } public function name(): string { return 'true'; } public function allowsNull(): bool { return false; } public function isTrue(): bool { return true; } } allowsNull = $nullable; } public function isAssignable(Type $other): bool { if ($this->allowsNull && $other instanceof NullType) { return true; } if ($other instanceof self) { return true; } if ($other instanceof ObjectType) { if ($this->isClosure($other)) { return true; } if ($this->hasInvokeMethod($other)) { return true; } } if ($other instanceof SimpleType) { if ($this->isFunction($other)) { return true; } if ($this->isClassCallback($other)) { return true; } if ($this->isObjectCallback($other)) { return true; } } return false; } public function name(): string { return 'callable'; } public function allowsNull(): bool { return $this->allowsNull; } public function isCallable(): bool { return true; } private function isClosure(ObjectType $type): bool { return !$type->className()->isNamespaced() && $type->className()->simpleName() === Closure::class; } private function hasInvokeMethod(ObjectType $type): bool { $className = $type->className()->qualifiedName(); assert(class_exists($className)); try { $class = new ReflectionClass($className); } catch (ReflectionException $e) { throw new RuntimeException( $e->getMessage(), (int) $e->getCode(), $e ); } if ($class->hasMethod('__invoke')) { return true; } return false; } private function isFunction(SimpleType $type): bool { if (!is_string($type->value())) { return false; } return function_exists($type->value()); } private function isObjectCallback(SimpleType $type): bool { if (!is_array($type->value())) { return false; } if (count($type->value()) !== 2) { return false; } if (!isset($type->value()[0], $type->value()[1])) { return false; } if (!is_object($type->value()[0]) || !is_string($type->value()[1])) { return false; } [$object, $methodName] = $type->value(); return (new ReflectionObject($object))->hasMethod($methodName); } private function isClassCallback(SimpleType $type): bool { if (!is_string($type->value()) && !is_array($type->value())) { return false; } if (is_string($type->value())) { if (strpos($type->value(), '::') === false) { return false; } [$className, $methodName] = explode('::', $type->value()); } if (is_array($type->value())) { if (count($type->value()) !== 2) { return false; } if (!isset($type->value()[0], $type->value()[1])) { return false; } if (!is_string($type->value()[0]) || !is_string($type->value()[1])) { return false; } [$className, $methodName] = $type->value(); } assert(isset($className) && is_string($className) && class_exists($className)); assert(isset($methodName) && is_string($methodName)); try { $class = new ReflectionClass($className); if ($class->hasMethod($methodName)) { $method = $class->getMethod($methodName); return $method->isPublic() && $method->isStatic(); } } catch (ReflectionException $e) { throw new RuntimeException( $e->getMessage(), (int) $e->getCode(), $e ); } return false; } } ensureMinimumOfTwoTypes(...$types); $this->ensureOnlyValidTypes(...$types); $this->types = $types; } public function isAssignable(Type $other): bool { foreach ($this->types as $type) { if ($type->isAssignable($other)) { return true; } } return false; } public function asString(): string { return $this->name(); } public function name(): string { $types = []; foreach ($this->types as $type) { if ($type->isIntersection()) { $types[] = '(' . $type->name() . ')'; continue; } $types[] = $type->name(); } sort($types); return implode('|', $types); } public function allowsNull(): bool { foreach ($this->types as $type) { if ($type instanceof NullType) { return true; } } return false; } public function isUnion(): bool { return true; } public function containsIntersectionTypes(): bool { foreach ($this->types as $type) { if ($type->isIntersection()) { return true; } } return false; } public function types(): array { return $this->types; } private function ensureMinimumOfTwoTypes(Type ...$types): void { if (count($types) < 2) { throw new RuntimeException( 'A union type must be composed of at least two types' ); } } private function ensureOnlyValidTypes(Type ...$types): void { foreach ($types as $type) { if ($type instanceof UnknownType) { throw new RuntimeException( 'A union type must not be composed of an unknown type' ); } if ($type instanceof VoidType) { throw new RuntimeException( 'A union type must not be composed of a void type' ); } } } } =') && strtolower($typeName) === 'never') { return new NeverType; } switch (strtolower($typeName)) { case 'callable': return new CallableType($allowsNull); case 'true': return new TrueType; case 'false': return new FalseType; case 'iterable': return new IterableType($allowsNull); case 'null': return new NullType; case 'object': return new GenericObjectType($allowsNull); case 'unknown type': return new UnknownType; case 'void': return new VoidType; case 'array': case 'bool': case 'boolean': case 'double': case 'float': case 'int': case 'integer': case 'real': case 'resource': case 'resource (closed)': case 'string': return new SimpleType($typeName, $allowsNull); default: return new ObjectType(TypeName::fromQualifiedName($typeName), $allowsNull); } } public function asString(): string { return ($this->allowsNull() ? '?' : '') . $this->name(); } public function isCallable(): bool { return false; } public function isTrue(): bool { return false; } public function isFalse(): bool { return false; } public function isGenericObject(): bool { return false; } public function isIntersection(): bool { return false; } public function isIterable(): bool { return false; } public function isMixed(): bool { return false; } public function isNever(): bool { return false; } public function isNull(): bool { return false; } public function isObject(): bool { return false; } public function isSimple(): bool { return false; } public function isStatic(): bool { return false; } public function isUnion(): bool { return false; } public function isUnknown(): bool { return false; } public function isVoid(): bool { return false; } abstract public function isAssignable(self $other): bool; abstract public function name(): string; abstract public function allowsNull(): bool; } getParameters() as $parameter) { $name = $parameter->getName(); assert($name !== ''); if (!$parameter->hasType()) { $parameters[] = new Parameter($name, new UnknownType); continue; } $type = $parameter->getType(); if ($type instanceof ReflectionNamedType) { $parameters[] = new Parameter( $name, $this->mapNamedType($type, $functionOrMethod) ); continue; } if ($type instanceof ReflectionUnionType) { $parameters[] = new Parameter( $name, $this->mapUnionType($type, $functionOrMethod) ); continue; } if ($type instanceof ReflectionIntersectionType) { $parameters[] = new Parameter( $name, $this->mapIntersectionType($type, $functionOrMethod) ); } } return $parameters; } public function fromReturnType(ReflectionFunctionAbstract $functionOrMethod): Type { if (!$this->hasReturnType($functionOrMethod)) { return new UnknownType; } $returnType = $this->returnType($functionOrMethod); assert($returnType instanceof ReflectionNamedType || $returnType instanceof ReflectionUnionType || $returnType instanceof ReflectionIntersectionType); if ($returnType instanceof ReflectionNamedType) { return $this->mapNamedType($returnType, $functionOrMethod); } if ($returnType instanceof ReflectionUnionType) { return $this->mapUnionType($returnType, $functionOrMethod); } if ($returnType instanceof ReflectionIntersectionType) { return $this->mapIntersectionType($returnType, $functionOrMethod); } } private function mapNamedType(ReflectionNamedType $type, ReflectionFunctionAbstract $functionOrMethod): Type { if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'self') { return ObjectType::fromName( $functionOrMethod->getDeclaringClass()->getName(), $type->allowsNull() ); } if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'static') { return new StaticType( TypeName::fromReflection($functionOrMethod->getDeclaringClass()), $type->allowsNull() ); } if ($type->getName() === 'mixed') { return new MixedType; } if ($functionOrMethod instanceof ReflectionMethod && $type->getName() === 'parent') { return ObjectType::fromName( $functionOrMethod->getDeclaringClass()->getParentClass()->getName(), $type->allowsNull() ); } return Type::fromName( $type->getName(), $type->allowsNull() ); } private function mapUnionType(ReflectionUnionType $type, ReflectionFunctionAbstract $functionOrMethod): Type { $types = []; foreach ($type->getTypes() as $_type) { assert($_type instanceof ReflectionNamedType || $_type instanceof ReflectionIntersectionType); if ($_type instanceof ReflectionNamedType) { $types[] = $this->mapNamedType($_type, $functionOrMethod); continue; } $types[] = $this->mapIntersectionType($_type, $functionOrMethod); } return new UnionType(...$types); } private function mapIntersectionType(ReflectionIntersectionType $type, ReflectionFunctionAbstract $functionOrMethod): Type { $types = []; foreach ($type->getTypes() as $_type) { assert($_type instanceof ReflectionNamedType); $types[] = $this->mapNamedType($_type, $functionOrMethod); } return new IntersectionType(...$types); } private function hasReturnType(ReflectionFunctionAbstract $functionOrMethod): bool { if ($functionOrMethod->hasReturnType()) { return true; } if (!method_exists($functionOrMethod, 'hasTentativeReturnType')) { return false; } return $functionOrMethod->hasTentativeReturnType(); } private function returnType(ReflectionFunctionAbstract $functionOrMethod): ?ReflectionType { if ($functionOrMethod->hasReturnType()) { return $functionOrMethod->getReturnType(); } if (!method_exists($functionOrMethod, 'getTentativeReturnType')) { return null; } return $functionOrMethod->getTentativeReturnType(); } } getNamespaceName(), $type->getShortName() ); } public function __construct(?string $namespaceName, string $simpleName) { if ($namespaceName === '') { $namespaceName = null; } $this->namespaceName = $namespaceName; $this->simpleName = $simpleName; } public function namespaceName(): ?string { return $this->namespaceName; } public function simpleName(): string { return $this->simpleName; } public function qualifiedName(): string { return $this->namespaceName === null ? $this->simpleName : $this->namespaceName . '\\' . $this->simpleName; } public function isNamespaced(): bool { return $this->namespaceName !== null; } } name = $name; $this->type = $type; } public function name(): string { return $this->name; } public function type(): Type { return $this->type; } } functions()) as $function) { uopz_delete($function); } } public function restoreGlobalVariables(Snapshot $snapshot): void { $superGlobalArrays = $snapshot->superGlobalArrays(); foreach ($superGlobalArrays as $superGlobalArray) { $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); } $globalVariables = $snapshot->globalVariables(); foreach (array_keys($GLOBALS) as $key) { if ($key !== 'GLOBALS' && !in_array($key, $superGlobalArrays, true) && !$snapshot->excludeList()->isGlobalVariableExcluded($key)) { if (array_key_exists($key, $globalVariables)) { $GLOBALS[$key] = $globalVariables[$key]; } else { unset($GLOBALS[$key]); } } } } public function restoreStaticAttributes(Snapshot $snapshot): void { $current = new Snapshot($snapshot->excludeList(), false, false, false, false, true, false, false, false, false); $newClasses = array_diff($current->classes(), $snapshot->classes()); unset($current); foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { foreach ($staticAttributes as $name => $value) { $reflector = new ReflectionProperty($className, $name); $reflector->setAccessible(true); $reflector->setValue(null, $value); } } foreach ($newClasses as $className) { $class = new ReflectionClass($className); $defaults = $class->getDefaultProperties(); foreach ($class->getProperties() as $attribute) { if (!$attribute->isStatic()) { continue; } $name = $attribute->getName(); if ($snapshot->excludeList()->isStaticAttributeExcluded($className, $name)) { continue; } if (!isset($defaults[$name])) { continue; } $attribute->setAccessible(true); $attribute->setValue(null, $defaults[$name]); } } } private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray): void { $superGlobalVariables = $snapshot->superGlobalVariables(); if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray]) && isset($superGlobalVariables[$superGlobalArray])) { $keys = array_keys( array_merge( $GLOBALS[$superGlobalArray], $superGlobalVariables[$superGlobalArray] ) ); foreach ($keys as $key) { if (isset($superGlobalVariables[$superGlobalArray][$key])) { $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; } else { unset($GLOBALS[$superGlobalArray][$key]); } } } } } constants() as $name => $value) { $result .= sprintf( 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", $name, $name, $this->exportVariable($value) ); } return $result; } public function globalVariables(Snapshot $snapshot): string { $result = <<<'EOT' call_user_func( function () { foreach (array_keys($GLOBALS) as $key) { unset($GLOBALS[$key]); } } ); EOT; foreach ($snapshot->globalVariables() as $name => $value) { $result .= sprintf( '$GLOBALS[%s] = %s;' . PHP_EOL, $this->exportVariable($name), $this->exportVariable($value) ); } return $result; } public function iniSettings(Snapshot $snapshot): string { $result = ''; foreach ($snapshot->iniSettings() as $key => $value) { $result .= sprintf( '@ini_set(%s, %s);' . "\n", $this->exportVariable($key), $this->exportVariable($value) ); } return $result; } private function exportVariable($variable): string { if (is_scalar($variable) || null === $variable || (is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { return var_export($variable, true); } return 'unserialize(' . var_export(serialize($variable), true) . ')'; } private function arrayOnlyContainsScalars(array $array): bool { $result = true; foreach ($array as $element) { if (is_array($element)) { $result = $this->arrayOnlyContainsScalars($element); } elseif (!is_scalar($element) && null !== $element) { $result = false; } if ($result === false) { break; } } return $result; } } globalVariables[$variableName] = true; } public function addClass(string $className): void { $this->classes[] = $className; } public function addSubclassesOf(string $className): void { $this->parentClasses[] = $className; } public function addImplementorsOf(string $interfaceName): void { $this->interfaces[] = $interfaceName; } public function addClassNamePrefix(string $classNamePrefix): void { $this->classNamePrefixes[] = $classNamePrefix; } public function addStaticAttribute(string $className, string $attributeName): void { if (!isset($this->staticAttributes[$className])) { $this->staticAttributes[$className] = []; } $this->staticAttributes[$className][$attributeName] = true; } public function isGlobalVariableExcluded(string $variableName): bool { return isset($this->globalVariables[$variableName]); } public function isStaticAttributeExcluded(string $className, string $attributeName): bool { if (in_array($className, $this->classes, true)) { return true; } foreach ($this->classNamePrefixes as $prefix) { if (strpos($className, $prefix) === 0) { return true; } } $class = new ReflectionClass($className); foreach ($this->parentClasses as $type) { if ($class->isSubclassOf($type)) { return true; } } foreach ($this->interfaces as $type) { if ($class->implementsInterface($type)) { return true; } } if (isset($this->staticAttributes[$className][$attributeName])) { return true; } return false; } } excludeList = $excludeList ?: new ExcludeList; if ($includeConstants) { $this->snapshotConstants(); } if ($includeFunctions) { $this->snapshotFunctions(); } if ($includeClasses || $includeStaticAttributes) { $this->snapshotClasses(); } if ($includeInterfaces) { $this->snapshotInterfaces(); } if ($includeGlobalVariables) { $this->setupSuperGlobalArrays(); $this->snapshotGlobals(); } if ($includeStaticAttributes) { $this->snapshotStaticAttributes(); } if ($includeIniSettings) { $this->iniSettings = ini_get_all(null, false); } if ($includeIncludedFiles) { $this->includedFiles = get_included_files(); } if ($includeTraits) { $this->traits = get_declared_traits(); } } public function excludeList(): ExcludeList { return $this->excludeList; } public function globalVariables(): array { return $this->globalVariables; } public function superGlobalVariables(): array { return $this->superGlobalVariables; } public function superGlobalArrays(): array { return $this->superGlobalArrays; } public function staticAttributes(): array { return $this->staticAttributes; } public function iniSettings(): array { return $this->iniSettings; } public function includedFiles(): array { return $this->includedFiles; } public function constants(): array { return $this->constants; } public function functions(): array { return $this->functions; } public function interfaces(): array { return $this->interfaces; } public function classes(): array { return $this->classes; } public function traits(): array { return $this->traits; } private function snapshotConstants(): void { $constants = get_defined_constants(true); if (isset($constants['user'])) { $this->constants = $constants['user']; } } private function snapshotFunctions(): void { $functions = get_defined_functions(); $this->functions = $functions['user']; } private function snapshotClasses(): void { foreach (array_reverse(get_declared_classes()) as $className) { $class = new ReflectionClass($className); if (!$class->isUserDefined()) { break; } $this->classes[] = $className; } $this->classes = array_reverse($this->classes); } private function snapshotInterfaces(): void { foreach (array_reverse(get_declared_interfaces()) as $interfaceName) { $class = new ReflectionClass($interfaceName); if (!$class->isUserDefined()) { break; } $this->interfaces[] = $interfaceName; } $this->interfaces = array_reverse($this->interfaces); } private function snapshotGlobals(): void { $superGlobalArrays = $this->superGlobalArrays(); foreach ($superGlobalArrays as $superGlobalArray) { $this->snapshotSuperGlobalArray($superGlobalArray); } foreach (array_keys($GLOBALS) as $key) { if ($key !== 'GLOBALS' && !in_array($key, $superGlobalArrays, true) && $this->canBeSerialized($GLOBALS[$key]) && !$this->excludeList->isGlobalVariableExcluded($key)) { $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); } } } private function snapshotSuperGlobalArray(string $superGlobalArray): void { $this->superGlobalVariables[$superGlobalArray] = []; if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { foreach ($GLOBALS[$superGlobalArray] as $key => $value) { $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value)); } } } private function snapshotStaticAttributes(): void { foreach ($this->classes as $className) { $class = new ReflectionClass($className); $snapshot = []; foreach ($class->getProperties() as $attribute) { if ($attribute->isStatic()) { $name = $attribute->getName(); if ($this->excludeList->isStaticAttributeExcluded($className, $name)) { continue; } $attribute->setAccessible(true); if (PHP_VERSION_ID >= 70400 && !$attribute->isInitialized()) { continue; } $value = $attribute->getValue(); if ($this->canBeSerialized($value)) { $snapshot[$name] = unserialize(serialize($value)); } } } if (!empty($snapshot)) { $this->staticAttributes[$className] = $snapshot; } } } private function setupSuperGlobalArrays(): void { $this->superGlobalArrays = [ '_ENV', '_POST', '_GET', '_COOKIE', '_SERVER', '_FILES', '_REQUEST', ]; } private function canBeSerialized($variable): bool { if (is_scalar($variable) || $variable === null) { return true; } if (is_resource($variable)) { return false; } foreach ($this->enumerateObjectsAndResources($variable) as $value) { if (is_resource($value)) { return false; } if (is_object($value)) { $class = new ReflectionClass($value); if ($class->isAnonymous()) { return false; } try { @serialize($value); } catch (Throwable $t) { return false; } } } return true; } private function enumerateObjectsAndResources($variable): array { if (isset(func_get_args()[1])) { $processed = func_get_args()[1]; } else { $processed = new Context; } $result = []; if ($processed->contains($variable)) { return $result; } $array = $variable; $processed->add($variable); if (is_array($variable)) { foreach ($array as $element) { if (!is_array($element) && !is_object($element) && !is_resource($element)) { continue; } if (!is_resource($element)) { $result = array_merge( $result, $this->enumerateObjectsAndResources($element, $processed) ); } else { $result[] = $element; } } } else { $result[] = $variable; foreach ((new ObjectReflector)->getAttributes($variable) as $value) { if (!is_array($value) && !is_object($value) && !is_resource($value)) { continue; } if (!is_resource($value)) { $result = array_merge( $result, $this->enumerateObjectsAndResources($value, $processed) ); } else { $result[] = $value; } } } return $result; } } codeUnits = $collection->asArray(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return isset($this->codeUnits[$this->position]); } public function key(): int { return $this->position; } public function current(): CodeUnit { return $this->codeUnits[$this->position]; } public function next(): void { $this->position++; } } add($item); } return $collection; } public static function fromList(CodeUnit ...$items): self { return self::fromArray($items); } private function __construct() { } public function asArray(): array { return $this->codeUnits; } public function getIterator(): CodeUnitCollectionIterator { return new CodeUnitCollectionIterator($this); } public function count(): int { return count($this->codeUnits); } public function isEmpty(): bool { return empty($this->codeUnits); } public function mergeWith(self $other): self { return self::fromArray( array_merge( $this->asArray(), $other->asArray() ) ); } private function add(CodeUnit $item): void { $this->codeUnits[] = $item; } } getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } public static function forClassMethod(string $className, string $methodName): ClassMethodUnit { self::ensureUserDefinedClass($className); $reflector = self::reflectorForClassMethod($className, $methodName); return new ClassMethodUnit( $className . '::' . $methodName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } public static function forInterface(string $interfaceName): InterfaceUnit { self::ensureUserDefinedInterface($interfaceName); $reflector = self::reflectorForClass($interfaceName); return new InterfaceUnit( $interfaceName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } public static function forInterfaceMethod(string $interfaceName, string $methodName): InterfaceMethodUnit { self::ensureUserDefinedInterface($interfaceName); $reflector = self::reflectorForClassMethod($interfaceName, $methodName); return new InterfaceMethodUnit( $interfaceName . '::' . $methodName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } public static function forTrait(string $traitName): TraitUnit { self::ensureUserDefinedTrait($traitName); $reflector = self::reflectorForClass($traitName); return new TraitUnit( $traitName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } public static function forTraitMethod(string $traitName, string $methodName): TraitMethodUnit { self::ensureUserDefinedTrait($traitName); $reflector = self::reflectorForClassMethod($traitName, $methodName); return new TraitMethodUnit( $traitName . '::' . $methodName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } public static function forFunction(string $functionName): FunctionUnit { $reflector = self::reflectorForFunction($functionName); if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined function', $functionName ) ); } return new FunctionUnit( $functionName, $reflector->getFileName(), range( $reflector->getStartLine(), $reflector->getEndLine() ) ); } private function __construct(string $name, string $sourceFileName, array $sourceLines) { $this->name = $name; $this->sourceFileName = $sourceFileName; $this->sourceLines = $sourceLines; } public function name(): string { return $this->name; } public function sourceFileName(): string { return $this->sourceFileName; } public function sourceLines(): array { return $this->sourceLines; } public function isClass(): bool { return false; } public function isClassMethod(): bool { return false; } public function isInterface(): bool { return false; } public function isInterfaceMethod(): bool { return false; } public function isTrait(): bool { return false; } public function isTraitMethod(): bool { return false; } public function isFunction(): bool { return false; } private static function ensureUserDefinedClass(string $className): void { try { $reflector = new ReflectionClass($className); if ($reflector->isInterface()) { throw new InvalidCodeUnitException( sprintf( '"%s" is an interface and not a class', $className ) ); } if ($reflector->isTrait()) { throw new InvalidCodeUnitException( sprintf( '"%s" is a trait and not a class', $className ) ); } if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined class', $className ) ); } } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private static function ensureUserDefinedInterface(string $interfaceName): void { try { $reflector = new ReflectionClass($interfaceName); if (!$reflector->isInterface()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not an interface', $interfaceName ) ); } if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined interface', $interfaceName ) ); } } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private static function ensureUserDefinedTrait(string $traitName): void { try { $reflector = new ReflectionClass($traitName); if (!$reflector->isTrait()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a trait', $traitName ) ); } if (!$reflector->isUserDefined()) { throw new InvalidCodeUnitException( sprintf( '"%s" is not a user-defined trait', $traitName ) ); } } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private static function reflectorForClass(string $className): ReflectionClass { try { return new ReflectionClass($className); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private static function reflectorForClassMethod(string $className, string $methodName): ReflectionMethod { try { return new ReflectionMethod($className, $methodName); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private static function reflectorForFunction(string $functionName): ReflectionFunction { try { return new ReflectionFunction($functionName); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } } sourceFileName(); if (!isset($result[$sourceFileName])) { $result[$sourceFileName] = []; } $result[$sourceFileName] = array_merge($result[$sourceFileName], $codeUnit->sourceLines()); } foreach (array_keys($result) as $sourceFileName) { $result[$sourceFileName] = array_values(array_unique($result[$sourceFileName])); sort($result[$sourceFileName]); } ksort($result); return $result; } public function stringToCodeUnits(string $unit): CodeUnitCollection { if (strpos($unit, '::') !== false) { [$firstPart, $secondPart] = explode('::', $unit); if (empty($firstPart) && $this->isUserDefinedFunction($secondPart)) { return CodeUnitCollection::fromList(CodeUnit::forFunction($secondPart)); } if ($this->isUserDefinedClass($firstPart)) { if ($secondPart === '') { return $this->publicMethodsOfClass($firstPart); } if ($secondPart === '') { return $this->protectedAndPrivateMethodsOfClass($firstPart); } if ($secondPart === '') { return $this->protectedMethodsOfClass($firstPart); } if ($secondPart === '') { return $this->publicAndPrivateMethodsOfClass($firstPart); } if ($secondPart === '') { return $this->privateMethodsOfClass($firstPart); } if ($secondPart === '') { return $this->publicAndProtectedMethodsOfClass($firstPart); } if ($this->isUserDefinedMethod($firstPart, $secondPart)) { return CodeUnitCollection::fromList(CodeUnit::forClassMethod($firstPart, $secondPart)); } } if ($this->isUserDefinedInterface($firstPart)) { return CodeUnitCollection::fromList(CodeUnit::forInterfaceMethod($firstPart, $secondPart)); } if ($this->isUserDefinedTrait($firstPart)) { return CodeUnitCollection::fromList(CodeUnit::forTraitMethod($firstPart, $secondPart)); } } else { if ($this->isUserDefinedClass($unit)) { $units = [CodeUnit::forClass($unit)]; foreach ($this->reflectorForClass($unit)->getTraits() as $trait) { if (!$trait->isUserDefined()) { continue; } $units[] = CodeUnit::forTrait($trait->getName()); } return CodeUnitCollection::fromArray($units); } if ($this->isUserDefinedInterface($unit)) { return CodeUnitCollection::fromList(CodeUnit::forInterface($unit)); } if ($this->isUserDefinedTrait($unit)) { return CodeUnitCollection::fromList(CodeUnit::forTrait($unit)); } if ($this->isUserDefinedFunction($unit)) { return CodeUnitCollection::fromList(CodeUnit::forFunction($unit)); } $unit = str_replace('', '', $unit); if ($this->isUserDefinedClass($unit)) { return $this->classAndParentClassesAndTraits($unit); } } throw new InvalidCodeUnitException( sprintf( '"%s" is not a valid code unit', $unit ) ); } private function publicMethodsOfClass(string $className): CodeUnitCollection { return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC); } private function publicAndProtectedMethodsOfClass(string $className): CodeUnitCollection { return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED); } private function publicAndPrivateMethodsOfClass(string $className): CodeUnitCollection { return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PRIVATE); } private function protectedMethodsOfClass(string $className): CodeUnitCollection { return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED); } private function protectedAndPrivateMethodsOfClass(string $className): CodeUnitCollection { return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PRIVATE); } private function privateMethodsOfClass(string $className): CodeUnitCollection { return $this->methodsOfClass($className, ReflectionMethod::IS_PRIVATE); } private function methodsOfClass(string $className, int $filter): CodeUnitCollection { $units = []; foreach ($this->reflectorForClass($className)->getMethods($filter) as $method) { if (!$method->isUserDefined()) { continue; } $units[] = CodeUnit::forClassMethod($className, $method->getName()); } return CodeUnitCollection::fromArray($units); } private function classAndParentClassesAndTraits(string $className): CodeUnitCollection { $units = [CodeUnit::forClass($className)]; $reflector = $this->reflectorForClass($className); foreach ($this->reflectorForClass($className)->getTraits() as $trait) { if (!$trait->isUserDefined()) { continue; } $units[] = CodeUnit::forTrait($trait->getName()); } while ($reflector = $reflector->getParentClass()) { if (!$reflector->isUserDefined()) { break; } $units[] = CodeUnit::forClass($reflector->getName()); foreach ($reflector->getTraits() as $trait) { if (!$trait->isUserDefined()) { continue; } $units[] = CodeUnit::forTrait($trait->getName()); } } return CodeUnitCollection::fromArray($units); } private function reflectorForClass(string $className): ReflectionClass { try { return new ReflectionClass($className); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private function isUserDefinedFunction(string $functionName): bool { if (!function_exists($functionName)) { return false; } try { return (new ReflectionFunction($functionName))->isUserDefined(); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private function isUserDefinedClass(string $className): bool { if (!class_exists($className)) { return false; } try { return (new ReflectionClass($className))->isUserDefined(); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private function isUserDefinedInterface(string $interfaceName): bool { if (!interface_exists($interfaceName)) { return false; } try { return (new ReflectionClass($interfaceName))->isUserDefined(); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private function isUserDefinedTrait(string $traitName): bool { if (!trait_exists($traitName)) { return false; } try { return (new ReflectionClass($traitName))->isUserDefined(); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } private function isUserDefinedMethod(string $className, string $methodName): bool { if (!class_exists($className)) { return false; } if (!method_exists($className, $methodName)) { return false; } try { return (new ReflectionMethod($className, $methodName))->isUserDefined(); } catch (\ReflectionException $e) { throw new ReflectionException( $e->getMessage(), (int) $e->getCode(), $e ); } } } arrays = []; $this->objects = new SplObjectStorage; } public function __destruct() { foreach ($this->arrays as &$array) { if (is_array($array)) { array_pop($array); array_pop($array); } } } public function add(&$value) { if (is_array($value)) { return $this->addArray($value); } if (is_object($value)) { return $this->addObject($value); } throw new InvalidArgumentException( 'Only arrays and objects are supported' ); } public function contains(&$value) { if (is_array($value)) { return $this->containsArray($value); } if (is_object($value)) { return $this->containsObject($value); } throw new InvalidArgumentException( 'Only arrays and objects are supported' ); } private function addArray(array &$array) { $key = $this->containsArray($array); if ($key !== false) { return $key; } $key = count($this->arrays); $this->arrays[] = &$array; if (!array_key_exists(PHP_INT_MAX, $array) && !array_key_exists(PHP_INT_MAX - 1, $array)) { $array[] = $key; $array[] = $this->objects; } else { do { $key = random_int(PHP_INT_MIN, PHP_INT_MAX); } while (array_key_exists($key, $array)); $array[$key] = $key; do { $key = random_int(PHP_INT_MIN, PHP_INT_MAX); } while (array_key_exists($key, $array)); $array[$key] = $this->objects; } return $key; } private function addObject($object): string { if (!$this->objects->contains($object)) { $this->objects->attach($object); } return spl_object_hash($object); } private function containsArray(array &$array) { $end = array_slice($array, -2); return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false; } private function containsObject($value) { if ($this->objects->contains($value)) { return spl_object_hash($value); } return false; } } lookupTable[$filename][$lineNumber])) { $this->updateLookupTable(); } if (isset($this->lookupTable[$filename][$lineNumber])) { return $this->lookupTable[$filename][$lineNumber]; } return $filename . ':' . $lineNumber; } private function updateLookupTable(): void { $this->processClassesAndTraits(); $this->processFunctions(); } private function processClassesAndTraits(): void { $classes = get_declared_classes(); $traits = get_declared_traits(); assert(is_array($classes)); assert(is_array($traits)); foreach (array_merge($classes, $traits) as $classOrTrait) { if (isset($this->processedClasses[$classOrTrait])) { continue; } $reflector = new ReflectionClass($classOrTrait); foreach ($reflector->getMethods() as $method) { $this->processFunctionOrMethod($method); } $this->processedClasses[$classOrTrait] = true; } } private function processFunctions(): void { foreach (get_defined_functions()['user'] as $function) { if (isset($this->processedFunctions[$function])) { continue; } $this->processFunctionOrMethod(new ReflectionFunction($function)); $this->processedFunctions[$function] = true; } } private function processFunctionOrMethod(ReflectionFunctionAbstract $functionOrMethod): void { if ($functionOrMethod->isInternal()) { return; } $name = $functionOrMethod->getName(); if ($functionOrMethod instanceof ReflectionMethod) { $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; } if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { $this->lookupTable[$functionOrMethod->getFileName()] = []; } foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; } } } contains($variable)) { return $objects; } $array = $variable; $processed->add($variable); if (is_array($variable)) { foreach ($array as $element) { if (!is_array($element) && !is_object($element)) { continue; } $objects = array_merge( $objects, $this->enumerate($element, $processed) ); } } else { $objects[] = $variable; $reflector = new ObjectReflector; foreach ($reflector->getAttributes($variable) as $value) { if (!is_array($value) && !is_object($value)) { continue; } $objects = array_merge( $objects, $this->enumerate($value, $processed) ); } } return $objects; } } $value) { $name = explode("\0", (string) $name); if (count($name) === 1) { $name = $name[0]; } else { if ($name[1] !== $className) { $name = $name[1] . '::' . $name[2]; } else { $name = $name[2]; } } $attributes[$name] = $value; } return $attributes; } } release = $release; $this->path = $path; } public function getVersion(): string { if ($this->version === null) { if (\substr_count($this->release, '.') + 1 === 3) { $this->version = $this->release; } else { $this->version = $this->release . '-dev'; } $git = $this->getGitInformation($this->path); if ($git) { if (\substr_count($this->release, '.') + 1 === 3) { $this->version = $git; } else { $git = \explode('-', $git); $this->version = $this->release . '-' . \end($git); } } } return $this->version; } private function getGitInformation(string $path) { if (!\is_dir($path . DIRECTORY_SEPARATOR . '.git')) { return false; } $process = \proc_open( 'git describe --tags', [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ], $pipes, $path ); if (!\is_resource($process)) { return false; } $result = \trim(\stream_get_contents($pipes[1])); \fclose($pipes[1]); \fclose($pipes[2]); $returnCode = \proc_close($process); if ($returnCode !== 0) { return false; } return $result; } } items = $items; } public function asArray(): array { return $this->items; } public function getIterator(): ComplexityCollectionIterator { return new ComplexityCollectionIterator($this); } public function count(): int { return count($this->items); } public function isEmpty(): bool { return empty($this->items); } public function cyclomaticComplexity(): int { $cyclomaticComplexity = 0; foreach ($this as $item) { $cyclomaticComplexity += $item->cyclomaticComplexity(); } return $cyclomaticComplexity; } } items = $items->asArray(); } public function rewind(): void { $this->position = 0; } public function valid(): bool { return isset($this->items[$this->position]); } public function key(): int { return $this->position; } public function current(): Complexity { return $this->items[$this->position]; } public function next(): void { $this->position++; } } name = $name; $this->cyclomaticComplexity = $cyclomaticComplexity; } public function name(): string { return $this->name; } public function cyclomaticComplexity(): int { return $this->cyclomaticComplexity; } } calculateForSourceString(file_get_contents($sourceFile)); } public function calculateForSourceString(string $source): ComplexityCollection { try { $nodes = (new ParserFactory)->createForHostVersion()->parse($source); assert($nodes !== null); return $this->calculateForAbstractSyntaxTree($nodes); } catch (Error $error) { throw new RuntimeException( $error->getMessage(), (int) $error->getCode(), $error ); } } public function calculateForAbstractSyntaxTree(array $nodes): ComplexityCollection { $traverser = new NodeTraverser; $complexityCalculatingVisitor = new ComplexityCalculatingVisitor(true); $traverser->addVisitor(new NameResolver); $traverser->addVisitor(new ParentConnectingVisitor); $traverser->addVisitor($complexityCalculatingVisitor); try { $traverser->traverse($nodes); } catch (Error $error) { throw new RuntimeException( $error->getMessage(), (int) $error->getCode(), $error ); } return $complexityCalculatingVisitor->result(); } } shortCircuitTraversal = $shortCircuitTraversal; } public function enterNode(Node $node): ?int { if (!$node instanceof ClassMethod && !$node instanceof Function_) { return null; } if ($node instanceof ClassMethod) { $name = $this->classMethodName($node); } else { $name = $this->functionName($node); } $statements = $node->getStmts(); assert(is_array($statements)); $this->result[] = new Complexity( $name, $this->cyclomaticComplexity($statements) ); if ($this->shortCircuitTraversal) { return NodeTraverser::DONT_TRAVERSE_CHILDREN; } return null; } public function result(): ComplexityCollection { return ComplexityCollection::fromList(...$this->result); } private function cyclomaticComplexity(array $statements): int { $traverser = new NodeTraverser; $cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor; $traverser->addVisitor($cyclomaticComplexityCalculatingVisitor); $traverser->traverse($statements); return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity(); } private function classMethodName(ClassMethod $node): string { $parent = $node->getAttribute('parent'); assert($parent instanceof Class_ || $parent instanceof Trait_); assert(isset($parent->namespacedName)); assert($parent->namespacedName instanceof Name); return $parent->namespacedName->toString() . '::' . $node->name->toString(); } private function functionName(Function_ $node): string { assert(isset($node->namespacedName)); assert($node->namespacedName instanceof Name); return $node->namespacedName->toString(); } } cyclomaticComplexity++; } } public function cyclomaticComplexity(): int { return $this->cyclomaticComplexity; } } recursiveExport($value, $indentation); } public function shortenedRecursiveExport(&$data, ?Context $context = null) { $result = []; $exporter = new self(); if (!$context) { $context = new Context; } $array = $data; $context->add($data); foreach ($array as $key => $value) { if (is_array($value)) { if ($context->contains($data[$key]) !== false) { $result[] = '*RECURSION*'; } else { $result[] = sprintf( 'array(%s)', $this->shortenedRecursiveExport($data[$key], $context) ); } } else { $result[] = $exporter->shortenedExport($value); } } return implode(', ', $result); } public function shortenedExport($value) { if (is_string($value)) { $string = str_replace("\n", '', $this->export($value)); if (function_exists('mb_strlen')) { if (mb_strlen($string) > 40) { $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7); } } else { if (strlen($string) > 40) { $string = substr($string, 0, 30) . '...' . substr($string, -7); } } return $string; } if (is_object($value)) { return sprintf( '%s Object (%s)', get_class($value), count($this->toArray($value)) > 0 ? '...' : '' ); } if (is_array($value)) { return sprintf( 'Array (%s)', count($value) > 0 ? '...' : '' ); } return $this->export($value); } public function toArray($value) { if (!is_object($value)) { return (array) $value; } $array = []; foreach ((array) $value as $key => $val) { if ("\0Error\0trace" === $key || "\0Exception\0trace" === $key) { continue; } if (preg_match('/^\0.+\0(.+)$/', (string) $key, $matches)) { $key = $matches[1]; } if ($key === "\0gcdata") { continue; } $array[$key] = $val; } if ($value instanceof SplObjectStorage) { foreach ($value as $key => $val) { $array[spl_object_hash($val)] = [ 'obj' => $val, 'inf' => $value->getInfo(), ]; } } return $array; } protected function recursiveExport(&$value, $indentation, $processed = null) { if ($value === null) { return 'null'; } if ($value === true) { return 'true'; } if ($value === false) { return 'false'; } if (is_float($value)) { $precisionBackup = ini_get('precision'); ini_set('precision', '-1'); try { $valueStr = (string) $value; if ((string) (int) $value === $valueStr) { return $valueStr . '.0'; } return $valueStr; } finally { ini_set('precision', $precisionBackup); } } if (gettype($value) === 'resource (closed)') { return 'resource (closed)'; } 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\x1b\x20-\xff]/', $value)) { return 'Binary String: 0x' . bin2hex($value); } return "'" . str_replace( '', "\n", str_replace( ["\r\n", "\n\r", "\r", "\n"], ['\r\n', '\n\r', '\r', '\n'], $value ) ) . "'"; } $whitespace = str_repeat(' ', 4 * $indentation); if (!$processed) { $processed = new Context; } if (is_array($value)) { if (($key = $processed->contains($value)) !== false) { return 'Array &' . $key; } $array = $value; $key = $processed->add($value); $values = ''; if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($value[$k], $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('Array &%s (%s)', $key, $values); } if (is_object($value)) { $class = get_class($value); if ($hash = $processed->contains($value)) { return sprintf('%s Object &%s', $class, $hash); } $hash = $processed->add($value); $values = ''; $array = $this->toArray($value); if (count($array) > 0) { foreach ($array as $k => $v) { $values .= sprintf( '%s %s => %s' . "\n", $whitespace, $this->recursiveExport($k, $indentation), $this->recursiveExport($v, $indentation + 1, $processed) ); } $values = "\n" . $values . $whitespace; } return sprintf('%s Object &%s (%s)', $class, $hash, $values); } return var_export($value, true); } } hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); } public function discardsComments(): bool { if (!$this->isOpcacheActive()) { return false; } if (ini_get('opcache.save_comments') !== '0') { return false; } return true; } public function performsJustInTimeCompilation(): bool { if (PHP_MAJOR_VERSION < 8) { return false; } if (!$this->isOpcacheActive()) { return false; } if (strpos(ini_get('opcache.jit'), '0') === 0) { return false; } return true; } public function getBinary(): string { if (self::$binary === null && $this->isHHVM()) { if ((self::$binary = getenv('PHP_BINARY')) === false) { self::$binary = PHP_BINARY; } self::$binary = escapeshellarg(self::$binary) . ' --php' . ' -d hhvm.php7.all=1'; } if (self::$binary === null && PHP_BINARY !== '') { self::$binary = escapeshellarg(PHP_BINARY); } if (self::$binary === null) { $possibleBinaryLocations = [ PHP_BINDIR . '/php', PHP_BINDIR . '/php-cli.exe', PHP_BINDIR . '/php.exe', ]; foreach ($possibleBinaryLocations as $binary) { if (is_readable($binary)) { self::$binary = escapeshellarg($binary); break; } } } if (self::$binary === null) { self::$binary = 'php'; } return self::$binary; } public function getNameWithVersion(): string { return $this->getName() . ' ' . $this->getVersion(); } public function getNameWithVersionAndCodeCoverageDriver(): string { if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) { return $this->getNameWithVersion(); } if ($this->hasPCOV()) { return sprintf( '%s with PCOV %s', $this->getNameWithVersion(), phpversion('pcov') ); } if ($this->hasXdebug()) { return sprintf( '%s with Xdebug %s', $this->getNameWithVersion(), phpversion('xdebug') ); } } public function getName(): string { if ($this->isHHVM()) { return 'HHVM'; } if ($this->isPHPDBG()) { return 'PHPDBG'; } return 'PHP'; } public function getVendorUrl(): string { if ($this->isHHVM()) { return 'http://hhvm.com/'; } return 'https://secure.php.net/'; } public function getVersion(): string { if ($this->isHHVM()) { return HHVM_VERSION; } return PHP_VERSION; } public function hasXdebug(): bool { return ($this->isPHP() || $this->isHHVM()) && extension_loaded('xdebug'); } public function isHHVM(): bool { return defined('HHVM_VERSION'); } public function isPHP(): bool { return !$this->isHHVM() && !$this->isPHPDBG(); } public function isPHPDBG(): bool { return PHP_SAPI === 'phpdbg' && !$this->isHHVM(); } public function hasPHPDBGCodeCoverage(): bool { return $this->isPHPDBG(); } public function hasPCOV(): bool { return $this->isPHP() && extension_loaded('pcov') && ini_get('pcov.enabled'); } public function getCurrentSettings(array $values): array { $diff = []; $files = []; if ($file = php_ini_loaded_file()) { $files[] = $file; } if ($scanned = php_ini_scanned_files()) { $files = array_merge( $files, array_map( 'trim', explode(",\n", $scanned) ) ); } foreach ($files as $ini) { $config = parse_ini_file($ini, true); foreach ($values as $value) { $set = ini_get($value); if (empty($set)) { continue; } if ((!isset($config[$value]) || ($set !== $config[$value]))) { $diff[$value] = sprintf('%s=%s', $value, $set); } } } return $diff; } private function isOpcacheActive(): bool { if (!extension_loaded('Zend OPcache')) { return false; } if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ini_get('opcache.enable_cli') === '1') { return true; } if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' && ini_get('opcache.enable') === '1') { return true; } return false; } } isWindows()) { return (defined('STDOUT') && function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM'); } if (!defined('STDOUT')) { return false; } return $this->isInteractive(STDOUT); } public function getNumberOfColumns(): int { if (!$this->isInteractive(defined('STDIN') ? STDIN : self::STDIN)) { return 80; } if ($this->isWindows()) { return $this->getNumberOfColumnsWindows(); } return $this->getNumberOfColumnsInteractive(); } public function isInteractive($fileDescriptor = self::STDOUT): bool { if (is_resource($fileDescriptor)) { if (function_exists('stream_isatty') && @stream_isatty($fileDescriptor)) { return true; } if (function_exists('fstat')) { $stat = @fstat(STDOUT); return $stat && 0020000 === ($stat['mode'] & 0170000); } return false; } return function_exists('posix_isatty') && @posix_isatty($fileDescriptor); } private function isWindows(): bool { return DIRECTORY_SEPARATOR === '\\'; } private function getNumberOfColumnsInteractive(): int { if (function_exists('shell_exec') && preg_match('#\d+ (\d+)#', shell_exec('stty size') ?: '', $match) === 1) { if ((int) $match[1] > 0) { return (int) $match[1]; } } if (function_exists('shell_exec') && preg_match('#columns = (\d+);#', shell_exec('stty') ?: '', $match) === 1) { if ((int) $match[1] > 0) { return (int) $match[1]; } } return 80; } private function getNumberOfColumnsWindows(): int { $ansicon = getenv('ANSICON'); $columns = 80; if (is_string($ansicon) && preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim($ansicon), $matches)) { $columns = (int) $matches[1]; } elseif (function_exists('proc_open')) { $process = proc_open( 'mode CON', [ 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ], $pipes, null, null, ['suppress_errors' => true] ); if (is_resource($process)) { $info = stream_get_contents($pipes[1]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { $columns = (int) $matches[2]; } } } return $columns - 1; } } [^\\v\\t"]+)#', $lines[$i], $fromMatch) && preg_match('#^\\+\\+\\+\\h+"?(?P[^\\v\\t"]+)#', $lines[$i + 1], $toMatch)) { if ($diff !== null) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; $collected = []; } $diff = new Diff($fromMatch['file'], $toMatch['file']); ++$i; } else { if (preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { continue; } $collected[] = $lines[$i]; } } if ($diff !== null && count($collected)) { $this->parseFileDiff($diff, $collected); $diffs[] = $diff; } return $diffs; } private function parseFileDiff(Diff $diff, array $lines): void { $chunks = []; $chunk = null; $diffLines = []; foreach ($lines as $line) { if (preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { $chunk = new Chunk( (int) $match['start'], isset($match['startrange']) ? max(1, (int) $match['startrange']) : 1, (int) $match['end'], isset($match['endrange']) ? max(1, (int) $match['endrange']) : 1 ); $chunks[] = $chunk; $diffLines = []; continue; } if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { $type = Line::UNCHANGED; if ($match['type'] === '+') { $type = Line::ADDED; } elseif ($match['type'] === '-') { $type = Line::REMOVED; } $diffLines[] = new Line($type, $match['line']); if (null !== $chunk) { $chunk->setLines($diffLines); } } } $diff->setChunks($chunks); } } start = $start; $this->startRange = $startRange; $this->end = $end; $this->endRange = $endRange; $this->lines = $lines; } public function getStart(): int { return $this->start; } public function getStartRange(): int { return $this->startRange; } public function getEnd(): int { return $this->end; } public function getEndRange(): int { return $this->endRange; } public function getLines(): array { return $this->lines; } public function setLines(array $lines): void { foreach ($lines as $line) { if (!$line instanceof Line) { throw new InvalidArgumentException; } } $this->lines = $lines; } } from = $from; $this->to = $to; $this->chunks = $chunks; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } public function getChunks(): array { return $this->chunks; } public function setChunks(array $chunks): void { $this->chunks = $chunks; } } $matrix[$o - $width]) { if ($firstOrLast > $matrix[$o - 1]) { $matrix[$o] = $firstOrLast; } else { $matrix[$o] = $matrix[$o - 1]; } } else { if ($firstOrLast > $matrix[$o - $width]) { $matrix[$o] = $firstOrLast; } else { $matrix[$o] = $matrix[$o - $width]; } } } } $i = $fromLength; $j = $toLength; while ($i > 0 && $j > 0) { if ($from[$i - 1] === $to[$j - 1]) { $common[] = $from[$i - 1]; --$i; --$j; } else { $o = ($j * $width) + $i; if ($matrix[$o - $width] > $matrix[$o - 1]) { --$j; } else { --$i; } } } return array_reverse($common); } } type = $type; $this->content = $content; } public function getContent(): string { return $this->content; } public function getType(): int { return $this->type; } } outputBuilder = $outputBuilder; } elseif (null === $outputBuilder) { $this->outputBuilder = new UnifiedDiffOutputBuilder; } elseif (is_string($outputBuilder)) { $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); } else { throw new InvalidArgumentException( sprintf( 'Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', is_object($outputBuilder) ? 'instance of "' . get_class($outputBuilder) . '"' : gettype($outputBuilder) . ' "' . $outputBuilder . '"' ) ); } } public function diff($from, $to, ?LongestCommonSubsequenceCalculator $lcs = null): string { $diff = $this->diffToArray( $this->normalizeDiffInput($from), $this->normalizeDiffInput($to), $lcs ); return $this->outputBuilder->getDiff($diff); } public function diffToArray($from, $to, ?LongestCommonSubsequenceCalculator $lcs = null): array { if (is_string($from)) { $from = $this->splitStringByLines($from); } elseif (!is_array($from)) { throw new InvalidArgumentException('"from" must be an array or string.'); } if (is_string($to)) { $to = $this->splitStringByLines($to); } elseif (!is_array($to)) { throw new InvalidArgumentException('"to" must be an array or string.'); } [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); if ($lcs === null) { $lcs = $this->selectLcsImplementation($from, $to); } $common = $lcs->calculate(array_values($from), array_values($to)); $diff = []; foreach ($start as $token) { $diff[] = [$token, self::OLD]; } reset($from); reset($to); foreach ($common as $token) { while (($fromToken = reset($from)) !== $token) { $diff[] = [array_shift($from), self::REMOVED]; } while (($toToken = reset($to)) !== $token) { $diff[] = [array_shift($to), self::ADDED]; } $diff[] = [$token, self::OLD]; array_shift($from); array_shift($to); } while (($token = array_shift($from)) !== null) { $diff[] = [$token, self::REMOVED]; } while (($token = array_shift($to)) !== null) { $diff[] = [$token, self::ADDED]; } foreach ($end as $token) { $diff[] = [$token, self::OLD]; } if ($this->detectUnmatchedLineEndings($diff)) { array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); } return $diff; } private function normalizeDiffInput($input) { if (!is_array($input) && !is_string($input)) { return (string) $input; } return $input; } private function splitStringByLines(string $input): array { return preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); } private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator { $memoryLimit = 100 * 1024 * 1024; if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { return new MemoryEfficientLongestCommonSubsequenceCalculator; } return new TimeEfficientLongestCommonSubsequenceCalculator; } private function calculateEstimatedFootprint(array $from, array $to) { $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; return $itemSize * min(count($from), count($to)) ** 2; } private function detectUnmatchedLineEndings(array $diff): bool { $newLineBreaks = ['' => true]; $oldLineBreaks = ['' => true]; foreach ($diff as $entry) { if (self::OLD === $entry[1]) { $ln = $this->getLinebreak($entry[0]); $oldLineBreaks[$ln] = true; $newLineBreaks[$ln] = true; } elseif (self::ADDED === $entry[1]) { $newLineBreaks[$this->getLinebreak($entry[0])] = true; } elseif (self::REMOVED === $entry[1]) { $oldLineBreaks[$this->getLinebreak($entry[0])] = true; } } if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { return false; } foreach ($newLineBreaks as $break => $set) { if (!isset($oldLineBreaks[$break])) { return true; } } foreach ($oldLineBreaks as $break => $set) { if (!isset($newLineBreaks[$break])) { return true; } } return false; } private function getLinebreak($line): string { if (!is_string($line)) { return ''; } $lc = substr($line, -1); if ("\r" === $lc) { return "\r"; } if ("\n" !== $lc) { return ''; } if ("\r\n" === substr($line, -2)) { return "\r\n"; } return "\n"; } private static function getArrayDiffParted(array &$from, array &$to): array { $start = []; $end = []; reset($to); foreach ($from as $k => $v) { $toK = key($to); if ($toK === $k && $v === $to[$k]) { $start[$k] = $v; unset($from[$k], $to[$k]); } else { break; } } end($from); end($to); do { $fromK = key($from); $toK = key($to); if (null === $fromK || null === $toK || current($from) !== current($to)) { break; } prev($from); prev($to); $end = [$fromK => $from[$fromK]] + $end; unset($from[$fromK], $to[$toK]); } while (true); return [$from, $to, $start, $end]; } } length($fromStart, $to); $llE = $this->length(array_reverse($fromEnd), array_reverse($to)); $jMax = 0; $max = 0; for ($j = 0; $j <= $cTo; $j++) { $m = $llB[$j] + $llE[$cTo - $j]; if ($m >= $max) { $max = $m; $jMax = $j; } } $toStart = array_slice($to, 0, $jMax); $toEnd = array_slice($to, $jMax); return array_merge( $this->calculate($fromStart, $toStart), $this->calculate($fromEnd, $toEnd) ); } private function length(array $from, array $to): array { $current = array_fill(0, count($to) + 1, 0); $cFrom = count($from); $cTo = count($to); for ($i = 0; $i < $cFrom; $i++) { $prev = $current; for ($j = 0; $j < $cTo; $j++) { if ($from[$i] === $to[$j]) { $current[$j + 1] = $prev[$j] + 1; } else { if ($current[$j] > $prev[$j + 1]) { $current[$j + 1] = $current[$j]; } else { $current[$j + 1] = $prev[$j + 1]; } } } } return $current; } } ' : gettype($value) . '#' . $value) ), $code, $previous ); } } header = $header; } public function getDiff(array $diff): string { $buffer = fopen('php://memory', 'r+b'); if ('' !== $this->header) { fwrite($buffer, $this->header); if ("\n" !== substr($this->header, -1, 1)) { fwrite($buffer, "\n"); } } foreach ($diff as $diffEntry) { if ($diffEntry[1] === Differ::ADDED) { fwrite($buffer, '+' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::REMOVED) { fwrite($buffer, '-' . $diffEntry[0]); } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { fwrite($buffer, ' ' . $diffEntry[0]); continue; } else { continue; } $lc = substr($diffEntry[0], -1); if ($lc !== "\n" && $lc !== "\r") { fwrite($buffer, "\n"); } } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); return $diff; } } true, 'commonLineThreshold' => 6, 'contextLines' => 3, 'fromFile' => null, 'fromFileDate' => null, 'toFile' => null, 'toFileDate' => null, ]; private $changed; private $collapseRanges; private $commonLineThreshold; private $header; private $contextLines; public function __construct(array $options = []) { $options = array_merge(self::$default, $options); if (!is_bool($options['collapseRanges'])) { throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); } if (!is_int($options['contextLines']) || $options['contextLines'] < 0) { throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); } if (!is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); } $this->assertString($options, 'fromFile'); $this->assertString($options, 'toFile'); $this->assertStringOrNull($options, 'fromFileDate'); $this->assertStringOrNull($options, 'toFileDate'); $this->header = sprintf( "--- %s%s\n+++ %s%s\n", $options['fromFile'], null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], $options['toFile'], null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'] ); $this->collapseRanges = $options['collapseRanges']; $this->commonLineThreshold = $options['commonLineThreshold']; $this->contextLines = $options['contextLines']; } public function getDiff(array $diff): string { if (0 === count($diff)) { return ''; } $this->changed = false; $buffer = fopen('php://memory', 'r+b'); fwrite($buffer, $this->header); $this->writeDiffHunks($buffer, $diff); if (!$this->changed) { fclose($buffer); return ''; } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); $last = substr($diff, -1); return "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } private function writeDiffHunks($output, array $diff): void { $upperLimit = count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; --$i) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = substr($diff[$i][0], -1); if ("\n" !== $lc) { array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!count($toFind)) { break; } } } } $cutOff = max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; $i = 0; foreach ($diff as $i => $entry) { if (0 === $entry[1]) { if (false === $hunkCapture) { ++$fromStart; ++$toStart; continue; } ++$sameCount; ++$toRange; ++$fromRange; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } $this->changed = true; if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { ++$toRange; } if (Differ::REMOVED === $entry[1]) { ++$fromRange; } } if (false === $hunkCapture) { return; } $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { fwrite($output, ',' . $fromRange); } fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { fwrite($output, ',' . $toRange); } fwrite($output, " @@\n"); for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { if ($diff[$i][1] === Differ::ADDED) { $this->changed = true; fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { $this->changed = true; fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { $this->changed = true; fwrite($output, $diff[$i][0]); } } } private function assertString(array $options, string $option): void { if (!is_string($options[$option])) { throw new ConfigurationException($option, 'a string', $options[$option]); } } private function assertStringOrNull(array $options, string $option): void { if (null !== $options[$option] && !is_string($options[$option])) { throw new ConfigurationException($option, 'a string or ', $options[$option]); } } } header = $header; $this->addLineNumbers = $addLineNumbers; } public function getDiff(array $diff): string { $buffer = fopen('php://memory', 'r+b'); if ('' !== $this->header) { fwrite($buffer, $this->header); if ("\n" !== substr($this->header, -1, 1)) { fwrite($buffer, "\n"); } } if (0 !== count($diff)) { $this->writeDiffHunks($buffer, $diff); } $diff = stream_get_contents($buffer, -1, 0); fclose($buffer); $last = substr($diff, -1); return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last ? $diff . "\n" : $diff; } private function writeDiffHunks($output, array $diff): void { $upperLimit = count($diff); if (0 === $diff[$upperLimit - 1][1]) { $lc = substr($diff[$upperLimit - 1][0], -1); if ("\n" !== $lc) { array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } } else { $toFind = [1 => true, 2 => true]; for ($i = $upperLimit - 1; $i >= 0; --$i) { if (isset($toFind[$diff[$i][1]])) { unset($toFind[$diff[$i][1]]); $lc = substr($diff[$i][0], -1); if ("\n" !== $lc) { array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); } if (!count($toFind)) { break; } } } } $cutOff = max($this->commonLineThreshold, $this->contextLines); $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; $toStart = $fromStart = 1; $i = 0; foreach ($diff as $i => $entry) { if (0 === $entry[1]) { if (false === $hunkCapture) { ++$fromStart; ++$toStart; continue; } ++$sameCount; ++$toRange; ++$fromRange; if ($sameCount === $cutOff) { $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 ? $hunkCapture : $this->contextLines; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $cutOff + $this->contextLines + 1, $fromStart - $contextStartOffset, $fromRange - $cutOff + $contextStartOffset + $this->contextLines, $toStart - $contextStartOffset, $toRange - $cutOff + $contextStartOffset + $this->contextLines, $output ); $fromStart += $fromRange; $toStart += $toRange; $hunkCapture = false; $sameCount = $toRange = $fromRange = 0; } continue; } $sameCount = 0; if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { continue; } if (false === $hunkCapture) { $hunkCapture = $i; } if (Differ::ADDED === $entry[1]) { ++$toRange; } if (Differ::REMOVED === $entry[1]) { ++$fromRange; } } if (false === $hunkCapture) { return; } $contextStartOffset = $hunkCapture - $this->contextLines < 0 ? $hunkCapture : $this->contextLines; $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; $toRange -= $sameCount; $this->writeHunk( $diff, $hunkCapture - $contextStartOffset, $i - $sameCount + $contextEndOffset + 1, $fromStart - $contextStartOffset, $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, $output ); } private function writeHunk( array $diff, int $diffStartIndex, int $diffEndIndex, int $fromStart, int $fromRange, int $toStart, int $toRange, $output ): void { if ($this->addLineNumbers) { fwrite($output, '@@ -' . $fromStart); if (!$this->collapseRanges || 1 !== $fromRange) { fwrite($output, ',' . $fromRange); } fwrite($output, ' +' . $toStart); if (!$this->collapseRanges || 1 !== $toRange) { fwrite($output, ',' . $toRange); } fwrite($output, " @@\n"); } else { fwrite($output, "@@ @@\n"); } for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { if ($diff[$i][1] === Differ::ADDED) { fwrite($output, '+' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::REMOVED) { fwrite($output, '-' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::OLD) { fwrite($output, ' ' . $diff[$i][0]); } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { fwrite($output, "\n"); } else { fwrite($output, ' ' . $diff[$i][0]); } } } } = $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } $capturing = false; } } if ($capturing !== false && $chunkSize >= $lineThreshold) { $commonChunks[$chunkStart] = $chunkStart + $chunkSize; } return $commonChunks; } } $arguments) { foreach ($arguments as $argument) { if (strpos($argument, '?') === 0) { $argument = substr($argument, 1); } if ($argument === 'resource') { $resourceFunctions[] = explode('\'', $function)[0]; } } } $resourceFunctions = array_unique($resourceFunctions); sort($resourceFunctions); $buffer = << * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\ResourceOperations; final class ResourceOperations { /** * @return string[] */ public static function getFunctions(): array { return [ EOT; foreach ($resourceFunctions as $function) { $buffer .= sprintf(" '%s',\n", $function); } $buffer .= <<< EOT ]; } } EOT; file_put_contents(__DIR__ . '/../src/ResourceOperations.php', $buffer); contains($object)) { throw new ComparisonFailure( $expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), false, 'Failed asserting that two objects are equal.' ); } } foreach ($expected as $object) { if (!$actual->contains($object)) { throw new ComparisonFailure( $expected, $actual, $this->exporter->export($expected), $this->exporter->export($actual), false, 'Failed asserting that two objects are equal.' ); } } } } nodeToText($expected, true, $ignoreCase); $actualAsString = $this->nodeToText($actual, true, $ignoreCase); if ($expectedAsString !== $actualAsString) { $type = $expected instanceof DOMDocument ? 'documents' : 'nodes'; throw new ComparisonFailure( $expected, $actual, $expectedAsString, $actualAsString, false, sprintf("Failed asserting that two DOM %s are equal.\n", $type) ); } } private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string { if ($canonicalize) { $document = new DOMDocument; try { @$document->loadXML($node->C14N()); } catch (ValueError $e) { } $node = $document; } $document = $node instanceof DOMDocument ? $node : $node->ownerDocument; $document->formatOutput = true; $document->normalizeDocument(); $text = $node instanceof DOMDocument ? $node->saveXML() : $document->saveXML($node); return $ignoreCase ? strtolower($text) : $text; } } f = $absDelta - floor($absDelta); $actualClone = (clone $actual) ->setTimezone(new DateTimeZone('UTC')); $expectedLower = (clone $expected) ->setTimezone(new DateTimeZone('UTC')) ->sub($delta); $expectedUpper = (clone $expected) ->setTimezone(new DateTimeZone('UTC')) ->add($delta); if ($actualClone < $expectedLower || $actualClone > $expectedUpper) { throw new ComparisonFailure( $expected, $actual, $this->dateTimeToString($expected), $this->dateTimeToString($actual), false, 'Failed asserting that two DateTime objects are equal.' ); } } private function dateTimeToString(DateTimeInterface $datetime): string { $string = $datetime->format('Y-m-d\TH:i:s.uO'); return $string ?: 'Invalid DateTimeInterface object'; } } registerDefaultComparators(); } public function getComparatorFor($expected, $actual) { foreach ($this->customComparators as $comparator) { if ($comparator->accepts($expected, $actual)) { return $comparator; } } foreach ($this->defaultComparators as $comparator) { if ($comparator->accepts($expected, $actual)) { return $comparator; } } throw new RuntimeException('No suitable Comparator implementation found'); } public function register(Comparator $comparator) { array_unshift($this->customComparators, $comparator); $comparator->setFactory($this); } public function unregister(Comparator $comparator) { foreach ($this->customComparators as $key => $_comparator) { if ($comparator === $_comparator) { unset($this->customComparators[$key]); } } } public function reset() { $this->customComparators = []; } private function registerDefaultComparators(): void { $this->registerDefaultComparator(new MockObjectComparator); $this->registerDefaultComparator(new DateTimeComparator); $this->registerDefaultComparator(new DOMNodeComparator); $this->registerDefaultComparator(new SplObjectStorageComparator); $this->registerDefaultComparator(new ExceptionComparator); $this->registerDefaultComparator(new ObjectComparator); $this->registerDefaultComparator(new ResourceComparator); $this->registerDefaultComparator(new ArrayComparator); $this->registerDefaultComparator(new NumericComparator); $this->registerDefaultComparator(new ScalarComparator); $this->registerDefaultComparator(new TypeComparator); } private function registerDefaultComparator(Comparator $comparator): void { $this->defaultComparators[] = $comparator; $comparator->setFactory($this); } } $value) { unset($remaining[$key]); if (!array_key_exists($key, $actual)) { $expectedAsString .= sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value) ); $equal = false; continue; } try { $comparator = $this->factory->getComparatorFor($value, $actual[$key]); $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); $expectedAsString .= sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value) ); $actualAsString .= sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($actual[$key]) ); } catch (ComparisonFailure $e) { $expectedAsString .= sprintf( " %s => %s\n", $this->exporter->export($key), $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected()) ); $actualAsString .= sprintf( " %s => %s\n", $this->exporter->export($key), $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual()) ); $equal = false; } } foreach ($remaining as $key => $value) { $actualAsString .= sprintf( " %s => %s\n", $this->exporter->export($key), $this->exporter->shortenedExport($value) ); $equal = false; } $expectedAsString .= ')'; $actualAsString .= ')'; if (!$equal) { throw new ComparisonFailure( $expected, $actual, $expectedAsString, $actualAsString, false, 'Failed asserting that two arrays are equal.' ); } } protected function indent($lines) { return trim(str_replace("\n", "\n ", $lines)); } } exporter->shortenedExport($actual), gettype($expected) ) ); } } } exporter->export($expected), $this->exporter->export($actual), false, sprintf( '%s is not instance of expected class "%s".', $this->exporter->export($actual), get_class($expected) ) ); } if (in_array([$actual, $expected], $processed, true) || in_array([$expected, $actual], $processed, true)) { return; } $processed[] = [$actual, $expected]; if ($actual !== $expected) { try { parent::assertEquals( $this->toArray($expected), $this->toArray($actual), $delta, $canonicalize, $ignoreCase, $processed ); } catch (ComparisonFailure $e) { throw new ComparisonFailure( $expected, $actual, substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5), substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5), false, 'Failed asserting that two objects are equal.' ); } } } protected function toArray($object) { return $this->exporter->toArray($object); } } exporter->export($expected), $this->exporter->export($actual) ); } } } expected = $expected; $this->actual = $actual; $this->expectedAsString = $expectedAsString; $this->actualAsString = $actualAsString; $this->message = $message; } public function getActual() { return $this->actual; } public function getExpected() { return $this->expected; } public function getActualAsString() { return $this->actualAsString; } public function getExpectedAsString() { return $this->expectedAsString; } public function getDiff() { if (!$this->actualAsString && !$this->expectedAsString) { return ''; } $differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); return $differ->diff($this->expectedAsString, $this->actualAsString); } public function toString() { return $this->message . $this->getDiff(); } } isInfinite($actual) && $this->isInfinite($expected)) { return; } if (($this->isInfinite($actual) xor $this->isInfinite($expected)) || ($this->isNan($actual) || $this->isNan($expected)) || abs($actual - $expected) > $delta) { throw new ComparisonFailure( $expected, $actual, '', '', false, sprintf( 'Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected) ) ); } } private function isInfinite($value): bool { return is_float($value) && is_infinite($value); } private function isNan($value): bool { return is_float($value) && is_nan($value); } } exporter = new Exporter; } public function setFactory(Factory $factory) { $this->factory = $factory; } abstract public function accepts($expected, $actual); abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false); } exporter->export($expected), $this->exporter->export($actual), false, 'Failed asserting that two strings are equal.' ); } if ($expectedToCompare != $actualToCompare) { throw new ComparisonFailure( $expected, $actual, '', '', false, sprintf( 'Failed asserting that %s matches expected %s.', $this->exporter->export($actual), $this->exporter->export($expected) ) ); } } } 1 && $arg[1] === '-' && !$longOptions)) { $nonOptions[] = $arg; continue; } if (strlen($arg) > 1 && $arg[1] === '-' && is_array($longOptions)) { $this->parseLongOption( substr($arg, 2), $longOptions, $options, $argv ); } else { $this->parseShortOption( substr($arg, 1), $shortOptions, $options, $argv ); } } return [$options, $nonOptions]; } private function parseShortOption(string $arg, string $shortOptions, array &$opts, array &$args): void { $argLength = strlen($arg); for ($i = 0; $i < $argLength; $i++) { $option = $arg[$i]; $optionArgument = null; if ($arg[$i] === ':' || ($spec = strstr($shortOptions, $option)) === false) { throw new UnknownOptionException('-' . $option); } assert(is_string($spec)); if (strlen($spec) > 1 && $spec[1] === ':') { if ($i + 1 < $argLength) { $opts[] = [$option, substr($arg, $i + 1)]; break; } if (!(strlen($spec) > 2 && $spec[2] === ':')) { $optionArgument = current($args); if (!$optionArgument) { throw new RequiredOptionArgumentMissingException('-' . $option); } assert(is_string($optionArgument)); next($args); } } $opts[] = [$option, $optionArgument]; } } private function parseLongOption(string $arg, array $longOptions, array &$opts, array &$args): void { $count = count($longOptions); $list = explode('=', $arg); $option = $list[0]; $optionArgument = null; if (count($list) > 1) { $optionArgument = $list[1]; } $optionLength = strlen($option); foreach ($longOptions as $i => $longOption) { $opt_start = substr($longOption, 0, $optionLength); if ($opt_start !== $option) { continue; } $opt_rest = substr($longOption, $optionLength); if ($opt_rest !== '' && $i + 1 < $count && $option[0] !== '=' && strpos($longOptions[$i + 1], $option) === 0) { throw new AmbiguousOptionException('--' . $option); } if (substr($longOption, -1) === '=') { if (substr($longOption, -2) !== '==' && !strlen((string) $optionArgument)) { if (false === $optionArgument = current($args)) { throw new RequiredOptionArgumentMissingException('--' . $option); } next($args); } } elseif ($optionArgument) { throw new OptionDoesNotAllowArgumentException('--' . $option); } $fullOption = '--' . preg_replace('/={1,2}$/', '', $longOption); $opts[] = [$fullOption, $optionArgument]; return; } throw new UnknownOptionException('--' . $option); } } linesOfCode = $linesOfCode; } public function enterNode(Node $node): void { $this->comments = array_merge($this->comments, $node->getComments()); if (!$node instanceof Expr) { return; } $this->linesWithStatements[] = $node->getStartLine(); } public function result(): LinesOfCode { $commentLinesOfCode = 0; foreach ($this->comments() as $comment) { $commentLinesOfCode += ($comment->getEndLine() - $comment->getStartLine() + 1); } return new LinesOfCode( $this->linesOfCode, $commentLinesOfCode, $this->linesOfCode - $commentLinesOfCode, count(array_unique($this->linesWithStatements)) ); } private function comments(): array { $comments = []; foreach ($this->comments as $comment) { $comments[$comment->getStartLine() . '_' . $comment->getStartTokenPos() . '_' . $comment->getEndLine() . '_' . $comment->getEndTokenPos()] = $comment; } return $comments; } } countInSourceString(file_get_contents($sourceFile)); } public function countInSourceString(string $source): LinesOfCode { $linesOfCode = substr_count($source, "\n"); if ($linesOfCode === 0 && !empty($source)) { $linesOfCode = 1; } try { $nodes = (new ParserFactory)->createForHostVersion()->parse($source); assert($nodes !== null); return $this->countInAbstractSyntaxTree($linesOfCode, $nodes); } catch (Error $error) { throw new RuntimeException( $error->getMessage(), (int) $error->getCode(), $error ); } } public function countInAbstractSyntaxTree(int $linesOfCode, array $nodes): LinesOfCode { $traverser = new NodeTraverser; $visitor = new LineCountingVisitor($linesOfCode); $traverser->addVisitor($visitor); try { $traverser->traverse($nodes); } catch (Error $error) { throw new RuntimeException( $error->getMessage(), (int) $error->getCode(), $error ); } return $visitor->result(); } } linesOfCode = $linesOfCode; $this->commentLinesOfCode = $commentLinesOfCode; $this->nonCommentLinesOfCode = $nonCommentLinesOfCode; $this->logicalLinesOfCode = $logicalLinesOfCode; } public function linesOfCode(): int { return $this->linesOfCode; } public function commentLinesOfCode(): int { return $this->commentLinesOfCode; } public function nonCommentLinesOfCode(): int { return $this->nonCommentLinesOfCode; } public function logicalLinesOfCode(): int { return $this->logicalLinesOfCode; } public function plus(self $other): self { return new self( $this->linesOfCode() + $other->linesOfCode(), $this->commentLinesOfCode() + $other->commentLinesOfCode(), $this->nonCommentLinesOfCode() + $other->nonCommentLinesOfCode(), $this->logicalLinesOfCode() + $other->logicalLinesOfCode(), ); } } output = ''; } /** * Executes a shell command. * Fails if exit code is > 0. You can disable this by passing `false` as second argument * * ```php * runShellCommand('phpunit'); * * // do not fail test when command fails * $I->runShellCommand('phpunit', false); * ``` */ public function runShellCommand(string $command, bool $failNonZero = true): void { $data = []; /** * \Symfony\Component\Console\Application::configureIO sets SHELL_VERBOSITY environment variable * which may affect execution of shell command */ if (\function_exists('putenv')) { @putenv('SHELL_VERBOSITY'); } exec("{$command}", $data, $resultCode); $this->result = $resultCode; $this->output = implode("\n", $data); if ($this->output === null) { Assert::fail("{$command} can't be executed"); } if ($resultCode !== 0 && $failNonZero) { Assert::fail("Result code was {$resultCode}.\n\n" . $this->output); } $this->debug(preg_replace('#s/\e\[\d+(?>(;\d+)*)m//g#', '', $this->output)); } /** * Checks that output from last executed command contains text */ public function seeInShellOutput(string $text): void { TestCase::assertStringContainsString($text, $this->output); } /** * Checks that output from latest command doesn't contain text */ public function dontSeeInShellOutput(string $text): void { $this->debug($this->output); TestCase::assertStringNotContainsString($text, $this->output); } public function seeShellOutputMatches(string $regex): void { TestCase::assertMatchesRegularExpression($regex, $this->output); } /** * Returns the output from latest command */ public function grabShellOutput(): string { return $this->output; } /** * Checks result code. To verify a result code > 0, you need to pass `false` as second argument to `runShellCommand()` * * ```php * seeResultCodeIs(0); * ``` */ public function seeResultCodeIs(int $code): void { $this->assertEquals($this->result, $code, "result code is {$code}"); } /** * Checks result code * * ```php * seeResultCodeIsNot(0); * ``` */ public function seeResultCodeIsNot(int $code): void { $this->assertNotEquals($this->result, $code, "result code is {$code}"); } } * To use this module with Composer you need "php-amqplib/php-amqplib": "~2.4" package. * * * ## Config * * * host: localhost - host to connect * * username: guest - username to connect * * password: guest - password to connect * * vhost: '/' - vhost to connect * * cleanup: true - defined queues will be purged before running every test. * * queues: [mail, twitter] - queues to cleanup * * single_channel - create and use only one channel during test execution * * ### Example * * modules: * enabled: * - AMQP: * host: 'localhost' * port: '5672' * username: 'guest' * password: 'guest' * vhost: '/' * queues: [queue1, queue2] * single_channel: false * * ## Public Properties * * * connection - AMQPStreamConnection - current connection */ class AMQP extends Module implements RequiresPackage { protected array $config = [ 'host' => 'localhost', 'username' => 'guest', 'password' => 'guest', 'port' => '5672', 'vhost' => '/', 'cleanup' => true, 'single_channel' => false, 'queues' => [] ]; public ?AMQPStreamConnection $connection = null; protected ?int $channelId = null; /** * @var string[] */ protected array $requiredFields = ['host', 'username', 'password', 'vhost']; public function _requires(): array { return [AMQPStreamConnection::class => '"php-amqplib/php-amqplib": "~2.4"']; } public function _initialize(): void { $host = $this->config['host']; $port = $this->config['port']; $username = $this->config['username']; $password = $this->config['password']; $vhost = $this->config['vhost']; try { $this->connection = new AMQPStreamConnection($host, $port, $username, $password, $vhost); } catch (Exception $exception) { throw new ModuleException(__CLASS__, $exception->getMessage() . ' while establishing connection to MQ server'); } } public function _before(TestInterface $test): void { if ($this->config['cleanup']) { $this->cleanup(); } } /** * Sends message to exchange by sending exchange name, message * and (optionally) a routing key * * ``` php * pushToExchange('exchange.emails', 'thanks'); * $I->pushToExchange('exchange.emails', new AMQPMessage('Thanks!')); * $I->pushToExchange('exchange.emails', new AMQPMessage('Thanks!'), 'severity'); * ``` */ public function pushToExchange(string $exchange, string|AMQPMessage $message, string $routing_key = null): void { $message = $message instanceof AMQPMessage ? $message : new AMQPMessage($message); $this->getChannel()->basic_publish($message, $exchange, $routing_key); } /** * Sends message to queue * * ``` php * pushToQueue('queue.jobs', 'create user'); * $I->pushToQueue('queue.jobs', new AMQPMessage('create')); * ``` */ public function pushToQueue(string $queue, string|AMQPMessage $message): void { $message = $message instanceof AMQPMessage ? $message : new AMQPMessage($message); $this->getChannel()->queue_declare($queue); $this->getChannel()->basic_publish($message, '', $queue); } /** * Declares an exchange * * This is an alias of method `exchange_declare` of `PhpAmqpLib\Channel\AMQPChannel`. * * ```php * declareExchange( * 'nameOfMyExchange', // exchange name * 'topic' // exchange type * ) * ``` * * @return mixed */ public function declareExchange( string $exchange, string $type, bool $passive = false, bool $durable = false, bool $auto_delete = true, bool $internal = false, bool $nowait = false, array $arguments = null, int $ticket = null ) { return $this->getChannel()->exchange_declare( $exchange, $type, $passive, $durable, $auto_delete, $internal, $nowait, $arguments, $ticket ); } /** * Declares queue, creates if needed * * This is an alias of method `queue_declare` of `PhpAmqpLib\Channel\AMQPChannel`. * * ```php * declareQueue( * 'nameOfMyQueue', // exchange name * ) * ``` * * @return mixed */ public function declareQueue( string $queue = '', bool $passive = false, bool $durable = false, bool $exclusive = false, bool $auto_delete = true, bool $nowait = false, array $arguments = null, int $ticket = null ): ?array { return $this->getChannel()->queue_declare( $queue, $passive, $durable, $exclusive, $auto_delete, $nowait, $arguments, $ticket ); } /** * Binds a queue to an exchange * * This is an alias of method `queue_bind` of `PhpAmqpLib\Channel\AMQPChannel`. * * ```php * bindQueueToExchange( * 'nameOfMyQueueToBind', // name of the queue * 'transactionTracking.transaction', // exchange name to bind to * 'your.routing.key' // Optionally, provide a binding key * ) * ``` * * @return mixed */ public function bindQueueToExchange( string $queue, string $exchange, string $routing_key = '', bool $nowait = false, array $arguments = null, int $ticket = null ) { return $this->getChannel()->queue_bind( $queue, $exchange, $routing_key, $nowait, $arguments, $ticket ); } /** * Add a queue to purge list */ public function scheduleQueueCleanup(string $queue): void { if (!in_array($queue, $this->config['queues'])) { $this->config['queues'][] = $queue; } } /** * Checks if message containing text received. * * **This method drops message from queue** * **This method will wait for message. If none is sent the script will stuck**. * * ``` php * pushToQueue('queue.emails', 'Hello, davert'); * $I->seeMessageInQueueContainsText('queue.emails','davert'); * ``` */ public function seeMessageInQueueContainsText(string $queue, string $text): void { $msg = $this->getChannel()->basic_get($queue); if (!$msg instanceof AMQPMessage) { $this->fail("Message was not received"); } if (!$msg instanceof AMQPMessage) { $this->fail("Received message is not format of AMQPMessage"); } $this->debugSection("Message", $msg->body); $this->assertStringContainsString($text, $msg->body); $msg->ack(); } /** * Count messages in queue. */ public function _countMessage(string $queue): int { [$queue, $messageCount] = $this->getChannel()->queue_declare($queue, true); return $messageCount; } /** * Checks that queue have expected number of message * * ``` php * pushToQueue('queue.emails', 'Hello, davert'); * $I->seeNumberOfMessagesInQueue('queue.emails',1); * ``` */ public function seeNumberOfMessagesInQueue(string $queue, int $expected): void { $messageCount = $this->_countMessage($queue); $this->assertEquals($expected, $messageCount); } /** * Checks that queue is empty * * ``` php * pushToQueue('queue.emails', 'Hello, davert'); * $I->purgeQueue('queue.emails'); * $I->seeQueueIsEmpty('queue.emails'); * ``` */ public function seeQueueIsEmpty(string $queue): void { $messageCount = $this->_countMessage($queue); $this->assertEquals(0, $messageCount); } /** * Checks if queue is not empty. * * ``` php * pushToQueue('queue.emails', 'Hello, davert'); * $I->dontSeeQueueIsEmpty('queue.emails'); * ``` */ public function dontSeeQueueIsEmpty(string $queue): void { $messageCount = $this->_countMessage($queue); $this->assertNotEquals(0, $messageCount); } /** * Takes last message from queue. * * ``` php * grabMessageFromQueue('queue.emails'); * ``` */ public function grabMessageFromQueue(string $queue): ?AMQPMessage { return $this->getChannel()->basic_get($queue); } /** * Purge a specific queue defined in config. * * ``` php * purgeQueue('queue.emails'); * ``` */ public function purgeQueue(string $queueName = ''): void { if (! in_array($queueName, $this->config['queues'])) { throw new ModuleException(__CLASS__, "'{$queueName}' doesn't exist in queues config list"); } $this->getChannel()->queue_purge($queueName, true); } /** * Purge all queues defined in config. * * ``` php * purgeAllQueues(); * ``` */ public function purgeAllQueues(): void { $this->cleanup(); } protected function getChannel(): AMQPChannel { if ($this->config['single_channel'] && $this->channelId === null) { $this->channelId = $this->connection->get_free_channel_id(); } return $this->connection->channel($this->channelId); } protected function cleanup(): void { if (!isset($this->config['queues'])) { throw new ModuleException(__CLASS__, "please set queues for cleanup"); } if (!$this->connection) { return; } foreach ($this->config['queues'] as $queue) { try { $this->getChannel()->queue_purge($queue); } catch (AMQPProtocolChannelException $exception) { // ignore if exchange/queue doesn't exist and rethrow exception if it's something else if ($exception->getCode() !== 404) { throw $exception; } } } } } seeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']); * * ``` * Will generate: * * ```sql * SELECT COUNT(*) FROM `users` WHERE `name` = 'Davert' AND `email` = 'davert@mail.com' * ``` * Since version 2.1.9 it's possible to use LIKE in a condition, as shown here: * * ```php * seeInDatabase('users', ['name' => 'Davert', 'email like' => 'davert%']); * * ``` * Will generate: * * ```sql * SELECT COUNT(*) FROM `users` WHERE `name` = 'Davert' AND `email` LIKE 'davert%' * ``` * Null comparisons are also available, as shown here: * * ```php * seeInDatabase('users', ['name' => null, 'email !=' => null]); * * ``` * Will generate: * * ```sql * SELECT COUNT(*) FROM `users` WHERE `name` IS NULL AND `email` IS NOT NULL * ``` * ## Public Properties * * dbh - contains the PDO connection * * driver - contains the Connection Driver * */ class Db extends Module implements DbInterface { /** * @var array */ protected array $config = [ 'populate' => false, 'cleanup' => false, 'reconnect' => false, 'waitlock' => 0, 'dump' => null, 'populator' => null, 'skip_cleanup_if_failed' => false, ]; /** * @var string[] */ protected array $requiredFields = ['dsn', 'user', 'password']; /** * @var string */ public const DEFAULT_DATABASE = 'default'; /** * @var Driver[] */ public array $drivers = []; /** * @var PDO[] */ public array $dbhs = []; public array $databasesPopulated = []; public array $databasesSql = []; protected array $insertedRows = []; public string $currentDatabase = self::DEFAULT_DATABASE; protected function getDatabases(): array { $databases = [$this->currentDatabase => $this->config]; if (!empty($this->config['databases'])) { foreach ($this->config['databases'] as $databaseKey => $databaseConfig) { $databases[$databaseKey] = array_merge([ 'populate' => false, 'cleanup' => false, 'reconnect' => false, 'waitlock' => 0, 'dump' => null, 'populator' => null, ], $databaseConfig); } } return $databases; } protected function connectToDatabases(): void { foreach ($this->getDatabases() as $databaseKey => $databaseConfig) { $this->connect($databaseKey, $databaseConfig); } } protected function cleanUpDatabases(): void { foreach ($this->getDatabases() as $databaseKey => $databaseConfig) { $this->_cleanup($databaseKey, $databaseConfig); } } protected function populateDatabases($configKey): void { foreach ($this->getDatabases() as $databaseKey => $databaseConfig) { if ($databaseConfig[$configKey]) { if (!$databaseConfig['populate']) { return; } if (isset($this->databasesPopulated[$databaseKey]) && $this->databasesPopulated[$databaseKey]) { return; } $this->_loadDump($databaseKey, $databaseConfig); } } } protected function readSqlForDatabases(): void { foreach ($this->getDatabases() as $databaseKey => $databaseConfig) { $this->readSql($databaseKey, $databaseConfig); } } protected function removeInsertedForDatabases(): void { foreach (array_keys($this->getDatabases()) as $databaseKey) { $this->amConnectedToDatabase($databaseKey); $this->removeInserted($databaseKey); } } protected function disconnectDatabases(): void { foreach (array_keys($this->getDatabases()) as $databaseKey) { $this->disconnect($databaseKey); } } protected function reconnectDatabases(): void { foreach ($this->getDatabases() as $databaseKey => $databaseConfig) { if ($databaseConfig['reconnect']) { $this->disconnect($databaseKey); $this->connect($databaseKey, $databaseConfig); } } } public function __get($name) { Notification::deprecate("Properties dbh and driver are deprecated in favor of Db::_getDbh and Db::_getDriver", "Db module"); if ($name == 'driver') { return $this->_getDriver(); } if ($name == 'dbh') { return $this->_getDbh(); } } public function _getDriver(): Driver { return $this->drivers[$this->currentDatabase]; } public function _getDbh(): PDO { return $this->dbhs[$this->currentDatabase]; } /** * Make sure you are connected to the right database. * * ```php * seeNumRecords(2, 'users'); //executed on default database * $I->amConnectedToDatabase('db_books'); * $I->seeNumRecords(30, 'books'); //executed on db_books database * //All the next queries will be on db_books * ``` * * @throws ModuleConfigException */ public function amConnectedToDatabase(string $databaseKey): void { if (empty($this->getDatabases()[$databaseKey]) && $databaseKey != self::DEFAULT_DATABASE) { throw new ModuleConfigException( __CLASS__, "\nNo database {$databaseKey} in the key databases.\n" ); } $this->currentDatabase = $databaseKey; } /** * Can be used with a callback if you don't want to change the current database in your test. * * ```php * seeNumRecords(2, 'users'); //executed on default database * $I->performInDatabase('db_books', function($I) { * $I->seeNumRecords(30, 'books'); //executed on db_books database * }); * $I->seeNumRecords(2, 'users'); //executed on default database * ``` * List of actions can be pragmatically built using `Codeception\Util\ActionSequence`: * * ```php * performInDatabase('db_books', ActionSequence::build() * ->seeNumRecords(30, 'books') * ); * ``` * Alternatively an array can be used: * * ```php * $I->performInDatabase('db_books', ['seeNumRecords' => [30, 'books']]); * ``` * * Choose the syntax you like the most and use it, * * Actions executed from array or ActionSequence will print debug output for actions, and adds an action name to * exception on failure. * * @param $databaseKey * @param ActionSequence|array|callable $actions * @throws ModuleConfigException */ public function performInDatabase($databaseKey, $actions): void { $backupDatabase = $this->currentDatabase; $this->amConnectedToDatabase($databaseKey); if (is_callable($actions)) { $actions($this); $this->amConnectedToDatabase($backupDatabase); return; } if (is_array($actions)) { $actions = ActionSequence::build()->fromArray($actions); } if (!$actions instanceof ActionSequence) { throw new InvalidArgumentException("2nd parameter, actions should be callback, ActionSequence or array"); } $actions->run($this); $this->amConnectedToDatabase($backupDatabase); } public function _initialize(): void { $this->connectToDatabases(); } public function __destruct() { $this->disconnectDatabases(); } public function _beforeSuite($settings = []): void { $this->readSqlForDatabases(); $this->connectToDatabases(); $this->cleanUpDatabases(); $this->populateDatabases('populate'); } private function readSql($databaseKey = null, $databaseConfig = null): void { if ($databaseConfig['populator']) { return; } if (!$databaseConfig['cleanup'] && !$databaseConfig['populate']) { return; } if (empty($databaseConfig['dump'])) { return; } if (!is_array($databaseConfig['dump'])) { $databaseConfig['dump'] = [$databaseConfig['dump']]; } $sql = ''; foreach ($databaseConfig['dump'] as $filePath) { $sql .= $this->readSqlFile($filePath); } if (!empty($sql)) { // split SQL dump into lines $this->databasesSql[$databaseKey] = preg_split('#\r\n|\n|\r#', $sql, -1, PREG_SPLIT_NO_EMPTY); } } /** * @throws ModuleConfigException|ModuleException */ private function readSqlFile(string $filePath): ?string { if (!file_exists(Configuration::projectDir() . $filePath)) { throw new ModuleConfigException( __CLASS__, "\nFile with dump doesn't exist.\n" . "Please, check path for sql file: " . $filePath ); } $sql = file_get_contents(Configuration::projectDir() . $filePath); // remove C-style comments (except MySQL directives) $replaced = preg_replace('#/\*(?!!\d+).*?\*/#s', '', $sql); if (!empty($sql) && is_null($replaced)) { throw new ModuleException( __CLASS__, "Please, increase pcre.backtrack_limit value in PHP CLI config" ); } return $replaced; } private function connect($databaseKey, $databaseConfig): void { if (!empty($this->drivers[$databaseKey]) && !empty($this->dbhs[$databaseKey])) { return; } $options = []; if (array_key_exists('ssl_key', $databaseConfig) && !empty($databaseConfig['ssl_key']) && defined(PDO::class . '::MYSQL_ATTR_SSL_KEY') ) { $options[PDO::MYSQL_ATTR_SSL_KEY] = (string) $databaseConfig['ssl_key']; } if (array_key_exists('ssl_cert', $databaseConfig) && !empty($databaseConfig['ssl_cert']) && defined(PDO::class . '::MYSQL_ATTR_SSL_CERT') ) { $options[PDO::MYSQL_ATTR_SSL_CERT] = (string) $databaseConfig['ssl_cert']; } if (array_key_exists('ssl_ca', $databaseConfig) && !empty($databaseConfig['ssl_ca']) && defined(PDO::class . '::MYSQL_ATTR_SSL_CA') ) { $options[PDO::MYSQL_ATTR_SSL_CA] = (string) $databaseConfig['ssl_ca']; } if (array_key_exists('ssl_cipher', $databaseConfig) && !empty($databaseConfig['ssl_cipher']) && defined(PDO::class . '::MYSQL_ATTR_SSL_CIPHER') ) { $options[PDO::MYSQL_ATTR_SSL_CIPHER] = (string) $databaseConfig['ssl_cipher']; } if (array_key_exists('ssl_verify_server_cert', $databaseConfig) && defined(PDO::class . '::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT') ) { $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (boolean) $databaseConfig[ 'ssl_verify_server_cert' ]; } try { $this->debugSection('Connecting To Db', ['config' => $databaseConfig, 'options' => $options]); $this->drivers[$databaseKey] = Driver::create($databaseConfig['dsn'], $databaseConfig['user'], $databaseConfig['password'], $options); } catch (PDOException $exception) { $message = $exception->getMessage(); if ($message === 'could not find driver') { [$missingDriver, ] = explode(':', $databaseConfig['dsn'], 2); $message = sprintf('could not find %s driver', $missingDriver); } throw new ModuleException(__CLASS__, $message . ' while creating PDO connection'); } if ($databaseConfig['waitlock']) { $this->_getDriver()->setWaitLock($databaseConfig['waitlock']); } if (isset($databaseConfig['initial_queries'])) { foreach ($databaseConfig['initial_queries'] as $initialQuery) { $this->drivers[$databaseKey]->executeQuery($initialQuery, []); } } $this->debugSection('Db', 'Connected to ' . $databaseKey . ' ' . $this->drivers[$databaseKey]->getDb()); $this->dbhs[$databaseKey] = $this->drivers[$databaseKey]->getDbh(); } private function disconnect($databaseKey): void { $this->debugSection('Db', 'Disconnected from ' . $databaseKey); $this->dbhs[$databaseKey] = null; $this->drivers[$databaseKey] = null; } public function _before(TestInterface $test): void { $this->reconnectDatabases(); $this->amConnectedToDatabase(self::DEFAULT_DATABASE); $this->cleanUpDatabases(); $this->populateDatabases('cleanup'); parent::_before($test); } public function _failed(TestInterface $test, $fail) { foreach ($this->getDatabases() as $databaseKey => $databaseConfig) { if ($databaseConfig['skip_cleanup_if_failed'] ?? false) { $this->insertedRows[$databaseKey] = []; } } } public function _after(TestInterface $test): void { $this->removeInsertedForDatabases(); parent::_after($test); } protected function removeInserted($databaseKey = null): void { $databaseKey = empty($databaseKey) ? self::DEFAULT_DATABASE : $databaseKey; if (empty($this->insertedRows[$databaseKey])) { return; } foreach (array_reverse($this->insertedRows[$databaseKey]) as $row) { try { $this->_getDriver()->deleteQueryByCriteria($row['table'], $row['primary']); } catch (Exception $e) { $this->debug("Couldn't delete record " . json_encode($row['primary'], JSON_THROW_ON_ERROR) ." from {$row['table']}"); } } $this->insertedRows[$databaseKey] = []; } public function _cleanup(string $databaseKey = null, array $databaseConfig = null): void { $databaseKey = empty($databaseKey) ? self::DEFAULT_DATABASE : $databaseKey; $databaseConfig = empty($databaseConfig) ? $this->config : $databaseConfig; if (!$databaseConfig['populate']) { return; } if (!$databaseConfig['cleanup']) { return; } if (isset($this->databasesPopulated[$databaseKey]) && !$this->databasesPopulated[$databaseKey]) { return; } $dbh = $this->dbhs[$databaseKey]; if (!$dbh) { throw new ModuleConfigException( __CLASS__, "No connection to database. Remove this module from config if you don't need database repopulation" ); } try { if (!$this->shouldCleanup($databaseConfig, $databaseKey)) { return; } $this->drivers[$databaseKey]->cleanup(); $this->databasesPopulated[$databaseKey] = false; } catch (Exception $e) { throw new ModuleException(__CLASS__, $e->getMessage()); } } protected function shouldCleanup(array $databaseConfig, string $databaseKey): bool { // If using populator and it's not empty, clean up regardless if (!empty($databaseConfig['populator'])) { return true; } // If no sql dump for $databaseKey or sql dump is empty, don't clean up return !empty($this->databasesSql[$databaseKey]); } public function _isPopulated() { return $this->databasesPopulated[$this->currentDatabase]; } public function _loadDump(string $databaseKey = null, array $databaseConfig = null): void { $databaseKey = empty($databaseKey) ? self::DEFAULT_DATABASE : $databaseKey; $databaseConfig = empty($databaseConfig) ? $this->config : $databaseConfig; if (!empty($databaseConfig['populator'])) { $this->loadDumpUsingPopulator($databaseKey, $databaseConfig); return; } $this->loadDumpUsingDriver($databaseKey); } protected function loadDumpUsingPopulator(string $databaseKey, array $databaseConfig): void { $populator = new DbPopulator($databaseConfig); $this->databasesPopulated[$databaseKey] = $populator->run(); } protected function loadDumpUsingDriver(string $databaseKey): void { if (!isset($this->databasesSql[$databaseKey])) { return; } if (!$this->databasesSql[$databaseKey]) { $this->debugSection('Db', 'No SQL loaded, loading dump skipped'); return; } $this->drivers[$databaseKey]->load($this->databasesSql[$databaseKey]); $this->databasesPopulated[$databaseKey] = true; } /** * Inserts an SQL record into a database. This record will be erased after the test, * unless you've configured "skip_cleanup_if_failed", and the test fails. * * ```php * haveInDatabase('users', array('name' => 'miles', 'email' => 'miles@davis.com')); * ``` */ public function haveInDatabase(string $table, array $data): int { $lastInsertId = $this->_insertInDatabase($table, $data); $this->addInsertedRow($table, $data, $lastInsertId); return $lastInsertId; } public function _insertInDatabase(string $table, array $data): int { $query = $this->_getDriver()->insert($table, $data); $parameters = array_values($data); $this->debugSection('Query', $query); $this->debugSection('Parameters', $parameters); $this->_getDriver()->executeQuery($query, $parameters); try { $lastInsertId = (int)$this->_getDriver()->lastInsertId($table); } catch (PDOException $e) { // ignore errors due to uncommon DB structure, // such as tables without _id_seq in PGSQL $lastInsertId = 0; $this->debugSection('DB error', $e->getMessage()); } return $lastInsertId; } private function addInsertedRow(string $table, array $row, $id): void { $primaryKey = $this->_getDriver()->getPrimaryKey($table); $primary = []; if ($primaryKey !== []) { $filledKeys = array_intersect($primaryKey, array_keys($row)); $missingPrimaryKeyColumns = array_diff_key($primaryKey, $filledKeys); if (count($missingPrimaryKeyColumns) === 0) { $primary = array_intersect_key($row, array_flip($primaryKey)); } elseif (count($missingPrimaryKeyColumns) === 1) { $primary = array_intersect_key($row, array_flip($primaryKey)); $missingColumn = reset($missingPrimaryKeyColumns); $primary[$missingColumn] = $id; } else { foreach ($primaryKey as $column) { if (isset($row[$column])) { $primary[$column] = $row[$column]; } else { throw new InvalidArgumentException( 'Primary key field ' . $column . ' is not set for table ' . $table ); } } } } else { $primary = $row; } $this->insertedRows[$this->currentDatabase][] = [ 'table' => $table, 'primary' => $primary, ]; } public function seeInDatabase(string $table, array $criteria = []): void { $res = $this->countInDatabase($table, $criteria); $this->assertGreaterThan( 0, $res, 'No matching records found for criteria ' . json_encode($criteria, JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE) . ' in table ' . $table ); } /** * Asserts that the given number of records were found in the database. * * ```php * seeNumRecords(1, 'users', ['name' => 'davert']) * ``` * * @param int $expectedNumber Expected number * @param string $table Table name * @param array $criteria Search criteria [Optional] */ public function seeNumRecords(int $expectedNumber, string $table, array $criteria = []): void { $actualNumber = $this->countInDatabase($table, $criteria); $this->assertSame( $expectedNumber, $actualNumber, sprintf( 'The number of found rows (%d) does not match expected number %d for criteria %s in table %s', $actualNumber, $expectedNumber, json_encode($criteria, JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE), $table ) ); } public function dontSeeInDatabase(string $table, array $criteria = []): void { $count = $this->countInDatabase($table, $criteria); $this->assertLessThan( 1, $count, 'Unexpectedly found matching records for criteria ' . json_encode($criteria, JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE) . ' in table ' . $table ); } /** * Count rows in a database * * @param string $table Table name * @param array $criteria Search criteria [Optional] * @return int */ protected function countInDatabase(string $table, array $criteria = []): int { return (int) $this->proceedSeeInDatabase($table, 'count(*)', $criteria); } /** * Fetches all values from the column in database. * Provide table name, desired column and criteria. * * @return mixed */ protected function proceedSeeInDatabase(string $table, string $column, array $criteria) { $query = $this->_getDriver()->select($column, $table, $criteria); $parameters = array_values($criteria); $this->debugSection('Query', $query); if (!empty($parameters)) { $this->debugSection('Parameters', $parameters); } $sth = $this->_getDriver()->executeQuery($query, $parameters); return $sth->fetchColumn(); } /** * Fetches all values from the column in database. * Provide table name, desired column and criteria. * * ``` php * grabColumnFromDatabase('users', 'email', array('name' => 'RebOOter')); * ``` */ public function grabColumnFromDatabase(string $table, string $column, array $criteria = []): array { $query = $this->_getDriver()->select($column, $table, $criteria); $parameters = array_values($criteria); $this->debugSection('Query', $query); $this->debugSection('Parameters', $parameters); $sth = $this->_getDriver()->executeQuery($query, $parameters); return $sth->fetchAll(PDO::FETCH_COLUMN, 0); } /** * Fetches a single column value from a database. * Provide table name, desired column and criteria. * * ``` php * grabFromDatabase('users', 'email', array('name' => 'Davert')); * ``` * Comparison expressions can be used as well: * * ```php * grabFromDatabase('posts', 'num_comments', ['num_comments >=' => 100]); * $mail = $I->grabFromDatabase('users', 'email', ['email like' => 'miles%']); * ``` * * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`. * * @return mixed Returns a single column value or false */ public function grabFromDatabase(string $table, string $column, array $criteria = []) { return $this->proceedSeeInDatabase($table, $column, $criteria); } /** * Fetches a whole entry from a database. * Make the test fail if the entry is not found. * Provide table name, desired column and criteria. * * ``` php * grabEntryFromDatabase('users', array('name' => 'Davert')); * ``` * Comparison expressions can be used as well: * * ```php * grabEntryFromDatabase('posts', ['num_comments >=' => 100]); * $user = $I->grabEntryFromDatabase('users', ['email like' => 'miles%']); * ``` * * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`. * * @return array Returns a single entry value * @throws PDOException|Exception */ public function grabEntryFromDatabase(string $table, array $criteria = []): array { $query = $this->_getDriver()->select('*', $table, $criteria); $parameters = array_values($criteria); $this->debugSection('Query', $query); $this->debugSection('Parameters', $parameters); $sth = $this->_getDriver()->executeQuery($query, $parameters); $result = $sth->fetch(PDO::FETCH_ASSOC, 0); if ($result === false) { throw new \AssertionError("No matching row found"); } return $result; } /** * Fetches a set of entries from a database. * Provide table name and criteria. * * ``` php * grabEntriesFromDatabase('users', array('name' => 'Davert')); * ``` * Comparison expressions can be used as well: * * ```php * grabEntriesFromDatabase('posts', ['num_comments >=' => 100]); * $user = $I->grabEntriesFromDatabase('users', ['email like' => 'miles%']); * ``` * * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`. * * @return array> Returns an array of all matched rows * @throws PDOException|Exception */ public function grabEntriesFromDatabase(string $table, array $criteria = []): array { $query = $this->_getDriver()->select('*', $table, $criteria); $parameters = array_values($criteria); $this->debugSection('Query', $query); $this->debugSection('Parameters', $parameters); $sth = $this->_getDriver()->executeQuery($query, $parameters); return $sth->fetchAll(PDO::FETCH_ASSOC); } /** * Returns the number of rows in a database * * @param string $table Table name * @param array $criteria Search criteria [Optional] * @return int */ public function grabNumRecords(string $table, array $criteria = []): int { return $this->countInDatabase($table, $criteria); } /** * Update an SQL record into a database. * * ```php * updateInDatabase('users', array('isAdmin' => true), array('email' => 'miles@davis.com')); * ``` */ public function updateInDatabase(string $table, array $data, array $criteria = []): void { $query = $this->_getDriver()->update($table, $data, $criteria); $parameters = [...array_values($data), ...array_values($criteria)]; $this->debugSection('Query', $query); if (!empty($parameters)) { $this->debugSection('Parameters', $parameters); } $this->_getDriver()->executeQuery($query, $parameters); } } seeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']); * ``` * Fails if no such user found. * * Comparison expressions can be used as well: * * ```php * seeInDatabase('posts', ['num_comments >=' => '0']); * $I->seeInDatabase('users', ['email like' => 'miles@davis.com']); * ``` * * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`. */ public function seeInDatabase(string $table, array $criteria = []): void; /** * Effect is opposite to ->seeInDatabase * * Asserts that there is no record with the given column values in a database. * Provide table name and column values. * * ``` php * dontSeeInDatabase('users', ['name' => 'Davert', 'email' => 'davert@mail.com']); * ``` * Fails if such user was found. * * Comparison expressions can be used as well: * * ```php * dontSeeInDatabase('posts', ['num_comments >=' => '0']); * $I->dontSeeInDatabase('users', ['email like' => 'miles%']); * ``` * * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`. */ public function dontSeeInDatabase(string $table, array $criteria = []): void; /** * Fetches a single column value from a database. * Provide table name, desired column and criteria. * * ``` php * grabFromDatabase('users', 'email', array('name' => 'Davert')); * ``` * Comparison expressions can be used as well: * * ```php * grabFromDatabase('posts', ['num_comments >=' => 100]); * $user = $I->grabFromDatabase('users', ['email like' => 'miles%']); * ``` * * Supported operators: `<`, `>`, `>=`, `<=`, `!=`, `like`. * * @return mixed */ public function grabFromDatabase(string $table, string $column, array $criteria = []); } dbh->exec('SET FOREIGN_KEY_CHECKS=0;'); $res = $this->dbh->query("SHOW FULL TABLES WHERE TABLE_TYPE LIKE '%TABLE';")->fetchAll(); foreach ($res as $row) { $this->dbh->exec('drop table `' . $row[0] . '`'); } $this->dbh->exec('SET FOREIGN_KEY_CHECKS=1;'); } protected function sqlQuery(string $query): void { $this->dbh->exec('SET FOREIGN_KEY_CHECKS=0;'); parent::sqlQuery($query); $this->dbh->exec('SET FOREIGN_KEY_CHECKS=1;'); } public function getQuotedName(string $name): string { return '`' . str_replace('.', '`.`', $name) . '`'; } /** * @return string[] */ public function getPrimaryKey(string $tableName): array { if (!isset($this->primaryKeys[$tableName])) { $primaryKey = []; $stmt = $this->getDbh()->query( 'SHOW KEYS FROM ' . $this->getQuotedName($tableName) . " WHERE Key_name = 'PRIMARY'" ); $columns = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($columns as $column) { $primaryKey []= $column['Column_name']; } $this->primaryKeys[$tableName] = $primaryKey; } return $this->primaryKeys[$tableName]; } } primary-key */ protected array $primaryKeys = []; public static function connect(string $dsn, string $user = null, string $password = null, array $options = null): PDO { $dbh = new PDO($dsn, $user, $password, $options); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $dbh; } /** * @static * * @see https://www.php.net/manual/en/pdo.construct.php * @see https://www.php.net/manual/de/ref.pdo-mysql.php#pdo-mysql.constants * * @return Db|SqlSrv|MySql|Oci|PostgreSql|Sqlite */ public static function create(string $dsn, string $user = null, string $password = null, array $options = null): Db { $provider = self::getProvider($dsn); switch ($provider) { case 'sqlite': return new Sqlite($dsn, $user, $password, $options); case 'mysql': return new MySql($dsn, $user, $password, $options); case 'pgsql': return new PostgreSql($dsn, $user, $password, $options); case 'mssql': case 'dblib': case 'sqlsrv': return new SqlSrv($dsn, $user, $password, $options); case 'oci': return new Oci($dsn, $user, $password, $options); default: return new Db($dsn, $user, $password, $options); } } public static function getProvider($dsn): string { return substr($dsn, 0, strpos($dsn, ':')); } /** * @see https://www.php.net/manual/en/pdo.construct.php * @see https://www.php.net/manual/de/ref.pdo-mysql.php#pdo-mysql.constants */ public function __construct(string $dsn, string $user = null, string $password = null, array $options = null) { $this->dbh = new PDO($dsn, $user, $password, $options); $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->dsn = $dsn; $this->user = $user; $this->password = $password; $this->options = $options; } public function __destruct() { if ($this->dbh !== null && $this->dbh->inTransaction()) { $this->dbh->rollBack(); } $this->dbh = null; } public function getDbh(): PDO { return $this->dbh; } public function getDb() { $matches = []; $matched = preg_match('#dbname=(\w+)#s', $this->dsn, $matches); if (!$matched) { return false; } return $matches[1]; } public function cleanup(): void { } /** * Set the lock waiting interval for the database session */ public function setWaitLock(int $seconds): void { } /** * @param string[] $sql */ public function load(array $sql): void { $query = ''; $delimiter = ';'; $delimiterLength = 1; foreach ($sql as $singleSql) { if (preg_match('#DELIMITER ([\;\$\|\\\]+)#i', $singleSql, $match)) { $delimiter = $match[1]; $delimiterLength = strlen($delimiter); continue; } $parsed = $this->sqlLine($singleSql); if ($parsed) { continue; } $query .= "\n" . rtrim($singleSql); if (substr($query, -1 * $delimiterLength, $delimiterLength) == $delimiter) { $this->sqlQuery(substr($query, 0, -1 * $delimiterLength)); $query = ''; } } if ($query !== '') { $this->sqlQuery($query); } } public function insert(string $tableName, array &$data): string { $columns = array_map( fn($name): string => $this->getQuotedName($name), array_keys($data) ); return sprintf( "INSERT INTO %s (%s) VALUES (%s)", $this->getQuotedName($tableName), implode(', ', $columns), implode(', ', array_fill(0, count($data), '?')) ); } public function select(string $column, string $tableName, array &$criteria): string { $where = $this->generateWhereClause($criteria); $query = "SELECT %s FROM %s %s"; return sprintf($query, $column, $this->getQuotedName($tableName), $where); } /** * @return string[] */ private function getSupportedOperators(): array { return [ 'like', '!=', '<=', '>=', '<', '>', ]; } protected function generateWhereClause(array &$criteria): string { if (empty($criteria)) { return ''; } $operands = $this->getSupportedOperators(); $params = []; foreach ($criteria as $k => $v) { if ($v === null) { if (strpos($k, ' !=') > 0) { $params[] = $this->getQuotedName(str_replace(" !=", '', $k)) . " IS NOT NULL "; } else { $params[] = $this->getQuotedName($k) . " IS NULL "; } unset($criteria[$k]); continue; } $hasOperand = false; // search for equals - no additional operand given foreach ($operands as $operand) { if (!stripos($k, " {$operand}") > 0) { continue; } $hasOperand = true; $k = str_ireplace(" {$operand}", '', $k); $operand = strtoupper($operand); $params[] = $this->getQuotedName($k) . " {$operand} ? "; break; } if (!$hasOperand) { $params[] = $this->getQuotedName($k) . " = ? "; } } return 'WHERE ' . implode('AND ', $params); } public function deleteQueryByCriteria(string $tableName, array $criteria): void { $where = $this->generateWhereClause($criteria); $query = 'DELETE FROM ' . $this->getQuotedName($tableName) . ' ' . $where; $this->executeQuery($query, array_values($criteria)); } public function lastInsertId(string $tableName): string { return (string)$this->getDbh()->lastInsertId(); } public function getQuotedName(string $name): string { return '"' . str_replace('.', '"."', $name) . '"'; } protected function sqlLine(string $sql): bool { $sql = trim($sql); return ( $sql === '' || $sql === ';' || preg_match('#^((--.*?)|(\#))#s', $sql) ); } protected function sqlQuery(string $query): void { try { $this->dbh->exec($query); } catch (PDOException $exception) { throw new ModuleException( \Codeception\Module\Db::class, $exception->getMessage() . "\nSQL query being executed: " . $query ); } } public function executeQuery($query, array $params): PDOStatement { $pdoStatement = $this->dbh->prepare($query); if (!$pdoStatement) { throw new Exception("Query '{$query}' can't be prepared."); } $i = 0; foreach ($params as $param) { ++$i; if (is_null($param)) { $type = PDO::PARAM_NULL; } elseif (is_bool($param)) { $type = PDO::PARAM_BOOL; } elseif (is_int($param)) { $type = PDO::PARAM_INT; } elseif (is_string($param) && $this->isBinary($param)) { $type = PDO::PARAM_LOB; } else { $type = PDO::PARAM_STR; } $pdoStatement->bindValue($i, $param, $type); } $pdoStatement->execute(); return $pdoStatement; } /** * @return string[] */ public function getPrimaryKey(string $tableName): array { return []; } protected function flushPrimaryColumnCache(): bool { $this->primaryKeys = []; return empty($this->primaryKeys); } public function update(string $tableName, array $data, array $criteria): string { if (empty($data)) { throw new InvalidArgumentException( "Query update can't be prepared without data." ); } $set = []; foreach (array_keys($data) as $column) { $set[] = $this->getQuotedName($column) . " = ?"; } $where = $this->generateWhereClause($criteria); return sprintf('UPDATE %s SET %s %s', $this->getQuotedName($tableName), implode(', ', $set), $where); } public function getOptions(): array { return $this->options; } protected function isBinary(string $string): bool { return false === mb_detect_encoding($string, null, true); } } filename = Configuration::projectDir() . $filename; $this->dsn = 'sqlite:' . $this->filename; parent::__construct($this->dsn, $user, $password, $options); } public function cleanup(): void { $this->dbh = null; gc_collect_cycles(); file_put_contents($this->filename, ''); $this->dbh = self::connect($this->dsn, $this->user, $this->password); } /** * @param string[] $sql */ public function load(array $sql): void { if ($this->hasSnapshot) { $this->dbh = null; copy($this->filename . '_snapshot', $this->filename); $this->dbh = new PDO($this->dsn, $this->user, $this->password); } else { if (file_exists($this->filename . '_snapshot')) { unlink($this->filename . '_snapshot'); } parent::load($sql); copy($this->filename, $this->filename . '_snapshot'); $this->hasSnapshot = true; } } /** * @return string[] */ public function getPrimaryKey(string $tableName): array { if (!isset($this->primaryKeys[$tableName])) { if ($this->hasRowId($tableName)) { return $this->primaryKeys[$tableName] = ['_ROWID_']; } $primaryKey = []; $query = 'PRAGMA table_info(' . $this->getQuotedName($tableName) . ')'; $stmt = $this->executeQuery($query, []); $columns = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($columns as $column) { if ($column['pk'] !== '0' && $column['pk'] !== 0) { $primaryKey []= $column['name']; } } $this->primaryKeys[$tableName] = $primaryKey; } return $this->primaryKeys[$tableName]; } private function hasRowId($tableName): bool { $params = ['type' => 'table', 'name' => $tableName]; $select = $this->select('sql', 'sqlite_master', $params); $result = $this->executeQuery($select, $params); $sql = $result->fetchColumn(); return strpos($sql, ') WITHOUT ROWID') === false; } } dbh->exec('ALTER SESSION SET ddl_lock_timeout = ' . $seconds); } public function cleanup(): void { $this->dbh->exec( "BEGIN FOR i IN (SELECT trigger_name FROM user_triggers) LOOP EXECUTE IMMEDIATE('DROP TRIGGER ' || user || '.\"' || i.trigger_name || '\"'); END LOOP; END;" ); $this->dbh->exec( "BEGIN FOR i IN (SELECT table_name FROM user_tables) LOOP EXECUTE IMMEDIATE('DROP TABLE ' || user || '.\"' || i.table_name || '\" CASCADE CONSTRAINTS'); END LOOP; END;" ); $this->dbh->exec( "BEGIN FOR i IN (SELECT sequence_name FROM user_sequences) LOOP EXECUTE IMMEDIATE('DROP SEQUENCE ' || user || '.\"' || i.sequence_name || '\"'); END LOOP; END;" ); $this->dbh->exec( "BEGIN FOR i IN (SELECT view_name FROM user_views) LOOP EXECUTE IMMEDIATE('DROP VIEW ' || user || '.\"' || i.view_name || '\"'); END LOOP; END;" ); } /** * SQL commands should ends with `//` in the dump file * IF you want to load triggers too. * IF you do not want to load triggers you can use the `;` characters * but in this case you need to change the $delimiter from `//` to `;` * * @param string[] $sql */ public function load(array $sql): void { $query = ''; $delimiter = '//'; $delimiterLength = 2; foreach ($sql as $singleSql) { if (preg_match('#DELIMITER ([\;\$\|\\\]+)#i', $singleSql, $match)) { $delimiter = $match[1]; $delimiterLength = strlen($delimiter); continue; } $parsed = $this->sqlLine($singleSql); if ($parsed) { continue; } $query .= "\n" . rtrim($singleSql); if (substr($query, -1 * $delimiterLength, $delimiterLength) == $delimiter) { $this->sqlQuery(substr($query, 0, -1 * $delimiterLength)); $query = ""; } } if ($query !== '') { $this->sqlQuery($query); } } /** * @return string[] */ public function getPrimaryKey(string $tableName): array { if (!isset($this->primaryKeys[$tableName])) { $primaryKey = []; $query = "SELECT cols.column_name FROM all_constraints cons, all_cons_columns cols WHERE cols.table_name = ? AND cons.constraint_type = 'P' AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDER BY cols.table_name, cols.position"; $stmt = $this->executeQuery($query, [$tableName]); $columns = $stmt->fetchAll(\PDO::FETCH_ASSOC); foreach ($columns as $column) { $primaryKey []= $column['COLUMN_NAME']; } $this->primaryKeys[$tableName] = $primaryKey; } return $this->primaryKeys[$tableName]; } } dsn, $matches); if (!$matched) { return false; } return $matches[1]; } public function cleanup(): void { $this->dbh->exec( " DECLARE constraints_cursor CURSOR FOR SELECT name, parent_object_id FROM sys.foreign_keys; OPEN constraints_cursor DECLARE @constraint sysname; DECLARE @parent int; DECLARE @table nvarchar(128); FETCH NEXT FROM constraints_cursor INTO @constraint, @parent; WHILE (@@FETCH_STATUS <> -1) BEGIN SET @table = OBJECT_NAME(@parent) EXEC ('ALTER TABLE [' + @table + '] DROP CONSTRAINT [' + @constraint + ']') FETCH NEXT FROM constraints_cursor INTO @constraint, @parent; END DEALLOCATE constraints_cursor;" ); $this->dbh->exec( " DECLARE tables_cursor CURSOR FOR SELECT name FROM sysobjects WHERE type = 'U'; OPEN tables_cursor DECLARE @tablename sysname; FETCH NEXT FROM tables_cursor INTO @tablename; WHILE (@@FETCH_STATUS <> -1) BEGIN EXEC ('DROP TABLE [' + @tablename + ']') FETCH NEXT FROM tables_cursor INTO @tablename; END DEALLOCATE tables_cursor;" ); } public function getQuotedName(string $name): string { return '[' . str_replace('.', '].[', $name) . ']'; } /** * @return string[] */ public function getPrimaryKey(string $tableName): array { if (!isset($this->primaryKeys[$tableName])) { $primaryKey = []; $query = " SELECT Col.Column_Name from INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab, INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col WHERE Col.Constraint_Name = Tab.Constraint_Name AND Col.Table_Name = Tab.Table_Name AND Constraint_Type = 'PRIMARY KEY' AND Col.Table_Name = ?"; $stmt = $this->executeQuery($query, [$tableName]); $columns = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($columns as $column) { $primaryKey []= $column['Column_Name']; } $this->primaryKeys[$tableName] = $primaryKey; } return $this->primaryKeys[$tableName]; } } sqlLine($singleSql); if ($parsed) { continue; } // Ignore $$ inside SQL standard string syntax such as in INSERT statements. if (!preg_match('#\'.*\$\$.*\'#', $singleSql)) { $pos = strpos($singleSql, '$$'); if (($pos !== false) && ($pos >= 0)) { $dollarsOpen = !$dollarsOpen; } } if (preg_match('#SET search_path = .*#i', $singleSql, $match)) { $this->searchPath = $match[0]; } $query .= "\n" . rtrim($singleSql); if (!$dollarsOpen && substr($query, -1 * $delimiterLength, $delimiterLength) == $delimiter) { $this->sqlQuery(substr($query, 0, -1 * $delimiterLength)); $query = ''; } } if ($query !== '') { $this->sqlQuery($query); } } public function cleanup(): void { $this->dbh->exec('DROP SCHEMA IF EXISTS public CASCADE;'); $this->dbh->exec('CREATE SCHEMA public;'); } public function sqlLine(string $sql): bool { if (!$this->putline) { return parent::sqlLine($sql); } if ($sql == '\.') { $this->putline = false; pg_put_line($this->connection, $sql . "\n"); pg_end_copy($this->connection); pg_close($this->connection); } else { pg_put_line($this->connection, $sql . "\n"); } return true; } public function sqlQuery(string $query): void { if (strpos(trim($query), 'COPY ') === 0) { if (!extension_loaded('pgsql')) { throw new ModuleException( \Codeception\Module\Db::class, "To run 'COPY' commands 'pgsql' extension should be installed" ); } $strConn = str_replace(';', ' ', substr($this->dsn, 6)); $strConn .= ' user=' . $this->user; $strConn .= ' password=' . $this->password; $this->connection = pg_connect($strConn); if ($this->searchPath !== null) { pg_query($this->connection, $this->searchPath); } pg_query($this->connection, $query); $this->putline = true; } else { $this->dbh->exec($query); } } /** * Get the last inserted ID of table. */ public function lastInsertId(string $tableName): string { /** * We make an assumption that the sequence name for this table * is based on how postgres names sequences for SERIAL columns */ $sequenceName = $this->getQuotedName($tableName . '_id_seq'); $lastSequence = null; try { $lastSequence = $this->getDbh()->lastInsertId($sequenceName); } catch (PDOException $exception) { // in this case, the sequence name might be combined with the primary key name } // here we check if for instance, it's something like table_primary_key_seq instead of table_id_seq // this could occur when you use some kind of import tool like pgloader if (!$lastSequence) { $primaryKeys = $this->getPrimaryKey($tableName); $pkName = array_shift($primaryKeys); $lastSequence = $this->getDbh()->lastInsertId($this->getQuotedName($tableName . '_' . $pkName . '_seq')); } return $lastSequence; } /** * Returns the primary key(s) of the table, based on: * https://wiki.postgresql.org/wiki/Retrieve_primary_key_columns. * * @return string[] */ public function getPrimaryKey(string $tableName): array { if (!isset($this->primaryKeys[$tableName])) { $primaryKey = []; $query = "SELECT a.attname FROM pg_index i JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) WHERE i.indrelid = '{$tableName}'::regclass AND i.indisprimary"; $stmt = $this->executeQuery($query, []); $columns = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($columns as $column) { $primaryKey []= $column['attname']; } $this->primaryKeys[$tableName] = $primaryKey; } return $this->primaryKeys[$tableName]; } } config = $config; //Convert To Array Format if (!isset($this->config['dump'])) { return; } if (is_array($this->config['dump'])) { return; } $this->config['dump'] = [$this->config['dump']]; } /** * Builds out a command replacing any found `$key` with its value if found in the given configuration. * * Process any $key found in the configuration array as a key of the array and replaces it with * the found value for the key. Example: * * ```php * 'Mauro']; * * // With the above parameters it will return `'Hello Mauro'`. * ``` * * @param string $command The command to be evaluated using the given config * @param string|null $dumpFile The dump file to build the command with. * @return string The resulting command string after evaluating any configuration's key */ protected function buildCommand(string $command, string $dumpFile = null): string { $dsn = $this->config['dsn'] ?? ''; $dsnVars = []; $dsnWithoutDriver = preg_replace('#^[a-z]+:#i', '', $dsn); foreach (explode(';', $dsnWithoutDriver) as $item) { $keyValueTuple = explode('=', $item); if (count($keyValueTuple) > 1) { [$k, $v] = array_values($keyValueTuple); $dsnVars[$k] = $v; } } $vars = array_merge($dsnVars, $this->config); if ($dumpFile !== null) { $vars['dump'] = $dumpFile; } foreach ($vars as $key => $value) { if (!is_array($value)) { $vars['$'.$key] = $value; } unset($vars[$key]); } return str_replace(array_keys($vars), $vars, $command); } /** * Executes the command built using the Db module configuration. * * Uses the PHP `exec` to spin off a child process for the built command. */ public function run(): bool { foreach ($this->buildCommands() as $command) { $this->runCommand($command); } return true; } private function runCommand($command): void { codecept_debug("[Db] Executing Populator: `{$command}`"); exec($command, $output, $exitCode); if (0 !== $exitCode) { throw new \RuntimeException( "The populator command did not end successfully: \n" . " Exit code: {$exitCode} \n" . " Output:" . implode("\n", $output) ); } codecept_debug("[Db] Populator Finished."); } public function buildCommands(): array { if ($this->commands !== []) { return $this->commands; } elseif (!isset($this->config['dump']) || $this->config['dump'] === false) { return [$this->buildCommand($this->config['populator'])]; } $this->commands = []; foreach ($this->config['dump'] as $dumpFile) { $this->commands[] = $this->buildCommand($this->config['populator'], $dumpFile); } return $this->commands; } } getModule('{{MODULE_NAME}}')->_saveScreenshot(codecept_output_dir().'screenshot_1.png'); * ``` * @api */ public function _saveScreenshot(string $filename); } getModule('{{MODULE_NAME}}')->_findElements('.items'); * $els = $this->getModule('{{MODULE_NAME}}')->_findElements(['name' => 'username']); * * $editLinks = $this->getModule('{{MODULE_NAME}}')->_findElements(['link' => 'Edit']); * // now you can iterate over $editLinks and check that all them have valid hrefs * ``` * * WebDriver module returns `Facebook\WebDriver\Remote\RemoteWebElement` instances * PhpBrowser and Framework modules return `Symfony\Component\DomCrawler\Crawler` instances * * @api */ public function _findElements(mixed $locator): iterable; } getModule('{{MODULE_NAME}}')->_savePageSource(codecept_output_dir().'page.html'); * ``` * @api */ public function _savePageSource(string $filename): void; /** * Use this method within an [interactive pause](https://codeception.com/docs/02-GettingStarted#Interactive-Pause) to save the HTML source code of the current page. * * ```php * makeHtmlSnapshot('edit_page'); * // saved to: tests/_output/debug/edit_page.html * $I->makeHtmlSnapshot(); * // saved to: tests/_output/debug/2017-05-26_14-24-11_4b3403665fea6.html * ``` */ public function makeHtmlSnapshot(string $name = null): void; } amOnPage('/'); * // opens /register page * $I->amOnPage('/register'); * ``` */ public function amOnPage(string $page): void; /** * Checks that the current page contains the given string (case insensitive). * * You can specify a specific HTML element (via CSS or XPath) as the second * parameter to only search within that element. * * ```php * see('Logout'); // I can suppose user is logged in * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page * $I->see('Sign Up', '//body/h1'); // with XPath * $I->see('Sign Up', ['css' => 'body h1']); // with strict CSS locator * ``` * * Note that the search is done after stripping all HTML tags from the body, * so `$I->see('strong')` will return true for strings like: * * - `

I am Stronger than thou

` * - `` * * But will *not* be true for strings like: * * - `Home` * - `
Home` * - `` * * For checking the raw source code, use `seeInSource()`. * * @param array|string $selector optional */ public function see(string $text, $selector = null): void; /** * Checks that the current page doesn't contain the text specified (case insensitive). * Give a locator as the second parameter to match a specific region. * * ```php * dontSee('Login'); // I can suppose user is already logged in * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page * $I->dontSee('Sign Up','//body/h1'); // with XPath * $I->dontSee('Sign Up', ['css' => 'body h1']); // with strict CSS locator * ``` * * Note that the search is done after stripping all HTML tags from the body, * so `$I->dontSee('strong')` will fail on strings like: * * - `

I am Stronger than thou

` * - `` * * But will ignore strings like: * * - `Home` * - `
Home` * - `` * * For checking the raw source code, use `seeInSource()`. * * @param array|string $selector optional */ public function dontSee(string $text, $selector = null): void; /** * Checks that the current page contains the given string in its * raw source code. * * ```php * seeInSource('

Green eggs & ham

'); * ``` */ public function seeInSource(string $raw): void; /** * Checks that the current page contains the given string in its * raw source code. * * ```php * dontSeeInSource('

Green eggs & ham

'); * ``` */ public function dontSeeInSource(string $raw): void; /** * Submits the given form on the page, with the given form * values. Pass the form field's values as an array in the second * parameter. * * Although this function can be used as a short-hand version of * `fillField()`, `selectOption()`, `click()` etc. it has some important * differences: * * * Only field *names* may be used, not CSS/XPath selectors nor field labels * * If a field is sent to this function that does *not* exist on the page, * it will silently be added to the HTTP request. This is helpful for testing * some types of forms, but be aware that you will *not* get an exception * like you would if you called `fillField()` or `selectOption()` with * a missing field. * * Fields that are not provided will be filled by their values from the page, * or from any previous calls to `fillField()`, `selectOption()` etc. * You don't need to click the 'Submit' button afterwards. * This command itself triggers the request to form's action. * * You can optionally specify which 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? *
* Subscribe to our newsletter? *
* Select pricing plan: * * *
* ``` * * You could write the following to submit it: * * ```php * submitForm( * '#userForm', * [ * 'user' => [ * 'login' => 'Davert', * 'password' => '123456', * 'agree' => true * ] * ], * 'submitButton' * ); * ``` * Note that "2" will be the submitted value for the "plan" field, as it is * the selected option. * * To uncheck the pre-checked checkbox "newsletter", call `$I->uncheckOption(['name' => 'user[newsletter]']);` *before*, * then submit the form as shown here (i.e. without the "newsletter" field in the `$params` array). * * You can also emulate a JavaScript submission by not specifying any * buttons in the third parameter to submitForm. * * ```php * submitForm( * '#userForm', * [ * 'user' => [ * 'login' => 'Davert', * 'password' => '123456', * 'agree' => true * ] * ] * ); * ``` * * This function works well when paired with `seeInFormFields()` * for quickly testing CRUD interfaces and form validation logic. * * ```php * 'value', * 'field2' => 'another value', * 'checkbox1' => true, * // ... * ]; * $I->submitForm('#my-form', $form, 'submitButton'); * // $I->amOnPage('/path/to/form-page') may be needed * $I->seeInFormFields('#my-form', $form); * ``` * * Parameter values can be set to arrays for multiple input fields * of the same name, or multi-select combo boxes. For checkboxes, * you can use either the string value or boolean `true`/`false` 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 * submitForm('#my-form', [ * 'field[]' => 'value', * 'field[]' => 'another value', // 'field[]' is already a defined key * ]); * ``` * * The solution is to pass an array value: * * ```php * submitForm('#my-form', [ * 'field' => [ * 'value', * 'another value', * ] * ]); * ``` */ public function submitForm($selector, array $params, string $button = null): void; /** * Perform a click on a link or a button, given by a locator. * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. * For buttons, the "value" attribute, "name" attribute, and inner text are searched. * For links, the link text is searched. * For images, the "alt" attribute and inner text of any parent links are searched. * * The second parameter is a context (CSS or XPath locator) to narrow the search. * * Note that if the locator matches a button of type `submit`, the form will be submitted. * * ```php * click('Logout'); * // button of form * $I->click('Submit'); * // CSS button * $I->click('#form input[type=submit]'); * // XPath * $I->click('//form/*[@type="submit"]'); * // link in context * $I->click('Logout', '#nav'); * // using strict locator * $I->click(['link' => 'Login']); * ``` * @param string|array $link */ public function click($link, $context = null): void; /** * Checks that there's a link with the specified text. * Give a full URL as the second parameter to match links with that exact URL. * * ```php * seeLink('Logout'); // matches Logout * $I->seeLink('Logout','/logout'); // matches Logout * ``` */ public function seeLink(string $text, string $url = null): void; /** * Checks that the page doesn't contain a link with the given string. * If the second parameter is given, only links with a matching "href" attribute will be checked. * * ```php * dontSeeLink('Logout'); // I suppose user is not logged in * $I->dontSeeLink('Checkout now', '/store/cart.php'); * ``` */ public function dontSeeLink(string $text, string $url = ''): void; /** * Checks that current URI contains the given string. * * ```php * seeInCurrentUrl('home'); * // to match: /users/1 * $I->seeInCurrentUrl('/users/'); * ``` */ public function seeInCurrentUrl(string $uri): void; /** * Checks that the current URL is equal to the given string. * Unlike `seeInCurrentUrl`, this only matches the full URL. * * ```php * seeCurrentUrlEquals('/'); * ``` */ public function seeCurrentUrlEquals(string $uri): void; /** * Checks that the current URL matches the given regular expression. * * ```php * seeCurrentUrlMatches('~^/users/(\d+)~'); * ``` */ public function seeCurrentUrlMatches(string $uri): void; /** * Checks that the current URI doesn't contain the given string. * * ```php * dontSeeInCurrentUrl('/users/'); * ``` */ public function dontSeeInCurrentUrl(string $uri): void; /** * Checks that the current URL doesn't equal the given string. * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. * * ```php * dontSeeCurrentUrlEquals('/'); * ``` */ public function dontSeeCurrentUrlEquals(string $uri): void; /** * Checks that current url doesn't match the given regular expression. * * ```php * dontSeeCurrentUrlMatches('~^/users/(\d+)~'); * ``` */ public function dontSeeCurrentUrlMatches(string $uri): void; /** * Executes the given regular expression against the current URI and returns the first capturing group. * If no parameters are provided, the full URI is returned. * * ```php * grabFromCurrentUrl('~^/user/(\d+)/~'); * $uri = $I->grabFromCurrentUrl(); * ``` */ public function grabFromCurrentUrl(string $uri = null): mixed; /** * Checks that the specified checkbox is checked. * * ```php * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); * ``` */ public function seeCheckboxIsChecked($checkbox): void; /** * Check that the specified checkbox is unchecked. * * ```php * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. * ``` */ public function dontSeeCheckboxIsChecked($checkbox): void; /** * Checks that the given input field or textarea *equals* (i.e. not just contains) the given value. * Fields are matched by label text, the "name" attribute, CSS, or XPath. * * ```php * seeInField('Body','Type your comment here'); * $I->seeInField('form textarea[name=body]','Type your comment here'); * $I->seeInField('form input[type=hidden]','hidden_value'); * $I->seeInField('#searchform input','Search'); * $I->seeInField('//form/*[@name=search]','Search'); * $I->seeInField(['name' => 'search'], 'Search'); * ``` * * @param string|array $field */ public function seeInField($field, $value): void; /** * Checks that an input field or textarea doesn't contain the given value. * For fuzzy locators, the field is matched by label text, CSS and XPath. * * ```php * dontSeeInField('Body','Type your comment here'); * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); * $I->dontSeeInField('form input[type=hidden]','hidden_value'); * $I->dontSeeInField('#searchform input','Search'); * $I->dontSeeInField('//form/*[@name=search]','Search'); * $I->dontSeeInField(['name' => 'search'], 'Search'); * ``` * @param string|array $field */ public function dontSeeInField($field, $value): void; /** * Checks if the array of form parameters (name => value) are set on the form matched with the * passed selector. * * ```php * seeInFormFields('form[name=myform]', [ * 'input1' => 'value', * 'input2' => 'other value', * ]); * ``` * * For multi-select elements, or to check values of multiple elements with the same name, an * array may be passed: * * ```php * seeInFormFields('.form-class', [ * 'multiselect' => [ * 'value1', * 'value2', * ], * 'checkbox[]' => [ * 'a checked value', * 'another checked value', * ], * ]); * ``` * * Additionally, checkbox values can be checked with a boolean. * * ```php * seeInFormFields('#form-id', [ * 'checkbox1' => true, // passes if checked * 'checkbox2' => false, // passes if unchecked * ]); * ``` * * Pair this with submitForm for quick testing magic. * * ```php * 'value', * 'field2' => 'another value', * 'checkbox1' => true, * // ... * ]; * $I->submitForm('//form[@id=my-form]', string $form, 'submitButton'); * // $I->amOnPage('/path/to/form-page') may be needed * $I->seeInFormFields('//form[@id=my-form]', string $form); * ``` */ public function seeInFormFields($formSelector, array $params): void; /** * Checks if the array of form parameters (name => value) are not set on the form matched with * the passed selector. * * ```php * dontSeeInFormFields('form[name=myform]', [ * 'input1' => 'non-existent value', * 'input2' => 'other non-existent value', * ]); * ``` * * To check that an element hasn't been assigned any one of many values, an array can be passed * as the value: * * ```php * dontSeeInFormFields('.form-class', [ * 'fieldName' => [ * 'This value shouldn\'t be set', * 'And this value shouldn\'t be set', * ], * ]); * ``` * * Additionally, checkbox values can be checked with a boolean. * * ```php * dontSeeInFormFields('#form-id', [ * 'checkbox1' => true, // fails if checked * 'checkbox2' => false, // fails if unchecked * ]); * ``` */ public function dontSeeInFormFields($formSelector, array $params): void; /** * Selects an option in a select tag or in radio button group. * * ```php * selectOption('form select[name=account]', 'Premium'); * $I->selectOption('form input[name=payment]', 'Monthly'); * $I->selectOption('//form/select[@name=account]', 'Monthly'); * ``` * * Provide an array for the second argument to select multiple options: * * ```php * selectOption('Which OS do you use?', ['Windows', 'Linux']); * ``` * * Or provide an associative array for the second argument to specifically define which selection method should be used: * * ```php * selectOption('Which OS do you use?', ['text' => 'Windows']); // Only search by text 'Windows' * $I->selectOption('Which OS do you use?', ['value' => 'windows']); // Only search by value 'windows' * ``` */ public function selectOption($select, $option): void; /** * Ticks a checkbox. For radio buttons, use the `selectOption` method instead. * * ```php * checkOption('#agree'); * ``` */ public function checkOption($option): void; /** * Unticks a checkbox. * * ```php * uncheckOption('#notify'); * ``` */ public function uncheckOption($option): void; /** * Fills a text field or textarea with the given string. * * ```php * fillField("//input[@type='text']", "Hello World!"); * $I->fillField(['name' => 'email'], 'jon@example.com'); * ``` */ public function fillField($field, $value): void; /** * Attaches a file relative to the Codeception `_data` directory to the given file upload field. * * ```php * attachFile('input[@type="file"]', 'prices.xls'); * ``` */ public function attachFile($field, string $filename): void; /** * Finds and returns the text contents of the given element. * If a fuzzy locator is used, the element is found using CSS, XPath, * and by matching the full page source by regular expression. * * ```php * grabTextFrom('h1'); * $heading = $I->grabTextFrom('descendant-or-self::h1'); * $value = $I->grabTextFrom('~grabValueFrom('Name'); * $name = $I->grabValueFrom('input[name=username]'); * $name = $I->grabValueFrom('descendant-or-self::form/descendant::input[@name = 'username']'); * $name = $I->grabValueFrom(['name' => 'username']); * ``` */ public function grabValueFrom($field): mixed; /** * Returns the value of the given attribute value from the given HTML element. For some attributes, the string `true` is returned instead of their literal value (e.g. `disabled="disabled"` or `required="required"`). * Fails if the element is not found. Returns `null` if the attribute is not present on the element. * * ```php * grabAttributeFrom('#tooltip', 'title'); * ``` */ public function grabAttributeFrom($cssOrXpath, string $attribute): mixed; /** * Grabs either the text content, or attribute values, of nodes * matched by $cssOrXpath and returns them as an array. * * ```html * First * Second * Third * ``` * * ```php * grabMultiple('a'); * * // would return ['#first', '#second', '#third'] * $aLinks = $I->grabMultiple('a', 'href'); * ``` * * @return string[] */ public function grabMultiple($cssOrXpath, string $attribute = null): array; /** * Checks that the given element exists on the page and is visible. * You can also specify expected attributes of this element. * Only works if `` tag is present. * * ```php * seeElement('.error'); * $I->seeElement('//form/input[1]'); * $I->seeElement('input', ['name' => 'login']); * $I->seeElement('input', ['value' => '123456']); * * // strict locator in first arg, attributes in second * $I->seeElement(['css' => 'form input'], ['name' => 'login']); * ``` */ public function seeElement($selector, array $attributes = []): void; /** * Checks that the given element is invisible or not present on the page. * You can also specify expected attributes of this element. * * ```php * dontSeeElement('.error'); * $I->dontSeeElement('//form/input[1]'); * $I->dontSeeElement('input', ['name' => 'login']); * $I->dontSeeElement('input', ['value' => '123456']); * ``` */ public function dontSeeElement($selector, array $attributes = []): void; /** * Checks that there are a certain number of elements matched by the given locator on the page. * * ```php * seeNumberOfElements('tr', 10); * $I->seeNumberOfElements('tr', [0,10]); // between 0 and 10 elements * ``` * * @param int|int[] $expected */ public function seeNumberOfElements($selector, array|int $expected): void; /** * Checks that the given option is selected. * * ```php * seeOptionIsSelected('#form input[name=payment]', 'Visa'); * ``` * * @return mixed|void */ public function seeOptionIsSelected($selector, string $optionText); /** * Checks that the given option is not selected. * * ```php * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); * ``` * * @return mixed|void */ public function dontSeeOptionIsSelected($selector, string $optionText); /** * Checks that the page title contains the given string. * * ```php * seeInTitle('Blog - Post #1'); * ``` * * @return mixed|void */ public function seeInTitle(string $title); /** * Checks that the page title does not contain the given string. * * @return mixed|void */ public function dontSeeInTitle(string $title); /** * Checks that a cookie with the given name is set. * You can set additional cookie params like `domain`, `path` as array passed in last argument. * * ```php * seeCookie('PHPSESSID'); * ``` * * @return mixed|void */ public function seeCookie(string $cookie, array $params = []); /** * Checks that there isn't a cookie with the given name. * You can set additional cookie params like `domain`, `path` as array passed in last argument. * * @return mixed|void */ public function dontSeeCookie(string $cookie, array $params = []); /** * Sets a cookie with the given name and value. * You can set additional cookie params like `domain`, `path`, `expires`, `secure` in array passed as last argument. * * ```php * setCookie('PHPSESSID', 'el4ukv0kqbvoirg7nkp4dncpk3'); * ``` * * @return mixed|void */ public function setCookie(string $name, ?string $val, array $params = []); /** * Unsets cookie with the given name. * You can set additional cookie params like `domain`, `path` in array passed as last argument. * * @return mixed|void */ public function resetCookie(string $cookie, array $params = []); /** * Grabs a cookie value. * You can set additional cookie params like `domain`, `path` in array passed as last argument. * If the cookie is set by an ajax request (XMLHttpRequest), there might be some delay caused by the browser, so try `$I->wait(0.1)`. */ public function grabCookie(string $cookie, array $params = []): mixed; /** * Grabs current page source code. * * @return string Current page source code. */ public function grabPageSource(): string; } loadSessionSnapshot('login')) return; * * // logging in * $I->amOnPage('/login'); * $I->fillField('name', 'jon'); * $I->fillField('password', '123345'); * $I->click('Login'); * * // saving snapshot * $I->saveSessionSnapshot('login'); * } * ``` * * @return mixed */ public function saveSessionSnapshot(string $name); /** * Loads cookies from a saved snapshot. * Allows to reuse same session across tests without additional login. * * See [saveSessionSnapshot](#saveSessionSnapshot) * * @return mixed */ public function loadSessionSnapshot(string $name); /** * Deletes session snapshot. * * See [saveSessionSnapshot](#saveSessionSnapshot) * * @return mixed */ public function deleteSessionSnapshot(string $name); } amOnSubdomain('user'); * $I->amOnPage('/'); * // moves to https://user.mysite.com/ * ``` * */ public function amOnSubdomain(string $subdomain): void; /** * Open web page at the given absolute URL and sets its hostname as the base host. * * ``` php * amOnUrl('https://codeception.com'); * $I->amOnPage('/quickstart'); // moves to https://codeception.com/quickstart * ``` */ public function amOnUrl(string $url): void; public function _getUrl(); } string = $this->normalizeText($string); $this->uri = $uri; } /** * Evaluates the constraint for parameter $other. Returns true if the * constraint is met, false otherwise. * * @param string $other Value or object to evaluate. * @return bool */ protected function matches($other): bool { $other = $this->normalizeText($other); return mb_stripos($other, $this->string, 0, 'UTF-8') !== false; } private function normalizeText(string $text): string { $text = strtr($text, "\r\n", " "); return trim(preg_replace('/\\s{2,}/', ' ', $text)); } /** * Returns a string representation of the constraint. */ public function toString(): string { return sprintf( 'contains "%s"', $this->string ); } /** * @param string $pageContent */ protected function failureDescription($pageContent): string { $message = $this->uriMessage('on page'); $message .= "\n--> "; $message .= mb_substr($pageContent, 0, 300, 'utf-8'); if (mb_strlen($pageContent, 'utf-8') > 300 && function_exists('codecept_output_dir')) { $message .= "\n[Content too long to display. See complete response in '" . codecept_output_dir() . "' directory]"; } return $message . "\n--> " . $this->toString(); } protected function uriMessage(string $onPage = ''): string { if (!$this->uri) { return ''; } return "{$onPage} {$this->uri}"; } } withHost($parts['host']); $base = $base->withPath(''); $base = $base->withQuery(''); $base = $base->withFragment(''); } if (isset($parts['path'])) { $path = $parts['path']; $basePath = $base->getPath(); if ((!str_starts_with($path, '/')) && !empty($path)) { if ($basePath !== '') { // if it ends with a slash, relative paths are below it if (preg_match('#/$#', $basePath)) { $path = $basePath . $path; } else { // remove double slashes $dir = rtrim(dirname($basePath), '\\/'); $path = $dir . '/' . $path; } } else { $path = '/' . ltrim($path, '/'); } } $base = $base->withPath($path); $base = $base->withQuery(''); $base = $base->withFragment(''); } if (isset($parts['query'])) { $base = $base->withQuery($parts['query']); $base = $base->withFragment(''); } if (isset($parts['fragment'])) { $base = $base->withFragment($parts['fragment']); } return (string)$base; } /** * Retrieve /path?query#fragment part of URL */ public static function retrieveUri(string $url): string { $uri = new Psr7Uri($url); return (string)(new Psr7Uri()) ->withPath($uri->getPath()) ->withQuery($uri->getQuery()) ->withFragment($uri->getFragment()); } public static function retrieveHost(string $url): string { $urlParts = parse_url($url); if (!isset($urlParts['host']) || !isset($urlParts['scheme'])) { throw new InvalidArgumentException("Wrong URL passes, host and scheme not set"); } $host = $urlParts['scheme'] . '://' . $urlParts['host']; if (isset($urlParts['port'])) { $host .= ':' . $urlParts['port']; } return $host; } public static function appendPath(string $url, string $path): string { $uri = new Psr7Uri($url); $cutUrl = (string)$uri->withQuery('')->withFragment(''); if ($path === '' || $path[0] === '#') { return $cutUrl . $path; } return rtrim($cutUrl, '/') . '/' . ltrim($path, '/'); } } see('Title', Locator::combine('h1','h2','h3')); * ``` * * This will search for `Title` text in either `h1`, `h2`, or `h3` tag. * You can also combine CSS selector with XPath locator: * * ```php * fillField(Locator::combine('form input[type=text]','//form/textarea[2]'), 'qwerty'); * ``` * * As a result the Locator will produce a mixed XPath value that will be used in fillField action. * * @static * @throws Exception */ public static function combine(string $selector1, string $selector2): string { $selectors = func_get_args(); foreach ($selectors as $k => $v) { $selectors[$k] = self::toXPath($v); if (!$selectors[$k]) { throw new Exception("{$v} is invalid CSS or XPath"); } } return implode(' | ', $selectors); } /** * Matches the *a* element with given URL * * ```php * see('Log In', Locator::href('/login.php')); * ``` * @static */ public static function href(string $url): string { return sprintf('//a[@href=normalize-space(%s)]', Translator::getXpathLiteral($url)); } /** * Matches the element with given tab index * * Do you often use the `TAB` key to navigate through the web page? How do your site respond to this navigation? * You could try to match elements by their tab position using `tabIndex` method of `Locator` class. * ```php * fillField(Locator::tabIndex(1), 'davert'); * $I->fillField(Locator::tabIndex(2) , 'qwerty'); * $I->click('Login'); * ``` * @static */ public static function tabIndex(int $index): string { return sprintf('//*[@tabindex = normalize-space(%d)]', $index); } /** * Matches option by text: * * ```php * seeElement(Locator::option('Male'), '#select-gender'); * ``` */ public static function option(string $value): string { return sprintf('//option[.=normalize-space("%s")]', $value); } protected static function toXPath(string $selector): ?string { try { return (new CssSelectorConverter())->toXPath($selector); } catch (ParseException $parseException) { if (self::isXPath($selector)) { return $selector; } } return null; } /** * Finds element by it's attribute(s) * * ```php * seeElement(Locator::find('img', ['title' => 'diagram'])); * ``` * @static */ public static function find(string $element, array $attributes): string { $operands = []; foreach ($attributes as $attribute => $value) { if (is_int($attribute)) { $operands[] = '@' . $value; } else { $operands[] = '@' . $attribute . ' = ' . Translator::getXpathLiteral($value); } } return sprintf('//%s[%s]', $element, implode(' and ', $operands)); } /** * Checks that provided string is CSS selector * * ```php * true * Locator::isCSS('body') => true * Locator::isCSS('//body/p/user') => false * ``` */ public static function isCSS(string $selector): bool { try { (new CssSelectorConverter())->toXPath($selector); } catch (ParseException $e) { return false; } return true; } /** * Checks that locator is an XPath * * ```php * false * Locator::isXPath('body') => false * Locator::isXPath('//body/p/user') => true * ``` */ public static function isXPath(string $locator): bool { $domDocument = new DOMDocument('1.0', 'UTF-8'); $domxPath = new DOMXPath($domDocument); return @$domxPath->evaluate($locator, $domDocument) !== false; } public static function isPrecise(WebDriverBy|array|string $locator): bool { if (is_array($locator)) { return true; } if ($locator instanceof WebDriverBy) { return true; } if (Locator::isID($locator)) { return true; } if (str_starts_with($locator, '//')) { return true; // simple xpath check } return false; } /** * Checks that a string is valid CSS ID * * ```php * true * Locator::isID('body') => false * Locator::isID('//body/p/user') => false * ``` */ public static function isID(string $id): bool { return (bool)preg_match('~^#[\w.\-\[\]=^\~:]+$~', $id); } /** * Checks that a string is valid CSS class * * ```php * true * Locator::isClass('body') => false * Locator::isClass('//body/p/user') => false * ``` */ public static function isClass(string $class): bool { return (bool)preg_match('#^\.[\w.\-\[\]=^~:]+$#', $class); } /** * Locates an element containing a text inside. * Either CSS or XPath locator can be passed, however they will be converted to XPath. * * ```php * tr', -2); // previous than last row * ``` * * @param string $element CSS or XPath locator * @param int|string $position xPath index */ public static function elementAt(string $element, int|string $position): string { if (is_int($position) && $position < 0) { ++$position; // -1 points to the last element $position = 'last()-' . abs($position); } if ($position === 0) { throw new InvalidArgumentException( '0 is not valid element position. XPath expects first element to have index 1' ); } return sprintf('(%s)[position()=%s]', self::toXPath($element), $position); } /** * Locates first element of group elements. * Either CSS or XPath locator can be passed as locator, * Equal to `Locator::elementAt($locator, 1)` * * ```php * getMechanism(); $locator = $selector->getValue(); return "{$type} '{$locator}'"; } throw new InvalidArgumentException("Unrecognized selector"); } } 'ftp', 'port' => 21, 'timeout' => 90, 'user' => 'anonymous', 'password' => '', 'key' => '', 'tmp' => 'tests/_data', 'passive' => false, 'cleanup' => true ]; /** * Required configuration fields * * @var string[] */ protected array $requiredFields = ['host']; // ----------- SETUP METHODS BELOW HERE -------------------------// /** * Setup connection and login with config settings */ public function _before(TestInterface $test): void { // Login using config settings $this->loginAs($this->config['user'], $this->config['password']); } /** * Close the FTP connection & Clear up */ public function _after(TestInterface $test): void { $this->_closeConnection(); // Clean up temp files if ($this->config['cleanup'] && file_exists($this->config['tmp'] . '/ftp_data_file.tmp')) { unlink($this->config['tmp'] . '/ftp_data_file.tmp'); } } /** * Change the logged in user mid-way through your test, this closes the * current connection to the server and initialises and new connection. * * On initiation of this modules you are automatically logged into * the server using the specified config options or defaulted * to anonymous user if not provided. * * ``` php * loginAs('user','password'); * ``` */ public function loginAs(string $user = 'anonymous', string $password = ''): void { $this->_openConnection($user, $password); // Create new connection and login. } /** * Enters a directory on the ftp system - FTP root directory is used by default */ public function amInPath(string $path): void { $this->_changeDirectory($this->path = $this->absolutizePath($path) . ($path == '/' ? '' : DIRECTORY_SEPARATOR)); $this->debug('Moved to ' . $this->path); } /** * Resolve path */ protected function absolutizePath(string $path): string { if (str_starts_with($path, '/')) { return $path; } return $this->path . $path; } // ----------- SEARCH METHODS BELOW HERE ------------------------// /** * Checks if file exists in path on the remote FTP/SFTP system. * DOES NOT OPEN the file when it's exists * * ``` php * seeFileFound('UserModel.php','app/models'); * ``` */ public function seeFileFound(string $filename, string $path = ''): void { $files = $this->grabFileList($path); $this->debug("see file: {$filename}"); $this->assertContains($filename, $files, "file {$filename} not found in {$path}"); } /** * Checks if file exists in path on the remote FTP/SFTP system, using regular expression as filename. * DOES NOT OPEN the file when it's exists * * ``` php * seeFileFoundMatches('/^UserModel_([0-9]{6}).php$/','app/models'); * ``` */ public function seeFileFoundMatches(string $regex, string $path = ''): void { foreach ($this->grabFileList($path) as $filename) { preg_match($regex, $filename, $matches); if (!empty($matches)) { $this->debug("file '{$filename}' matches '{$regex}'"); return; } } $this->fail("no file matches found for '{$regex}'"); } /** * Checks if file does not exist in path on the remote FTP/SFTP system */ public function dontSeeFileFound(string $filename, string $path = ''): void { $files = $this->grabFileList($path); $this->debug("don't see file: {$filename}"); $this->assertNotContains($filename, $files); } /** * Checks if file does not exist in path on the remote FTP/SFTP system, using regular expression as filename. * DOES NOT OPEN the file when it's exists */ public function dontSeeFileFoundMatches(string $regex, string $path = ''): void { foreach ($this->grabFileList($path) as $filename) { preg_match($regex, $filename, $matches); if (!empty($matches)) { $this->fail("file matches found for {$regex}"); } } $this->assertTrue(true); $this->debug("no files match '{$regex}'"); } // ----------- UTILITY METHODS BELOW HERE -------------------------// /** * Opens a file (downloads from the remote FTP/SFTP system to a tmp directory for processing) * and stores it's content. * * Usage: * * ``` php * openFile('composer.json'); * $I->seeInThisFile('codeception/codeception'); * ``` */ public function openFile(string $filename): void { $this->_openFile($this->absolutizePath($filename)); } /** * Saves contents to tmp file and uploads the FTP/SFTP system. * Overwrites current file on server if exists. * * ``` php * writeToFile('composer.json', 'some data here'); * ``` */ public function writeToFile(string $filename, string $contents): void { $this->_writeToFile($this->absolutizePath($filename), $contents); } /** * Create a directory on the server * * ``` php * makeDir('vendor'); * ``` */ public function makeDir(string $dirname): void { $this->makeDirectory($this->absolutizePath($dirname)); } /** * Currently not supported in this module, overwrite inherited method */ public function copyDir(string $src, string $dst): void { $this->fail('copyDir() currently unsupported by FTP module'); } /** * Rename/Move file on the FTP/SFTP server * * ``` php * renameFile('composer.lock', 'composer_old.lock'); * ``` */ public function renameFile(string $filename, string $rename): void { $this->renameDirectory($this->absolutizePath($filename), $this->absolutizePath($rename)); } /** * Rename/Move directory on the FTP/SFTP server * * ``` php * renameDir('vendor', 'vendor_old'); * ``` */ public function renameDir(string $dirname, string $rename): void { $this->renameDirectory($this->absolutizePath($dirname), $this->absolutizePath($rename)); } /** * Deletes a file on the remote FTP/SFTP system * * ``` php * deleteFile('composer.lock'); * ``` */ public function deleteFile(string $filename): void { $this->delete($this->absolutizePath($filename)); } /** * Deletes directory with all subdirectories on the remote FTP/SFTP server * * ``` php * deleteDir('vendor'); * ``` */ public function deleteDir(string $dirname): void { $this->delete($this->absolutizePath($dirname)); } /** * Erases directory contents on the FTP/SFTP server * * ``` php * cleanDir('logs'); * ``` */ public function cleanDir(string $dirname): void { $this->clearDirectory($this->absolutizePath($dirname)); } // ----------- GRABBER METHODS BELOW HERE -----------------------// /** * Grabber method for returning file/folders listing in an array * * ```php * grabFileList(); * $count = $I->grabFileList('TEST', false); // Include . .. .thumbs.db * ``` * * @param bool $ignore - suppress '.', '..' and '.thumbs.db' */ public function grabFileList(string $path = '', bool $ignore = true): array { $absolutizePath = $this->absolutizePath($path) . ($path != '' && !str_ends_with($path, '/') ? DIRECTORY_SEPARATOR : ''); $files = $this->_listFiles($absolutizePath); $display_files = []; if (is_array($files) && !empty($files)) { $this->debug('File List:'); foreach ($files as &$file) { if (strtolower($file) != '.' && strtolower($file) != '..' && strtolower($file) != 'thumbs.db' ) { // Ignore '.', '..' and 'thumbs.db' // Replace full path from file listings if returned in listing $file = str_replace( $absolutizePath, '', $file ); $display_files[] = $file; $this->debug(' - ' . $file); } } return $ignore ? $display_files : $files; } $this->debug("File List: "); return []; } /** * Grabber method for returning file/folders count in directory * * ```php * grabFileCount(); * $count = $I->grabFileCount('TEST', false); // Include . .. .thumbs.db * ``` * * @param bool $ignore - suppress '.', '..' and '.thumbs.db' */ public function grabFileCount(string $path = '', bool $ignore = true): int { $count = count($this->grabFileList($path, $ignore)); $this->debug("File Count: {$count}"); return $count; } /** * Grabber method to return file size * * ```php * grabFileSize('test.txt'); * ``` */ public function grabFileSize(string $filename): int { $fileSize = $this->size($filename); $this->debug(sprintf('%s has a file size of %s', $filename, $fileSize)); return $fileSize; } /** * Grabber method to return last modified timestamp * * ```php * grabFileModified('test.txt'); * ``` */ public function grabFileModified(string $filename): int { $time = $this->modified($filename); $this->debug("{$filename} was last modified at {$time}"); return $time; } /** * Grabber method to return current working directory * * ```php * grabDirectory(); * ``` */ public function grabDirectory(): string { $pwd = $this->_directory(); $this->debug("PWD: {$pwd}"); return $pwd; } // ----------- SERVER CONNECTION METHODS BELOW HERE -------------// /** * Open a new FTP/SFTP connection and authenticate user. */ private function _openConnection(string $user = 'anonymous', string $password = ''): void { $this->_closeConnection(); // Close connection if already open if ($this->isSFTP()) { $this->sftpConnect($user, $password); } else { $this->ftpConnect($user, $password); } $pwd = $this->grabDirectory(); $this->path = $pwd . ($pwd == '/' ? '' : DIRECTORY_SEPARATOR); } /** * Close open FTP/SFTP connection */ private function _closeConnection(): void { if (!$this->ftp) { return; } if (!$this->isSFTP()) { ftp_close($this->ftp); $this->ftp = null; } } /** * Get the file listing for FTP/SFTP connection * * @return string[] */ private function _listFiles(string $path): array { $files = $this->isSFTP() ? @$this->ftp->nlist($path) : @ftp_nlist($this->ftp, $path); if ($files === false) { $this->fail("couldn't list files"); } return $files; } /** * Get the current directory for the FTP/SFTP connection */ private function _directory(): string { $pwd = $this->isSFTP() ? @$this->ftp->pwd() : @ftp_pwd($this->ftp); if (!$pwd) { $this->fail("couldn't get current directory"); } return $pwd; } /** * Change the working directory on the FTP/SFTP server */ private function _changeDirectory(string $path): void { $changed = $this->isSFTP() ? @$this->ftp->chdir($path) : @ftp_chdir($this->ftp, $path); if (!$changed) { $this->fail("couldn't change directory {$path}"); } } /** * Download remote file to local tmp directory and open contents. */ private function _openFile(string $filename): void { // Check local tmp directory if (!is_dir($this->config['tmp']) || !is_writable($this->config['tmp'])) { $this->fail('tmp directory not found or is not writable'); } // Download file to local tmp directory $tmp_file = $this->config['tmp'] . "/ftp_data_file.tmp"; if ($this->isSFTP()) { $downloaded = @$this->ftp->get($filename, $tmp_file); } else { $downloaded = @ftp_get($this->ftp, $tmp_file, $filename, FTP_BINARY); } if (!$downloaded) { $this->fail('failed to download file to tmp directory'); } // Open file content to variable if ($this->file = file_get_contents($tmp_file)) { $this->filePath = $filename; } else { $this->fail('failed to open tmp file'); } } /** * Write data to local tmp file and upload to server */ private function _writeToFile(string $filename, string $contents): void { // Check local tmp directory if (!is_dir($this->config['tmp']) || !is_writable($this->config['tmp'])) { $this->fail('tmp directory not found or is not writable'); } // Build temp file $tmp_file = $this->config['tmp'] . "/ftp_data_file.tmp"; file_put_contents($tmp_file, $contents); // Update variables $this->filePath = $filename; $this->file = $contents; // Upload the file to server if ($this->isSFTP()) { $flag = defined('NET_SFTP_LOCAL_FILE') ? NET_SFTP_LOCAL_FILE : \phpseclib\Net\SFTP::SOURCE_LOCAL_FILE; $uploaded = @$this->ftp->put($filename, $tmp_file, $flag); } else { $uploaded = ftp_put($this->ftp, $filename, $tmp_file, FTP_BINARY); } if (!$uploaded) { $this->fail('failed to upload file to server'); } } /** * Make new directory on server */ private function makeDirectory(string $path): void { $created = $this->isSFTP() ? @$this->ftp->mkdir($path, true) : @ftp_mkdir($this->ftp, $path); if (!$created) { $this->fail("couldn't make directory {$path}"); } $this->debug("Make directory: {$path}"); } /** * Rename/Move directory/file on server */ private function renameDirectory(string $path, string $rename): void { $renamed = $this->isSFTP() ? @$this->ftp->rename($path, $rename) : @ftp_rename($this->ftp, $path, $rename); if (!$renamed) { $this->fail("couldn't rename directory {$path} to {$rename}"); } $this->debug(sprintf('Renamed directory: %s to %s', $path, $rename)); } /** * Delete file on server */ private function delete(string $filename, bool $isDir = false): void { $deleted = $this->isSFTP() ? @$this->ftp->delete($filename, $isDir) : @$this->ftpDelete($filename); if (!$deleted) { $this->fail("couldn't delete {$filename}"); } $this->debug("Deleted: {$filename}"); } /** * Function to recursively delete folder, used for PHP FTP build in client. */ private function ftpDelete(string $directory): bool { // here we attempt to delete the file/directory if (!@ftp_rmdir($this->ftp, $directory) && !@ftp_delete($this->ftp, $directory)) { // if the attempt to delete fails, get the file listing $fileList = @ftp_nlist($this->ftp, $directory); // loop through the file list and recursively delete the FILE in the list foreach ($fileList as $file) { $this->ftpDelete($file); } // if the file list is empty, delete the DIRECTORY we passed $this->ftpDelete($directory); } return true; } /** * Clear directory on server of all content */ private function clearDirectory(string $path): void { $this->debug("Clear directory: {$path}"); $this->delete($path); $this->makeDirectory($path); } /** * Return the size of a given file */ private function size(string $filename): int { $size = $this->isSFTP() ? (int)@$this->ftp->size($filename) : @ftp_size($this->ftp, $filename); if ($size < 0) { $this->fail("couldn't get the file size for {$filename}"); } return $size; } /** * Return the last modified time of a given file */ private function modified(string $filename): int { if ($this->isSFTP()) { $info = @$this->ftp->lstat($filename); if ($info) { return $info['mtime']; } } elseif (($time = @ftp_mdtm($this->ftp, $filename)) !== 0) { return $time; } $this->fail("couldn't get the file size for {$filename}"); } protected function sftpConnect(string $user, string $password): void { if (class_exists('Net_SFTP')) { $this->ftp = new \Net_SFTP($this->config['host'], $this->config['port'], $this->config['timeout']); } elseif (class_exists(\phpseclib\Net\SFTP::class)) { $this->ftp = new \phpseclib\Net\SFTP($this->config['host'], $this->config['port'], $this->config['timeout']); } else { throw new ModuleException('FTP', 'phpseclib/phpseclib library is not installed'); } if (!empty($this->config['key'])) { $keyFile = file_get_contents($this->config['key']); if (class_exists('Crypt_RSA')) { $password = new \Crypt_RSA(); } elseif (class_exists(\phpseclib\Crypt\RSA::class)) { $password = new \phpseclib\Crypt\RSA(); } else { throw new ModuleException('FTP', 'phpseclib/phpseclib library is not installed'); } $password->loadKey($keyFile); } if (!$this->ftp->login($user, $password)) { $this->fail('failed to authenticate user'); } } protected function ftpConnect(string $user, string $password): void { $this->ftp = ftp_connect($this->config['host'], $this->config['port'], $this->config['timeout']); if ($this->ftp === false) { $this->ftp = null; $this->fail('failed to connect to ftp server'); } // Login using given access details if (!@ftp_login($this->ftp, $user, $password)) { $this->fail('failed to authenticate user'); } // Set passive mode option (ftp only option) if (isset($this->config['passive'])) { ftp_pasv($this->ftp, $this->config['passive']); } } protected function isSFTP(): bool { return strtolower($this->config['type']) === 'sftp'; } } wantTo('create article'); * $I->click('New Article'); * $I->fillField('Title', sq('Article')); * $I->fillField('Body', 'Demo article with Lorem Ipsum'); * $I->click('save'); * $I->see(sq('Article') ,'#articles') * ``` * * Populating Database: * * ``` php * haveInDatabase('users', array('login' => sq("user$i"), 'email' => sq("user$i").'@email.com'); * } * ``` * * Cest Suite tests: * * ``` php * createUser(sqs('user') . '@mailserver.com', sqs('login'), sqs('pwd')); * } * * public function checkEmail(AcceptanceTester $I) * { * $I->seeInEmailTo(sqs('user') . '@mailserver.com', sqs('login')); * } * * public function removeUser(AcceptanceTester $I) * { * $I->removeUser(sqs('user') . '@mailserver.com'); * } * } * ``` * * ### Config * * By default produces unique string with param as a prefix: * * ``` * sq('user') => 'user_876asd8as87a' * ``` * * This behavior can be configured using `prefix` config param. * * Old style sequences: * * ```yaml * Sequence: * prefix: '_' * ``` * * Using id param inside prefix: * * ```yaml * Sequence: * prefix: '{id}.' * ``` */ class Sequence extends Module { /** * @var array */ public static array $hash = []; /** * @var array */ public static array $suiteHash = []; public static string $prefix = ''; /** * @var array */ protected array $config = ['prefix' => '{id}_']; public function _initialize(): void { static::$prefix = $this->config['prefix']; } public function _after(TestInterface $test): void { self::$hash = []; } public function _afterSuite(): void { self::$suiteHash = []; } } getModule('REST')->haveHttpHeader('Content-Type', 'application/json'); $resp = parent::run($container); $container->getModule('REST')->seeResponseIsJson(); return json_decode($resp, true, 512, JSON_THROW_ON_ERROR); } public static function getTemplate(Template $template): ?Template { $action = $template->getVar('action'); // should only be applied to send* methods if (!str_starts_with($action, 'send')) return null; $conditionalDoc = "* JSON response will be automatically decoded \n " . $template->getVar('doc'); return $template ->place('doc', $conditionalDoc) ->place('action', $action . 'AsJson') ->place('step', 'AsJson'); } } */ protected array $config = [ 'url' => '', 'aws' => '' ]; protected string $dependencyMessage = <<client = &$this->connectionModule->client; $this->resetVariables(); } protected function resetVariables(): void { $this->params = []; $this->response = ''; $this->connectionModule->headers = []; } public function _conflicts(): string { return \Codeception\Lib\Interfaces\API::class; } public function _depends(): array { return [InnerBrowser::class => $this->dependencyMessage]; } /** * @return string[] */ public function _parts(): array { return ['xml', 'json']; } public function _inject(InnerBrowser $connection) { $this->connectionModule = $connection; if ($this->connectionModule instanceof Framework) { $this->isFunctional = true; } if ($this->connectionModule instanceof PhpBrowser && !$this->connectionModule->_getConfig('url')) { $this->connectionModule->_setConfig(['url' => $this->config['url']]); } } public function _failed(TestInterface $test, $fail) { if ($this->response === null || $this->response === '' || $this->response === '0') { return; } $printedResponse = $this->response; if ($this->isBinaryData($printedResponse)) { $printedResponse = $this->binaryToDebugString($printedResponse); } $test->getMetadata()->addReport('body', $printedResponse); } protected function getRunningClient(): AbstractBrowser { if ($this->client->getInternalRequest() === null) { throw new ModuleException($this, "Response is empty. Use `\$I->sendXXX()` methods to send HTTP request"); } return $this->client; } /** * Sets a HTTP header to be used for all subsequent requests. Use [`deleteHeader`](#deleteHeader) to unset it. * * ```php * haveHttpHeader('Content-Type', 'application/json'); * // all next requests will contain this header * ``` * * @part json * @part xml */ public function haveHttpHeader(string $name, string $value): void { $this->connectionModule->haveHttpHeader($name, $value); } /** * Deletes a HTTP header (that was originally added by [haveHttpHeader()](#haveHttpHeader)), * so that subsequent requests will not send it anymore. * * Example: * ```php * haveHttpHeader('X-Requested-With', 'Codeception'); * $I->sendGet('test-headers.php'); * // ... * $I->deleteHeader('X-Requested-With'); * $I->sendPost('some-other-page.php'); * ``` * * @param string $name the name of the header to delete. * @part json * @part xml */ public function deleteHeader(string $name): void { $this->connectionModule->deleteHeader($name); } /** * Checks over the given HTTP header and (optionally) * its value, asserting that are there * * @param $value * @part json * @part xml */ public function seeHttpHeader(string $name, $value = null): void { if ($value !== null) { $this->assertSame( $value, $this->getRunningClient()->getInternalResponse()->getHeader($name) ); return; } $this->assertNotNull($this->getRunningClient()->getInternalResponse()->getHeader($name)); } /** * Checks over the given HTTP header and (optionally) * its value, asserting that are not there * * @param $value * @part json * @part xml */ public function dontSeeHttpHeader(string $name, $value = null): void { if ($value !== null) { $this->assertNotEquals( $value, $this->getRunningClient()->getInternalResponse()->getHeader($name) ); return; } $this->assertNull($this->getRunningClient()->getInternalResponse()->getHeader($name)); } /** * Checks that http response header is received only once. * HTTP RFC2616 allows multiple response headers with the same name. * You can check that you didn't accidentally sent the same header twice. * * ``` php * seeHttpHeaderOnce('Cache-Control'); * ``` * * @part json * @part xml */ public function seeHttpHeaderOnce(string $name): void { $headers = $this->getRunningClient()->getInternalResponse()->getHeader($name, false); $this->assertCount(1, $headers); } /** * Returns the value of the specified header name * * @param bool $first Whether to return the first value or all header values * @return string|array The first header value if $first is true, an array of values otherwise * @part json * @part xml */ public function grabHttpHeader(string $name, bool $first = true): string|array|null { return $this->getRunningClient()->getInternalResponse()->getHeader($name, $first); } /** * Adds HTTP authentication via username/password. * * @part json * @part xml */ public function amHttpAuthenticated(string $username, string $password): void { if ($this->isFunctional) { $this->client->setServerParameter('PHP_AUTH_USER', $username); $this->client->setServerParameter('PHP_AUTH_PW', $password); } else { $this->client->setAuth($username, $password); } } /** * Adds Digest authentication via username/password. * * @part json * @part xml */ public function amDigestAuthenticated(string $username, string $password): void { if ($this->isFunctional) { throw new ModuleException(__METHOD__, 'Not supported by functional modules'); } $this->client->setAuth($username, $password, 'digest'); } /** * Adds Bearer authentication via access token. * * @part json * @part xml */ public function amBearerAuthenticated(string $accessToken): void { $this->haveHttpHeader('Authorization', 'Bearer ' . $accessToken); } /** * Adds NTLM authentication via username/password. * Requires client to be Guzzle >=6.3.0 * Out of scope for functional modules. * * Example: * ```php * amNTLMAuthenticated('jon_snow', 'targaryen'); * ``` * * @throws ModuleException * @part json * @part xml */ public function amNTLMAuthenticated(string $username, string $password): void { if ($this->isFunctional) { throw new ModuleException(__METHOD__, 'Not supported by functional modules'); } if (!defined('\GuzzleHttp\Client::MAJOR_VERSION') && !defined('\GuzzleHttp\Client::VERSION')) { throw new ModuleException(__METHOD__, 'Not supported if not using a Guzzle client'); } $this->client->setAuth($username, $password, 'ntlm'); } /** * Allows to send REST request using AWS Authorization * * Only works with PhpBrowser * Example Config: * ```yml * modules: * enabled: * - REST: * aws: * key: accessKey * secret: accessSecret * service: awsService * region: awsRegion * ``` * Code: * ```php * amAWSAuthenticated(); * ``` * @throws ConfigurationException */ public function amAWSAuthenticated(array $additionalAWSConfig = []): void { if (method_exists($this->client, 'setAwsAuth')) { $config = array_merge($this->config['aws'], $additionalAWSConfig); if (!isset($config['key'])) { throw new ConfigurationException('AWS Key is not set'); } if (!isset($config['secret'])) { throw new ConfigurationException('AWS Secret is not set'); } if (!isset($config['service'])) { throw new ConfigurationException('AWS Service is not set'); } if (!isset($config['region'])) { throw new ConfigurationException('AWS Region is not set'); } $this->client->setAwsAuth($config); } } /** * Sends a POST request to given uri. Parameters and files can be provided separately. * * Example: * ```php * sendPost('/message', ['subject' => 'Read this!', 'to' => 'johndoe@example.com']); * //simple upload method * $I->sendPost('/message/24', ['inline' => 0], ['attachmentFile' => codecept_data_dir('sample_file.pdf')]); * //uploading a file with a custom name and mime-type. This is also useful to simulate upload errors. * $I->sendPost('/message/24', ['inline' => 0], [ * 'attachmentFile' => [ * 'name' => 'document.pdf', * 'type' => 'application/pdf', * 'error' => UPLOAD_ERR_OK, * 'size' => filesize(codecept_data_dir('sample_file.pdf')), * 'tmp_name' => codecept_data_dir('sample_file.pdf') * ] * ]); * // If your field names contain square brackets (e.g. ``), * // PHP parses them into an array. In this case you need to pass the fields like this: * $I->sendPost('/add-task', ['form' => [ * 'task' => 'lorem ipsum', * 'category' => 'miscellaneous', * ]]); * ``` * * @param array|string|JsonSerializable $params * @param array $files A list of filenames or "mocks" of $_FILES (each entry being an array with the following * keys: name, type, error, size, tmp_name (pointing to the real file path). Each key works * as the "name" attribute of a file input field. * * @see https://php.net/manual/en/features.file-upload.post-method.php * @see codecept_data_dir() * @part json * @part xml */ public function sendPost(string $url, $params = [], array $files = []) { return $this->execute('POST', $url, $params, $files); } /** * Sends a HEAD request to given uri. * * @part json * @part xml */ public function sendHead(string $url, array $params = []) { return $this->execute('HEAD', $url, $params); } /** * Sends an OPTIONS request to given uri. * * @part json * @part xml */ public function sendOptions(string $url, array $params = []): void { $this->execute('OPTIONS', $url, $params); } /** * Sends a GET request to given uri. * * ```php * sendGet('/users'); * * // send get with query params * $I->sendGet('/orders', ['id' => 1]) * ``` * * @part json * @part xml */ public function sendGet(string $url, array $params = []) { return $this->execute('GET', $url, $params); } /** * Sends PUT request to given uri. * * ```php * sendPut('/message/1', ['subject' => 'Read this!']); * ``` * * @param array|string|JsonSerializable $params * @part json * @part xml */ public function sendPut(string $url, $params = [], array $files = []) { return $this->execute('PUT', $url, $params, $files); } /** * Sends PATCH request to given uri. * * ```php * sendPatch('/message/1', ['subject' => 'Read this!']); * ``` * * @param array|string|JsonSerializable $params * @part json * @part xml */ public function sendPatch(string $url, $params = [], array $files = []) { return $this->execute('PATCH', $url, $params, $files); } /** * Sends DELETE request to given uri. * * ```php * sendDelete('/message/1'); * ``` * * @part json * @part xml */ public function sendDelete(string $url, array $params = [], array $files = []) { return $this->execute('DELETE', $url, $params, $files); } /** * Sends a HTTP request. * * @param array|string|JsonSerializable $params * @part json * @part xml */ public function send(string $method, string $url, $params = [], array $files = []) { return $this->execute(strtoupper($method), $url, $params, $files); } /** * Sets Headers "Link" as one header "Link" based on linkEntries * * @param array $linkEntries (entry is array with keys "uri" and "link-param") * * @link https://tools.ietf.org/html/rfc2068#section-19.6.2.4 * * @author samva.ua@gmail.com */ private function setHeaderLink(array $linkEntries): void { $values = []; foreach ($linkEntries as $linkEntry) { Assert::assertArrayHasKey( 'uri', $linkEntry, 'linkEntry should contain property "uri"' ); Assert::assertArrayHasKey( 'link-param', $linkEntry, 'linkEntry should contain property "link-param"' ); $values[] = $linkEntry['uri'] . '; ' . $linkEntry['link-param']; } $this->haveHttpHeader('Link', implode(', ', $values)); } /** * Sends LINK request to given uri. * * @param array $linkEntries (entry is array with keys "uri" and "link-param") * * @link https://tools.ietf.org/html/rfc2068#section-19.6.2.4 * * @author samva.ua@gmail.com * @part json * @part xml */ public function sendLink(string $url, array $linkEntries): void { $this->setHeaderLink($linkEntries); $this->execute('LINK', $url); } /** * Sends UNLINK request to given uri. * * @param array $linkEntries (entry is array with keys "uri" and "link-param") * @link https://tools.ietf.org/html/rfc2068#section-19.6.2.4 * @author samva.ua@gmail.com * @part json * @part xml */ public function sendUnlink(string $url, array $linkEntries): void { $this->setHeaderLink($linkEntries); $this->execute('UNLINK', $url); } /** * @param $method * @param $url * @param array|string|object $parameters * @param array $files * @throws ModuleException|ExternalUrlException|JsonException */ protected function execute($method, $url, $parameters = [], $files = []) { // allow full url to be requested if (!$url) { $url = $this->config['url']; } elseif (!is_string($url)) { throw new ModuleException(__CLASS__, 'URL must be string'); } elseif (!str_contains($url, '://') && $this->config['url']) { $url = rtrim($this->config['url'], '/') . '/' . ltrim($url, '/'); } $this->params = $parameters; $isQueryParamsAwareMethod = in_array($method, self::QUERY_PARAMS_AWARE_METHODS, true); if ($isQueryParamsAwareMethod) { if (!is_array($parameters)) { throw new ModuleException(__CLASS__, $method . ' parameters must be passed in array format'); } } else { $parameters = $this->encodeApplicationJson($method, $parameters); } if (is_array($parameters) || $isQueryParamsAwareMethod) { if ($isQueryParamsAwareMethod) { if (!empty($parameters)) { if (str_contains($url, '?')) { $url .= '&'; } else { $url .= '?'; } $url .= http_build_query($parameters); } $this->debugSection("Request", sprintf('%s %s', $method, $url)); $files = []; } else { $this->debugSection("Request", sprintf('%s %s ', $method, $url) . json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION | JSON_THROW_ON_ERROR) ); $files = $this->formatFilesArray($files); } $this->response = $this->connectionModule->_request($method, $url, $parameters, $files); } else { $requestData = $parameters; if ($this->isBinaryData($requestData)) { $requestData = $this->binaryToDebugString($requestData); } $this->debugSection("Request", sprintf('%s %s ', $method, $url) . $requestData); $this->response = $this->connectionModule->_request($method, $url, [], $files, [], $parameters); } $printedResponse = $this->response; if ($this->isBinaryData((string)$printedResponse)) { $printedResponse = $this->binaryToDebugString($printedResponse); } $short = $this->_getConfig('shortDebugResponse'); if (!is_null($short)) { $printedResponse = $this->shortenMessage($printedResponse, $short); $this->debugSection("Shortened Response", $printedResponse); } else { $this->debugSection("Response", $printedResponse); } return $this->response; } /** * Check if data has non-printable bytes and it is not a valid unicode string * * @param string $data the text or binary data string */ protected function isBinaryData(string $data): bool { return !ctype_print($data) && false === mb_detect_encoding($data, mb_detect_order(), true); } /** * Format a binary string for debug printing * * @param string $data the binary data string * @return string the debug string */ protected function binaryToDebugString(string $data): string { return '[binary-data length:' . strlen($data) . ' md5:' . md5($data) . ']'; } protected function encodeApplicationJson(string $method, $parameters) { if ( array_key_exists('Content-Type', $this->connectionModule->headers) && ($this->connectionModule->headers['Content-Type'] === 'application/json' || preg_match('#^application/.+\+json$#', $this->connectionModule->headers['Content-Type']) ) ) { if ($parameters instanceof JsonSerializable) { return json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION | JSON_THROW_ON_ERROR); } if (is_array($parameters) || $parameters instanceof ArrayAccess) { $parameters = $this->scalarizeArray($parameters); return json_encode($parameters, JSON_PRESERVE_ZERO_FRACTION | JSON_THROW_ON_ERROR); } } if ($parameters instanceof JsonSerializable) { throw new ModuleException(__CLASS__, $method . ' parameters is JsonSerializable object, but Content-Type header is not set to application/json'); } if (!is_string($parameters) && !is_array($parameters)) { throw new ModuleException(__CLASS__, $method . ' parameters must be array, string or object implementing JsonSerializable interface'); } return $parameters; } private function formatFilesArray(array $files): array { foreach ($files as $name => $value) { if (is_string($value)) { $this->checkFileBeforeUpload($value); $files[$name] = [ 'name' => basename($value), 'tmp_name' => $value, 'size' => filesize($value), 'type' => $this->getFileType($value), 'error' => 0, ]; continue; } if (is_array($value)) { if (isset($value['tmp_name'])) { $this->checkFileBeforeUpload($value['tmp_name']); if (!isset($value['name'])) { $value['name'] = basename($value['tmp_name']); } if (!isset($value['size'])) { $value['size'] = filesize($value['tmp_name']); } if (!isset($value['type'])) { $value['type'] = $this->getFileType($value['tmp_name']); } if (!isset($value['error'])) { $value['error'] = 0; } } else { $files[$name] = $this->formatFilesArray($value); } } elseif (is_object($value)) { /** * do nothing, probably the user knows what he is doing * @issue https://github.com/Codeception/Codeception/issues/3298 */ } else { throw new ModuleException(__CLASS__, sprintf('Invalid value of key %s in files array', $name)); } } return $files; } private function getFileType($file): string { if (function_exists('mime_content_type') && mime_content_type($file)) { return mime_content_type($file); } return 'application/octet-stream'; } private function checkFileBeforeUpload(string $file): void { if (!file_exists($file)) { throw new ModuleException(__CLASS__, sprintf('File %s does not exist', $file)); } if (!is_readable($file)) { throw new ModuleException(__CLASS__, sprintf('File %s is not readable', $file)); } if (!is_file($file)) { throw new ModuleException(__CLASS__, sprintf('File %s is not a regular file', $file)); } } /** * Extends the function Module::validateConfig for shorten messages * */ protected function validateConfig(): void { parent::validateConfig(); $short = $this->_getConfig('shortDebugResponse'); if (!is_null($short) && (!is_int($short) || $short < 0)) { throw new ModuleConfigException(__CLASS__, 'The value of "shortDebugMessage" should be integer and greater or equal "0".'); } } /** * Checks whether last response was valid JSON. * This is done with json_last_error function. * * @part json */ public function seeResponseIsJson(): void { $responseContent = $this->connectionModule->_getResponseContent(); Assert::assertNotEquals('', $responseContent, 'response is empty'); $this->decodeAndValidateJson($responseContent); } /** * Checks whether the last response contains text. * * @part json * @part xml */ public function seeResponseContains(string $text): void { $this->assertStringContainsString($text, $this->connectionModule->_getResponseContent(), 'REST response contains'); } /** * Checks whether last response do not contain text. * * @part json * @part xml */ public function dontSeeResponseContains(string $text): void { $this->assertStringNotContainsString($text, $this->connectionModule->_getResponseContent(), 'REST response contains'); } /** * Checks whether the last JSON response contains provided array. * The response is converted to array with json_decode($response, true) * Thus, JSON is represented by associative array. * This method matches that response array contains provided array. * * Examples: * * ``` php * seeResponseContainsJson(array('name' => 'john')); * * // response {user: john, profile: { email: john@gmail.com }} * $I->seeResponseContainsJson(array('email' => 'john@gmail.com')); * * ``` * * This method recursively checks if one array can be found inside of another. * * @part json */ public function seeResponseContainsJson(array $json = []): void { Assert::assertThat( $this->connectionModule->_getResponseContent(), new JsonContains($json) ); } /** * Checks whether last response matches the supplied json schema (https://json-schema.org/) * Supply schema as json string. * * Examples: * * ``` php * seeResponseIsValidOnJsonSchemaString('{"type": "object"}'); * * // response {"name": "john", "age": 20} * $schema = [ * "properties" => [ * "age" => [ * "type" => "integer", * "minimum" => 18 * ] * ] * ]; * $I->seeResponseIsValidOnJsonSchemaString(json_encode($schema)); * * ``` * * @part json */ public function seeResponseIsValidOnJsonSchemaString(string $schema): void { $responseContent = $this->connectionModule->_getResponseContent(); Assert::assertNotEquals('', $responseContent, 'response is empty'); $responseObject = $this->decodeAndValidateJson($responseContent); Assert::assertNotEquals('', $schema, 'schema is empty'); $schemaObject = $this->decodeAndValidateJson($schema, "Invalid schema json: %s. System message: %s."); $validator = new JsonSchemaValidator(); $validator->validate($responseObject, $schemaObject, JsonConstraint::CHECK_MODE_VALIDATE_SCHEMA); $outcome = $validator->isValid(); $error = ''; if (!$outcome) { $errors = $validator->getErrors(); $error = array_shift($errors)["message"]; } Assert::assertTrue( $outcome, $error ); } /** * Checks whether last response matches the supplied json schema (https://json-schema.org/) * Supply schema as relative file path in your project directory or an absolute path * * @part json * @see codecept_absolute_path() */ public function seeResponseIsValidOnJsonSchema(string $schemaFilename): void { $file = codecept_absolute_path($schemaFilename); if (!file_exists($file)) { throw new ModuleException(__CLASS__, sprintf('File %s does not exist', $file)); } $this->seeResponseIsValidOnJsonSchemaString(file_get_contents($file)); } /** * Converts string to json and asserts that no error occurred while decoding. * * @param string $jsonString the json encoded string * @param string $errorFormat optional string for custom sprintf format */ protected function decodeAndValidateJson(string $jsonString, string $errorFormat = "Invalid json: %s. System message: %s.") { $json = json_decode($jsonString); $errorCode = json_last_error(); $errorMessage = json_last_error_msg(); Assert::assertSame( JSON_ERROR_NONE, $errorCode, sprintf( $errorFormat, $jsonString, $errorMessage ) ); return $json; } /** * Returns current response so that it can be used in next scenario steps. * * Example: * * ``` php * grabResponse(); * $I->sendPut('/user', array('id' => $user_id, 'name' => 'davert')); * ``` * * @part json * @part xml */ public function grabResponse(): string { return $this->connectionModule->_getResponseContent(); } /** * See [#jsonpath](#jsonpath) for general info on JSONPath. * Even for a single value an array is returned. * Example: * * ``` php * grabDataFromResponseByJsonPath('$..users[0].id'); * $I->sendPut('/user', array('id' => $firstUserId[0], 'name' => 'davert')); * ``` * * @return array Array of matching items * @throws Exception * @part json */ public function grabDataFromResponseByJsonPath(string $jsonPath): array { return (new JsonArray($this->connectionModule->_getResponseContent()))->filterByJsonPath($jsonPath); } /** * Checks if json structure in response matches the xpath provided. * JSON is not supposed to be checked against XPath, yet it can be converted to xml and used with XPath. * This assertion allows you to check the structure of response json. * * * ```json * { "store": { * "book": [ * { "category": "reference", * "author": "Nigel Rees", * "title": "Sayings of the Century", * "price": 8.95 * }, * { "category": "fiction", * "author": "Evelyn Waugh", * "title": "Sword of Honour", * "price": 12.99 * } * ], * "bicycle": { * "color": "red", * "price": 19.95 * } * } * } * ``` * * ```php * seeResponseJsonMatchesXpath('//store/book/author'); * // first book in store has author * $I->seeResponseJsonMatchesXpath('//store/book[1]/author'); * // at least one item in store has price * $I->seeResponseJsonMatchesXpath('/store//price'); * ``` * @part json */ public function seeResponseJsonMatchesXpath(string $xPath): void { $response = $this->connectionModule->_getResponseContent(); $this->assertGreaterThan( 0, (new JsonArray($response))->filterByXPath($xPath)->length, "Received JSON did not match the XPath `{$xPath}`.\nJson Response: \n" . $response ); } /** * Checks if applying xpath to json structure in response matches the expected result. * JSON is not supposed to be checked against XPath, yet it can be converted to xml and used with XPath. * This assertion allows you to check the structure of response json. * * * ```json * { "store": { * "book": [ * { "category": "reference", * "author": "Nigel Rees", * "title": "Sayings of the Century", * "price": 8.95 * }, * { "category": "fiction", * "author": "Evelyn Waugh", * "title": "Sword of Honour", * "price": 12.99 * } * ], * "bicycle": { * "color": "red", * "price": 19.95 * } * } * } * ``` * * ```php * seeResponseJsonXpathEvaluatesTo('count(//store/book/author) > 0', true); * // count the number of books written by given author is 5 * $I->seeResponseJsonMatchesXpath("//author[text() = 'Nigel Rees']", 1.0); * ``` * @part json */ public function seeResponseJsonXpathEvaluatesTo(string $xPath, $expected): void { $response = $this->connectionModule->_getResponseContent(); $this->assertEquals( $expected, (new JsonArray($response))->evaluateXPath($xPath), "Received JSON did not evualated XPath `{$xPath}` as expected.\nJson Response: \n" . $response ); } /** * Opposite to seeResponseJsonXpathEvaluatesTo * * @part json */ public function dontSeeResponseJsonXpathEvaluatesTo(string $xPath, $expected): void { $response = $this->connectionModule->_getResponseContent(); $this->assertNotEquals( $expected, (new JsonArray($response))->evaluateXPath($xPath), "Received JSON did not evualated XPath `{$xPath}` as expected.\nJson Response: \n" . $response ); } /** * Opposite to seeResponseJsonMatchesXpath * * @part json */ public function dontSeeResponseJsonMatchesXpath(string $xPath): void { $response = $this->connectionModule->_getResponseContent(); $this->assertSame( 0, (new JsonArray($response))->filterByXPath($xPath)->length, "Received JSON matched the XPath `{$xPath}`.\nJson Response: \n" . $response ); } /** * See [#jsonpath](#jsonpath) for general info on JSONPath. * Checks if JSON structure in response matches JSONPath. * * ```json * { "store": { * "book": [ * { "category": "reference", * "author": "Nigel Rees", * "title": "Sayings of the Century", * "price": 8.95 * }, * { "category": "fiction", * "author": "Evelyn Waugh", * "title": "Sword of Honour", * "price": 12.99 * } * ], * "bicycle": { * "color": "red", * "price": 19.95 * } * } * } * ``` * * ```php * seeResponseJsonMatchesJsonPath('$.store.book[*].author'); * // first book in store has author * $I->seeResponseJsonMatchesJsonPath('$.store.book[0].author'); * // at least one item in store has price * $I->seeResponseJsonMatchesJsonPath('$.store..price'); * ``` * * @part json */ public function seeResponseJsonMatchesJsonPath(string $jsonPath): void { $response = $this->connectionModule->_getResponseContent(); $this->assertNotEmpty( (new JsonArray($response))->filterByJsonPath($jsonPath), "Received JSON did not match the JsonPath `{$jsonPath}`.\nJson Response: \n" . $response ); } /** * See [#jsonpath](#jsonpath) for general info on JSONPath. * Opposite to [`seeResponseJsonMatchesJsonPath()`](#seeResponseJsonMatchesJsonPath) * * @part json */ public function dontSeeResponseJsonMatchesJsonPath(string $jsonPath): void { $response = $this->connectionModule->_getResponseContent(); $this->assertEmpty( (new JsonArray($response))->filterByJsonPath($jsonPath), "Received JSON matched the JsonPath `{$jsonPath}`.\nJson Response: \n" . $response ); } /** * Opposite to seeResponseContainsJson * * @part json */ public function dontSeeResponseContainsJson(array $json = []): void { $jsonResponseArray = new JsonArray($this->connectionModule->_getResponseContent()); $this->assertFalse( $jsonResponseArray->containsArray($json), "Response JSON contains provided JSON\n" . "- " . var_export($json, true) . "\n" . "+ " . var_export($jsonResponseArray->toArray(), true) ); } /** * Checks that JSON matches provided types. * In case you don't know the actual values of JSON data returned you can match them by type. * It starts the check with a root element. If JSON data is an array it will check all elements of it. * You can specify the path in the json which should be checked with JsonPath * * Basic example: * * ```php * seeResponseMatchesJsonType([ * 'user_id' => 'integer', * 'name' => 'string|null', * 'is_active' => 'boolean' * ]); * * // narrow down matching with JsonPath: * // {"users": [{ "name": "davert"}, {"id": 1}]} * $I->seeResponseMatchesJsonType(['name' => 'string'], '$.users[0]'); * ``` * * You can check if the record contains fields with the data types you expect. * The list of possible data types: * * * string * * integer * * float * * array (json object is array as well) * * boolean * * null * * You can also use nested data type structures, and define multiple types for the same field: * * ```php * seeResponseMatchesJsonType([ * 'user_id' => 'integer|string', // multiple types * 'company' => ['name' => 'string'] * ]); * ``` * * You can also apply filters to check values. Filter can be applied with a `:` char after the type declaration, * or after another filter if you need more than one. * * Here is the list of possible filters: * * * `integer:>{val}` - checks that integer is greater than {val} (works with float and string types too). * * `integer:<{val}` - checks that integer is lower than {val} (works with float and string types too). * * `string:url` - checks that value is valid url. * * `string:date` - checks that value is date in JavaScript format: https://weblog.west-wind.com/posts/2014/Jan/06/JavaScript-JSON-Date-Parsing-and-real-Dates * * `string:email` - checks that value is a valid email according to https://emailregex.com/ * * `string:regex({val})` - checks that string matches a regex provided with {val} * * This is how filters can be used: * * ```php * 'davert@codeception.com'} * $I->seeResponseMatchesJsonType([ * 'user_id' => 'string:>0:<1000', // multiple filters can be used * 'email' => 'string:regex(~\@~)' // we just check that @ char is included * ]); * * // {'user_id': '1'} * $I->seeResponseMatchesJsonType([ * 'user_id' => 'string:>0', // works with strings as well * ]); * ``` * * You can also add custom filters by using `{@link JsonType::addCustomFilter()}`. * See [JsonType reference](https://codeception.com/docs/reference/JsonType). * * @part json * @see JsonType */ public function seeResponseMatchesJsonType(array $jsonType, string $jsonPath = null): void { $jsonArray = new JsonArray($this->connectionModule->_getResponseContent()); if ($jsonPath) { $jsonArray = $jsonArray->filterByJsonPath($jsonPath); } Assert::assertThat($jsonArray, new JsonTypeConstraint($jsonType)); } /** * Opposite to `seeResponseMatchesJsonType`. * * @part json * @param array $jsonType JsonType structure * @see seeResponseMatchesJsonType */ public function dontSeeResponseMatchesJsonType(array $jsonType, string $jsonPath = null): void { $jsonArray = new JsonArray($this->connectionModule->_getResponseContent()); if ($jsonPath) { $jsonArray = $jsonArray->filterByJsonPath($jsonPath); } Assert::assertThat($jsonArray, new JsonTypeConstraint($jsonType, false)); } /** * Checks if response is exactly the same as provided. * * @part json * @part xml */ public function seeResponseEquals($expected): void { $this->assertSame($expected, $this->connectionModule->_getResponseContent()); } /** * Checks response code equals to provided value. * * ```php * seeResponseCodeIs(200); * * // preferred to use \Codeception\Util\HttpCode * $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); * ``` * * @part json * @part xml */ public function seeResponseCodeIs(int $code): void { $this->connectionModule->seeResponseCodeIs($code); } /** * Checks that response code is not equal to provided value. * * ```php * dontSeeResponseCodeIs(200); * * // preferred to use \Codeception\Util\HttpCode * $I->dontSeeResponseCodeIs(\Codeception\Util\HttpCode::OK); * ``` * * @part json * @part xml */ public function dontSeeResponseCodeIs(int $code): void { $this->connectionModule->dontSeeResponseCodeIs($code); } /** * Checks that the response code is 2xx * * @part json * @part xml */ public function seeResponseCodeIsSuccessful(): void { $this->connectionModule->seeResponseCodeIsSuccessful(); } /** * Checks that the response code 3xx * * @part json * @part xml */ public function seeResponseCodeIsRedirection(): void { $this->connectionModule->seeResponseCodeIsRedirection(); } /** * Checks that the response code is 4xx * * @part json * @part xml */ public function seeResponseCodeIsClientError(): void { $this->connectionModule->seeResponseCodeIsClientError(); } /** * Checks that the response code is 5xx * * @part json * @part xml */ public function seeResponseCodeIsServerError(): void { $this->connectionModule->seeResponseCodeIsServerError(); } /** * Checks whether last response was valid XML. * This is done with libxml_get_last_error function. * * @part xml */ public function seeResponseIsXml(): void { libxml_use_internal_errors(true); $doc = simplexml_load_string($this->connectionModule->_getResponseContent()); $num = ''; $title = ''; if ($doc === false) { $error = libxml_get_last_error(); $num = $error->code; $title = trim($error->message); libxml_clear_errors(); } libxml_use_internal_errors(false); Assert::assertNotSame( false, $doc, sprintf('xml decoding error #%s with message "%s".', $num, $title) ); } /** * Checks whether XML response matches XPath * * ```php * seeXmlResponseMatchesXpath('//root/user[@id=1]'); * ``` * @part xml */ public function seeXmlResponseMatchesXpath(string $xPath): void { $xmlStructure = new XmlStructure($this->connectionModule->_getResponseContent()); $this->assertTrue($xmlStructure->matchesXpath($xPath), 'xpath not matched'); } /** * Checks whether XML response does not match XPath * * ```php * dontSeeXmlResponseMatchesXpath('//root/user[@id=1]'); * ``` * @part xml */ public function dontSeeXmlResponseMatchesXpath(string $xPath): void { $structure = new XmlStructure($this->connectionModule->_getResponseContent()); $this->assertFalse($structure->matchesXpath($xPath), 'accidentally matched xpath'); } /** * Finds and returns text contents of element. * Element is matched by either CSS or XPath * * @param mixed $cssOrXPath * @part xml */ public function grabTextContentFromXmlElement($cssOrXPath): string { $el = (new XmlStructure($this->connectionModule->_getResponseContent()))->matchElement($cssOrXPath); return $el->textContent; } /** * Finds and returns attribute of element. * Element is matched by either CSS or XPath * * @part xml */ public function grabAttributeFromXmlElement(string $cssOrXPath, string $attribute): string { $el = (new XmlStructure($this->connectionModule->_getResponseContent()))->matchElement($cssOrXPath); if (!$el->hasAttribute($attribute)) { $this->fail(sprintf("Attribute not found in element matched by '%s'", $cssOrXPath)); } return $el->getAttribute($attribute); } /** * Checks XML response equals provided XML. * Comparison is done by canonicalizing both xml`s. * * Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes). * * @param mixed $xml * @part xml */ public function seeXmlResponseEquals($xml): void { Assert::assertXmlStringEqualsXmlString($this->connectionModule->_getResponseContent(), $xml); } /** * Checks XML response does not equal to provided XML. * Comparison is done by canonicalizing both xml`s. * * Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes). * * @param mixed $xml * @part xml */ public function dontSeeXmlResponseEquals($xml): void { Assert::assertXmlStringNotEqualsXmlString( $this->connectionModule->_getResponseContent(), $xml ); } /** * Checks XML response includes provided XML. * Comparison is done by canonicalizing both xml`s. * Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes). * * Example: * * ``` php * seeXmlResponseIncludes("1"); * ``` * * @param mixed $xml * @part xml */ public function seeXmlResponseIncludes($xml): void { $this->assertStringContainsString( XmlUtils::toXml($xml)->C14N(), XmlUtils::toXml($this->connectionModule->_getResponseContent())->C14N(), "found in XML Response" ); } /** * Checks XML response does not include provided XML. * Comparison is done by canonicalizing both xml`s. * Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes). * * @param mixed $xml * @part xml */ public function dontSeeXmlResponseIncludes($xml): void { $this->assertStringNotContainsString( XmlUtils::toXml($xml)->C14N(), XmlUtils::toXml($this->connectionModule->_getResponseContent())->C14N(), "found in XML Response" ); } /** * Checks if the hash of a binary response is exactly the same as provided. * Parameter can be passed as any hash string supported by `hash()`, with an * optional second parameter to specify the hash type, which defaults to sha1. * * Example: Using sha1 hash key * * ```php * seeBinaryResponseEquals("df589122eac0f6a7bd8795436e692e3675cadc3b"); * ``` * * Example: Using sha1 for a file contents * * ```php * seeBinaryResponseEquals(md5($fileData)); * ``` * Example: Using sha256 hash * * ```php * seeBinaryResponseEquals(hash("sha256", base64_decode($fileData)), 'sha256'); * ``` * * @param string $hash the hashed data response expected * @param string $algo the hash algorithm to use. Default sha1. * @part json * @part xml */ public function seeBinaryResponseEquals(string $hash, string $algo = 'sha1'): void { $responseHash = hash($algo, $this->connectionModule->_getResponseContent()); $this->assertSame($hash, $responseHash); } /** * Checks if the hash of a binary response is not the same as provided. * * ```php * dontSeeBinaryResponseEquals("8c90748342f19b195b9c6b4eff742ded"); * ``` * Opposite to `seeBinaryResponseEquals` * * @param string $hash the hashed data response expected * @param string $algo the hash algorithm to use. Default md5. * @part json * @part xml */ public function dontSeeBinaryResponseEquals(string $hash, string $algo = 'sha1'): void { $responseHash = hash($algo, $this->connectionModule->_getResponseContent()); $this->assertNotEquals($hash, $responseHash); } /** * Prevents automatic redirects to be followed by the client * * ```php * stopFollowingRedirects(); * ``` * * @part xml * @part json */ public function stopFollowingRedirects(): void { $this->client->followRedirects(false); } /** * Enables automatic redirects to be followed by the client * * ```php * startFollowingRedirects(); * ``` * * @part xml * @part json */ public function startFollowingRedirects(): void { $this->client->followRedirects(true); } /** * Sets SERVER parameters valid for all next requests. * this will remove old ones. * * ```php * $I->setServerParameters([]); * ``` */ public function setServerParameters(array $params): void { $this->client->setServerParameters($params); } /** * Sets SERVER parameter valid for all next requests. * * ```php * $I->haveServerParameter('name', 'value'); * ``` */ public function haveServerParameter($name, $value): void { $this->client->setServerParameter($name, $value); } } 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. */ protected function matches($jsonArray): bool { if ($jsonArray instanceof JsonArray) { $jsonArray = $jsonArray->toArray(); } $matched = (new JsonTypeUtil($jsonArray))->matches($this->jsonType); if ($this->match) { if ($matched !== true) { throw new ExpectationFailedException($matched); } } elseif ($matched === true) { $jsonArray = json_encode($jsonArray, JSON_THROW_ON_ERROR); throw new ExpectationFailedException('Unexpectedly response matched: ' . $jsonArray); } return true; } /** * Returns a string representation of the constraint. */ public function toString(): string { //unused return ''; } protected function failureDescription($other): string { //unused return ''; } } 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. */ protected function matches($other): bool { $jsonResponseArray = new JsonArray($other); if (!is_array($jsonResponseArray->toArray())) { throw new AssertionFailedError('JSON response is not an array: ' . $other); } $jsonArrayContainsArray = $jsonResponseArray->containsArray($this->expected); if ($jsonArrayContainsArray) { return true; } $comparator = new ArrayComparator(); $comparator->setFactory(new Factory()); try { $comparator->assertEquals($this->expected, $jsonResponseArray->toArray()); } catch (ComparisonFailure $failure) { throw new ExpectationFailedException( "Response JSON does not contain the provided JSON\n", $failure ); } return false; } /** * Returns a string representation of the constraint. */ public function toString(): string { //unused return ''; } protected function failureDescription($other): string { //unused return ''; } } 'davert', 'id' => 1]); * $jsonType->matches([ * 'name' => 'string:!empty', * 'id' => 'integer:>0|string:>0', * ]); // => true * * $jsonType->matches([ * 'id' => 'string', * ]); // => `id: 1` is not of type string * ``` * * Class JsonType * @package Codeception\Util */ class JsonType { /** * @var array|JsonArray */ protected $jsonArray; protected static array $customFilters = []; /** * Creates instance of JsonType * Pass an array or `\Codeception\Util\JsonArray` with data. * If non-associative array is passed - the very first element of it will be used for matching. * * @param $jsonArray array|JsonArray */ public function __construct($jsonArray) { if ($jsonArray instanceof JsonArray) { $jsonArray = $jsonArray->toArray(); } $this->jsonArray = $jsonArray; } /** * Adds custom filter to JsonType list. * You should specify a name and parameters of a filter. * * Example: * * ```php * use it as 'string:slug' * * // add custom function to matcher with `len($val)` syntax * // parameter matching patterns should be valid regex and start with `/` char * JsonType::addCustomFilter('/len\((.*?)\)/', function($value, $len) { * return strlen($value) == $len; * }); * // use it as 'string:len(5)' * ``` */ public static function addCustomFilter(string $name, callable $callable): void { static::$customFilters[$name] = $callable; } /** * Removes all custom filters */ public static function cleanCustomFilters(): void { static::$customFilters = []; } /** * Checks data against passed JsonType. * If matching fails function returns a string with a message describing failure. * On success returns `true`. */ public function matches(array $jsonType): string|bool { if (array_key_exists(0, $this->jsonArray) && is_array($this->jsonArray[0])) { // a list of items $msg = ''; foreach ($this->jsonArray as $singleJsonArray) { $res = $this->typeComparison($singleJsonArray, $jsonType); if ($res !== true) { $msg .= "\n" . $res; } } if ($msg !== '') { return $msg; } return true; } return $this->typeComparison($this->jsonArray, $jsonType); } protected function typeComparison(array $data, array $jsonType): string|bool { foreach ($jsonType as $key => $type) { if (!array_key_exists($key, $data)) { return sprintf("Key `%s` doesn't exist in ", $key) . json_encode($data, JSON_THROW_ON_ERROR); } if (is_array($jsonType[$key])) { $message = $this->typeComparison($data[$key], $jsonType[$key]); if (is_string($message)) { return $message; } continue; } $regexMatcher = '/:regex\((((\()|(\{)|(\[)|(<)|(.)).*?(?(3)\)|(?(4)\}|(?(5)\]|(?(6)>|\7)))))\)/'; $regexes = []; // Match the string ':regex(' and any characters until a ending regex delimiter followed by character ')' // Place the 'any character' + delimiter matches in to an array. preg_match_all($regexMatcher, $type, $regexes); // Do the same match as above, but replace the the 'any character' + delimiter with a place holder ($${count}). $filterType = preg_replace_callback($regexMatcher, function (): string { static $count = 0; return ':regex($$' . $count++ . ')'; }, $type); $matchTypes = preg_split("#(?![^]\(]*\))\|#", $filterType); $matched = false; $currentType = strtolower(gettype($data[$key])); if ($currentType === 'double') { $currentType = 'float'; } foreach ($matchTypes as $matchType) { $filters = preg_split("#(?![^]\(]*\))\:#", $matchType); $expectedType = strtolower(trim(array_shift($filters))); if ($expectedType !== $currentType) { continue; } $matched = true; foreach ($filters as $filter) { // Fill regex pattern back into the filter. $filter = preg_replace_callback('#\$\$\d+#', function ($m) use ($regexes) { $pos = (int)substr($m[0], 2); return $regexes[1][$pos]; }, $filter); $matched = $matched && $this->matchFilter($filter, (string)$data[$key]); } if ($matched) { break; } } if (!$matched) { return sprintf("`$key: %s` is of type `$type`", var_export($data[$key], true)); } } return true; } protected function matchFilter(string $filter, string $value) { $filter = trim($filter); if (str_starts_with($filter, '!')) { return !$this->matchFilter(substr($filter, 1), $value); } // apply custom filters foreach (static::$customFilters as $customFilter => $callable) { if (str_starts_with($customFilter, '/') && preg_match($customFilter, $filter, $matches)) { array_shift($matches); return call_user_func_array($callable, array_merge([$value], $matches)); } if ($customFilter == $filter) { return $callable($value); } } if (str_starts_with($filter, '=')) { return $value === substr($filter, 1); } if ($filter === 'url') { return filter_var($value, FILTER_VALIDATE_URL); } if ($filter === 'date') { return preg_match( '#^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|(\+|-)([\d|:]*))?$#', $value ); } if ($filter === 'email') { // from https://emailregex.com/ // @codingStandardsIgnoreStart return preg_match('#^(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4]\d)|(?:1\d{2})|(?:[1-9]?\d))(?:\.(?:(?:25[0-5])|(?:2[0-4]\d)|(?:1\d{2})|(?:[1-9]?\d))){3}))\]))$#iD', $value); // @codingStandardsIgnoreEnd } if ($filter === 'empty') { return empty($value); } if (preg_match('#^regex\((.*?)\)$#', $filter, $matches)) { return preg_match($matches[1], $value); } if (preg_match('#^>=(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value >= (float)$matches[1]; } if (preg_match('#^<=(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value <= (float)$matches[1]; } if (preg_match('#^>(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value > (float)$matches[1]; } if (preg_match('#^<(-?[\d\.]+)$#', $filter, $matches)) { return (float)$value < (float)$matches[1]; } return false; } } haystack = $haystack; } public function getHaystack(): array { return $this->haystack; } public function containsArray(array $needle): bool { return $needle == $this->arrayIntersectRecursive($needle, $this->haystack); } /** * @return array|bool * @author tiger.seo@gmail.com * @link https://www.php.net/manual/en/function.array-intersect-assoc.php#39822 * * @author nleippe@integr8ted.com */ private function arrayIntersectRecursive(mixed $arr1, mixed $arr2): bool|array|null { if (!is_array($arr1) || !is_array($arr2)) { return false; } // if it is not an associative array we do not compare keys if ($this->arrayIsSequential($arr1) && $this->arrayIsSequential($arr2)) { return $this->sequentialArrayIntersect($arr1, $arr2); } return $this->associativeArrayIntersect($arr1, $arr2); } /** * This array has sequential keys? */ private function arrayIsSequential(array $array): bool { return array_keys($array) === range(0, count($array) - 1); } private function sequentialArrayIntersect(array $arr1, array $arr2): array { $ret = []; // Do not match the same item of $arr2 against multiple items of $arr1 $matchedKeys = []; foreach ($arr1 as $key1 => $value1) { foreach ($arr2 as $key2 => $value2) { if (isset($matchedKeys[$key2])) { continue; } $return = $this->arrayIntersectRecursive($value1, $value2); if ($return !== false && $return == $value1) { $ret[$key1] = $return; $matchedKeys[$key2] = true; break; } if ($this->isEqualValue($value1, $value2)) { $ret[$key1] = $value1; $matchedKeys[$key2] = true; break; } } } return $ret; } /** * @return array|bool|null */ private function associativeArrayIntersect(array $arr1, array $arr2): bool|array|null { $commonKeys = array_intersect(array_keys($arr1), array_keys($arr2)); $ret = []; foreach ($commonKeys as $key) { $return = $this->arrayIntersectRecursive($arr1[$key], $arr2[$key]); if ($return !== false) { $ret[$key] = $return; continue; } if ($this->isEqualValue($arr1[$key], $arr2[$key])) { $ret[$key] = $arr1[$key]; } } if (empty($commonKeys)) { foreach ($arr2 as $arr) { $return = $this->arrayIntersectRecursive($arr1, $arr); if ($return && $return == $arr1) { return $return; } } } if (count($ret) < min(count($arr1), count($arr2))) { return null; } return $ret; } private function isEqualValue($val1, $val2): bool { if (is_numeric($val1)) { $val1 = (string)$val1; } if (is_numeric($val2)) { $val2 = (string)$val2; } return $val1 === $val2; } } jsonArray = $jsonDecode; if (JSON_ERROR_NONE !== json_last_error()) { throw new InvalidArgumentException( sprintf( "Invalid json: %s. System message: %s.", $jsonString, json_last_error_msg() ), json_last_error() ); } } public function toXml(): DOMDocument { if ($this->jsonXml) { return $this->jsonXml; } $root = 'root'; $jsonArray = $this->jsonArray; if (count($jsonArray) == 1) { $value = reset($jsonArray); if (is_array($value)) { $root = key($jsonArray); $jsonArray = $value; } } $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = false; $root = $dom->createElement($root); $dom->appendChild($root); $this->arrayToXml($dom, $root, $jsonArray); $this->jsonXml = $dom; return $dom; } public function toArray(): array { return $this->jsonArray; } public function filterByXPath(string $xPath): DOMNodeList|false { $path = new DOMXPath($this->toXml()); return $path->query($xPath); } public function evaluateXPath(string $xPath): mixed { $path = new DOMXPath($this->toXml()); return $path->evaluate($xPath); } public function filterByJsonPath(string $jsonPath): array { if (!class_exists(JSONPath::class)) { throw new Exception('JSONPath library not installed. Please add `softcreatr/jsonpath` to composer.json'); } return (new JSONPath($this->jsonArray))->find($jsonPath)->getData(); } public function getXmlString(): string|bool { return $this->toXml()->saveXML(); } public function containsArray(array $needle): bool { return (new ArrayContainsComparator($this->jsonArray))->containsArray($needle); } private function arrayToXml(DOMDocument $doc, DOMNode $node, array $array): void { foreach ($array as $key => $value) { if (is_numeric($key)) { $subNode = $doc->createElement($node->nodeName); $node->parentNode->appendChild($subNode); } else { try { $subNode = $doc->createElement($key); } catch (Exception) { $key = $this->getValidTagNameForInvalidKey($key); $subNode = $doc->createElement($key); } $node->appendChild($subNode); } if (is_array($value)) { $this->arrayToXml($doc, $subNode, $value); } else { $this->setValue($subNode, $value); } } } private function setValue($subNode, $value) { switch(gettype($value)) { case 'boolean': $subNode->nodeValue = $value?'true':'false'; $subNode->setAttribute('type', 'boolean'); break; case 'integer': case 'double': $subNode->nodeValue = (string) $value; $subNode->setAttribute('type', 'number'); break; case 'NULL': $subNode->nodeValue = ''; $subNode->setAttribute('type', 'null'); break; default: $subNode->nodeValue = htmlspecialchars((string) $value); $subNode->setAttribute('type', 'string'); break; } } private function getValidTagNameForInvalidKey($key) { static $map = []; if (!isset($map[$key])) { $tagName = 'invalidTag' . (count($map) + 1); $map[$key] = $tagName; codecept_debug($tagName . ' is "' . $key . '"'); } return $map[$key]; } } queueDriver->openConnection($this->config); } /** * Provide and override for the config settings and allow custom settings depending on the service being used. */ protected function validateConfig(): void { $this->queueDriver = $this->createQueueDriver(); $this->requiredFields = $this->queueDriver->getRequiredConfig(); $this->config = array_merge($this->queueDriver->getDefaultConfig(), $this->config); parent::validateConfig(); } /** * @throws ModuleConfigException */ protected function createQueueDriver(): \Codeception\Lib\Interfaces\Queue { switch ($this->config['type']) { case 'aws': case 'sqs': case 'aws_sqs': return new AmazonSQS(); case 'iron': case 'iron_mq': return new Iron(); case 'beanstalk': case 'beanstalkd': case 'beanstalkq': // Account for different versions of Pheanstalk. if (interface_exists(PheanstalkJobIdInterface::class)) { return new Pheanstalk4(); } else { return new Beanstalk(); } default: throw new ModuleConfigException( __CLASS__, "Unknown queue type {$this->config['type']}; Supported queue types are: aws, iron, beanstalk" ); } } // ----------- SEARCH METHODS BELOW HERE ------------------------// /** * Check if a queue/tube exists on the queueing server. * * ```php * seeQueueExists('default'); * ``` * * @param string $queue Queue name */ public function seeQueueExists(string $queue): void { $this->assertContains($queue, $this->queueDriver->getQueues()); } /** * Check if a queue/tube does NOT exist on the queueing server. * * ```php * dontSeeQueueExists('default'); * ``` * * @param string $queue Queue name */ public function dontSeeQueueExists(string $queue): void { $this->assertNotContains($queue, $this->queueDriver->getQueues()); } /** * Check if a queue/tube is empty of all messages * * ```php * seeEmptyQueue('default'); * ``` * * @param string $queue Queue name */ public function seeEmptyQueue(string $queue): void { $this->assertEquals(0, $this->queueDriver->getMessagesCurrentCountOnQueue($queue)); } /** * Check if a queue/tube is NOT empty of all messages * * ```php * dontSeeEmptyQueue('default'); * ``` * * @param string $queue Queue name */ public function dontSeeEmptyQueue(string $queue): void { $this->assertNotEquals(0, $this->queueDriver->getMessagesCurrentCountOnQueue($queue)); } /** * Check if a queue/tube has a given current number of messages * * ```php * seeQueueHasCurrentCount('default', 10); * ``` * * @param string $queue Queue name * @param int $expected Number of messages expected */ public function seeQueueHasCurrentCount(string $queue, int $expected): void { $this->assertEquals($expected, $this->queueDriver->getMessagesCurrentCountOnQueue($queue)); } /** * Check if a queue/tube does NOT have a given current number of messages * * ```php * dontSeeQueueHasCurrentCount('default', 10); * ``` * * @param string $queue Queue name * @param int $expected Number of messages expected */ public function dontSeeQueueHasCurrentCount(string $queue, int $expected): void { $this->assertNotEquals($expected, $this->queueDriver->getMessagesCurrentCountOnQueue($queue)); } /** * Check if a queue/tube has a given total number of messages * * ```php * seeQueueHasTotalCount('default', 10); * ``` * * @param string $queue Queue name * @param int $expected Number of messages expected */ public function seeQueueHasTotalCount(string $queue, int $expected): void { $this->assertEquals($expected, $this->queueDriver->getMessagesTotalCountOnQueue($queue)); } /** * Check if a queue/tube does NOT have a given total number of messages * * ```php * dontSeeQueueHasTotalCount('default', 10); * ``` * * @param string $queue Queue name * @param int $expected Number of messages expected */ public function dontSeeQueueHasTotalCount(string $queue, int $expected): void { $this->assertNotEquals($expected, $this->queueDriver->getMessagesTotalCountOnQueue($queue)); } // ----------- UTILITY METHODS BELOW HERE -------------------------// /** * Add a message to a queue/tube * * ```php * addMessageToQueue('this is a messages', 'default'); * ``` * * @param string $message Message Body * @param string $queue Queue name */ public function addMessageToQueue(string $message, string $queue): void { $this->queueDriver->addMessageToQueue($message, $queue); } /** * Clear all messages of the queue/tube * * ```php * clearQueue('default'); * ``` * * @param string $queue Queue name */ public function clearQueue(string $queue): void { $this->queueDriver->clearQueue($queue); } // ----------- GRABBER METHODS BELOW HERE -----------------------// /** * Grabber method to get the list of queues/tubes on the server * * ```php * grabQueues(); * ``` * * @return string[] List of Queues/Tubes */ public function grabQueues(): array { return $this->queueDriver->getQueues(); } /** * Grabber method to get the current number of messages on the queue/tube (pending/ready) * * ```php * grabQueueCurrentCount('default'); * ``` * * @param string $queue Queue name * @return int Count */ public function grabQueueCurrentCount(string $queue) { return $this->queueDriver->getMessagesCurrentCountOnQueue($queue); } /** * Grabber method to get the total number of messages on the queue/tube * * ```php * grabQueueTotalCount('default'); * ``` * * @param string $queue Queue name * @return int Count */ public function grabQueueTotalCount(string $queue) { return $this->queueDriver->getMessagesTotalCountOnQueue($queue); } } $config */ public function openConnection(array $config): void; /** * Post/Put a message on to the queue server * * @param string $message Message Body to be send */ public function addMessageToQueue(string $message, string $queueName): void; /** * Return a list of queues/tubes on the queueing server * * @return string[] Array of Queues */ public function getQueues(): array; /** * Count the current number of messages on the queue. */ public function getMessagesCurrentCountOnQueue(string $queueName): int; /** * Count the total number of messages on the queue. */ public function getMessagesTotalCountOnQueue(string $queueName): int; public function clearQueue(string $queueName): void; /** * @return string[] */ public function getRequiredConfig(): array; /** * @return array */ public function getDefaultConfig(): array; } $config */ public function openConnection(array $config): void { $this->queue = new Pheanstalk($config['host'], $config['port'], $config['timeout']); } /** * Post/Put a message on to the queue server * * @param string $message Message Body to be send */ public function addMessageToQueue(string $message, string $queueName): void { $this->queue->putInTube($queueName, $message); } /** * Count the total number of messages on the queue. */ public function getMessagesTotalCountOnQueue(string $queueName): int { try { return (int)$this->queue->statsTube($queueName)['total-jobs']; } catch (ConnectionException $connectionException) { Assert::fail(sprintf('queue [%s] not found', $queueName)); } } public function clearQueue(string $queueName = 'default'): void { while ($job = $this->queue->reserveFromTube($queueName, 0)) { $this->queue->delete($job); } } /** * Return a list of queues/tubes on the queueing server * * @return string[] Array of Queues */ public function getQueues(): array { return $this->queue->listTubes(); } /** * Count the current number of messages on the queue. */ public function getMessagesCurrentCountOnQueue(string $queueName): int { try { return (int)$this->queue->statsTube($queueName)['current-jobs-ready']; } catch (ConnectionException $e) { Assert::fail(sprintf('queue [%s] not found', $queueName)); } } /** * @return string[] */ public function getRequiredConfig(): array { return ['host']; } /** * @return array */ public function getDefaultConfig(): array { return ['port' => 11300, 'timeout' => 90]; } } queue = Pheanstalk::create($config['host'], $config['port'], $config['timeout']); } /** * Post/Put a message on to the queue server * * @param string $message Message Body to be send */ public function addMessageToQueue(string $message, string $queueName): void { $this->queue->useTube($queueName); $this->queue->put($message); } /** * Return a list of queues/tubes on the queueing server * * @return string[] Array of Queues */ public function getQueues(): array { return $this->queue->listTubes(); } /** * Count the current number of messages on the queue. */ public function getMessagesCurrentCountOnQueue(string $queueName): int { $response = $this->queue->statsTube($queueName); return $response->getResponseName() !== ResponseInterface::RESPONSE_NOT_FOUND ? (int)$response['current-jobs-ready'] : 0; } /** * Count the total number of messages on the queue. */ public function getMessagesTotalCountOnQueue(string $queueName): int { $response = $this->queue->statsTube($queueName); return $response->getResponseName() !== ResponseInterface::RESPONSE_NOT_FOUND ? (int)$response['total-jobs'] : 0; } public function clearQueue(string $queueName): void { $this->queue->useTube($queueName); while (null !== $job = $this->queue->peekBuried()) { $this->queue->delete($job); } while (null !== $job = $this->queue->peekDelayed()) { $this->queue->delete($job); } while (null !== $job = $this->queue->peekReady()) { $this->queue->delete($job); } } /** * @return string[] */ public function getRequiredConfig(): array { return []; } /** * @return array */ public function getDefaultConfig(): array { return ['port' => 11300, 'timeout' => 90, 'host' => 'localhost']; } } queue = new IronMQ( [ "token" => $config['token'], "project_id" => $config['project'], "host" => $config['host'] ] ); } /** * Post/Put a message on to the queue server * * @param string $message Message Body to be send */ public function addMessageToQueue(string $message, string $queueName): void { $this->queue->postMessage($queueName, $message); } /** * Return a list of queues/tubes on the queueing server * * @return string[] Array of Queues */ public function getQueues(): array { // Format the output to suit $queues = []; foreach ($this->queue->getQueues() as $queue) { $queues[] = $queue->name; } return $queues; } /** * Count the current number of messages on the queue. */ public function getMessagesCurrentCountOnQueue(string $queueName): int { try { return (int)$this->queue->getQueue($queueName)->size; } catch (Http_Exception $ex) { Assert::fail("queue [$queueName] not found"); } } /** * Count the total number of messages on the queue. */ public function getMessagesTotalCountOnQueue(string $queueName): int { try { return (int)$this->queue->getQueue($queueName)->total_messages; } catch (Http_Exception $e) { Assert::fail("queue [$queueName] not found"); } } public function clearQueue(string $queueName): void { try { $this->queue->clearQueue($queueName); } catch (Http_Exception $ex) { Assert::fail("queue [$queueName] not found"); } } /** * @return string[] */ public function getRequiredConfig(): array { return ['host', 'token', 'project']; } /** * @return array */ public function getDefaultConfig(): array { return []; } } $config['region'], ]; if (! empty($config['key']) && ! empty($config['secret'])) { $params['credentials'] = new Credentials($config['key'], $config['secret']); } if (! empty($config['profile'])) { $params['profile'] = $config['profile']; } if (! empty($config['version'])) { $params['version'] = $config['version']; } if (! empty($config['endpoint'])) { $params['endpoint'] = $config['endpoint']; } $this->queue = new SqsClient($params); } /** * Post/Put a message on to the queue server * * @param string $message Message Body to be send */ public function addMessageToQueue(string $message, string $queueName): void { $this->queue->sendMessage( [ 'QueueUrl' => $this->getQueueURL($queueName), 'MessageBody' => $message, ] ); } /** * Return a list of queues/tubes on the queueing server * * @return string[] Array of Queues */ public function getQueues(): array { $queueNames = []; $queues = $this->queue->listQueues(['QueueNamePrefix' => ''])->get('QueueUrls'); foreach ($queues as $queue) { $tokens = explode('/', $queue); $queueNames[] = $tokens[count($tokens) - 1]; } return $queueNames; } /** * Count the current number of messages on the queue. */ public function getMessagesCurrentCountOnQueue(string $queueName): int { return (int)$this->queue->getQueueAttributes( [ 'QueueUrl' => $this->getQueueURL($queueName), 'AttributeNames' => ['ApproximateNumberOfMessages'], ] )->get('Attributes')['ApproximateNumberOfMessages']; } /** * Count the total number of messages on the queue. */ public function getMessagesTotalCountOnQueue(string $queueName): int { return (int)$this->queue->getQueueAttributes( [ 'QueueUrl' => $this->getQueueURL($queueName), 'AttributeNames' => ['ApproximateNumberOfMessages'], ] )->get('Attributes')['ApproximateNumberOfMessages']; } public function clearQueue(string $queueName): void { $queueURL = $this->getQueueURL($queueName); while (true) { $res = $this->queue->receiveMessage(['QueueUrl' => $queueURL]); if (!$res->getPath('Messages')) { return; } foreach ($res->getPath('Messages') as $msg) { $this->queue->deleteMessage( [ 'QueueUrl' => $queueURL, 'ReceiptHandle' => $msg['ReceiptHandle'] ] ); } } } /** * Get the queue/tube URL from the queue name (AWS function only) */ private function getQueueURL(string $queueName): string { $queues = $this->queue->listQueues(['QueueNamePrefix' => ''])->get('QueueUrls'); foreach ($queues as $queueURL) { $tokens = explode('/', $queueURL); if (strtolower($queueName) === strtolower($tokens[count($tokens) - 1])) { return $queueURL; } } throw new TestRuntimeException('queue [' . $queueName . '] not found'); } /** * @return string[] */ public function getRequiredConfig(): array { return ['region']; } /** * @return array */ public function getDefaultConfig(): array { return []; } } */ protected array $config = [ 'headers' => [], 'verify' => false, 'expect' => false, 'timeout' => 30, 'curl' => [], 'refresh_max_interval' => 10, 'handler' => 'curl', 'middleware' => null, // required defaults (not recommended to change) 'allow_redirects' => false, 'http_errors' => false, 'cookies' => true, ]; /** * @var string[] */ protected array $guzzleConfigFields = [ 'auth', 'proxy', 'verify', 'cert', 'query', 'ssl_key', 'proxy', 'expect', 'version', 'timeout', 'connect_timeout' ]; /** * @var Guzzle */ public ?AbstractBrowser $client = null; public ?GuzzleClient $guzzle = null; public function _initialize() { $this->_initializeSession(); } public function _before(TestInterface $test) { if (!$this->client) { $this->client = new Guzzle(); } $this->_prepareSession(); } public function _getUrl() { return $this->config['url']; } /** * Alias to `haveHttpHeader` */ public function setHeader(string $name, string $value): void { $this->haveHttpHeader($name, $value); } public function amHttpAuthenticated($username, $password): void { $this->client->setAuth($username, $password); } public function amOnUrl($url): void { $host = Uri::retrieveHost($url); $config = $this->config; $config['url'] = $host; $this->_reconfigure($config); $page = substr($url, strlen($host)); if ($page === '') { $page = '/'; } $this->debugSection('Host', $host); $this->amOnPage($page); } public function amOnSubdomain($subdomain): void { $url = $this->config['url']; $url = preg_replace('#(https?://)(.*\.)(.*\.)#', "$1$3", $url); // removing current subdomain $url = preg_replace('#(https?://)(.*)#', sprintf('$1%s.$2', $subdomain), $url); // inserting new $config = $this->config; $config['url'] = $url; $this->_reconfigure($config); } protected function onReconfigure() { $this->_prepareSession(); } /** * Low-level API method. * If Codeception commands are not enough, use [Guzzle HTTP Client](https://guzzlephp.org/) methods directly * * Example: * * ``` php * executeInGuzzle(function (\GuzzleHttp\Client $client) { * $client->get('/get', ['query' => ['foo' => 'bar']]); * }); * ``` * * It is not recommended to use this command on a regular basis. * If Codeception lacks important Guzzle Client methods, implement them and submit patches. * * @return mixed */ public function executeInGuzzle(Closure $function) { return $function($this->guzzle); } /** * @return int|string */ public function _getResponseCode() { return $this->getResponseStatusCode(); } public function _initializeSession(): void { // independent sessions need independent cookies $this->client = new Guzzle(); $this->_prepareSession(); } public function _prepareSession(): void { $defaults = array_intersect_key($this->config, array_flip($this->guzzleConfigFields)); $curlOptions = []; foreach ($this->config['curl'] as $key => $val) { if (defined($key)) { $curlOptions[constant($key)] = $val; } } $this->headers = $this->config['headers']; $this->setCookiesFromOptions(); $defaults['base_uri'] = $this->config['url']; $defaults['curl'] = $curlOptions; $handler = Guzzle::createHandler($this->config['handler']); if ($handler && is_array($this->config['middleware'])) { foreach ($this->config['middleware'] as $middleware) { $handler->push($middleware); } } $defaults['handler'] = $handler; $this->guzzle = new GuzzleClient($defaults); $this->client->setRefreshMaxInterval($this->config['refresh_max_interval']); $this->client->setClient($this->guzzle); } public function _backupSession(): array { return [ 'client' => $this->client, 'guzzle' => $this->guzzle, 'crawler' => $this->crawler, 'headers' => $this->headers, ]; } public function _loadSession($session): void { foreach ($session as $key => $val) { $this->$key = $val; } } public function _closeSession($session = null): void { unset($session); } } false, 'headers' => [], ]; protected int $refreshMaxInterval = 0; protected ?AwsCredentials $awsCredentials = null; protected ?AwsSignatureV4 $awsSignature = null; protected ?GuzzleClient $client = null; /** * Sets the maximum allowable timeout interval for a meta tag refresh to * automatically redirect a request. * * A meta tag detected with an interval equal to or greater than $seconds * would not result in a redirect. A meta tag without a specified interval * or one with a value less than $seconds would result in the client * automatically redirecting to the specified URL * * @param int $seconds Number of seconds */ public function setRefreshMaxInterval(int $seconds): void { $this->refreshMaxInterval = $seconds; } public function setClient(GuzzleClient $guzzleClient): void { $this->client = $guzzleClient; } /** * Sets the request header to the passed value. The header will be * sent along with the next request. * * Passing an empty value clears the header, which is the equivalent * of calling deleteHeader. * * @param string $name the name of the header * @param string $value the value of the header */ public function setHeader(string $name, string $value): void { if ($value === '') { $this->deleteHeader($name); } else { $this->requestOptions['headers'][$name] = $value; } } /** * Deletes the header with the passed name from the list of headers * that will be sent with the request. * * @param string $name the name of the header to delete. */ public function deleteHeader(string $name): void { unset($this->requestOptions['headers'][$name]); } public function setAuth(string $username, string $password, string $type = 'basic'): void { if ($username === '') { unset($this->requestOptions['auth']); return; } $this->requestOptions['auth'] = [$username, $password, $type]; } /** * Taken from Mink\BrowserKitDriver */ protected function createResponse(Psr7Response $psr7Response): BrowserKitResponse { $body = (string) $psr7Response->getBody(); $headers = $psr7Response->getHeaders(); $contentType = null; if (isset($headers['Content-Type'])) { $contentType = reset($headers['Content-Type']); } if (!$contentType) { $contentType = 'text/html'; } if (strpos($contentType, 'charset=') === false) { if (preg_match('#]+charset *= *["\']?([a-zA-Z\-0-9]+)#i', $body, $matches)) { $contentType .= ';charset=' . $matches[1]; } $headers['Content-Type'] = [$contentType]; } $status = $psr7Response->getStatusCode(); if ($status < 300 || $status >= 400) { $matches = []; $matchesMeta = preg_match( '#]+http-equiv="refresh" content="\s*(\d*)\s*;\s*url=(.*?)"#i', $body, $matches ); if (!$matchesMeta && isset($headers['Refresh'])) { // match by header preg_match( '#^\s*(\d*)\s*;\s*url=(.*)#i', (string) reset($headers['Refresh']), $matches ); } if ((!empty($matches)) && (empty($matches[1]) || $matches[1] < $this->refreshMaxInterval)) { $uri = new Psr7Uri($this->getAbsoluteUri($matches[2])); $currentUri = new Psr7Uri($this->getHistory()->current()->getUri()); if ($uri->withFragment('') !== $currentUri->withFragment('')) { $status = 302; $headers['Location'] = $matchesMeta ? htmlspecialchars_decode((string) $uri) : (string)$uri; } } } return new BrowserKitResponse($body, $status, $headers); } protected function getAbsoluteUri(string $uri): string { $baseUri = $this->client->getConfig('base_uri'); if (strpos($uri, '://') === false && strpos($uri, '//') !== 0) { if (strpos($uri, '/') === 0) { $baseUriPath = $baseUri->getPath(); if (!empty($baseUriPath) && strpos($uri, (string) $baseUriPath) === 0) { $uri = substr($uri, strlen($baseUriPath)); } return Uri::appendPath((string)$baseUri, $uri); } // relative url if (!$this->getHistory()->isEmpty()) { return Uri::mergeUrls($this->getHistory()->current()->getUri(), $uri); } } return Uri::mergeUrls((string)$baseUri, $uri); } protected function doRequest($request) { /** @var $request BrowserKitRequest **/ $guzzleRequest = new Psr7Request( $request->getMethod(), $request->getUri(), $this->extractHeaders($request), $request->getContent() ); $options = $this->requestOptions; $options['cookies'] = $this->extractCookies($guzzleRequest->getUri()->getHost()); $multipartData = $this->extractMultipartFormData($request); if (!empty($multipartData)) { $options['multipart'] = $multipartData; } $formData = $this->extractFormData($request); if (empty($multipartData) && $formData) { $options['form_params'] = $formData; } try { if (null !== $this->awsCredentials) { $response = $this->client->send($this->awsSignature->signRequest($guzzleRequest, $this->awsCredentials), $options); } else { $response = $this->client->send($guzzleRequest, $options); } } catch (RequestException $exception) { if (!$exception->hasResponse()) { throw $exception; } $response = $exception->getResponse(); } return $this->createResponse($response); } /** * @return array */ protected function extractHeaders(BrowserKitRequest $request): array { $headers = []; $server = $request->getServer(); $contentHeaders = ['Content-Length' => true, 'Content-Md5' => true, 'Content-Type' => true]; foreach ($server as $header => $val) { $header = html_entity_decode(implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $header))))), ENT_NOQUOTES); if (strpos($header, 'Http-') === 0) { $headers[substr($header, 5)] = $val; } elseif (isset($contentHeaders[$header])) { $headers[$header] = $val; } } return $headers; } protected function extractFormData(BrowserKitRequest $browserKitRequest): ?array { if (!in_array(strtoupper($browserKitRequest->getMethod()), ['POST', 'PUT', 'PATCH', 'DELETE'])) { return null; } // guessing if it is a form data $headers = $browserKitRequest->getServer(); // not a form if (isset($headers['HTTP_CONTENT_TYPE']) && $headers['HTTP_CONTENT_TYPE'] !== 'application/x-www-form-urlencoded') { return null; } if ($browserKitRequest->getContent() !== null) { return null; } return $browserKitRequest->getParameters(); } protected function extractMultipartFormData(BrowserKitRequest $browserKitRequest) { if (!in_array(strtoupper($browserKitRequest->getMethod()), ['POST', 'PUT', 'PATCH'])) { return []; } $parts = $this->mapFiles($browserKitRequest->getFiles()); if (empty($parts)) { return []; } foreach ($browserKitRequest->getParameters() as $k => $parameter) { $parts = $this->formatMultipart($parts, $k, $parameter); } return $parts; } protected function formatMultipart($parts, $key, $value) { if (is_array($value)) { foreach ($value as $subKey => $subValue) { $parts = array_merge($this->formatMultipart([], $key.sprintf('[%s]', $subKey), $subValue), $parts); } return $parts; } $parts[] = ['name' => $key, 'contents' => (string) $value]; return $parts; } protected function mapFiles($requestFiles, $arrayName = ''): array { $files = []; foreach ($requestFiles as $name => $info) { if (!empty($arrayName)) { $name = $arrayName . '[' . $name . ']'; } if (is_array($info)) { if (isset($info['tmp_name'])) { if ($info['tmp_name']) { $handle = fopen($info['tmp_name'], 'rb'); $filename = $info['name'] ?? null; $file = [ 'name' => $name, 'contents' => $handle, 'filename' => $filename ]; if (isset($info['type'])) { $file['headers'] = [ 'content-type' => $info['type'] ]; } $files[] = $file; } } else { $files = array_merge($files, $this->mapFiles($info, $name)); } } else { $files[] = [ 'name' => $name, 'contents' => fopen($info, 'rb') ]; } } return $files; } protected function extractCookies($host): GuzzleCookieJar { $jar = []; $cookies = $this->getCookieJar()->all(); foreach ($cookies as $cookie) { $setCookie = SetCookie::fromString((string)$cookie); if (!$setCookie->getDomain()) { $setCookie->setDomain($host); } $jar[] = $setCookie; } return new GuzzleCookieJar(false, $jar); } public static function createHandler($handler): GuzzleHandlerStack { if ($handler instanceof GuzzleHandlerStack) { return $handler; } if ($handler === 'curl') { return GuzzleHandlerStack::create(new CurlHandler()); } if ($handler === 'stream') { return GuzzleHandlerStack::create(new StreamHandler()); } if (is_string($handler) && class_exists($handler)) { return GuzzleHandlerStack::create(new $handler); } if (is_callable($handler)) { return GuzzleHandlerStack::create($handler); } return GuzzleHandlerStack::create(); } public function setAwsAuth($config): void { $this->awsCredentials = new AwsCredentials($config['key'], $config['secret']); $this->awsSignature = new AwsSignatureV4($config['service'], $config['region']); } } */ protected array $config = [ 'host' => 'localhost', 'port' => 11211 ]; /** * Code to run before each test. * * @throws ModuleConfigException */ public function _before(TestInterface $test): void { if (class_exists('\Memcache')) { $this->memcache = new \Memcache; $this->memcache->connect($this->config['host'], $this->config['port']); } elseif (class_exists('\Memcached')) { $this->memcache = new Memcached; $this->memcache->addServer($this->config['host'], $this->config['port']); } else { throw new ModuleConfigException(__CLASS__, 'Memcache classes not loaded'); } } /** * Code to run after each test. */ public function _after(TestInterface $test): void { if (empty($this->memcache)) { return; } $this->memcache->flush(); if (get_class($this->memcache) == 'Memcache') { $this->memcache->close(); } elseif (get_class($this->memcache) == 'Memcached') { $this->memcache->quit(); } } /** * Grabs value from memcached by key. * * Example: * * ``` php * grabValueFromMemcached('users_count'); * ``` */ public function grabValueFromMemcached(string $key): mixed { $value = $this->memcache->get($key); $this->debugSection("Value", $value); return $value; } /** * Checks item in Memcached exists and the same as expected. * * Examples: * * ``` php * seeInMemcached('users_count'); * * // Checks a 'users_count' exists and has the value 200 * $I->seeInMemcached('users_count', 200); * ``` */ public function seeInMemcached(string $key, mixed $value = null): void { $actual = $this->memcache->get($key); $this->debugSection("Value", $actual); if (null === $value) { $this->assertNotFalse($actual, "Cannot find key '{$key}' in Memcached"); } else { $this->assertEquals($value, $actual, "Cannot find key '{$key}' in Memcached with the provided value"); } } /** * Checks item in Memcached doesn't exist or is the same as expected. * * Examples: * * ``` php * dontSeeInMemcached('users_count'); * * // Checks a 'users_count' exists does not exist or its value is not the one provided * $I->dontSeeInMemcached('users_count', 200); * ``` */ public function dontSeeInMemcached(string $key, mixed $value = null): void { $actual = $this->memcache->get($key); $this->debugSection("Value", $actual); if (null === $value) { $this->assertFalse($actual, "The key '{$key}' exists in Memcached"); } elseif (false !== $actual) { $this->assertEquals($value, $actual, "The key '{$key}' exists in Memcached with the provided value"); } } /** * Stores an item `$value` with `$key` on the Memcached server. */ public function haveInMemcached(string $key, mixed $value, int $expiration = 0): void { if (get_class($this->memcache) == 'Memcache') { $this->assertTrue($this->memcache->set($key, $value, 0, $expiration)); } elseif (get_class($this->memcache) == 'Memcached') { $this->assertTrue($this->memcache->set($key, $value, $expiration)); } } /** * Flushes all Memcached data. */ public function clearMemcache(): void { $this->memcache->flush(); } } internalDomains = null; } } |array|array */ protected array $defaultCookieParameters = ['expires' => null, 'path' => '/', 'domain' => '', 'secure' => false]; /** * @var string[]|null */ protected ?array $internalDomains = null; private ?string $baseUrl = null; public function _failed(TestInterface $test, $fail) { try { if (!$this->client || !$this->client->getInternalResponse()) { return; } } catch (BadMethodCallException) { //Symfony 5 throws exception if request() method threw an exception. //The "request()" method must be called before "Symfony\Component\BrowserKit\AbstractBrowser::getInternalResponse()" return; } $filename = preg_replace('#\W#', '.', Descriptor::getTestSignatureUnique($test)); $extensions = [ 'application/json' => 'json', 'text/xml' => 'xml', 'application/xml' => 'xml', 'text/plain' => 'txt' ]; try { $internalResponse = $this->client->getInternalResponse(); } catch (BadMethodCallException) { $internalResponse = false; } $responseContentType = $internalResponse ? (string) $internalResponse->getHeader('content-type') : ''; [$responseMimeType] = explode(';', $responseContentType); $extension = $extensions[$responseMimeType] ?? 'html'; $filename = mb_strcut($filename, 0, 244, 'utf-8') . '.fail.' . $extension; $this->_savePageSource($report = codecept_output_dir() . $filename); $test->getMetadata()->addReport('html', $report); $test->getMetadata()->addReport('response', $report); } public function _after(TestInterface $test) { $this->client = null; $this->crawler = null; $this->forms = []; $this->headers = []; } /** * @return class-string */ public function _conflicts(): string { return \Codeception\Lib\Interfaces\Web::class; } public function _findElements(mixed $locator): iterable { return $this->match($locator); } /** * Send custom request to a backend using method, uri, parameters, etc. * Use it in Helpers to create special request actions, like accessing API * Returns a string with response body. * * ```php * getModule('{{MODULE_NAME}}')->_request('POST', '/api/v1/users', ['name' => $name]); * $user = json_decode($userData); * return $user->id; * } * ``` * Does not load the response into the module so you can't interact with response page (click, fill forms). * To load arbitrary page for interaction, use `_loadPage` method. * * @throws ExternalUrlException|ModuleException * @api * @see `_loadPage` */ public function _request( string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null ): ?string { $this->clientRequest($method, $uri, $parameters, $files, $server, $content); return $this->_getResponseContent(); } /** * Returns content of the last response * Use it in Helpers when you want to retrieve response of request performed by another module. * * ```php * assertStringContainsString($text, $this->getModule('{{MODULE_NAME}}')->_getResponseContent(), "response contains"); * } * ``` * * @api * @throws ModuleException */ public function _getResponseContent(): string { return $this->getRunningClient()->getInternalResponse()->getContent(); } protected function clientRequest( string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null, bool $changeHistory = true ): SymfonyCrawler { $this->debugSection("Request Headers", $this->headers); foreach ($this->headers as $header => $val) { // moved from REST module if ($val === null || $val === '') { continue; } $header = str_replace('-', '_', strtoupper($header)); $server["HTTP_{$header}"] = $val; // Issue #827 - symfony foundation requires 'CONTENT_TYPE' without HTTP_ if ($this instanceof Framework && $header === 'CONTENT_TYPE') { $server[$header] = $val; } } $server['REQUEST_TIME'] = time(); $server['REQUEST_TIME_FLOAT'] = microtime(true); if ($this instanceof Framework) { if (preg_match('#^(//|https?://(?!localhost))#', $uri)) { $hostname = parse_url($uri, PHP_URL_HOST); if (!$this->isInternalDomain($hostname)) { throw new ExternalUrlException($this::class . " can't open external URL: " . $uri); } } if (!in_array($method, ['GET', 'HEAD', 'OPTIONS'], true) && $content === null && !empty($parameters)) { $content = http_build_query($parameters); } } if (method_exists($this->client, 'isFollowingRedirects')) { $isFollowingRedirects = $this->client->isFollowingRedirects(); $maxRedirects = $this->client->getMaxRedirects(); } else { //Symfony 2.7 support $isFollowingRedirects = ReflectionHelper::readPrivateProperty($this->client, 'followRedirects', 'Symfony\Component\BrowserKit\Client'); $maxRedirects = ReflectionHelper::readPrivateProperty($this->client, 'maxRedirects', 'Symfony\Component\BrowserKit\Client'); } if (!$isFollowingRedirects) { $result = $this->client->request($method, $uri, $parameters, $files, $server, $content, $changeHistory); $this->debugResponse($uri); return $result; } $this->client->followRedirects(false); $result = $this->client->request($method, $uri, $parameters, $files, $server, $content, $changeHistory); $this->debugResponse($uri); return $this->redirectIfNecessary($result, $maxRedirects, 0); } protected function isInternalDomain(string $domain): bool { if ($this->internalDomains === null) { $this->internalDomains = $this->getInternalDomains(); } foreach ($this->internalDomains as $pattern) { if (preg_match($pattern, $domain)) { return true; } } return false; } /** * Opens a page with arbitrary request parameters. * Useful for testing multi-step forms on a specific step. * * ```php * getModule('{{MODULE_NAME}}')->_loadPage('POST', '/checkout/step2', ['order' => $orderId]); * } * ``` * * @api */ public function _loadPage( string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null ): void { $this->crawler = $this->clientRequest($method, $uri, $parameters, $files, $server, $content); $this->baseUrl = $this->retrieveBaseUrl(); $this->forms = []; } /** * @throws ModuleException */ private function getCrawler(): SymfonyCrawler { if (!$this->crawler) { throw new ModuleException($this, 'Crawler is null. Perhaps you forgot to call "amOnPage"?'); } return $this->crawler; } private function getRunningClient(): AbstractBrowser { try { if ($this->client->getInternalRequest() === null) { throw new ModuleException( $this, "Page not loaded. Use `\$I->amOnPage` (or hidden API methods `_request` and `_loadPage`) to open it" ); } } catch (BadMethodCallException) { //Symfony 5 throw new ModuleException( $this, "Page not loaded. Use `\$I->amOnPage` (or hidden API methods `_request` and `_loadPage`) to open it" ); } return $this->client; } public function _savePageSource(string $filename): void { file_put_contents($filename, $this->_getResponseContent()); } /** * Authenticates user for HTTP_AUTH */ public function amHttpAuthenticated(string $username, string $password): void { $this->client->setServerParameter('PHP_AUTH_USER', $username); $this->client->setServerParameter('PHP_AUTH_PW', $password); } /** * Sets the HTTP header to the passed value - which is used on * subsequent HTTP requests through PhpBrowser. * * Example: * ```php * haveHttpHeader('X-Requested-With', 'Codeception'); * $I->amOnPage('test-headers.php'); * ``` * * To use special chars in Header Key use HTML Character Entities: * Example: * Header with underscore - 'Client_Id' * should be represented as - 'Client_Id' or 'Client_Id' * * ```php * haveHttpHeader('Client_Id', 'Codeception'); * ``` * * @param string $name the name of the request header * @param string $value the value to set it to for subsequent * requests */ public function haveHttpHeader(string $name, string $value): void { $name = implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $name))))); $this->headers[$name] = $value; } /** * Deletes the header with the passed name. Subsequent requests * will not have the deleted header in its request. * * Example: * ```php * haveHttpHeader('X-Requested-With', 'Codeception'); * $I->amOnPage('test-headers.php'); * // ... * $I->deleteHeader('X-Requested-With'); * $I->amOnPage('some-other-page.php'); * ``` * * @param string $name the name of the header to delete. */ public function deleteHeader(string $name): void { $name = implode('-', array_map('ucfirst', explode('-', strtolower(str_replace('_', '-', $name))))); unset($this->headers[$name]); } public function amOnPage(string $page): void { $this->_loadPage('GET', $page); } public function click($link, $context = null): void { if ($context) { $this->crawler = $this->match($context); } if (is_array($link)) { $this->clickByLocator($link); return; } $anchor = $this->strictMatch(['link' => $link]); if (count($anchor) === 0) { $anchor = $this->getCrawler()->selectLink($link); } if (count($anchor) > 0) { $this->openHrefFromDomNode($anchor->getNode(0)); return; } $buttonText = str_replace('"', "'", $link); $button = $this->crawler->selectButton($buttonText); if (count($button) && $this->clickButton($button->getNode(0))) { return; } try { $this->clickByLocator($link); } catch (MalformedLocatorException) { throw new ElementNotFound("name={$link}", "'{$link}' is invalid CSS and XPath selector and Link or Button"); } } /** * @param string|string[] $link */ protected function clickByLocator(string|array $link): ?bool { $nodes = $this->match($link); if ($nodes->count() === 0) { throw new ElementNotFound($link, 'Link or Button by name or CSS or XPath'); } foreach ($nodes as $node) { $tag = $node->tagName; $type = $node->getAttribute('type'); if ($tag === 'a') { $this->openHrefFromDomNode($node); return true; } if (in_array($tag, ['input', 'button']) && in_array($type, ['submit', 'image'])) { return $this->clickButton($node); } } return null; } /** * Clicks the link or submits the form when the button is clicked * * @return bool clicked something */ private function clickButton(DOMNode $node): bool { /** * First we check if the button is associated to a form. * It is associated to a form when it has a nonempty form */ $formAttribute = $node->attributes->getNamedItem('form'); if (isset($formAttribute)) { $form = empty($formAttribute->nodeValue) ? null : $this->filterByCSS('#' . $formAttribute->nodeValue)->getNode(0); } else { // Check parents $currentNode = $node; $form = null; while ($currentNode->parentNode !== null) { $currentNode = $currentNode->parentNode; if ($currentNode->nodeName === 'form') { $form = $node; break; } } } if (isset($form)) { $buttonName = $node->getAttribute('name'); $formParams = $buttonName !== '' ? [$buttonName => $node->getAttribute('value')] : []; $this->proceedSubmitForm( new SymfonyCrawler($form, $this->getAbsoluteUrlFor($this->_getCurrentUri()), $this->getBaseUrl()), $formParams ); return true; } // Check if the button is inside an anchor. $currentNode = $node; while ($currentNode->parentNode !== null) { $currentNode = $currentNode->parentNode; if ($currentNode->nodeName === 'a') { $this->openHrefFromDomNode($currentNode); return true; } } throw new TestRuntimeException('Button is not inside a link or a form'); } private function openHrefFromDomNode(DOMNode $node): void { $link = new Link($node, $this->getBaseUrl()); $this->amOnPage(preg_replace('/#.*/', '', $link->getUri())); } private function getBaseUrl(): ?string { return $this->baseUrl; } private function retrieveBaseUrl(): string { $baseUrl = ''; $baseHref = $this->crawler->filter('base'); if (count($baseHref) > 0) { $baseUrl = $baseHref->getNode(0)->getAttribute('href'); } if ($baseUrl === '') { $baseUrl = $this->_getCurrentUri(); } return $this->getAbsoluteUrlFor($baseUrl); } public function see(string $text, $selector = null): void { if (!$selector) { $this->assertPageContains($text); return; } $nodes = $this->match($selector); $this->assertDomContains($nodes, $this->stringifySelector($selector), $text); } public function dontSee(string $text, $selector = null): void { if (!$selector) { $this->assertPageNotContains($text); return; } $nodes = $this->match($selector); $this->assertDomNotContains($nodes, $this->stringifySelector($selector), $text); } public function seeInSource(string $raw): void { $this->assertPageSourceContains($raw); } public function dontSeeInSource(string $raw): void { $this->assertPageSourceNotContains($raw); } public function seeLink(string $text, string $url = null): void { $crawler = $this->getCrawler()->selectLink($text); if ($crawler->count() === 0) { $this->fail("No links containing text '{$text}' were found in page " . $this->_getCurrentUri()); } if ($url) { $crawler = $crawler->filterXPath(sprintf('.//a[substring(@href, string-length(@href) - string-length(%1$s) + 1)=%1$s]', SymfonyCrawler::xpathLiteral($url))); if ($crawler->count() === 0) { $this->fail("No links containing text '{$text}' and URL '{$url}' were found in page " . $this->_getCurrentUri()); } } $this->assertTrue(true); } public function dontSeeLink(string $text, string $url = ''): void { $crawler = $this->getCrawler()->selectLink($text); if (!$url && $crawler->count() > 0) { $this->fail("Link containing text '{$text}' was found in page " . $this->_getCurrentUri()); } $crawler = $crawler->filterXPath( sprintf('.//a[substring(@href, string-length(@href) - string-length(%1$s) + 1)=%1$s]', SymfonyCrawler::xpathLiteral((string) $url)) ); if ($crawler->count() > 0) { $this->fail("Link containing text '{$text}' and URL '{$url}' was found in page " . $this->_getCurrentUri()); } } /** * @throws ModuleException */ public function _getCurrentUri(): string { return Uri::retrieveUri($this->getRunningClient()->getHistory()->current()->getUri()); } public function seeInCurrentUrl(string $uri): void { $this->assertStringContainsString($uri, $this->_getCurrentUri()); } public function dontSeeInCurrentUrl(string $uri): void { $this->assertStringNotContainsString($uri, $this->_getCurrentUri()); } public function seeCurrentUrlEquals(string $uri): void { $this->assertSame(rtrim($uri, '/'), rtrim($this->_getCurrentUri(), '/')); } public function dontSeeCurrentUrlEquals(string $uri): void { $this->assertNotSame(rtrim($uri, '/'), rtrim($this->_getCurrentUri(), '/')); } public function seeCurrentUrlMatches(string $uri): void { $this->assertRegExp($uri, $this->_getCurrentUri()); } public function dontSeeCurrentUrlMatches(string $uri): void { $this->assertNotRegExp($uri, $this->_getCurrentUri()); } public function grabFromCurrentUrl(string $uri = null): mixed { 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): void { $checkboxes = $this->getFieldsByLabelOrCss($checkbox); $this->assertGreaterThan(0, $checkboxes->filter('input[checked]')->count()); } public function dontSeeCheckboxIsChecked($checkbox): void { $checkboxes = $this->getFieldsByLabelOrCss($checkbox); $this->assertSame(0, $checkboxes->filter('input[checked]')->count()); } public function seeInField($field, $value): void { $nodes = $this->getFieldsByLabelOrCss($field); $this->assert($this->proceedSeeInField($nodes, $value)); } public function dontSeeInField($field, $value): void { $nodes = $this->getFieldsByLabelOrCss($field); $this->assertNot($this->proceedSeeInField($nodes, $value)); } public function seeInFormFields($formSelector, array $params): void { $this->proceedSeeInFormFields($formSelector, $params, false); } public function dontSeeInFormFields($formSelector, array $params): void { $this->proceedSeeInFormFields($formSelector, $params, true); } protected function proceedSeeInFormFields($formSelector, array $params, $assertNot) { $form = $this->match($formSelector)->first(); if ($form->count() === 0) { throw new ElementNotFound($formSelector, 'Form'); } $fields = []; foreach ($params as $name => $values) { $this->pushFormField($fields, $form, $name, $values); } foreach ($fields as [$field, $values]) { if (!is_array($values)) { $values = [$values]; } foreach ($values as $value) { $ret = $this->proceedSeeInField($field, $value); if ($assertNot) { $this->assertNot($ret); } else { $this->assert($ret); } } } } /** * Map an array element passed to seeInFormFields to its corresponding field, * recursing through array values if the field is not found. * * @param array $fields The previously found fields. * @param SymfonyCrawler $form The form in which to search for fields. * @param string $name The field's name. * @param mixed $values */ protected function pushFormField(array &$fields, SymfonyCrawler $form, string $name, $values): void { $field = $form->filterXPath(sprintf('.//*[@name=%s]', SymfonyCrawler::xpathLiteral($name))); if ($field->count() !== 0) { $fields[] = [$field, $values]; } elseif (is_array($values)) { foreach ($values as $key => $value) { $this->pushFormField($fields, $form, sprintf('%s[%s]', $name, $key), $value); } } else { throw new ElementNotFound( sprintf('//*[@name=%s]', SymfonyCrawler::xpathLiteral($name)), 'Form' ); } } protected function proceedSeeInField(Crawler $fields, $value): array { $testValues = $this->getValueAndTextFromField($fields); if (!is_array($testValues)) { $testValues = [$testValues]; } if (is_bool($value) && $value && !empty($testValues)) { $value = reset($testValues); } elseif (empty($testValues)) { $testValues = ['']; } return [ 'Contains', (string)$value, $testValues, sprintf( "Failed asserting that `%s` is in %s's value: %s", $value, $fields->getNode(0)->nodeName, var_export($testValues, true) ) ]; } /** * Get the values of a set of fields and also the texts of selected options. */ protected function getValueAndTextFromField(Crawler $nodes): array|string { if ($nodes->filter('textarea')->count() !== 0) { return (new TextareaFormField($nodes->filter('textarea')->getNode(0)))->getValue(); } $input = $nodes->filter('input'); if ($input->count() !== 0) { return $this->getInputValue($input); } if ($nodes->filter('select')->count() !== 0) { $options = $nodes->filter('option[selected]'); $values = []; foreach ($options as $option) { $values[] = $option->getAttribute('value'); $values[] = $option->textContent; $values[] = trim($option->textContent); } return $values; } $this->fail("Element {$nodes} is not a form field or does not contain a form field"); } /** * Get the values of a set of input fields. */ protected function getInputValue(SymfonyCrawler $input): array|string { $inputType = $input->attr('type'); if ($inputType === 'checkbox' || $inputType === 'radio') { $values = []; foreach ($input->filter(':checked') as $checkbox) { $values[] = $checkbox->getAttribute('value'); } return $values; } return (new InputFormField($input->getNode(0)))->getValue(); } /** * Strips out one pair of trailing square brackets from a field's * name. * * @param string $name the field name * @return string the name after stripping trailing square brackets */ protected function getSubmissionFormFieldName(string $name): string { if (str_ends_with($name, '[]')) { return substr($name, 0, -2); } return $name; } /** * Replaces boolean values in $params with the corresponding field's * value for checkbox form fields. * * The function loops over all input checkbox fields, checking if a * corresponding key is set in $params. If it is, and the value is * boolean or an array containing booleans, the value(s) are * replaced in the array with the real value of the checkbox, and * the array is returned. * * @param SymfonyCrawler $form the form to find checkbox elements * @param array $params the parameters to be submitted * @return array the $params array after replacing bool values */ protected function setCheckboxBoolValues(Crawler $form, array $params): array { $checkboxes = $form->filter('input[type=checkbox]'); $chFoundByName = []; foreach ($checkboxes as $checkbox) { $fieldName = $this->getSubmissionFormFieldName($checkbox->getAttribute('name')); $pos = $chFoundByName[$fieldName] ?? 0; $skip = !isset($params[$fieldName]) || (!is_array($params[$fieldName]) && !is_bool($params[$fieldName])) || (is_array($params[$fieldName]) && ($pos >= count($params[$fieldName]) || !is_bool($params[$fieldName][$pos])) ); if ($skip) { continue; } $values = $params[$fieldName]; if ($values === true) { $params[$fieldName] = $checkbox->hasAttribute('value') ? $checkbox->getAttribute('value') : 'on'; $chFoundByName[$fieldName] = $pos + 1; } elseif (is_array($values)) { if ($values[$pos] === true) { $params[$fieldName][$pos] = $checkbox->hasAttribute('value') ? $checkbox->getAttribute('value') : 'on'; $chFoundByName[$fieldName] = $pos + 1; } else { array_splice($params[$fieldName], $pos, 1); } } else { unset($params[$fieldName]); } } return $params; } /** * Submits the form currently selected in the passed SymfonyCrawler, after * setting any values passed in $params and setting the value of the * passed button name. * * @param SymfonyCrawler $frmCrawl the form to submit * @param array $params additional parameter values to set on the * form * @param string|null $button the name of a submit button in the form */ protected function proceedSubmitForm(Crawler $frmCrawl, array $params, string $button = null): void { $url = null; $form = $this->getFormFor($frmCrawl); $defaults = $this->getFormValuesFor($form); $merged = array_merge($defaults, $params); $requestParams = $this->setCheckboxBoolValues($frmCrawl, $merged); if (!empty($button)) { $btnCrawl = $frmCrawl->filterXPath(sprintf( '//*[not(@disabled) and @type="submit" and @name=%s]', SymfonyCrawler::xpathLiteral($button) )); if (count($btnCrawl) > 0) { $requestParams[$button] = $btnCrawl->attr('value'); $formaction = $btnCrawl->attr('formaction'); if ($formaction) { $url = $formaction; } } } if ($url === null) { $url = $this->getFormUrl($frmCrawl); } if (strcasecmp($form->getMethod(), 'GET') === 0) { $url = Uri::mergeUrls($url, '?' . http_build_query($requestParams)); } $url = preg_replace('#\#.*#', '', $url); $this->debugSection('Uri', $url); $this->debugSection('Method', $form->getMethod()); $this->debugSection('Parameters', $requestParams); $requestParams= $this->getFormPhpValues($requestParams); $this->crawler = $this->clientRequest( $form->getMethod(), $url, $requestParams, $form->getPhpFiles() ); $this->forms = []; } public function submitForm($selector, array $params, string $button = null): void { $form = $this->match($selector)->first(); if (count($form) === 0) { throw new ElementNotFound($this->stringifySelector($selector), 'Form'); } $this->proceedSubmitForm($form, $params, $button); } /** * Returns an absolute URL for the passed URI with the current URL * as the base path. * * @param string $uri the absolute or relative URI * @return string the absolute URL * @throws TestRuntimeException if either the current * URL or the passed URI can't be parsed */ protected function getAbsoluteUrlFor(string $uri): string { $currentUrl = $this->getRunningClient()->getHistory()->current()->getUri(); if (empty($uri) || str_starts_with($uri, '#')) { return $currentUrl; } return Uri::mergeUrls($currentUrl, $uri); } /** * Returns the form action's absolute URL. * * @throws TestRuntimeException if either the current * URL or the URI of the form's action can't be parsed */ protected function getFormUrl(Crawler $form): string { $action = $form->form()->getUri(); return $this->getAbsoluteUrlFor($action); } /** * Returns a crawler Form object for the form pointed to by the * passed SymfonyCrawler. * * The returned form is an independent Crawler created to take care * of the following issues currently experienced by Crawler's form * object: * - input fields disabled at a higher level (e.g. by a surrounding * fieldset) still return values * - Codeception expects an empty value to match an unselected * select box. * * The function clones the crawler's node and creates a new crawler * because it destroys or adds to the DOM for the form to achieve * the desired functionality. Other functions simply querying the * DOM wouldn't expect them. * * @param SymfonyCrawler $form the form */ private function getFormFromCrawler(Crawler $form): SymfonyForm { $fakeDom = new DOMDocument(); $fakeDom->appendChild($fakeDom->importNode($form->getNode(0), true)); //add fields having form attribute with id of this form $formId = $form->attr('id'); if ($formId !== null) { $fakeForm = $fakeDom->firstChild; $topParent = $this->getAncestorsFor($form)->last(); $fieldsByFormAttribute = $topParent->filter( sprintf('input[form=%s],select[form=%s],textarea[form=%s]', $formId, $formId, $formId) ); foreach ($fieldsByFormAttribute as $field) { $fakeForm->appendChild($fakeDom->importNode($field, true)); } } $node = $fakeDom->documentElement; $action = $this->getFormUrl($form); $cloned = new SymfonyCrawler($node, $action, $this->getBaseUrl()); $shouldDisable = $cloned->filter( 'input:disabled:not([disabled]),select option:disabled,select optgroup:disabled option:not([disabled]),textarea:disabled:not([disabled]),select:disabled:not([disabled])' ); foreach ($shouldDisable as $field) { $field->parentNode->removeChild($field); } return $cloned->form(); } /** * Returns the DomCrawler\Form object for the form pointed to by * $node or its closes form parent. */ protected function getFormFor(Crawler $node): SymfonyForm { if (strcasecmp($node->first()->getNode(0)->tagName, 'form') === 0) { $form = $node->first(); } else { $form = $this->getAncestorsFor($node)->filter('form')->first(); } if (!$form) { $this->fail('The selected node is not a form and does not have a form ancestor.'); } $identifier = $form->attr('id') ?: $form->attr('action'); if (!isset($this->forms[$identifier])) { $this->forms[$identifier] = $this->getFormFromCrawler($form); } return $this->forms[$identifier]; } /** * Returns the ancestors of the passed SymfonyCrawler. * * symfony/dom-crawler deprecated parents() in favor of ancestors() * This provides backward compatibility with < 5.3.0-BETA-1 * * @param SymfonyCrawler $crawler the crawler * @return SymfonyCrawler the ancestors */ private function getAncestorsFor(SymfonyCrawler $crawler): SymfonyCrawler { if (method_exists($crawler, 'ancestors')) { return $crawler->ancestors(); } return $crawler->parents(); } /** * Returns an array of name => value pairs for the passed form. * * For form fields containing a name ending in [], an array is * created out of all field values with the given name. * * @param SymfonyForm $form the form * @return array an array of name => value pairs */ protected function getFormValuesFor(SymfonyForm $form): array { $formNodeCrawler = new Crawler($form->getFormNode()); $values = []; $fields = $form->all(); foreach ($fields as $field) { if ($field instanceof FileFormField || $field->isDisabled()) { continue; } if (!$field->hasValue()) { // if unchecked a checkbox and if there is hidden input with same name to submit unchecked value $hiddenInput = $formNodeCrawler->filter('input[type=hidden][name="'.$field->getName().'"]:not([disabled])'); if (count($hiddenInput) === 0) { continue; } else { // there might be multiple hidden input with same name, but we will only grab last one's value $fieldValue = $hiddenInput->last()->attr('value'); } } else { $fieldValue = $field->getValue(); } $fieldName = $this->getSubmissionFormFieldName($field->getName()); if (str_ends_with($field->getName(), '[]')) { if (!isset($values[$fieldName])) { $values[$fieldName] = []; } $values[$fieldName][] = $fieldValue; } else { $values[$fieldName] = $fieldValue; } } return $values; } public function fillField($field, $value): void { $value = (string) $value; $input = $this->getFieldByLabelOrCss($field); $form = $this->getFormFor($input); $name = $input->attr('name'); $dynamicField = $input->getNode(0)->tagName === 'textarea' ? new TextareaFormField($input->getNode(0)) : new InputFormField($input->getNode(0)); $formField = $this->matchFormField($name, $form, $dynamicField); $formField->setValue($value); $input->getNode(0)->setAttribute('value', htmlspecialchars($value)); $inputGetNode = $input->getNode(0); if ($inputGetNode->tagName === 'textarea') { $input->getNode(0)->nodeValue = htmlspecialchars($value); } } protected function getFieldsByLabelOrCss($field): SymfonyCrawler { $input = null; if (is_array($field)) { $input = $this->strictMatch($field); if (count($input) === 0) { throw new ElementNotFound($field); } return $input; } // by label $label = $this->strictMatch(['xpath' => sprintf('.//label[descendant-or-self::node()[text()[normalize-space()=%s]]]', SymfonyCrawler::xpathLiteral($field))]); if (count($label) > 0) { $label = $label->first(); if ($label->attr('for')) { $input = $this->strictMatch(['id' => $label->attr('for')]); } else { $input = $this->strictMatch(['xpath' => sprintf('.//label[descendant-or-self::node()[text()[normalize-space()=%s]]]//input', SymfonyCrawler::xpathLiteral($field))]); } } // by name if (!isset($input)) { $input = $this->strictMatch(['name' => $field]); } // by CSS and XPath if (count($input) === 0) { $input = $this->match($field); } if (count($input) === 0) { throw new ElementNotFound($field, 'Form field by Label or CSS'); } return $input; } protected function getFieldByLabelOrCss($field): SymfonyCrawler { $input = $this->getFieldsByLabelOrCss($field); return $input->first(); } public function selectOption($select, $option): void { $field = $this->getFieldByLabelOrCss($select); $form = $this->getFormFor($field); $fieldName = $this->getSubmissionFormFieldName($field->attr('name')); if (is_array($option)) { if (!isset($option[0])) { // strict option locator $form[$fieldName]->select($this->matchOption($field, $option)); codecept_debug($option); return; } $options = []; foreach ($option as $opt) { $options[] = $this->matchOption($field, $opt); } $form[$fieldName]->select($options); return; } $dynamicField = new ChoiceFormField($field->getNode(0)); $formField = $this->matchFormField($fieldName, $form, $dynamicField); $selValue = $this->matchOption($field, $option); if (is_array($formField)) { foreach ($formField as $field) { $values = $field->availableOptionValues(); foreach ($values as $val) { if ($val === $option) { $field->select($selValue); return; } } } return; } $formField->select((string) $this->matchOption($field, $option)); } /** * @return mixed */ protected function matchOption(Crawler $field, string|array $option) { if (isset($option['value'])) { return $option['value']; } if (isset($option['text'])) { $option = $option['text']; } $options = $field->filterXPath(sprintf('//option[text()=normalize-space("%s")]|//input[@type="radio" and @value=normalize-space("%s")]', $option, $option)); if ($options->count() !== 0) { $firstMatchingDomNode = $options->getNode(0); if ($firstMatchingDomNode->tagName === 'option') { $firstMatchingDomNode->setAttribute('selected', 'selected'); } else { $firstMatchingDomNode->setAttribute('checked', 'checked'); } $valueAttribute = $options->first()->attr('value'); //attr() returns null when option has no value attribute if ($valueAttribute !== null) { return $valueAttribute; } return $options->first()->text(); } return $option; } public function checkOption($option): void { $this->proceedCheckOption($option)->tick(); } public function uncheckOption($option): void { $this->proceedCheckOption($option)->untick(); } /** * @param string|string[] $option */ protected function proceedCheckOption(string|array $option): ChoiceFormField { $form = $this->getFormFor($field = $this->getFieldByLabelOrCss($option)); $name = $field->attr('name'); if ($field->getNode(0) === null) { throw new TestRuntimeException("Form field {$name} is not located"); } // If the name is an array than we compare objects to find right checkbox $formField = $this->matchFormField($name, $form, new ChoiceFormField($field->getNode(0))); $field->getNode(0)->setAttribute('checked', 'checked'); if (!$formField instanceof ChoiceFormField) { throw new TestRuntimeException("Form field {$name} is not a checkable"); } return $formField; } public function attachFile($field, string $filename): void { $form = $this->getFormFor($field = $this->getFieldByLabelOrCss($field)); $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}"); } $name = $field->attr('name'); $formField = $this->matchFormField($name, $form, new FileFormField($field->getNode(0))); if (is_array($formField)) { $this->fail("Field {$name} is ignored on upload, field {$name} is treated as array."); } $formField->upload($filePath); } /** * Sends an ajax GET request with the passed parameters. * See `sendAjaxPostRequest()` */ public function sendAjaxGetRequest(string $uri, array $params = []): void { $this->sendAjaxRequest('GET', $uri, $params); } /** * Sends an ajax POST request with the passed parameters. * The appropriate HTTP header is added automatically: * `X-Requested-With: XMLHttpRequest` * Example: * ``` php * sendAjaxPostRequest('/add-task', ['task' => 'lorem ipsum']); * ``` * Some frameworks (e.g. Symfony) create field names in the form of an "array": * `` * In this case you need to pass the fields like this: * ``` php * sendAjaxPostRequest('/add-task', ['form' => [ * 'task' => 'lorem ipsum', * 'category' => 'miscellaneous', * ]]); * ``` */ public function sendAjaxPostRequest(string $uri, array $params = []): void { $this->sendAjaxRequest('POST', $uri, $params); } /** * Sends an ajax request, using the passed HTTP method. * See `sendAjaxPostRequest()` * Example: * ``` php * sendAjaxRequest('PUT', '/posts/7', ['title' => 'new title']); * ``` */ public function sendAjaxRequest(string $method, string $uri, array $params = []): void { $this->clientRequest($method, $uri, $params, [], ['HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'], null, false); } /** * @param mixed $url */ protected function debugResponse($url): void { $this->debugSection('Page', $url); $this->debugSection('Response', $this->getResponseStatusCode()); $this->debugSection('Request Cookies', $this->getRunningClient()->getInternalRequest()->getCookies()); $this->debugSection('Response Headers', $this->getRunningClient()->getInternalResponse()->getHeaders()); } public function makeHtmlSnapshot(string $name = null): void { if (empty($name)) { $name = uniqid(date("Y-m-d_H-i-s_"), true); } $debugDir = codecept_output_dir() . 'debug'; if (!is_dir($debugDir)) { mkdir($debugDir); } $fileName = $debugDir . DIRECTORY_SEPARATOR . $name . '.html'; $this->_savePageSource($fileName); $this->debugSection('Snapshot Saved', "file://{$fileName}"); } public function _getResponseStatusCode() { return $this->getResponseStatusCode(); } protected function getResponseStatusCode() { // depending on Symfony version $response = $this->getRunningClient()->getInternalResponse(); if (method_exists($response, 'getStatusCode')) { return $response->getStatusCode(); } if (method_exists($response, 'getStatus')) { return $response->getStatus(); } return "N/A"; } /** * @param string|string[] $selector */ protected function match(string|array $selector): SymfonyCrawler { if (is_array($selector)) { return $this->strictMatch($selector); } if (Locator::isCSS($selector)) { return $this->getCrawler()->filter($selector); } if (Locator::isXPath($selector)) { return $this->getCrawler()->filterXPath($selector); } throw new MalformedLocatorException($selector, 'XPath or CSS'); } /** * @param string[] $by * @throws TestRuntimeException */ protected function strictMatch(array $by): SymfonyCrawler { $type = key($by); $locator = $by[$type]; return match ($type) { 'id' => $this->filterByCSS(sprintf('#%s', $locator)), 'name' => $this->filterByXPath(sprintf('.//*[@name=%s]', SymfonyCrawler::xpathLiteral($locator))), 'css' => $this->filterByCSS($locator), 'xpath' => $this->filterByXPath($locator), 'link' => $this->filterByXPath(sprintf('.//a[.=%s or contains(./@title, %s)]', SymfonyCrawler::xpathLiteral($locator), SymfonyCrawler::xpathLiteral($locator))), 'class' => $this->filterByCSS(".{$locator}"), default => throw new TestRuntimeException( "Locator type '{$by}' is not defined. Use either: xpath, css, id, link, class, name" ), }; } protected function filterByAttributes(Crawler $nodes, array $attributes) { foreach ($attributes as $attr => $val) { $nodes = $nodes->reduce( static fn(Crawler $node): bool => $node->attr($attr) === $val ); } return $nodes; } public function grabTextFrom($cssOrXPathOrRegex): mixed { if (is_string($cssOrXPathOrRegex) && @preg_match($cssOrXPathOrRegex, $this->client->getInternalResponse()->getContent(), $matches)) { return $matches[1]; } $nodes = $this->match($cssOrXPathOrRegex); if ($nodes->count() !== 0) { return $nodes->first()->text(); } throw new ElementNotFound($cssOrXPathOrRegex, 'Element that matches CSS or XPath or Regex'); } public function grabAttributeFrom($cssOrXpath, string $attribute): mixed { $nodes = $this->match($cssOrXpath); if ($nodes->count() === 0) { throw new ElementNotFound($cssOrXpath, 'Element that matches CSS or XPath'); } return $nodes->first()->attr($attribute); } public function grabMultiple($cssOrXpath, string $attribute = null): array { $result = []; $nodes = $this->match($cssOrXpath); foreach ($nodes as $node) { $result[] = $attribute !== null ? $node->getAttribute($attribute) : $node->textContent; } return $result; } public function grabValueFrom($field): mixed { $nodes = $this->match($field); if ($nodes->count() === 0) { throw new ElementNotFound($field, 'Field'); } if ($nodes->filter('textarea')->count() !== 0) { return (new TextareaFormField($nodes->filter('textarea')->getNode(0)))->getValue(); } $input = $nodes->filter('input'); if ($input->count() !== 0) { return $this->getInputValue($input); } if ($nodes->filter('select')->count() !== 0) { $field = new ChoiceFormField($nodes->filter('select')->getNode(0)); $options = $nodes->filter('option[selected]'); $values = []; foreach ($options as $option) { $values[] = $option->getAttribute('value'); } if (!$field->isMultiple()) { return reset($values); } return $values; } $this->fail("Element {$nodes} is not a form field or does not contain a form field"); } public function setCookie($name, $val, $params = []) { $cookies = $this->client->getCookieJar(); $params = array_merge($this->defaultCookieParameters, $params); $expires = $params['expiry'] ?? null; // WebDriver compatibility $expires = isset($params['expires']) && !$expires ? $params['expires'] : null; $path = $params['path'] ?? null; $domain = $params['domain'] ?? ''; $secure = $params['secure'] ?? false; $httpOnly = $params['httpOnly'] ?? true; $encodedValue = $params['encodedValue'] ?? false; $cookies->set(new Cookie($name, $val, $expires, $path, $domain, $secure, $httpOnly, $encodedValue)); $this->debugCookieJar(); } public function grabCookie(string $cookie, array $params = []): mixed { $params = array_merge($this->defaultCookieParameters, $params); $this->debugCookieJar(); $cookies = $this->getRunningClient()->getCookieJar()->get($cookie, $params['path'], $params['domain']); if ($cookies === null) { return null; } return $cookies->getValue(); } /** * Grabs current page source code. * * @throws ModuleException if no page was opened. * @return string Current page source code. */ public function grabPageSource(): string { return $this->_getResponseContent(); } public function seeCookie($cookie, $params = []) { $params = array_merge($this->defaultCookieParameters, $params); $this->debugCookieJar(); $this->assertNotNull($this->client->getCookieJar()->get($cookie, $params['path'], $params['domain'])); } public function dontSeeCookie($cookie, $params = []) { $params = array_merge($this->defaultCookieParameters, $params); $this->debugCookieJar(); $this->assertNull($this->client->getCookieJar()->get($cookie, $params['path'], $params['domain'])); } public function resetCookie($cookie, $params = []) { $params = array_merge($this->defaultCookieParameters, $params); $this->client->getCookieJar()->expire($cookie, $params['path'], $params['domain']); $this->debugCookieJar(); } private function stringifySelector($selector): string { if (is_array($selector)) { return trim(json_encode($selector, JSON_THROW_ON_ERROR), '{}'); } return $selector; } public function seeElement($selector, array $attributes = []): void { $nodes = $this->match($selector); $selector = $this->stringifySelector($selector); if (!empty($attributes)) { $nodes = $this->filterByAttributes($nodes, $attributes); $selector .= "' with attribute(s) '" . trim(json_encode($attributes, JSON_THROW_ON_ERROR), '{}'); } $this->assertDomContains($nodes, $selector); } public function dontSeeElement($selector, array $attributes = []): void { $nodes = $this->match($selector); $selector = $this->stringifySelector($selector); if (!empty($attributes)) { $nodes = $this->filterByAttributes($nodes, $attributes); $selector .= "' with attribute(s) '" . trim(json_encode($attributes, JSON_THROW_ON_ERROR), '{}'); } $this->assertDomNotContains($nodes, $selector); } public function seeNumberOfElements($selector, $expected): void { $counted = count($this->match($selector)); if (is_array($expected)) { [$floor, $ceil] = $expected; $this->assertTrue( $floor <= $counted && $ceil >= $counted, 'Number of elements counted differs from expected range' ); } else { $this->assertSame( $expected, $counted, 'Number of elements counted differs from expected number' ); } } public function seeOptionIsSelected($selector, $optionText) { $selected = $this->matchSelectedOption($selector); $this->assertDomContains($selected, 'selected option'); //If element is radio then we need to check value $value = $selected->getNode(0)->tagName === 'option' ? $selected->text() : $selected->getNode(0)->getAttribute('value'); $this->assertSame($optionText, $value); } public function dontSeeOptionIsSelected($selector, $optionText) { $selected = $this->matchSelectedOption($selector); if ($selected->count() === 0) { $this->assertSame(0, $selected->count()); return; } //If element is radio then we need to check value $value = $selected->getNode(0)->tagName === 'option' ? $selected->text() : $selected->getNode(0)->getAttribute('value'); $this->assertNotSame($optionText, $value); } protected function matchSelectedOption($select): SymfonyCrawler { $nodes = $this->getFieldsByLabelOrCss($select); $selectedOptions = $nodes->filter('option[selected],input:checked'); if ($selectedOptions->count() === 0) { $selectedOptions = $nodes->filter('option,input')->first(); } return $selectedOptions; } /** * Asserts that current page has 404 response status code. */ public function seePageNotFound(): void { $this->seeResponseCodeIs(404); } /** * Checks that response code is equal to value provided. * * ```php * seeResponseCodeIs(200); * * // recommended \Codeception\Util\HttpCode * $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); * ``` */ public function seeResponseCodeIs(int $code): void { $failureMessage = sprintf( 'Expected HTTP Status Code: %s. Actual Status Code: %s', HttpCode::getDescription($code), HttpCode::getDescription($this->getResponseStatusCode()) ); $this->assertSame($code, $this->getResponseStatusCode(), $failureMessage); } /** * Checks that response code is between a certain range. Between actually means [from <= CODE <= to] */ public function seeResponseCodeIsBetween(int $from, int $to): void { $failureMessage = sprintf( 'Expected HTTP Status Code between %s and %s. Actual Status Code: %s', HttpCode::getDescription($from), HttpCode::getDescription($to), HttpCode::getDescription($this->getResponseStatusCode()) ); $this->assertGreaterThanOrEqual($from, $this->getResponseStatusCode(), $failureMessage); $this->assertLessThanOrEqual($to, $this->getResponseStatusCode(), $failureMessage); } /** * Checks that response code is equal to value provided. * * ```php * dontSeeResponseCodeIs(200); * * // recommended \Codeception\Util\HttpCode * $I->dontSeeResponseCodeIs(\Codeception\Util\HttpCode::OK); * ``` */ public function dontSeeResponseCodeIs(int $code): void { $failureMessage = sprintf( 'Expected HTTP status code other than %s', HttpCode::getDescription($code) ); $this->assertNotSame($code, $this->getResponseStatusCode(), $failureMessage); } /** * Checks that the response code 2xx */ public function seeResponseCodeIsSuccessful(): void { $this->seeResponseCodeIsBetween(200, 299); } /** * Checks that the response code 3xx */ public function seeResponseCodeIsRedirection(): void { $this->seeResponseCodeIsBetween(300, 399); } /** * Checks that the response code is 4xx */ public function seeResponseCodeIsClientError(): void { $this->seeResponseCodeIsBetween(400, 499); } /** * Checks that the response code is 5xx */ public function seeResponseCodeIsServerError(): void { $this->seeResponseCodeIsBetween(500, 599); } public function seeInTitle($title) { $nodes = $this->getCrawler()->filter('title'); if ($nodes->count() === 0) { throw new ElementNotFound("", "Tag"); } $this->assertStringContainsString($title, $nodes->first()->text(), "page title contains {$title}"); } public function dontSeeInTitle($title) { $nodes = $this->getCrawler()->filter('title'); if ($nodes->count() === 0) { $this->assertTrue(true); return; } $this->assertStringNotContainsString($title, $nodes->first()->text(), "page title contains {$title}"); } protected function assertDomContains($nodes, string $message, string $text = ''): void { $constraint = new CrawlerConstraint($text, $this->_getCurrentUri()); $this->assertThat($nodes, $constraint, $message); } protected function assertDomNotContains($nodes, string $message, string $text = ''): void { $constraint = new CrawlerNotConstraint($text, $this->_getCurrentUri()); $this->assertThat($nodes, $constraint, $message); } protected function assertPageContains(string $needle, string $message = ''): void { $constraint = new PageConstraint($needle, $this->_getCurrentUri()); $this->assertThat( $this->getNormalizedResponseContent(), $constraint, $message ); } protected function assertPageNotContains(string $needle, string $message = ''): void { $constraint = new PageConstraint($needle, $this->_getCurrentUri()); $this->assertThatItsNot( $this->getNormalizedResponseContent(), $constraint, $message ); } protected function assertPageSourceContains(string $needle, string $message = ''): void { $constraint = new PageConstraint($needle, $this->_getCurrentUri()); $this->assertThat( $this->_getResponseContent(), $constraint, $message ); } protected function assertPageSourceNotContains(string $needle, string $message = ''): void { $constraint = new PageConstraint($needle, $this->_getCurrentUri()); $this->assertThatItsNot( $this->_getResponseContent(), $constraint, $message ); } /** * @param array|object $form */ protected function matchFormField(string $name, $form, FormField $dynamicField): FormField|array { if (!str_ends_with($name, '[]')) { return $form[$name]; } $name = substr($name, 0, -2); /** @var FormField $item */ foreach ($form[$name] as $item) { if ($item == $dynamicField) { return $item; } } throw new TestRuntimeException("None of form fields by {$name}[] were not matched"); } protected function filterByCSS(string $locator): SymfonyCrawler { if (!Locator::isCSS($locator)) { throw new MalformedLocatorException($locator, 'css'); } return $this->getCrawler()->filter($locator); } protected function filterByXPath(string $locator): SymfonyCrawler { if (!Locator::isXPath($locator)) { throw new MalformedLocatorException($locator, 'xpath'); } return $this->getCrawler()->filterXPath($locator); } protected function getFormPhpValues(array $requestParams): array { foreach ($requestParams as $name => $value) { $qs = http_build_query([$name => $value]); if (!empty($qs)) { // If the field's name is of the form of "array[key]", // we'll remove it from the request parameters // and set the "array" key instead which will contain the actual array. if (strpos($name, '[') && strpos($name, ']') > strpos($name, '[')) { unset($requestParams[$name]); } parse_str($qs, $expandedValue); $varName = substr($name, 0, strlen(key($expandedValue))); $requestParams = array_replace_recursive($requestParams, [$varName => current($expandedValue)]); } } return $requestParams; } protected function redirectIfNecessary(SymfonyCrawler $result, int $maxRedirects, int $redirectCount): SymfonyCrawler { $locationHeader = $this->client->getInternalResponse()->getHeader('Location'); $statusCode = $this->getResponseStatusCode(); if ($locationHeader && $statusCode >= 300 && $statusCode < 400) { if ($redirectCount === $maxRedirects) { throw new LogicException(sprintf( 'The maximum number (%d) of redirections was reached.', $maxRedirects )); } $this->debugSection('Redirecting to', $locationHeader); $result = $this->client->followRedirect(); $this->debugResponse($locationHeader); return $this->redirectIfNecessary($result, $maxRedirects, $redirectCount + 1); } $this->client->followRedirects(true); return $result; } /** * Switch to iframe or frame on the page. * * Example: * ``` html * <iframe name="another_frame" src="http://example.com"> * ``` * * ``` php * <?php * # switch to iframe * $I->switchToIframe("another_frame"); * ``` */ public function switchToIframe(string $name): void { $iframe = $this->match("iframe[name={$name}]")->first(); if (count($iframe) === 0) { $iframe = $this->match("frame[name={$name}]")->first(); } if (count($iframe) === 0) { throw new ElementNotFound("name={$name}", 'Iframe'); } $uri = $iframe->getNode(0)->getAttribute('src'); $this->amOnPage($uri); } /** * Moves back in history. * * @param int $numberOfSteps (default value 1) */ public function moveBack(int $numberOfSteps = 1): void { $request = null; if (!is_int($numberOfSteps) || $numberOfSteps < 1) { throw new InvalidArgumentException('numberOfSteps must be positive integer'); } try { $history = $this->getRunningClient()->getHistory(); for ($i = $numberOfSteps; $i > 0; --$i) { $request = $history->back(); } } catch (LogicException $exception) { throw new InvalidArgumentException( sprintf( 'numberOfSteps is set to %d, but there are only %d previous steps in the history', $numberOfSteps, $numberOfSteps - $i ), $exception->getCode(), $exception); } $this->_loadPage( $request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent() ); } protected function debugCookieJar(): void { $cookies = $this->client->getCookieJar()->all(); $cookieStrings = array_map('strval', $cookies); $this->debugSection('Cookie Jar', $cookieStrings); } protected function setCookiesFromOptions() { if (isset($this->config['cookies']) && is_array($this->config['cookies']) && !empty($this->config['cookies'])) { $domain = parse_url($this->config['url'], PHP_URL_HOST); $cookieJar = $this->client->getCookieJar(); foreach ($this->config['cookies'] as &$cookie) { if (!is_array($cookie) || !array_key_exists('Name', $cookie) || !array_key_exists('Value', $cookie)) { throw new InvalidArgumentException('Cookies must have at least Name and Value attributes'); } if (!isset($cookie['Domain'])) { $cookie['Domain'] = $domain; } if (!isset($cookie['Expires'])) { $cookie['Expires'] = null; } if (!isset($cookie['Path'])) { $cookie['Path'] = '/'; } if (!isset($cookie['Secure'])) { $cookie['Secure'] = false; } if (!isset($cookie['HttpOnly'])) { $cookie['HttpOnly'] = false; } $cookieJar->set(new Cookie( $cookie['Name'], $cookie['Value'], $cookie['Expires'], $cookie['Path'], $cookie['Domain'], $cookie['Secure'], $cookie['HttpOnly'] )); } } } protected function getNormalizedResponseContent(): string { $content = $this->_getResponseContent(); // Since strip_tags has problems with JS code that contains // an <= operator the script tags have to be removed manually first. $content = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $content); $content = strip_tags($content); $content = html_entity_decode($content, ENT_QUOTES); $content = str_replace("\n", ' ', $content); return preg_replace('#\s{2,}#', ' ', $content); } /** * Sets SERVER parameters valid for all next requests. * this will remove old ones. * * ```php * $I->setServerParameters([]); * ``` */ public function setServerParameters(array $params): void { $this->client->setServerParameters($params); } /** * Sets SERVER parameter valid for all next requests. * * ```php * $I->haveServerParameter('name', 'value'); * ``` */ public function haveServerParameter(string $name, string $value): void { $this->client->setServerParameter($name, $value); } /** * Prevents automatic redirects to be followed by the client. * * ```php * <?php * $I->stopFollowingRedirects(); * ``` */ public function stopFollowingRedirects(): void { $this->client->followRedirects(false); } /** * Enables automatic redirects to be followed by the client. * * ```php * <?php * $I->startFollowingRedirects(); * ``` */ public function startFollowingRedirects(): void { $this->client->followRedirects(true); } /** * Follow pending redirect if there is one. * * ```php * <?php * $I->followRedirect(); * ``` */ public function followRedirect(): void { $this->client->followRedirect(); } /** * Sets the maximum number of redirects that the Client can follow. * * ```php * <?php * $I->setMaxRedirects(2); * ``` */ public function setMaxRedirects(int $maxRedirects): void { $this->client->setMaxRedirects($maxRedirects); } } <?php declare(strict_types=1); namespace Codeception\Constraint; use Codeception\Exception\ElementNotFound; use DOMElement; use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\Comparator\ComparisonFailure; use Symfony\Component\DomCrawler\Crawler as SymfonyDomCrawler; use function rtrim; use function sprintf; use function strpos; class Crawler extends Page { /** * @param SymfonyDomCrawler $nodes * @return bool */ protected function matches($nodes): bool { if (!$nodes->count()) { return false; } if ($this->string === '') { return true; } foreach ($nodes as $node) { if (parent::matches($node->nodeValue)) { return true; } } return false; } /** * @param SymfonyDomCrawler $nodes * @param string $selector * @param ComparisonFailure|null $comparisonFailure */ protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null): void { 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'); if ($nodes->count() < 10) { $output .= $this->nodesList($nodes); } else { $output = sprintf('%s [total %d elements]', rtrim($output, ' '), $nodes->count()); } $output .= "\ncontains text '{$this->string}'"; throw new ExpectationFailedException( $output, $comparisonFailure ); } /** * @param DOMElement[] $other * @return string */ protected function failureDescription($other): string { $description = ''; foreach ($other as $o) { $description .= parent::failureDescription($o->textContent); } return $description; } protected function nodesList(SymfonyDomCrawler $domCrawler, string $contains = null): string { $output = ''; foreach ($domCrawler as $node) { if ($contains && strpos($node->nodeValue, $contains) === false) { continue; } $output .= "\n+ " . $node->C14N(); } return $output; } } <?php declare(strict_types=1); namespace Codeception\Constraint; use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\Comparator\ComparisonFailure; use Symfony\Component\DomCrawler\Crawler as SymfonyCrawler; class CrawlerNot extends Crawler { /** * @param SymfonyCrawler $nodes * @return bool */ protected function matches($nodes): bool { return !parent::matches($nodes); } /** * @param SymfonyCrawler $nodes * @param string $selector * @param ComparisonFailure|null $comparisonFailure */ protected function fail($nodes, $selector, ComparisonFailure $comparisonFailure = null): void { if (!$this->string) { throw new 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 ExpectationFailedException( $output, $comparisonFailure ); } public function toString(): string { if ($this->string) { return 'that contains text "' . $this->string . '"'; } return ''; } } <?php declare(strict_types=1); namespace Codeception\Util; /** * Class containing constants of HTTP Status Codes * and method to print HTTP code with its description. * * Usage: * * ```php * <?php * use \Codeception\Util\HttpCode; * * // using REST, PhpBrowser, or any Framework module * $I->seeResponseCodeIs(HttpCode::OK); * $I->dontSeeResponseCodeIs(HttpCode::NOT_FOUND); * ``` */ class HttpCode { // const CONTINUE = 100; /** * @var int */ public const SWITCHING_PROTOCOLS = 101; /** * @var int */ public const PROCESSING = 102; // RFC2518 /** * @var int */ public const EARLY_HINTS = 103; // RFC8297 /** * @var int */ public const OK = 200; /** * @var int */ public const CREATED = 201; /** * @var int */ public const ACCEPTED = 202; /** * @var int */ public const NON_AUTHORITATIVE_INFORMATION = 203; /** * @var int */ public const NO_CONTENT = 204; /** * @var int */ public const RESET_CONTENT = 205; /** * @var int */ public const PARTIAL_CONTENT = 206; /** * @var int */ public const MULTI_STATUS = 207; // RFC4918 /** * @var int */ public const ALREADY_REPORTED = 208; // RFC5842 /** * @var int */ public const IM_USED = 226; // RFC3229 /** * @var int */ public const MULTIPLE_CHOICES = 300; /** * @var int */ public const MOVED_PERMANENTLY = 301; /** * @var int */ public const FOUND = 302; /** * @var int */ public const SEE_OTHER = 303; /** * @var int */ public const NOT_MODIFIED = 304; /** * @var int */ public const USE_PROXY = 305; /** * @var int */ public const RESERVED = 306; /** * @var int */ public const TEMPORARY_REDIRECT = 307; /** * @var int */ public const PERMANENTLY_REDIRECT = 308; // RFC7238 /** * @var int */ public const BAD_REQUEST = 400; /** * @var int */ public const UNAUTHORIZED = 401; /** * @var int */ public const PAYMENT_REQUIRED = 402; /** * @var int */ public const FORBIDDEN = 403; /** * @var int */ public const NOT_FOUND = 404; /** * @var int */ public const METHOD_NOT_ALLOWED = 405; /** * @var int */ public const NOT_ACCEPTABLE = 406; /** * @var int */ public const PROXY_AUTHENTICATION_REQUIRED = 407; /** * @var int */ public const REQUEST_TIMEOUT = 408; /** * @var int */ public const CONFLICT = 409; /** * @var int */ public const GONE = 410; /** * @var int */ public const LENGTH_REQUIRED = 411; /** * @var int */ public const PRECONDITION_FAILED = 412; /** * @var int */ public const REQUEST_ENTITY_TOO_LARGE = 413; /** * @var int */ public const REQUEST_URI_TOO_LONG = 414; /** * @var int */ public const UNSUPPORTED_MEDIA_TYPE = 415; /** * @var int */ public const REQUESTED_RANGE_NOT_SATISFIABLE = 416; /** * @var int */ public const EXPECTATION_FAILED = 417; /** * @var int */ public const I_AM_A_TEAPOT = 418; // RFC2324 /** * @var int */ public const MISDIRECTED_REQUEST = 421; // RFC7540 /** * @var int */ public const UNPROCESSABLE_ENTITY = 422; // RFC4918 /** * @var int */ public const LOCKED = 423; // RFC4918 /** * @var int */ public const FAILED_DEPENDENCY = 424; // RFC4918 /** * @var int */ public const RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817 /** * @var int */ public const UPGRADE_REQUIRED = 426; // RFC2817 /** * @var int */ public const PRECONDITION_REQUIRED = 428; // RFC6585 /** * @var int */ public const TOO_MANY_REQUESTS = 429; // RFC6585 /** * @var int */ public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585 /** * @var int */ public const UNAVAILABLE_FOR_LEGAL_REASONS = 451; /** * @var int */ public const INTERNAL_SERVER_ERROR = 500; /** * @var int */ public const NOT_IMPLEMENTED = 501; /** * @var int */ public const BAD_GATEWAY = 502; /** * @var int */ public const SERVICE_UNAVAILABLE = 503; /** * @var int */ public const GATEWAY_TIMEOUT = 504; /** * @var int */ public const VERSION_NOT_SUPPORTED = 505; /** * @var int */ public const VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295 /** * @var int */ public const INSUFFICIENT_STORAGE = 507; // RFC4918 /** * @var int */ public const LOOP_DETECTED = 508; // RFC5842 /** * @var int */ public const NOT_EXTENDED = 510; // RFC2774 /** * @var int */ public const NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585 /** * @var array<int, string> */ private static array $codes = [ 100 => 'Continue', 102 => 'Processing', 103 => 'Early Hints', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 208 => 'Already Reported', 226 => 'IM Used', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Reserved', 307 => 'Temporary Redirect', 308 => 'Permanent Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 418 => 'Unassigned', 421 => 'Misdirected Request', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Too Early', 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 Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 510 => 'Not Extended', 511 => 'Network Authentication Required' ]; /** * Returns string with HTTP code and its description * * ```php * <?php * HttpCode::getDescription(200); // '200 (OK)' * HttpCode::getDescription(401); // '401 (Unauthorized)' * ``` */ public static function getDescription(int $code): int|string { if (isset(self::$codes[$code])) { return sprintf('%d (%s)', $code, self::$codes[$code]); } return $code; } } <?php declare(strict_types=1); namespace Codeception\Exception; use Exception; class ExternalUrlException extends Exception { } <?php declare(strict_types=1); namespace Codeception\Template; use Codeception\InitTemplate; use Codeception\Module\PhpBrowser; use Codeception\Util\Template; use Symfony\Component\Yaml\Yaml; class Api extends InitTemplate { protected string $configTemplate = <<<EOF # suite config suites: api: actor: ApiTester path: . modules: enabled: - REST: url: {{url}} depends: PhpBrowser step_decorators: - \Codeception\Step\AsJson paths: tests: {{baseDir}} output: {{baseDir}}/_output data: {{baseDir}}/Support/Data support: {{baseDir}}/Support settings: shuffle: false lint: true EOF; protected string $firstTest = <<<EOF <?php namespace {{namespace}}; use {{namespace}}\{{support_namespace}}\ApiTester; class ApiCest { public function tryApi(ApiTester \$I) { \$I->sendGet('/'); \$I->seeResponseCodeIs(200); \$I->seeResponseIsJson(); } } EOF; public function setup(): void { $this->checkInstalled(); $this->say("Let's prepare Codeception for REST API testing"); $this->say(''); $dir = $this->ask("Where tests will be stored?", 'tests'); $url = $this->ask("Start url for tests", "http://localhost/api"); if (!class_exists('\\Codeception\\Module\\REST') || !class_exists(PhpBrowser::class)) { $this->addModulesToComposer(['REST', 'PhpBrowser']); } $this->createDirectoryFor($outputDir = $dir . DIRECTORY_SEPARATOR . '_output'); $this->createDirectoryFor($supportDir = $dir . DIRECTORY_SEPARATOR . 'Support'); $this->createEmptyDirectory($supportDir . DIRECTORY_SEPARATOR . 'Data'); $this->createDirectoryFor($supportDir . DIRECTORY_SEPARATOR . '_generated'); $this->gitIgnore($outputDir); $this->gitIgnore($supportDir . DIRECTORY_SEPARATOR . '_generated'); $this->sayInfo("Created test directories inside at {$dir}"); $configFile = (new Template($this->configTemplate)) ->place('url', $url) ->place('baseDir', $dir) ->produce(); $namespace = rtrim($this->namespace, '\\'); $configFile = "namespace: $namespace\nsupport_namespace: {$this->supportNamespace}\n" . $configFile; $this->createFile('codeception.yml', $configFile); $settings = Yaml::parse($configFile)['suites']['api']; $settings['support_namespace'] = $this->supportNamespace; $this->createActor('ApiTester', $supportDir, $settings); $this->sayInfo("Created global config codeception.yml inside the root directory"); $this->createFile( $dir . DIRECTORY_SEPARATOR . 'ApiCest.php', (new Template($this->firstTest)) ->place('namespace', $this->namespace) ->place('support_namespace', $this->supportNamespace) ->produce() ); $this->sayInfo("Created a demo test ApiCest.php"); $this->say(); $this->saySuccess("INSTALLATION COMPLETE"); $this->say(); $this->say("<bold>Next steps:</bold>"); $this->say("1. Edit <bold>{$dir}/ApiCest.php</bold> to write first API tests"); $this->say("2. Run tests using: <comment>codecept run</comment>"); $this->say(); $this->say("<bold>Happy testing!</bold>"); } } <?php declare(strict_types=1); namespace Codeception\Template; use Codeception\Extension\RunFailed; use Codeception\InitTemplate; use Codeception\Module\Asserts; use Codeception\Module\PhpBrowser; use Symfony\Component\Yaml\Yaml; class Bootstrap extends InitTemplate { // defaults protected string $supportDir = 'tests/Support'; protected string $dataDir = 'tests/Support/Data'; protected string $envsDir = 'tests/_envs'; protected string $outputDir = 'tests/_output'; // default since v5 protected string $namespace = 'Tests'; protected string $supportNamespace = 'Support'; public function setup(): void { $this->checkInstalled($this->workDir); $input = $this->input; if ($input->getOption('namespace')) { $this->namespace = trim($input->getOption('namespace'), '\\'); } if ($input->hasOption('actor') && $input->getOption('actor')) { $this->actorSuffix = $input->getOption('actor'); } $this->say( "<fg=white;bg=magenta> Bootstrapping Codeception </fg=white;bg=magenta>\n" ); $this->createGlobalConfig(); $this->say("File codeception.yml created <- global configuration"); $this->createDirs(); if ($input->hasOption('empty') && $input->getOption('empty')) { return; } if (!class_exists(Asserts::class) || !class_exists(PhpBrowser::class)) { $this->addModulesToComposer(['PhpBrowser', 'Asserts']); } $this->createUnitSuite(); $this->createFunctionalSuite(); $this->createAcceptanceSuite(); $this->say(" --- "); $this->say(); $this->saySuccess('Codeception is installed for acceptance, functional, and unit testing'); $this->say(); $this->say("<bold>Next steps:</bold>"); $this->say('1. Edit <bold>tests/acceptance.suite.yml</bold> to set url of your application. Change PhpBrowser to WebDriver to enable browser testing'); $this->say("2. Edit <bold>tests/functional.suite.yml</bold> to enable a framework module. Remove this file if you don't use a framework"); $this->say("3. Create your first acceptance tests using <comment>codecept g:cest acceptance First</comment>"); $this->say("4. Write first test in <bold>tests/acceptance/FirstCest.php</bold>"); $this->say("5. Run tests using: <comment>codecept run</comment>"); } protected function createDirs(): void { $this->createDirectoryFor('tests'); $this->createDirectoryFor($this->outputDir); $this->createEmptyDirectory($this->dataDir); $this->createDirectoryFor($this->supportDir . DIRECTORY_SEPARATOR . '_generated'); $this->createDirectoryFor($this->supportDir . DIRECTORY_SEPARATOR . "Helper"); $this->gitIgnore($this->outputDir); $this->gitIgnore($this->supportDir . DIRECTORY_SEPARATOR . '/_generated'); } protected function createFunctionalSuite(string $actor = 'Functional'): void { $suiteConfig = <<<EOF # Codeception Test Suite Configuration # # Suite for functional tests # Emulate web requests and make application process them # Include one of framework modules (Symfony, Yii2, Laravel, Phalcon5) to use it # Remove this suite if you don't use frameworks actor: {$actor}{$this->actorSuffix} modules: enabled: # add a framework module here step_decorators: ~ EOF; $this->createSuite('Functional', $actor, $suiteConfig); $this->say("tests/Functional/ created <- functional tests"); $this->say("tests/Functional.suite.yml written <- functional test suite configuration"); } protected function createAcceptanceSuite(string $actor = 'Acceptance'): void { $suiteConfig = <<<EOF # Codeception Acceptance Test Suite Configuration # # Perform tests in a browser by either emulating one using PhpBrowser, or in a real browser using WebDriver. # If you need both WebDriver and PhpBrowser tests, create a separate suite for each. actor: {$actor}{$this->actorSuffix} modules: enabled: - PhpBrowser: url: http://localhost/myapp # Add Codeception\Step\Retry trait to AcceptanceTester to enable retries step_decorators: - Codeception\Step\ConditionalAssertion - Codeception\Step\TryTo - Codeception\Step\Retry EOF; $this->createSuite('Acceptance', $actor, $suiteConfig); $this->say("tests/Acceptance/ created <- acceptance tests"); $this->say("tests/Acceptance.suite.yml written <- acceptance test suite configuration"); } protected function createUnitSuite(string $actor = 'Unit'): void { $suiteConfig = <<<EOF # Codeception Test Suite Configuration # # Suite for unit or integration tests. actor: {$actor}{$this->actorSuffix} modules: enabled: - Asserts step_decorators: ~ EOF; $this->createSuite('Unit', $actor, $suiteConfig); $this->say("tests/Unit/ created <- unit tests"); $this->say("tests/Unit.suite.yml written <- unit test suite configuration"); } public function createGlobalConfig(): void { $basicConfig = [ 'support_namespace' => $this->supportNamespace, 'paths' => [ 'tests' => 'tests', 'output' => $this->outputDir, 'data' => $this->dataDir, 'support' => $this->supportDir, 'envs' => $this->envsDir, ], 'actor_suffix' => 'Tester', 'extensions' => [ 'enabled' => [RunFailed::class] ] ]; $str = Yaml::dump($basicConfig, 4); if ($this->namespace !== '') { $namespace = rtrim($this->namespace, '\\'); $str = "namespace: {$namespace}\n" . $str; } $this->createFile('codeception.yml', $str); } protected function createSuite(string $suite, string $actor, string $config): void { $settings = Yaml::parse($config); $settings['support_namespace'] = $this->supportNamespace; $this->createDirectoryFor("tests/{$suite}", "{$suite}.suite.yml"); $this->createActor($actor . $this->actorSuffix, $this->supportDir, $settings); $this->createFile('tests' . DIRECTORY_SEPARATOR . "{$suite}.suite.yml", $config); } } <?php declare(strict_types=1); namespace Codeception\Template; use Codeception\InitTemplate; use Codeception\Module\Asserts; use Codeception\Util\Template; use Symfony\Component\Yaml\Yaml; class Unit extends InitTemplate { protected string $configTemplate = <<<EOF suites: unit: path: . {{tester}} settings: shuffle: true lint: true paths: tests: {{baseDir}} output: {{baseDir}}/_output support: {{baseDir}}/Support data: {{baseDir}}/Support/Data EOF; protected string $testerAndModules = <<<EOF actor: UnitTester modules: enabled: # add more modules here - Asserts step_decorators: ~ EOF; public function setup(): void { $this->sayInfo('This will install Codeception for unit testing only'); $this->say(); $dir = $this->ask("Where tests will be stored?", 'tests'); if ($this->namespace === '') { $this->namespace = $this->ask("Enter a default namespace for tests (or skip this step)"); } $this->say(); $this->say("Codeception provides additional features for integration tests"); $this->say("Like accessing frameworks, ORM, Database."); $haveTester = $this->ask("Do you wish to enable them?", false); $this->createDirectoryFor($outputDir = $dir . DIRECTORY_SEPARATOR . '_output'); $this->createDirectoryFor($supportDir = $dir . DIRECTORY_SEPARATOR . 'Support'); $this->createEmptyDirectory($supportDir . DIRECTORY_SEPARATOR . 'Data'); $this->createDirectoryFor($supportDir . DIRECTORY_SEPARATOR . '_generated'); $this->gitIgnore($outputDir); $this->gitIgnore($supportDir . DIRECTORY_SEPARATOR . '_generated'); $configFile = (new Template($this->configTemplate)) ->place('baseDir', $dir) ->place('tester', $haveTester ? $this->testerAndModules : '') ->produce(); $namespace = rtrim($this->namespace, '\\'); $configFile = "namespace: $namespace\nsupport_namespace: {$this->supportNamespace}\n" . $configFile; $this->createFile('codeception.yml', $configFile); if (!class_exists(Asserts::class)) { $this->addModulesToComposer(['Asserts']); } if ($haveTester) { $settings = Yaml::parse($configFile)['suites']['unit']; $settings['support_namespace'] = $this->supportNamespace; $this->createActor('UnitTester', $supportDir, $settings); } $this->gitIgnore($outputDir); $this->sayInfo("Created test directory inside at {$dir}"); $this->say(); $this->saySuccess("INSTALLATION COMPLETE"); $this->say(); $this->say('Unit tests will be executed in random order'); $this->say('Use @depends annotation to change the order of tests'); if ($haveTester) { $this->say('To access DI, ORM, Database enable corresponding modules in codeception.yml'); $this->say('Use <bold>$this->tester</bold> object inside Codeception\Test\Unit to call their methods'); $this->say("For example: \$this->tester->seeInDatabase('users', ['name' => 'davert'])"); } $this->say(); $this->say("<bold>Next steps:</bold>"); $this->say("Create the first test using <comment>codecept g:test unit MyTest</comment>"); $this->say("Run tests with <comment>codecept run</comment>"); $this->say("<bold>Happy testing!</bold>"); } } <?php declare(strict_types=1); namespace Codeception\Template; use Codeception\InitTemplate; use Codeception\Util\Template; use Symfony\Component\Yaml\Yaml; class Acceptance extends InitTemplate { protected string $configTemplate = <<<EOF # suite config suites: acceptance: actor: AcceptanceTester path: . modules: enabled: - WebDriver: url: {{url}} browser: {{browser}} # add Codeception\Step\Retry trait to AcceptanceTester to enable retries step_decorators: - Codeception\Step\ConditionalAssertion - Codeception\Step\TryTo - Codeception\Step\Retry extensions: enabled: [Codeception\Extension\RunFailed] params: - env gherkin: [] # additional paths paths: tests: {{baseDir}} output: {{baseDir}}/_output data: {{baseDir}}/Support/Data support: {{baseDir}}/Support envs: {{baseDir}}/_envs settings: shuffle: false lint: true EOF; protected string $firstTest = <<<EOF <?php namespace {{namespace}}; use {{namespace}}\{{support_namespace}}\AcceptanceTester; class LoginCest { public function _before(AcceptanceTester \$I) { \$I->amOnPage('/'); } public function loginSuccessfully(AcceptanceTester \$I) { // write a positive login test } public function loginWithInvalidPassword(AcceptanceTester \$I) { // write a negative login test } } EOF; public function setup(): void { $this->checkInstalled(); $this->say("Let's prepare Codeception for acceptance testing"); $this->say("Create your tests and run them in real browser"); $this->say(''); $dir = $this->ask("Where tests will be stored?", 'tests'); $browser = $this->ask("Select a browser for testing", ['chrome', 'firefox']); if ($browser === 'chrome') { $this->sayInfo("Ensure that you have Selenium Server and ChromeDriver installed before running tests"); } if ($browser === 'firefox') { $this->sayInfo("Ensure that you have Selenium Server and GeckoDriver installed before running tests"); } $url = $this->ask("Start url for tests", "http://localhost"); $this->createDirectoryFor($outputDir = $dir . DIRECTORY_SEPARATOR . '_output'); $this->createDirectoryFor($supportDir = $dir . DIRECTORY_SEPARATOR . 'Support'); $this->createEmptyDirectory($supportDir . DIRECTORY_SEPARATOR . 'Data'); $this->createDirectoryFor($supportDir . DIRECTORY_SEPARATOR . '_generated'); $this->gitIgnore($outputDir); $this->gitIgnore($supportDir . DIRECTORY_SEPARATOR . '_generated'); $this->sayInfo("Created test directories inside at {$dir}"); if (!class_exists('\\Codeception\\Module\\WebDriver')) { // composer version $this->addModulesToComposer(['WebDriver']); } $configFile = (new Template($this->configTemplate)) ->place('url', $url) ->place('browser', $browser) ->place('baseDir', $dir) ->produce(); $namespace = rtrim($this->namespace, '\\'); $configFile = "namespace: $namespace\nsupport_namespace: {$this->supportNamespace}\n" . $configFile; $this->createFile('codeception.yml', $configFile); $settings = Yaml::parse($configFile)['suites']['acceptance']; $settings['support_namespace'] = $this->supportNamespace; $this->createActor('AcceptanceTester', $supportDir, $settings); $this->sayInfo("Created global config codeception.yml inside the root directory"); $this->createFile( $dir . DIRECTORY_SEPARATOR . 'LoginCest.php', (new Template($this->firstTest)) ->place('namespace', $this->namespace) ->place('support_namespace', $this->supportNamespace) ->produce() ); $this->sayInfo("Created a demo test LoginCest.php"); $this->say(); $this->saySuccess("INSTALLATION COMPLETE"); $this->say(); $this->say("<bold>Next steps:</bold>"); $this->say('1. Launch Selenium Server and webserver'); $this->say("2. Edit <bold>{$dir}/LoginCest.php</bold> to test login of your application"); $this->say("3. Run tests using: <comment>codecept run</comment>"); $this->say(); $this->say("HINT: Add '\\Codeception\\Step\\Retry' trait to AcceptanceTester class to enable auto-retries"); $this->say("HINT: See https://codeception.com/docs/03-AcceptanceTests#retry"); $this->say("<bold>Happy testing!</bold>"); } } <?php declare(strict_types=1); namespace Codeception\Template; use Codeception\Configuration; use Codeception\InitTemplate; use Exception; class Dependencies extends InitTemplate { /** * @var string */ public const DONATE_LINK = 'https://opencollective.com/codeception'; public function setup(): void { if (!$this->isInstalled()) { $this->sayWarning('Codeception is not installed in this dir.'); return; } $this->sayInfo('Install Codeception Modules as Composer Packages'); $this->say(''); $config = Configuration::config(); $modules = []; $suites = Configuration::suites(); if (empty($suites)) { $this->sayError("No suites found in current config."); $this->sayWarning('If you use sub-configs with `include` option, run this script on subconfigs:'); $this->sayWarning('Example: php vendor/bin/codecept init dependencies -c backend/'); throw new Exception("No suites found, can't upgrade"); } foreach (Configuration::suites() as $suite) { $suiteConfig = Configuration::suiteSettings($suite, $config); $modules = array_merge($modules, Configuration::modules($suiteConfig)); } $numPackages = $this->addModulesToComposer($modules); if ($numPackages === 0) { $this->sayWarning("No change needed! Everything is installed"); return; } $this->saySuccess("Done installing!"); $this->say(''); $this->say('Please consider donating to Codeception on regular basis:'); $this->say(''); $this->say('<bold>' . self::DONATE_LINK . '</bold>'); $this->say(''); $this->say("It's ok to pay for reliable software."); $this->say('Talk to your manager & support further development of Codeception!'); } private function isInstalled(): bool { try { $this->checkInstalled(); } catch (Exception) { return true; } return false; } } <?php declare(strict_types=1); namespace Codeception; use ArrayAccess; use ArrayIterator; use Countable; use IteratorAggregate; use PHPUnit\Framework\AssertionFailedError; use Traversable; class Example implements ArrayAccess, Countable, IteratorAggregate { public function __construct(protected $data) { } /** * Whether an offset exists * * @link https://php.net/manual/en/arrayaccess.offsetexists.php * @param mixed $offset <p>An offset to check for.</p> * @return bool true on success or false on failure. * The return value will be casted to boolean if non-boolean was returned. */ public function offsetExists(mixed $offset): bool { return array_key_exists($offset, $this->data); } /** * Offset to retrieve * * @link https://php.net/manual/en/arrayaccess.offsetget.php * @param mixed $offset <p>The offset to retrieve.</p> * @return mixed Can return all value types. */ public function offsetGet(mixed $offset): mixed { if (!$this->offsetExists($offset)) { throw new AssertionFailedError(sprintf("Example %s doesn't exist", $offset)); } return $this->data[$offset]; } /** * Offset to set * * @link https://php.net/manual/en/arrayaccess.offsetset.php * @param mixed $offset <p>The offset to assign the value to.</p> * @param mixed $value <p>The value to set.</p> */ public function offsetSet(mixed $offset, mixed $value): void { $this->data[$offset] = $value; } /** * Offset to unset * * @link https://php.net/manual/en/arrayaccess.offsetunset.php * @param mixed $offset <p>The offset to unset.</p> */ public function offsetUnset(mixed $offset): void { unset($this->data[$offset]); } /** * Count elements of an object * * @link https://php.net/manual/en/countable.count.php * @return int The custom count as an integer. * The return value is cast to an integer. */ public function count(): int { return count($this->data); } /** * Retrieve an external iterator * * @link https://php.net/manual/en/iteratoraggregate.getiterator.php * @return Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b> */ public function getIterator(): Traversable { return new ArrayIterator($this->data); } } <?php declare(strict_types=1); namespace Codeception; use ArrayAccess; use Codeception\Exception\ModuleConfigException; use Codeception\Exception\ModuleException; use Codeception\Lib\Interfaces\RequiresPackage; use Codeception\Lib\ModuleContainer; use Codeception\Util\Shared\Asserts; use Exception; /** * Basic class for Modules and Helpers. * You must extend from it while implementing own helpers. * * Public methods of this class start with `_` prefix in order to ignore them in actor classes. * Module contains **HOOKS** which allow to handle test execution routine. * */ abstract class Module { use Asserts; /** * By setting it to false module wan't inherit methods of parent class. */ public static bool $includeInheritedActions = true; /** * Allows to explicitly set what methods have this class. */ public static array $onlyActions = []; /** * Allows to explicitly exclude actions from module. */ public static array $excludeActions = []; /** * Allows to rename actions */ public static array $aliases = []; protected array $storage = []; protected array $config = []; protected array $backupConfig = []; protected array $requiredFields = []; /** * Module constructor. * * Requires module container (to provide access between modules of suite) and config. */ public function __construct(protected ModuleContainer $moduleContainer, ?array $config = null) { $this->backupConfig = $this->config; if (is_array($config)) { $this->_setConfig($config); } } /** * Allows to define initial module config. * Can be used in `_beforeSuite` hook of Helpers or Extensions * * ```php * <?php * public function _beforeSuite($settings = []) { * $this->getModule('otherModule')->_setConfig($this->myOtherConfig); * } * ``` * * @throws ModuleConfigException|ModuleException */ public function _setConfig(array $config): void { $this->config = array_merge($this->config, $config); $this->backupConfig = $this->config; $this->validateConfig(); } /** * Allows to redefine config for a specific test. * Config is restored at the end of a test. * * ```php * <?php * // cleanup DB only for specific group of tests * public function _before(Test $test) { * if (in_array('cleanup', $test->getMetadata()->getGroups()) { * $this->getModule('Db')->_reconfigure(['cleanup' => true]); * } * } * ``` * * @throws ModuleConfigException|ModuleException */ public function _reconfigure(array $config): void { $this->config = array_merge($this->backupConfig, $config); $this->onReconfigure(); $this->validateConfig(); } /** * HOOK to be executed when config changes with `_reconfigure`. */ protected function onReconfigure() { // update client on reconfigurations } /** * Reverts config changed by `_reconfigure` */ public function _resetConfig(): void { $this->config = $this->backupConfig; } /** * Validates current config for required fields and required packages. * * @throws ModuleConfigException|ModuleException */ protected function validateConfig(): void { $fields = array_keys($this->config); if (array_intersect($this->requiredFields, $fields) !== $this->requiredFields) { throw new ModuleConfigException( $this::class, "\nOptions: " . implode(', ', $this->requiredFields) . " are required\n" . "Please, update the configuration and set all the required fields\n\n" ); } if ($this instanceof RequiresPackage) { $errorMessage = ''; foreach ($this->_requires() as $className => $package) { if (class_exists($className)) { continue; } $errorMessage .= "Class {$className} can't be loaded, please add {$package} to composer.json\n"; } if ($errorMessage !== '') { throw new ModuleException($this, $errorMessage); } } } /** * Returns a module name for a Module, a class name for Helper */ public function _getName(): string { $moduleName = '\\' . $this::class; if (str_starts_with($moduleName, ModuleContainer::MODULE_NAMESPACE)) { return substr($moduleName, strlen(ModuleContainer::MODULE_NAMESPACE)); } return $moduleName; } /** * Checks if a module has required fields */ public function _hasRequiredFields(): bool { return !empty($this->requiredFields); } /** * **HOOK** triggered after module is created and configuration is loaded */ public function _initialize() { } /** * **HOOK** executed before suite */ public function _beforeSuite(array $settings = []) { } /** * **HOOK** executed after suite */ public function _afterSuite() { } /** * **HOOK** executed before step */ public function _beforeStep(Step $step) { } /** * **HOOK** executed after step */ public function _afterStep(Step $step) { } /** * **HOOK** executed before test */ public function _before(TestInterface $test) { } /** * **HOOK** executed after test */ public function _after(TestInterface $test) { } /** * **HOOK** executed when test fails but before `_after` */ public function _failed(TestInterface $test, Exception $fail) { } /** * Print debug message to the screen. */ protected function debug(mixed $message): void { codecept_debug($message); } /** * Print debug message with a title */ protected function debugSection(string $title, mixed $message): void { if (is_array($message) || is_object($message)) { $message = stripslashes(json_encode($message, JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE)); } $this->debug("[{$title}] {$message}"); } /** * Short text message to an amount of chars */ protected function shortenMessage(string $message, int $chars = 150): string { return mb_substr($message, 0, $chars, 'utf-8'); } /** * Checks that module is enabled. */ protected function hasModule(string $name): bool { return $this->moduleContainer->hasModule($name); } /** * Get all enabled modules */ protected function getModules(): array { return $this->moduleContainer->all(); } /** * Get another module by its name: * * ```php * <?php * $this->getModule('WebDriver')->_findElements('.items'); * ``` * * @throws ModuleException */ protected function getModule(string $name): Module { if (!$this->hasModule($name)) { $this->moduleContainer->throwMissingModuleExceptionWithSuggestion(__CLASS__, $name); } return $this->moduleContainer->getModule($name); } /** * Get config values or specific config item. * * @param string|null $key * @return mixed the config item's value or null if it doesn't exist */ public function _getConfig(string $key = null): mixed { if (!$key) { return $this->config; } if (isset($this->config[$key])) { return $this->config[$key]; } return null; } protected function scalarizeArray(array $array): array { foreach ($array as $k => $v) { if (!is_null($v) && !is_scalar($v)) { $array[$k] = (is_array($v) || $v instanceof ArrayAccess) ? $this->scalarizeArray($v) : (string)$v; } } return $array; } } <?php declare(strict_types=1); namespace Codeception; /** * Contains all events dispatched by Codeception. * * @author tiger-seo <tiger.seo@gmail.com> */ final class Events { /** * Private constructor. This class cannot be instantiated. */ private function __construct() { } /** * The <b>MODULE_INIT</b> event occurs before modules are initialized. * * The event listener method receives a {@link \Codeception\Event\SuiteEvent} instance. * * @var string */ public const MODULE_INIT = 'module.init'; /** * The <b>SUITE_INIT</b> event occurs when suite is initialized. * Modules are created and initialized, but Actor class is not loaded. * * The event listener method receives a {@link \Codeception\Event\SuiteEvent} instance. * * @var string */ public const SUITE_INIT = 'suite.init'; /** * The <b>SUITE_BEFORE</b> event occurs before suite is executed. * * The event listener method receives a {@link \Codeception\Event\SuiteEvent} instance. * * @var string */ public const SUITE_BEFORE = 'suite.before'; /** * The <b>SUITE_AFTER</b> event occurs after suite has been executed. * * The event listener method receives a {@link \Codeception\Event\SuiteEvent} instance. * * @var string */ public const SUITE_AFTER = 'suite.after'; /** * The event listener method receives a {@link \Codeception\Event\TestEvent} instance. * * @var string */ public const TEST_START = 'test.start'; /** * The event listener method receives a {@link \Codeception\Event\TestEvent} instance. * * @var string */ public const TEST_BEFORE = 'test.before'; /** * The event listener method receives a {@link \Codeception\Event\StepEvent} instance. * * @var string */ public const STEP_BEFORE = 'step.before'; /** * The event listener method receives a {@link \Codeception\Event\StepEvent} instance. * * @var string */ public const STEP_AFTER = 'step.after'; /** * The <b>TEST_FAIL</b> event occurs whenever test has failed. * * The event listener method receives a {@link \Codeception\Event\FailEvent} instance. * * @var string */ public const TEST_FAIL = 'test.fail'; /** * The <b>TEST_ERROR</b> event occurs whenever test got an error while being executed. * * The event listener method receives a {@link \Codeception\Event\FailEvent} instance. * * @var string */ public const TEST_ERROR = 'test.error'; /** * The event listener method receives a {@link \Codeception\Event\TestEvent} instance. * * @var string */ public const TEST_PARSED = 'test.parsed'; /** * The event listener method receives a {@link \Codeception\Event\FailEvent} instance. * * @var string */ public const TEST_INCOMPLETE = 'test.incomplete'; /** * The event listener method receives a {@link \Codeception\Event\FailEvent} instance. * * @var string */ public const TEST_SKIPPED = 'test.skipped'; /** * The event listener method receives a {@link \Codeception\Event\FailEvent} instance. * * @var string */ public const TEST_WARNING = 'test.warning'; /** * The <b>TEST_USELESS</b> event occurs whenever test does not execute any assertions * or when it calls expectNotToPerformAssertions and then performs some assertion. * * The event listener method receives a {@link \Codeception\Event\FailEvent} instance. * * @var string */ public const TEST_USELESS = 'test.useless'; /** * The event listener method receives a {@link \Codeception\Event\TestEvent} instance. * * @var string */ public const TEST_SUCCESS = 'test.success'; /** * The event listener method receives a {@link \Codeception\Event\TestEvent} instance. * * @var string */ public const TEST_AFTER = 'test.after'; /** * The event listener method receives a {@link \Codeception\Event\TestEvent} instance. * * @var string */ public const TEST_END = 'test.end'; /** * The event listener method receives a {@link \Codeception\Event\FailEvent} instance. * * @var string */ public const TEST_FAIL_PRINT = 'test.fail.print'; /** * The event listener method receives a {@link \Codeception\Event\PrintResultEvent} instance. * * @var string */ public const RESULT_PRINT_AFTER = 'result.print.after'; } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Exception\TestParseException; use Codeception\Lib\Console\Message; use Codeception\Lib\Parser; use Exception; use ParseError; use function basename; use function file_get_contents; /** * Executes tests delivered in Cept format. * Prepares metadata, parses test body on preload, and executes a test in `test` method. * * Note: If the time came to delete Cept format, please delete Actor::wantTo method too */ class Cept extends Test implements Interfaces\Plain, Interfaces\ScenarioDriven, Interfaces\Reported, Interfaces\Dependent { use Feature\ScenarioLoader; protected Parser $parser; public function __construct(string $name, string $file) { $metadata = new Metadata(); $metadata->setName($name); $metadata->setFilename($file); $this->setMetadata($metadata); $this->createScenario(); $this->parser = new Parser($this->getScenario(), $this->getMetadata()); } public function __clone(): void { $this->scenario = clone $this->scenario; } public function preload(): void { $this->getParser()->prepareToRun($this->getSourceCode()); } public function test(): void { $scenario = $this->getScenario(); $testFile = $this->getMetadata()->getFilename(); try { require $testFile; } catch (ParseError $error) { throw new TestParseException($testFile, $error->getMessage(), $error->getLine()); } } public function getSignature(): string { return $this->getMetadata()->getName() . 'Cept'; } public function toString(): string { return $this->getSignature() . ': ' . Message::ucfirst($this->getFeature()); } public function getSourceCode(): string { $fileName = $this->getFileName(); if (!$sourceCode = file_get_contents($fileName)) { throw new Exception("Could not get content of file {$fileName}, please check its permissions."); } return $sourceCode; } /** * @return array<string, string> */ public function getReportFields(): array { return [ 'name' => basename($this->getFileName(), 'Cept.php'), 'file' => $this->getFileName(), 'feature' => $this->getFeature() ]; } protected function getParser(): Parser { return $this->parser; } public function fetchDependencies(): array { return $this->getMetadata()->getDependencies(); } } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Example; use Codeception\Exception\ConfigurationException; use Codeception\Exception\UselessTestException; use Codeception\Lib\Console\Message; use Codeception\Lib\Di; use Codeception\Lib\Parser; use Codeception\Step\Comment; use Codeception\Util\Annotation; use Codeception\Util\ReflectionHelper; use Exception; use LogicException; use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SkippedTest; use PHPUnit\Metadata\Api\CodeCoverage; use PHPUnit\Runner\Version as PHPUnitVersion; use PHPUnit\Util\Test as TestUtil; use ReflectionMethod; use function array_slice; use function file; use function implode; use function is_callable; use function method_exists; use function preg_replace; use function sprintf; use function strtolower; use function trim; /** * Executes tests delivered in Cest format. * * Handles loading of Cest cases, executing specific methods, following the order from `#Before` and `#After` attributes. */ class Cest extends Test implements Interfaces\ScenarioDriven, Interfaces\Reported, Interfaces\Dependent, Interfaces\StrictCoverage { use Feature\ScenarioLoader; protected Parser $parser; protected object $testInstance; protected string $testClass; protected string $testMethod; public function __construct(object $testInstance, string $methodName, string $fileName) { $metadata = new Metadata(); $metadata->setName($methodName); $metadata->setFilename($fileName); $classAnnotations = Annotation::forClass($testInstance); $metadata->setParamsFromAnnotations($classAnnotations->raw()); $metadata->setParamsFromAttributes($classAnnotations->attributes()); $methodAnnotations = Annotation::forMethod($testInstance, $methodName); $metadata->setParamsFromAnnotations($methodAnnotations->raw()); $metadata->setParamsFromAttributes($methodAnnotations->attributes()); $this->setMetadata($metadata); $this->testInstance = $testInstance; $this->testClass = $testInstance::class; $this->testMethod = $methodName; $this->createScenario(); $this->parser = new Parser($this->getScenario(), $this->getMetadata()); } public function __clone(): void { $this->scenario = clone $this->scenario; } public function preload(): void { $this->scenario->setFeature($this->getSpecFromMethod()); $code = $this->getSourceCode(); $this->parser->parseFeature($code); $this->getMetadata()->getService('di')->injectDependencies($this->testInstance); // add example params to feature if ($this->getMetadata()->getCurrent('example')) { $step = new Comment('', $this->getMetadata()->getCurrent('example')); $this->getScenario()->setFeature($this->getScenario()->getFeature() . ' | ' . $step->getArgumentsAsString(100)); } } public function getSourceCode(): string { $method = new ReflectionMethod($this->testInstance, $this->testMethod); $startLine = $method->getStartLine() - 1; // it's actually - 1, otherwise you wont get the function() block $endLine = $method->getEndLine(); $source = file($method->getFileName()); return implode("", array_slice($source, $startLine, $endLine - $startLine)); } public function getSpecFromMethod(): string { $text = $this->testMethod; $text = preg_replace('#([A-Z]+)([A-Z][a-z])#', '\\1 \\2', $text); $text = preg_replace('#([a-z\d])([A-Z])#', '\\1 \\2', $text); return strtolower($text); } public function test(): void { $actorClass = $this->getMetadata()->getCurrent('actor'); if ($actorClass === null) { throw new ConfigurationException( 'actor setting is missing in suite configuration. Replace `class_name` with `actor` in config to fix this' ); } /** @var Di $di */ $di = $this->getMetadata()->getService('di'); $di->set($this->getScenario()); $I = $di->instantiate($actorClass); try { $this->executeHook($I, 'before'); $this->executeBeforeMethods($this->testMethod, $I); $this->executeTestMethod($I); $this->executeAfterMethods($this->testMethod, $I); $this->executeHook($I, 'passed'); } catch (IncompleteTestError | SkippedTest | UselessTestException $exception) { // don't call failed hook throw $exception; } catch (Exception $exception) { $this->executeHook($I, 'failed'); throw $exception; } finally { $this->executeHook($I, 'after'); } } protected function executeHook($I, string $hook): void { if (is_callable([$this->testInstance, "_{$hook}"])) { $this->invoke("_{$hook}", [$I, $this->scenario]); } } protected function executeBeforeMethods(string $testMethod, $I): void { $methods = Annotation::forMethod($this->testClass, $testMethod)->fetchAll('before'); foreach ($methods as $method) { $this->executeContextMethod(trim($method), $I); } } protected function executeAfterMethods(string $testMethod, $I): void { $methods = Annotation::forMethod($this->testClass, $testMethod)->fetchAll('after'); foreach ($methods as $method) { $this->executeContextMethod(trim($method), $I); } } protected function executeContextMethod(string $context, $I): void { if (method_exists($this->testInstance, $context)) { $this->executeBeforeMethods($context, $I); $this->invoke($context, [$I, $this->scenario]); $this->executeAfterMethods($context, $I); return; } throw new LogicException( "Method {$context} defined in annotation but does not exist in " . $this->testClass ); } protected function invoke($methodName, array $context): void { foreach ($context as $class) { $this->getMetadata()->getService('di')->set($class); } $this->getMetadata()->getService('di')->injectDependencies($this->testInstance, $methodName, $context); } protected function executeTestMethod($I): void { if (!method_exists($this->testInstance, $this->testMethod)) { throw new Exception("Method {$this->testMethod} can't be found in tested class"); } if ($this->getMetadata()->getCurrent('example')) { $this->invoke( $this->testMethod, [$I, $this->scenario, new Example($this->getMetadata()->getCurrent('example'))] ); return; } $this->invoke($this->testMethod, [$I, $this->scenario]); } public function toString(): string { return sprintf( '%s: %s', ReflectionHelper::getClassShortName($this->getTestInstance()), Message::ucfirst($this->getFeature()), ); } public function getSignature(): string { return $this->testClass . ":" . $this->testMethod; } public function getTestInstance(): object { return $this->testInstance; } public function getTestMethod(): string { return $this->testMethod; } public function getReportFields(): array { return [ 'name' => $this->testMethod, 'class' => $this->testClass, 'file' => $this->getFileName(), 'feature' => $this->getFeature(), ]; } protected function getParser(): Parser { return $this->parser; } public function fetchDependencies(): array { $names = []; foreach ($this->getMetadata()->getDependencies() as $dependency) { foreach ((array)$dependency as $required) { if (!str_contains($required, ':') && method_exists($this->getTestInstance(), $required)) { $required = $this->testClass . ":{$required}"; } $names[] = $required; } } return $names; } public function getLinesToBeCovered(): array|bool { if (PHPUnitVersion::series() < 10) { return TestUtil::getLinesToBeCovered($this->testClass, $this->testMethod); } return (new CodeCoverage())->linesToBeCovered($this->testClass, $this->testMethod); } public function getLinesToBeUsed(): array { if (PHPUnitVersion::series() < 10) { return TestUtil::getLinesToBeUsed($this->testClass, $this->testMethod); } return (new CodeCoverage())->linesToBeUsed($this->testClass, $this->testMethod); } } <?php namespace Codeception\Test\Interfaces; use PHPUnit\Framework\SelfDescribing; interface Descriptive extends SelfDescribing { public function getFileName(): string; public function getSignature(): string; } <?php namespace Codeception\Test\Interfaces; interface StrictCoverage { public function getLinesToBeCovered(): array|bool; public function getLinesToBeUsed(): array; } <?php namespace Codeception\Test\Interfaces; interface Dependent { public function fetchDependencies(): array; } <?php namespace Codeception\Test\Interfaces; /** * TestCases that do not follow OOP */ interface Plain { } <?php namespace Codeception\Test\Interfaces; use Codeception\Scenario; interface ScenarioDriven { public function getFeature(): ?string; public function getScenario(): Scenario; public function getScenarioText(string $format = 'text'): string; public function preload(): void; public function getSourceCode(): string; } <?php namespace Codeception\Test\Interfaces; interface Reported { /** * Field values for XML reports */ public function getReportFields(): array; } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Test\Interfaces\Descriptive; use Codeception\Test\Interfaces\Plain; use Codeception\TestInterface; use PHPUnit\Framework\SelfDescribing; use function codecept_relative_path; use function json_encode; use function method_exists; use function preg_replace; use function realpath; use function sha1; use function str_replace; use function strtolower; use function substr; use function ucfirst; class Descriptor { /** * Provides a test name which can be located by */ public static function getTestSignature(Descriptive $test): string { return $test->getSignature(); } /** * Provides a test name which is unique for individual iterations of tests using examples */ public static function getTestSignatureUnique(SelfDescribing $testCase): string { $env = ''; $example = ''; if ( method_exists($testCase, 'getScenario') && !empty($testCase->getScenario()?->current('env')) ) { $env = ':' . $testCase->getScenario()->current('env'); } if ( method_exists($testCase, 'getMetaData') && !empty($testCase->getMetadata()->getCurrent('example')) ) { $currentExample = json_encode($testCase->getMetadata()->getCurrent('example'), JSON_THROW_ON_ERROR | JSON_INVALID_UTF8_SUBSTITUTE); $example = ':' . substr(sha1($currentExample), 0, 7); } return self::getTestSignature($testCase) . $env . $example; } public static function getTestAsString(SelfDescribing $testCase): string { return $testCase->toString(); } public static function getTestCaseNameAsString(string $testCaseName): string { $text = $testCaseName; $text = preg_replace('#([A-Z]+)([A-Z][a-z])#', '\\1 \\2', $text); $text = preg_replace('#([a-z\d])([A-Z])#', '\\1 \\2', $text); $text = preg_replace('#^test #', '', $text); $text = ucfirst(strtolower($text)); return str_replace(['::', 'with data set'], [':', '|'], $text); } /** * Provides a test file name relative to Codeception root */ public static function getTestFileName(Descriptive $test): string { return codecept_relative_path(realpath($test->getFileName())); } public static function getTestFullName(Plain|Descriptive $test): string { if ($test instanceof Plain) { return self::getTestFileName($test); } $signature = $test->getSignature(); // cut everything before ":" from signature return self::getTestFileName($test) . ':' . preg_replace('#^(.*?):#', '', $signature); } /** * Provides a test data set index */ public static function getTestDataSetIndex(SelfDescribing $testCase): string { if ($testCase instanceof TestInterface) { $index = $testCase->getMetadata()->getIndex(); if ($index === null) { return ''; } if (is_int($index)) { return ' with data set #' . $index; } return ' with data set "' . $index . '"'; } return ''; } } <?php declare(strict_types=1); namespace Codeception\Test\Loader; use Codeception\Test\Cept as CeptFormat; use function basename; class Cept implements LoaderInterface { /** * @var CeptFormat[] */ protected array $tests = []; public function getPattern(): string { return '~Cept\.php$~'; } public function loadTests(string $filename): void { $name = basename($filename, 'Cept.php'); $cept = new CeptFormat($name, $filename); $this->tests[] = $cept; } /** * @return CeptFormat[] */ public function getTests(): array { return $this->tests; } } <?php declare(strict_types=1); namespace Codeception\Test\Loader; use Codeception\Command\Shared\ActorTrait; use Codeception\Lib\Parser; use Codeception\Scenario; use Codeception\Test\Cest as CestFormat; use Codeception\Test\DataProvider; use ReflectionClass; use function get_class_methods; class Cest implements LoaderInterface { use ActorTrait; /** * @var CestFormat[] */ protected array $tests = []; protected array $settings = []; public function __construct(array $settings = []) { $this->settings = $settings; } /** * @return CestFormat[] */ public function getTests(): array { return $this->tests; } public function getPattern(): string { return '~Cest\.php$~'; } public function loadTests(string $filename): void { Parser::load($filename); $testClasses = Parser::getClassesFromFile($filename); foreach ($testClasses as $testClass) { if (!str_ends_with($testClass, 'Cest')) { continue; } if (!(new ReflectionClass($testClass))->isInstantiable()) { continue; } $unit = new $testClass(); $methods = get_class_methods($testClass); foreach ($methods as $method) { if (str_starts_with($method, '_')) { continue; } $test = new CestFormat($unit, $method, $filename); $I = $this->getActor($test); $examples = DataProvider::getDataForMethod( new \ReflectionMethod($testClass, $method), new \ReflectionClass($testClass), $I, ); if ($examples === null) { $this->tests[] = new CestFormat($unit, $method, $filename); continue; } foreach ($examples as $i => $example) { $test = new CestFormat($unit, $method, $filename); $test->getMetadata()->setCurrent(['example' => $example]); $test->getMetadata()->setIndex($i); $this->tests[] = $test; } } } } } <?php declare(strict_types=1); namespace Codeception\Test\Loader; use Codeception\Lib\Parser; use Codeception\Test\DataProvider; use Codeception\Test\TestCaseWrapper; use Codeception\Util\Annotation; use PHPUnit\Framework\TestCase; use PHPUnit\Runner\Version as PHPUnitVersion; use PHPUnit\Util\Test as TestUtil; use ReflectionClass; use ReflectionMethod; class Unit implements LoaderInterface { protected array $tests = []; public function getPattern(): string { return '~Test\.php$~'; } public function loadTests(string $filename): void { Parser::load($filename); $testClasses = Parser::getClassesFromFile($filename); foreach ($testClasses as $testClass) { $reflected = new ReflectionClass($testClass); if (!$reflected->isInstantiable()) { continue; } // find hook methods $beforeClassMethods = ['setUpBeforeClass']; $afterClassMethods = ['tearDownAfterClass']; foreach ($reflected->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { $methodName = $method->getName(); $methodAnnotations = Annotation::forMethod($testClass, $methodName); $beforeClassAnnotation = $methodAnnotations->fetch('beforeClass'); if ($beforeClassAnnotation !== null) { $beforeClassMethods [] = $methodName; } $afterClassAnnotation = $methodAnnotations->fetch('afterClass'); if ($afterClassAnnotation !== null) { $afterClassMethods [] = $methodName; } } foreach ($reflected->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { $tests = $this->createTestsFromPhpUnitMethod($reflected, $method); foreach ($tests as $test) { $this->tests[] = new TestCaseWrapper($test, $beforeClassMethods, $afterClassMethods); // only the first instance gets before/after class methods $beforeClassMethods = $afterClassMethods = []; } } } } public function getTests(): array { return $this->tests; } /** * @return TestCase[] */ protected function createTestsFromPhpUnitMethod(ReflectionClass $class, ReflectionMethod $method): array { if (!TestUtil::isTestMethod($method)) { return []; } $className = $class->getName(); $methodName = $method->getName(); $data = DataProvider::getDataForMethod($method, $class); if (!isset($data)) { return [ new $className($methodName) ]; } $result = []; foreach ($data as $key => $item) { if (PHPUnitVersion::series() < 10) { $testInstance = new $className($methodName, $item, $key); } else { $testInstance = new $className($methodName); $testInstance->setData($key, $item); } $result [] = $testInstance; } return $result; } /** * @param string[] $beforeClassMethods * @param string[] $afterClassMethods */ protected function enhancePhpunitTest( TestCase $testCase, array $beforeClassMethods, array $afterClassMethods, ): TestCaseWrapper { $test = new TestCaseWrapper($testCase, $beforeClassMethods, $afterClassMethods); return $test; } } <?php declare(strict_types=1); namespace Codeception\Test\Loader; use Behat\Gherkin\Filter\RoleFilter; use Behat\Gherkin\Keywords\ArrayKeywords as GherkinKeywords; use Behat\Gherkin\Lexer as GherkinLexer; use Behat\Gherkin\Node\ExampleNode; use Behat\Gherkin\Node\FeatureNode; use Behat\Gherkin\Node\OutlineNode; use Behat\Gherkin\Node\ScenarioInterface; use Behat\Gherkin\Node\ScenarioNode; use Behat\Gherkin\Parser as GherkinParser; use Codeception\Configuration; use Codeception\Exception\ParseException; use Codeception\Exception\TestParseException; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Test\Gherkin as GherkinFormat; use Codeception\Util\Annotation; use ReflectionClass; use function array_keys; use function array_map; use function array_merge; use function class_exists; use function dirname; use function file_get_contents; use function get_class_methods; use function glob; use function implode; use function preg_match; use function preg_quote; use function preg_replace; use function sprintf; use function str_replace; class Gherkin implements LoaderInterface { use Classname; protected static array $defaultSettings = [ 'namespace' => '', 'actor' => '', 'gherkin' => [ 'contexts' => [ 'default' => [], 'tag' => [], 'role' => [] ] ] ]; /** * @var GherkinFormat[] */ protected array $tests = []; protected GherkinParser $parser; protected array $settings = []; protected array $steps = []; /** * @param array<string, mixed> $settings * @throws TestParseException */ public function __construct(array $settings = []) { $this->settings = Configuration::mergeConfigs(self::$defaultSettings, $settings); if (!class_exists(GherkinKeywords::class)) { throw new TestParseException('Feature file can only be parsed with Behat\Gherkin library. Please install `behat/gherkin` with Composer'); } $gherkin = new ReflectionClass(\Behat\Gherkin\Gherkin::class); $gherkinClassPath = dirname($gherkin->getFileName()); $i18n = require $gherkinClassPath . '/../../../i18n.php'; $keywords = new GherkinKeywords($i18n); $lexer = new GherkinLexer($keywords); $this->parser = new GherkinParser($lexer); $this->fetchGherkinSteps(); } protected function fetchGherkinSteps(): void { $contexts = $this->settings['gherkin']['contexts']; foreach ($contexts['tag'] as $tag => $tagContexts) { $this->addSteps($tagContexts, "tag:{$tag}"); } foreach ($contexts['role'] as $role => $roleContexts) { $this->addSteps($roleContexts, "role:{$role}"); } if (empty($this->steps) && empty($contexts['default']) && $this->settings['actor']) { // if no context is set, actor to be a context $actorContext = $this->supportNamespace() . $this->settings['actor']; if ($actorContext) { $contexts['default'][] = $actorContext; } } if ( isset($this->settings['gherkin']['contexts']['path']) && isset($this->settings['gherkin']['contexts']['namespace_prefix']) ) { $files = glob($this->settings['gherkin']['contexts']['path'] . '/*/*.php'); // Strip off include path $files = str_replace([$this->settings['gherkin']['contexts']['path'], '.php', '/'], ['', '', '\\'], $files); // Add namespace prefix $namespace = $this->settings['gherkin']['contexts']['namespace_prefix']; $dynamicContexts = array_map(fn ($path): string => $namespace . $path, $files); $this->addSteps($dynamicContexts); } $this->addSteps($contexts['default']); } protected function addSteps(array $contexts, string $group = 'default'): void { if (!isset($this->steps[$group])) { $this->steps[$group] = []; } foreach ($contexts as $context) { if (is_string($context) && !class_exists($context)) { throw new \InvalidArgumentException( sprintf("Context class %s does not exist", $context) ); } $methods = get_class_methods((new \ReflectionClass($context))->newInstanceWithoutConstructor()); if ($methods === []) { continue; } foreach ($methods as $method) { $annotation = Annotation::forMethod($context, $method); foreach (['Given', 'When', 'Then'] as $type) { $patterns = $annotation->fetchAll($type); foreach ($patterns as $pattern) { if (!$pattern) { continue; } $this->validatePattern($pattern); $pattern = $this->makePlaceholderPattern($pattern); $this->steps[$group][$pattern] = [$context, $method]; } } } } } public function makePlaceholderPattern(string $pattern): string { if (isset($this->settings['describe_steps'])) { return $pattern; } if (!str_starts_with($pattern, '/')) { $pattern = preg_quote($pattern); $pattern = preg_replace('#(\w+)/(\w+)#', '(?:$1|$2)', $pattern); // or $pattern = preg_replace('#\\\\\((\w)\\\\\)#', '$1?', $pattern); // (s) $replacePattern = sprintf( '(?|\"%s\"|%s)', "((?|[^\"\\\\\\]|\\\\\\.)*?)", // matching escaped string in "" '[\D]{0,1}([\d\,\.]+)[\D]{0,1}' ); // or matching numbers with optional $ or € chars // params converting from :param to match 11 and "aaa" and "aaa\"aaa" $pattern = preg_replace('#"?\\\:(\w+)"?#', $replacePattern, $pattern); $pattern = "#^{$pattern}$#u"; // validating this pattern is slow, so we skip it now } return $pattern; } private function validatePattern(string $pattern): void { if (!str_starts_with($pattern, '/')) { return; // not a user-regex but a string with placeholder } if (@preg_match($pattern, ' ') === false) { throw new ParseException("Loading Gherkin step with regex\n \n{$pattern}\n \nfailed. This regular expression is invalid."); } } public function loadTests(string $filename): void { $featureNode = $this->parser->parse(file_get_contents($filename), $filename); if (!$featureNode instanceof FeatureNode) { return; } foreach ($featureNode->getScenarios() as $scenarioNode) { /** @var ScenarioInterface $scenarioNode */ $steps = $this->steps['default']; // load default context foreach (array_merge($scenarioNode->getTags(), $featureNode->getTags()) as $tag) { // load tag contexts if (isset($this->steps["tag:{$tag}"])) { $steps = array_merge($steps, $this->steps["tag:{$tag}"]); } } $roles = $this->settings['gherkin']['contexts']['role']; // load role contexts foreach (array_keys($roles) as $role) { $filter = new RoleFilter($role); if ($filter->isFeatureMatch($featureNode)) { $steps = array_merge($steps, $this->steps["role:{$role}"]); break; } } if ($scenarioNode instanceof OutlineNode) { foreach ($scenarioNode->getExamples() as $example) { /** @var ExampleNode $example */ $params = implode(', ', $example->getTokens()); $exampleNode = new ScenarioNode( $scenarioNode->getTitle() . " | {$params}", $scenarioNode->getTags(), $example->getSteps(), $example->getKeyword(), $example->getLine() ); $this->tests[] = new GherkinFormat($featureNode, $exampleNode, $steps); } continue; } $this->tests[] = new GherkinFormat($featureNode, $scenarioNode, $steps); } } /** * @return GherkinFormat[] */ public function getTests(): array { return $this->tests; } public function getPattern(): string { return '~\.feature$~'; } public function getSteps(): array { return $this->steps; } } <?php namespace Codeception\Test\Loader; interface LoaderInterface { public function loadTests(string $filename): void; public function getTests(): array; public function getPattern(): string; } <?php declare(strict_types=1); namespace Codeception\Test; use AllowDynamicProperties; use Codeception\Configuration; use Codeception\Exception\ModuleException; use Codeception\Lib\Di; use Codeception\Lib\PauseShell; use Codeception\Module; use Codeception\PHPUnit\TestCase; use Codeception\ResultAggregator; use Codeception\Scenario; use Codeception\Test\Feature\Stub; use Codeception\TestInterface; use Codeception\Util\Debug; use function get_class; use function lcfirst; use function method_exists; /** * Represents tests from PHPUnit compatible format. */ #[AllowDynamicProperties] class Unit extends TestCase implements Interfaces\Reported, Interfaces\Dependent, TestInterface { use Stub; private ?Metadata $metadata = null; private ?Scenario $scenario = null; public function __clone(): void { if ($this->scenario !== null) { $this->scenario = clone $this->scenario; } } public function getMetadata(): Metadata { if (!$this->metadata) { $this->metadata = new Metadata(); } return $this->metadata; } public function getScenario(): ?Scenario { return $this->scenario; } public function setMetadata(?Metadata $metadata): void { $this->metadata = $metadata; } public function getResultAggregator(): ResultAggregator { throw new \LogicException('This method should not be called, TestCaseWrapper class must be used instead'); } protected function _setUp() { if ($this->getMetadata()->isBlocked()) { if ($this->getMetadata()->getSkip() !== null) { $this->markTestSkipped($this->getMetadata()->getSkip()); } if ($this->getMetadata()->getIncomplete() !== null) { $this->markTestIncomplete($this->getMetadata()->getIncomplete()); } return; } /** @var Di $di */ $di = $this->getMetadata()->getService('di'); // auto-inject $tester property if (($this->getMetadata()->getCurrent('actor')) && ($property = lcfirst(Configuration::config()['actor_suffix']))) { $this->$property = $di->instantiate($this->getMetadata()->getCurrent('actor')); } $this->scenario = $di->get(Scenario::class); // Auto inject into the _inject method $di->injectDependencies($this); // injecting dependencies $this->_before(); } /** * @Override */ protected function _before() { } protected function _tearDown() { $this->_after(); } /** * @Override */ protected function _after() { } public function getModule(string $module): Module { $modules = $this->getMetadata()->getCurrent('modules'); if (!isset($modules[$module])) { throw new ModuleException($module, "Module can't be accessed"); } return $modules[$module]; } /** * Starts interactive pause in this test * * @param array<string, mixed> $vars * @return void */ public function pause(array $vars = []): void { if (!Debug::isEnabled()) { return; } $psy = (new PauseShell())->getShell(); $psy->setBoundObject($this); $psy->setScopeVariables($vars); $psy->run(); } /** * Returns current values */ public function getCurrent(?string $current): mixed { return $this->getMetadata()->getCurrent($current); } public function getReportFields(): array { return [ 'name' => $this->getName(false), 'class' => get_class($this), 'file' => $this->getMetadata()->getFilename() ]; } public function fetchDependencies(): array { $names = []; foreach ($this->getMetadata()->getDependencies() as $required) { if (!str_contains($required, ':') && method_exists($this, $required)) { $required = get_class($this) . ":{$required}"; } $names[] = $required; } return $names; } public function getFileName(): string { return $this->getMetadata()->getFilename(); } public function getSignature(): string { return $this->getName(false); } } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Exception\InjectionException; use Codeception\Util\Annotation; use function array_merge; use function array_merge_recursive; use function array_unique; class Metadata { protected ?string $name = null; protected ?string $filename = null; protected string $feature = ''; protected null|int|string $index = null; protected array $params = [ 'env' => [], 'group' => [], 'depends' => [], 'skip' => null, 'incomplete' => null ]; protected array $current = []; protected array $services = []; protected array $reports = []; /** * @var string[] */ private array $beforeClassMethods = []; /** * @var string[] */ private array $afterClassMethods = []; public function getEnv(): array { return $this->params['env']; } public function getGroups(): array { return array_unique($this->params['group']); } /** * @param string[] $groups */ public function setGroups(array $groups): void { $this->params['group'] = array_merge($this->params['group'], $groups); } public function getSkip(): ?string { return $this->params['skip']; } public function setSkip(string $skip): void { $this->params['skip'] = $skip; } public function getIncomplete(): ?string { return $this->params['incomplete']; } public function setIncomplete(string $incomplete): void { $this->params['incomplete'] = $incomplete; } public function getCurrent(?string $key = null): mixed { if ($key) { if (isset($this->current[$key])) { return $this->current[$key]; } if ($key === 'name') { return $this->getName(); } return null; } return $this->current; } public function setCurrent(array $currents): void { $this->current = array_merge($this->current, $currents); } public function getName(): string { return $this->name; } public function setName(string $name): void { $this->name = $name; } public function getFilename(): string { return $this->filename; } public function setIndex(int|string $index): void { $this->index = $index; } public function getIndex(): null|int|string { return $this->index; } public function setFilename(string $filename): void { $this->filename = $filename; } public function getDependencies(): array { return $this->params['depends']; } public function isBlocked(): bool { return $this->getSkip() !== null || $this->getIncomplete() !== null; } public function getFeature(): string { return $this->feature; } public function setFeature(string $feature): void { $this->feature = $feature; } public function getService(string $service): object { if (!isset($this->services[$service])) { throw new InjectionException("Service {$service} is not defined and can't be accessed from a test"); } return $this->services[$service]; } public function setServices(array $services): void { $this->services = $services; } /** * Returns all test reports */ public function getReports(): array { return $this->reports; } public function addReport(string $type, $report): void { $this->reports[$type] = $report; } /** * Returns test params like: env, group, skip, incomplete, etc. * Can return by annotation or return all if no key passed */ public function getParam(string $key = null): mixed { if ($key) { if (isset($this->params[$key])) { return $this->params[$key]; } return null; } return $this->params; } public function setParamsFromAnnotations($annotations): void { $params = Annotation::fetchAllAnnotationsFromDocblock((string)$annotations); $this->params = array_merge_recursive($this->params, $params); $this->setSingularValueForSomeParams(); } private function setSingularValueForSomeParams(): void { foreach (['skip', 'incomplete'] as $single) { if (is_array($this->params[$single])) { $this->params[$single] = $this->params[$single][0] ?? $this->params[$single][1] ?? ''; } } } public function setParamsFromAttributes($attributes): void { $params = []; foreach ($attributes as $attribute) { $name = lcfirst(str_replace('Codeception\\Attribute\\', '', $attribute->getName())); if ($attribute->isRepeated()) { $params[$name] ??= []; $params[$name][] = $attribute->getArguments(); continue; } $params[$name] = $attribute->getArguments(); } $this->params = array_merge_recursive($this->params, $params); // flatten arrays for some attributes foreach (['group', 'env', 'before', 'after', 'prepare'] as $single) { if (!isset($this->params[$single])) { continue; }; if (!is_array($this->params[$single])) { continue; }; $this->params[$single] = array_map(fn($a) => is_array($a) ? $a : [$a], $this->params[$single]); $this->params[$single] = array_merge(...$this->params[$single]); } $this->setSingularValueForSomeParams(); } /** * @deprecated */ public function setParams(array $params): void { $this->params = array_merge_recursive($this->params, $params); } /** * @param string[] $beforeClassMethods */ public function setBeforeClassMethods(array $beforeClassMethods): void { $this->beforeClassMethods = $beforeClassMethods; } /** * @return string[] */ public function getBeforeClassMethods(): array { return $this->beforeClassMethods; } /** * @param string[] $afterClassMethods */ public function setAfterClassMethods(array $afterClassMethods): void { $this->afterClassMethods = $afterClassMethods; } /** * @return string[] */ public function getAfterClassMethods(): array { return $this->afterClassMethods; } } <?php declare(strict_types=1); namespace Codeception\Test; class Filter { private ?string $namePattern = null; private ?int $filterMin = null; private ?int $filterMax = null; /** * @param string[] $includeGroups * @param string[] $excludeGroups * @param string $namePattern */ public function __construct( private ?array $includeGroups, private ?array $excludeGroups, ?string $namePattern ) { if ($namePattern === null) { return; } // Validates regexp without E_WARNING set_error_handler(function () { }, E_WARNING); $isRegularExpression = preg_match($namePattern, '') !== false; restore_error_handler(); if ($isRegularExpression === false) { // Handles: // * :testAssertEqualsSucceeds#4 // * "testAssertEqualsSucceeds#4-8 if (preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $namePattern, $matches)) { if (isset($matches[3]) && $matches[2] < $matches[3]) { $namePattern = sprintf( '%s.*with data set #(\d+)$', $matches[1] ); $this->filterMin = (int)$matches[2]; $this->filterMax = (int)$matches[3]; } else { $namePattern = sprintf( '%s.*with data set #%s$', $matches[1], $matches[2] ); } } elseif (preg_match('/^(.*?)@(.+)$/', $namePattern, $matches)) { // Handles: // * :testDetermineJsonError@JSON_ERROR_NONE // * :testDetermineJsonError@JSON.* $namePattern = sprintf( '%s.*with data set "%s"$', $matches[1], $matches[2] ); } // Escape delimiters in regular expression. Do NOT use preg_quote, // to keep magic characters. $namePattern = sprintf( '/%s/i', str_replace( '/', '\\/', $namePattern ) ); } $this->namePattern = $namePattern; } public function isNameAccepted(Test $test): bool { if ($this->namePattern === null) { return true; } $name = Descriptor::getTestSignature($test) . Descriptor::getTestDataSetIndex($test); $accepted = preg_match($this->namePattern, $name, $matches); if ($accepted && $this->filterMax !== null) { $set = end($matches); $accepted = $set >= $this->filterMin && $set <= $this->filterMax; } return (bool)$accepted; } public function isGroupAccepted(Test $test, array $groups): bool { if ($this->includeGroups !== null && $this->includeGroups !== [] && count(\array_intersect($groups, $this->includeGroups)) === 0) { return false; } if ($this->excludeGroups !== null && $this->excludeGroups !== [] && count(\array_intersect($groups, $this->excludeGroups)) > 0) { return false; } return true; } } <?php declare(strict_types=1); namespace Codeception\Test\Feature; use PHPUnit\Framework\Assert; trait AssertionCounter { protected int $numAssertions = 0; public function getNumAssertions(): int { return $this->numAssertions; } /** * This method is not covered by the backward compatibility promise * for PHPUnit, but is nice to have for extensions. */ public function addToAssertionCount(int $count): void { $this->numAssertions += $count; } protected function assertionCounterStart(): void { Assert::resetCount(); } protected function assertionCounterEnd(): void { $this->numAssertions = Assert::getCount(); } } <?php declare(strict_types=1); namespace Codeception\Test\Feature; use Codeception\Coverage\PhpCodeCoverageFactory; use Codeception\Event\FailEvent; use Codeception\ResultAggregator; use Codeception\Test\Descriptor; use Codeception\Test\Interfaces\StrictCoverage; use Codeception\Test\Test; use Codeception\Test\Test as CodeceptTest; use PHPUnit\Runner\Version as PHPUnitVersion; use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; use SebastianBergmann\CodeCoverage\Test\TestStatus\TestStatus; trait CodeCoverage { abstract public function getResultAggregator(): ResultAggregator; public function codeCoverageStart(): void { $codeCoverage = PhpCodeCoverageFactory::build(); $codeCoverage->start(Descriptor::getTestSignature($this)); } public function codeCoverageEnd(string $status, float $time): void { $codeCoverage = PhpCodeCoverageFactory::build(); if ($this instanceof StrictCoverage) { $linesToBeCovered = $this->getLinesToBeCovered(); $linesToBeUsed = $this->getLinesToBeUsed(); } else { $linesToBeCovered = []; $linesToBeUsed = []; } try { if (PHPUnitVersion::series() < 10) { $codeCoverage->stop(true, $linesToBeCovered, $linesToBeUsed); } else { $status = match ($status) { Test::STATUS_OK => TestStatus::success(), Test::STATUS_FAIL, Test::STATUS_ERROR => TestStatus::failure(), default => TestStatus::unknown(), }; $codeCoverage->stop(true, $status, $linesToBeCovered, $linesToBeUsed); } } catch (CodeCoverageException $exception) { if ($status === CodeceptTest::STATUS_OK) { $this->getResultAggregator()->addError(new FailEvent($this, $exception, $time)); } } } } <?php declare(strict_types=1); namespace Codeception\Test\Feature; use Codeception\Lib\Parser; use Codeception\Scenario; use Codeception\Test\Metadata; trait ScenarioLoader { private Scenario $scenario; abstract public function getMetadata(): Metadata; protected function createScenario(): void { $this->scenario = new Scenario($this); } public function getScenario(): Scenario { return $this->scenario; } public function getFeature(): string { return $this->getScenario()->getFeature(); } public function getScenarioText(string $format = 'text'): string { $code = $this->getSourceCode(); $this->getParser()->parseFeature($code); $this->getParser()->parseSteps($code); if ($format == 'html') { return $this->getScenario()->getHtml(); } return $this->getScenario()->getText(); } abstract protected function getParser(): Parser; abstract public function getSourceCode(): string; } <?php declare(strict_types=1); namespace Codeception\Test\Feature; use Codeception\Test\Metadata; trait MetadataCollector { private Metadata $metadata; protected function setMetadata(Metadata $metadata): void { $this->metadata = $metadata; } public function getMetadata(): Metadata { return $this->metadata; } public function getName(): string { return $this->getMetadata()->getName(); } public function getFileName(): string { return $this->getMetadata()->getFilename(); } } <?php declare(strict_types=1); namespace Codeception\Test\Feature; use Codeception\Event\FailEvent; use Codeception\ResultAggregator; use Codeception\Test\Metadata; use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SkippedTestError; use PHPUnit\Framework\SkippedWithMessageException; use PHPUnit\Runner\Version as PHPUnitVersion; trait IgnoreIfMetadataBlocked { abstract public function getMetadata(): Metadata; abstract protected function ignore(bool $ignored): void; abstract protected function getResultAggregator(): ResultAggregator; protected function ignoreIfMetadataBlockedStart(): void { if (!$this->getMetadata()->isBlocked()) { return; } $this->ignore(true); if ($this->getMetadata()->getSkip() !== null) { $skipMessage = (string)$this->getMetadata()->getSkip(); if (PHPUnitVersion::series() < 10) { $skippedTestError = new SkippedTestError($skipMessage); } else { $skippedTestError = new SkippedWithMessageException($skipMessage); } $this->getResultAggregator()->addFailure(new FailEvent($this, $skippedTestError, 0)); return; } if ($this->getMetadata()->getIncomplete() !== null) { $incompleteTestError = new IncompleteTestError((string)$this->getMetadata()->getIncomplete()); $this->getResultAggregator()->addFailure(new FailEvent($this, $incompleteTestError, 0)); } } } <?php declare(strict_types=1); namespace Codeception\Test; use Behat\Gherkin\Node\FeatureNode; use Behat\Gherkin\Node\ScenarioInterface; use Behat\Gherkin\Node\ScenarioNode; use Behat\Gherkin\Node\StepNode; use Behat\Gherkin\Node\TableNode; use Codeception\Lib\Di; use Codeception\Lib\Generator\GherkinSnippets; use Codeception\Scenario; use Codeception\Step\Comment; use Codeception\Step\Meta; use Codeception\Test\Interfaces\Reported; use Codeception\Test\Interfaces\ScenarioDriven; use Exception; use function array_merge; use function array_pop; use function array_shift; use function basename; use function call_user_func_array; use function count; use function explode; use function file_get_contents; use function is_array; use function is_string; use function preg_match; use function var_export; class Gherkin extends Test implements ScenarioDriven, Reported { protected array $steps = []; protected FeatureNode $featureNode; protected ScenarioInterface $scenarioNode; protected Scenario $scenario; public function __construct(FeatureNode $featureNode, ScenarioInterface $scenarioNode, array $steps = []) { $this->featureNode = $featureNode; $this->scenarioNode = $scenarioNode; $this->steps = $steps; $this->setMetadata(new Metadata()); $this->scenario = new Scenario($this); $this->getMetadata()->setName($scenarioNode->getTitle()); $this->getMetadata()->setFeature((string)$featureNode->getTitle()); $this->getMetadata()->setFilename($featureNode->getFile()); } public function __clone(): void { $this->scenario = clone $this->scenario; } public function preload(): void { $this->getMetadata()->setGroups($this->featureNode->getTags()); $this->getMetadata()->setGroups($this->scenarioNode->getTags()); $this->scenario->setMetaStep(null); if (($background = $this->featureNode->getBackground()) !== null) { foreach ($background->getSteps() as $step) { $this->validateStep($step); } } foreach ($this->scenarioNode->getSteps() as $step) { $this->validateStep($step); } if ($this->getMetadata()->getIncomplete()) { $this->getMetadata()->setIncomplete($this->getMetadata()->getIncomplete() . "\nRun gherkin:snippets to define missing steps"); } } public function getSignature(): string { return basename($this->getFileName(), '.feature') . ':' . $this->getScenarioTitle(); } public function test(): void { $this->makeContexts(); $description = explode("\n", (string)$this->featureNode->getDescription()); foreach ($description as $line) { $this->getScenario()->runStep(new Comment($line)); } if (($background = $this->featureNode->getBackground()) !== null) { foreach ($background->getSteps() as $step) { $this->runStep($step); } } foreach ($this->scenarioNode->getSteps() as $step) { $this->runStep($step); } } protected function validateStep(StepNode $stepNode): void { $stepText = $stepNode->getText(); if (GherkinSnippets::stepHasPyStringArgument($stepNode)) { $stepText .= ' ""'; } $matches = []; foreach ($this->steps as $pattern => $context) { $res = preg_match($pattern, $stepText); if (!$res) { continue; } $matches[$pattern] = $context; } if ($matches === []) { // There were no matches, meaning that the user should first add a step definition for this step $incomplete = $this->getMetadata()->getIncomplete(); $this->getMetadata()->setIncomplete("{$incomplete}\nStep definition for `{$stepText}` not found in contexts"); } if (count($matches) > 1) { // There were more than one match, meaning that we don't know which step definition to execute for this step $incomplete = $this->getMetadata()->getIncomplete(); $matchingDefinitions = []; foreach ($matches as $pattern => $context) { $matchingDefinitions[] = '- ' . $pattern . ' (' . self::contextAsString($context) . ')'; } $this->getMetadata()->setIncomplete( "{$incomplete}\nAmbiguous step: `{$stepText}` matches multiple definitions:\n" . implode("\n", $matchingDefinitions) ); } } private function contextAsString($context): string { if (is_array($context) && count($context) === 2) { [$class, $method] = $context; if (is_string($class) && is_string($method)) { return $class . ':' . $method; } } return var_export($context, true); } protected function runStep(StepNode $stepNode): void { $params = []; if ($stepNode->hasArguments()) { $args = $stepNode->getArguments(); $table = $args[0]; if ($table instanceof TableNode) { $params = [$table->getTableAsString()]; } } $meta = new Meta($stepNode->getText(), $params); $meta->setPrefix($stepNode->getKeyword()); $this->scenario->setMetaStep($meta); // enable metastep $stepText = $stepNode->getText(); $hasPyStringArg = GherkinSnippets::stepHasPyStringArgument($stepNode); if ($hasPyStringArg) { // pretend it is inline argument $stepText .= ' ""'; } $this->getScenario()->comment(''); // make metastep to be printed even if no steps in it foreach ($this->steps as $pattern => $context) { $matches = []; if (!preg_match($pattern, $stepText, $matches)) { continue; } array_shift($matches); if ($hasPyStringArg) { // get rid off last fake argument array_pop($matches); } if ($stepNode->hasArguments()) { $matches = array_merge($matches, $stepNode->getArguments()); } call_user_func_array($context, $matches); // execute the step break; } $this->scenario->setMetaStep(null); // disable metastep } protected function makeContexts(): void { /** @var Di $di */ $di = $this->getMetadata()->getService('di'); $di->set($this->getScenario()); $actorClass = $this->getMetadata()->getCurrent('actor'); if ($actorClass) { $di->instantiate($actorClass); } foreach ($this->steps as $pattern => $step) { $di->instantiate($step[0]); $this->steps[$pattern][0] = $di->get($step[0]); } } public function toString(): string { return $this->getFeature() . ': ' . $this->getScenarioTitle(); } public function getFeature(): string { return $this->getMetadata()->getFeature(); } public function getScenarioTitle(): string { return $this->getMetadata()->getName(); } public function getScenario(): Scenario { return $this->scenario; } public function getScenarioText(string $format = 'text'): string { $fileName = $this->getFileName(); if (!$scenarioText = file_get_contents($fileName)) { throw new Exception("Could not get scenario {$fileName}, please check its permissions."); } return $scenarioText; } public function getSourceCode(): string { return ''; } public function getScenarioNode(): ScenarioNode { return $this->scenarioNode; } public function getFeatureNode(): FeatureNode { return $this->featureNode; } /** * Field values for XML reports * * @return array<string, string> */ public function getReportFields(): array { return [ 'name' => $this->toString(), 'feature' => $this->getFeature(), 'file' => $this->getFileName(), ]; } } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Exception\UselessTestException; use Codeception\Scenario; use Codeception\Test\Interfaces\Dependent; use Codeception\Test\Interfaces\Descriptive; use Codeception\Test\Interfaces\Reported; use Codeception\Test\Interfaces\StrictCoverage; use Codeception\TestInterface; use Codeception\Util\Annotation; use Codeception\Util\ReflectionHelper; use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; use PHPUnit\Metadata\Api\CodeCoverage; use PHPUnit\Runner\Version as PHPUnitVersion; use PHPUnit\Util\Test as TestUtil; use ReflectionClass; /** * Wrapper for TestCase tests behaving like native Codeception test format */ class TestCaseWrapper extends Test implements Reported, Dependent, StrictCoverage, TestInterface, Descriptive { private Metadata $metadata; /** * @var array<string, mixed> */ private static array $testResults = []; /** * @param string[] $beforeClassMethods * @param string[] $afterClassMethods */ public function __construct( private TestCase $testCase, array $beforeClassMethods = [], array $afterClassMethods = [], ) { $this->metadata = new Metadata(); $metadata = $this->metadata; if (PHPUnitVersion::series() < 10) { $methodName = $testCase->getName(false); } else { $methodName = $testCase->name(); } $metadata->setName($methodName); $metadata->setFilename((new ReflectionClass($testCase))->getFileName()); if ($testCase->dataName() !== '') { $metadata->setIndex($testCase->dataName()); } $classAnnotations = Annotation::forClass($testCase); $metadata->setParamsFromAnnotations($classAnnotations->raw()); $metadata->setParamsFromAttributes($classAnnotations->attributes()); $methodAnnotations = Annotation::forMethod($testCase, $methodName); $metadata->setParamsFromAnnotations($methodAnnotations->raw()); $metadata->setParamsFromAttributes($methodAnnotations->attributes()); $metadata->setBeforeClassMethods($beforeClassMethods); $metadata->setAfterClassMethods($afterClassMethods); } public function __clone(): void { $this->testCase = clone $this->testCase; } public function getTestCase(): TestCase { return $this->testCase; } public function getMetadata(): Metadata { return $this->metadata; } public function getScenario(): ?Scenario { if ($this->testCase instanceof Unit) { return $this->testCase->getScenario(); } return null; } public function fetchDependencies(): array { $names = []; foreach ($this->metadata->getDependencies() as $required) { if (!str_contains($required, ':') && method_exists($this->testCase::class, $required)) { $required = $this->testCase::class . ':' . $required; } $names[] = $required; } return $names; } /** * @return array<string, string> */ public function getReportFields(): array { return [ 'name' => $this->getNameWithDataSet(), 'class' => $this->testCase::class, 'file' => $this->metadata->getFilename() ]; } public function getLinesToBeCovered(): array|bool { $class = $this->testCase::class; $method = $this->metadata->getName(); if (PHPUnitVersion::series() < 10) { return TestUtil::getLinesToBeCovered($class, $method); } return (new CodeCoverage())->linesToBeCovered($class, $method); } public function getLinesToBeUsed(): array { $class = $this->testCase::class; $method = $this->metadata->getName(); if (PHPUnitVersion::series() < 10) { return TestUtil::getLinesToBeUsed($class, $method); } return (new CodeCoverage())->linesToBeUsed($class, $method); } public function test(): void { $dependencyInput = []; foreach ($this->fetchDependencies() as $dependency) { $dependencyInput[] = self::$testResults[$dependency] ?? null; } $this->testCase->setDependencyInput($dependencyInput); $this->testCase->runBare(); $this->testCase->addToAssertionCount(Assert::getCount()); if (PHPUnitVersion::series() < 10) { self::$testResults[$this->getSignature()] = $this->testCase->getResult(); } else { self::$testResults[$this->getSignature()] = $this->testCase->result(); } $numberOfAssertionsPerformed = $this->getNumAssertions(); if ( $this->reportUselessTests && $numberOfAssertionsPerformed > 0 && $this->testCase->doesNotPerformAssertions() ) { throw new UselessTestException( sprintf( 'This test indicates it does not perform assertions but %d assertions were performed', $numberOfAssertionsPerformed ) ); } } /** * Is the test expected to not perform assertions with `expectNotToPerformAssertions`? */ protected function doesNotPerformAssertions(): bool { return $this->testCase->doesNotPerformAssertions(); } public function toString(): string { $text = Descriptor::getTestCaseNameAsString($this->getNameWithDataSet()); return ReflectionHelper::getClassShortName($this->testCase) . ': ' . $text; } public function getFileName(): string { return $this->metadata->getFilename(); } public function getSignature(): string { return $this->testCase::class . ':' . $this->metadata->getName(); } private function getNameWithDataSet(): string { if (PHPUnitVersion::series() < 10) { return $this->testCase->getName(true); } return $this->testCase->nameWithDataSet(); } /** * Override this method from the {@see \Codeception\Test\Feature\AssertionCounter} so that we use PHPUnit's * assertion count instead of our own. * This is needed because PHPUnit's {@see TestCase} has a {@see TestCase::addToAssertionCount()} method which is * both internally and externally used to increase the assertion count. Externally it is called from tearDown * methods, for example when using Mockery. */ public function getNumAssertions(): int { if (PHPUnitVersion::series() < 10) { return $this->testCase->getNumAssertions(); } else { return $this->testCase->numberOfAssertionsPerformed(); } } } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Actor; use Codeception\Exception\InvalidTestException; use Codeception\Exception\TestParseException; use Codeception\Util\Annotation; use Codeception\Util\ReflectionHelper; use ReflectionClass; use ReflectionException; use ReflectionMethod; use function sprintf; class DataProvider { public static function getDataForMethod(ReflectionMethod $method, ?ReflectionClass $class = null, ?Actor $I = null): ?iterable { $testClass = self::getTestClass($method, $class); $testClassName = $testClass->getName(); $methodName = $method->getName(); // example annotation $rawExamples = array_values( Annotation::forMethod($testClassName, $methodName)->fetchAll('example'), ); if ($rawExamples !== []) { $rawExample = reset($rawExamples); if (is_string($rawExample)) { $result = array_map( static fn ($v): ?array => Annotation::arrayValue($v), $rawExamples ); } else { $result = $rawExamples; } } else { $result = []; } // dataProvider annotation $dataProviderAnnotations = Annotation::forMethod($testClassName, $methodName)->fetchAll('dataProvider'); // lowercase for back compatible if (empty($dataProviderAnnotations)) { $dataProviderAnnotations = Annotation::forMethod($testClassName, $methodName)->fetchAll('dataprovider'); } if ($result === [] && $dataProviderAnnotations === []) { return null; } foreach ($dataProviderAnnotations as $dataProviderAnnotation) { [$dataProviderClassName, $dataProviderMethodName] = self::parseDataProviderAnnotation( $dataProviderAnnotation, $testClassName, $methodName, ); try { $dataProviderMethod = new ReflectionMethod($dataProviderClassName, $dataProviderMethodName); if ($dataProviderMethod->isStatic()) { $dataProviderResult = call_user_func([$dataProviderClassName, $dataProviderMethodName], $I); } else { $testInstance = new $dataProviderClassName($dataProviderMethodName); if ($dataProviderMethod->isPublic()) { $dataProviderResult = $testInstance->$dataProviderMethodName($I); } else { $dataProviderResult = ReflectionHelper::invokePrivateMethod( $testInstance, $dataProviderMethodName, [$I] ); } } foreach ($dataProviderResult as $key => $value) { if (is_int($key)) { $result [] = $value; } else { $result[$key] = $value; } } } catch (ReflectionException) { throw new InvalidTestException(sprintf( "DataProvider '%s' for %s::%s is invalid or not callable", $dataProviderAnnotation, $testClassName, $methodName )); } } return $result; } /** * @return string[] * @throws TestParseException */ public static function parseDataProviderAnnotation( string $annotation, string $testClassName, string $testMethodName, ): array { $parts = explode('::', $annotation); if (count($parts) > 2) { throw new InvalidTestException( sprintf( 'Data provider "%s" specified for %s::%s is invalid', $annotation, $testClassName, $testMethodName, ) ); } if (count($parts) === 2) { return $parts; } return [ $testClassName, $annotation, ]; } /** * Retrieves actual test class for dataProvider. */ private static function getTestClass(ReflectionMethod $dataProviderMethod, ?ReflectionClass $testClass): ReflectionClass { $dataProviderDeclaringClass = $dataProviderMethod->getDeclaringClass(); // data provider in abstract class? if ($dataProviderDeclaringClass->isAbstract() && null !== $testClass && $dataProviderDeclaringClass->name !== $testClass->name) { $dataProviderDeclaringClass = $testClass; } return $dataProviderDeclaringClass; } } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Exception\ConfigurationException; use Codeception\Test\Loader\Cept as CeptLoader; use Codeception\Test\Loader\Cest as CestLoader; use Codeception\Test\Loader\Gherkin as GherkinLoader; use Codeception\Test\Loader\LoaderInterface; use Codeception\Test\Loader\Unit as UnitLoader; use Exception; use Symfony\Component\Finder\Finder; use function array_merge; use function file_exists; use function getcwd; use function is_dir; use function preg_match; use function str_replace; /** * Loads all Codeception supported test formats from a directory. * * ``` php * <?php * $testLoader = new \Codeception\TestLoader('tests/unit'); * $testLoader->loadTests(); * $tests = $testLoader->getTests(); * ``` * You can load specific file * * ``` php * <?php * $testLoader = new \Codeception\TestLoader('tests/unit'); * $testLoader->loadTest('UserTest.php'); * $testLoader->loadTest('PostTest.php'); * $tests = $testLoader->getTests(); * ``` * or a subdirectory * * ``` php * <?php * $testLoader = new \Codeception\TestLoader('tests/unit'); * $testLoader->loadTest('models'); // all tests from tests/unit/models * $tests = $testLoader->getTests(); * ``` * */ class Loader { /** * @var LoaderInterface[] */ protected array $formats = []; protected array $tests = []; protected ?string $path = null; private ?string $shard = null; public function __construct(array $suiteSettings) { $this->path = $suiteSettings['path']; $this->shard = $suiteSettings['shard'] ?? null; $this->formats = [ new CeptLoader(), new CestLoader($suiteSettings), new UnitLoader(), new GherkinLoader($suiteSettings) ]; if (isset($suiteSettings['formats'])) { foreach ($suiteSettings['formats'] as $format) { $this->formats[] = new $format($suiteSettings); } } } public function getTests(): array { if ($this->shard) { $this->shard = trim($this->shard); if (!preg_match('~^\d+\/\d+$~', $this->shard)) { throw new ConfigurationException('Shard must be set as --shard=CURRENT/TOTAL where CURRENT and TOTAL are number. For instance: --shard=1/3'); } [$shard, $totalShards] = explode('/', $this->shard); if ($shard < 1) { throw new ConfigurationException("Incorrect shard index. Use 1/{$totalShards} to start the first shard"); } if ($totalShards < $shard) { throw new ConfigurationException('Total shards are less than current shard'); } $chunks = $this->splitTestsIntoChunks((int)$totalShards); return $chunks[$shard - 1] ?? []; } return $this->tests; } private function splitTestsIntoChunks(int $chunks): array { if (empty($this->tests)) { return []; } return array_chunk($this->tests, intval(ceil(sizeof($this->tests) / $chunks))); } protected function relativeName(string $file): string { return str_replace([$this->path, '\\'], ['', '/'], $file); } protected function findPath(string $path): string { if ( !file_exists($path) && !str_ends_with($path, '.php') && file_exists($newPath = $path . '.php') ) { return $newPath; } return $path; } protected function makePath(string $originalPath): string { $path = $this->path . $this->relativeName($originalPath); if ( file_exists($newPath = $this->findPath($path)) || file_exists($newPath = $this->findPath(getcwd() . "/{$originalPath}")) ) { $path = $newPath; } if (!file_exists($path)) { throw new Exception("File or path {$originalPath} not found"); } return $path; } public function loadTest(string $path): void { $path = $this->makePath($path); foreach ($this->formats as $format) { if (preg_match($format->getPattern(), $path)) { $format->loadTests($path); $this->tests = $format->getTests(); return; } } if (is_dir($path)) { $currentPath = $this->path; $this->path = $path; $this->loadTests(); $this->path = $currentPath; return; } throw new Exception('Test format not supported. Please, check you use the right suffix. Available filetypes: Cept, Cest, Test'); } public function loadTests(string $fileName = null): void { if ($fileName) { $this->loadTest($fileName); return; } $finder = Finder::create()->files()->sortByName()->in($this->path)->followLinks(); foreach ($this->formats as $format) { $formatFinder = clone($finder); $testFiles = $formatFinder->name($format->getPattern()); foreach ($testFiles as $test) { $pathname = str_replace(["//", "\\\\"], ["/", "\\"], $test->getPathname()); $format->loadTests($pathname); } $this->tests = array_merge($this->tests, $format->getTests()); } } } <?php declare(strict_types=1); namespace Codeception\Test; use Codeception\Event\FailEvent; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Exception\UselessTestException; use Codeception\PHPUnit\Wrapper\Test as TestWrapper; use Codeception\ResultAggregator; use Codeception\Test\Interfaces\ScenarioDriven; use Codeception\TestInterface; use PHPUnit\Framework\Assert; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\Exception; use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SkippedTest; use PHPUnit\Framework\SkippedTestError; use PHPUnit\Runner\Version as PHPUnitVersion; use RuntimeException; use SebastianBergmann\Timer\Timer; use Symfony\Component\EventDispatcher\EventDispatcher; use Throwable; use function array_reverse; use function method_exists; // phpcs:disable if (PHPUnitVersion::series() < 10) { require_once __DIR__ . '/../../PHPUnit/Wrapper/PhpUnit9/Test.php'; } else { require_once __DIR__ . '/../../PHPUnit/Wrapper/PhpUnit10/Test.php'; } // phpcs:enable /** * The most simple testcase (with only one test in it) which can be executed by PHPUnit/Codeception. * It can be extended with included traits. Turning on/off a trait should not break class functionality. * * Class has exactly one method to be executed for testing, wrapped with before/after callbacks delivered from included traits. * A trait providing before/after callback should contain corresponding protected methods: `{traitName}Start` and `{traitName}End`, * then this trait should be enabled in `hooks` property. * * Inherited class must implement `test` method. */ abstract class Test extends TestWrapper implements TestInterface, Interfaces\Descriptive { use Feature\AssertionCounter; use Feature\CodeCoverage; use Feature\MetadataCollector; use Feature\IgnoreIfMetadataBlocked; private ?ResultAggregator $resultAggregator = null; private bool $ignored = false; private ?EventDispatcher $eventDispatcher = null; /** * Enabled traits with methods to be called before and after the test. */ protected array $hooks = [ 'ignoreIfMetadataBlocked', 'codeCoverage', 'assertionCounter', 'errorLogger' ]; /** * @var string */ public const STATUS_FAIL = 'fail'; /** * @var string */ public const STATUS_ERROR = 'error'; /** * @var string */ public const STATUS_OK = 'ok'; /** * @var string */ public const STATUS_PENDING = 'pending'; /** * @var string */ public const STATUS_USELESS = 'useless'; /** * @var string */ public const STATUS_INCOMPLETE = 'incomplete'; /** * @var string */ public const STATUS_SKIPPED = 'skipped'; protected bool $reportUselessTests = false; private bool $collectCodeCoverage = false; /** * Everything inside this method is treated as a test. * * @return mixed */ abstract public function test(); /** * Test representation */ abstract public function toString(): string; public function collectCodeCoverage(bool $enabled): void { $this->collectCodeCoverage = $enabled; } public function reportUselessTests(bool $enabled): void { $this->reportUselessTests = $enabled; } public function setEventDispatcher(EventDispatcher $eventDispatcher): void { $this->eventDispatcher = $eventDispatcher; } /** * Runs a test and collects its result in a TestResult instance. * Executes before/after hooks coming from traits. */ final public function realRun(ResultAggregator $result): void { $this->resultAggregator = $result; $status = self::STATUS_PENDING; $time = 0; $e = null; $timer = new Timer(); $result->addTest($this); try { $this->fire(Events::TEST_BEFORE, new TestEvent($this)); foreach ($this->hooks as $hook) { if ($hook === 'codeCoverage' && !$this->collectCodeCoverage) { continue; } if (method_exists($this, $hook . 'Start')) { $this->{$hook . 'Start'}(); } } $failedToStart = false; } catch (\Exception $e) { $failedToStart = true; $result->addError(new FailEvent($this, $e, $time)); $this->fire(Events::TEST_ERROR, new FailEvent($this, $e, $time)); } if (!$this->ignored && !$failedToStart) { Assert::resetCount(); $timer->start(); try { $this->test(); $status = self::STATUS_OK; $eventType = Events::TEST_SUCCESS; $this->checkConditionalAsserts($result); } catch (UselessTestException $e) { $result->addUseless(new FailEvent($this, $e, $time)); $status = self::STATUS_USELESS; $eventType = Events::TEST_USELESS; } catch (IncompleteTestError $e) { $result->addIncomplete(new FailEvent($this, $e, $time)); $status = self::STATUS_INCOMPLETE; $eventType = Events::TEST_INCOMPLETE; } catch (SkippedTest | SkippedTestError $e) { $result->addSkipped(new FailEvent($this, $e, $time)); $status = self::STATUS_SKIPPED; $eventType = Events::TEST_SKIPPED; } catch (AssertionFailedError $e) { $result->addFailure(new FailEvent($this, $e, $time)); $status = self::STATUS_FAIL; $eventType = Events::TEST_FAIL; } catch (Exception $e) { $result->addError(new FailEvent($this, $e, $time)); $status = self::STATUS_ERROR; $eventType = Events::TEST_ERROR; } catch (Throwable $e) { $result->addError(new FailEvent($this, $e, $time)); $status = self::STATUS_ERROR; $eventType = Events::TEST_ERROR; } $time = $timer->stop()->asSeconds(); $this->callTestEndHooks($status, $time, $e); // We need to get the number of performed assertions _after_ calling the test end hooks because the // AssertionCounter needs to set the number of performed assertions first. $result->addToAssertionCount($this->numberOfAssertionsPerformed()); if ( $this->reportUselessTests && $this->numberOfAssertionsPerformed() === 0 && !$this->doesNotPerformAssertions() && $eventType === Events::TEST_SUCCESS ) { $eventType = Events::TEST_USELESS; $e = new UselessTestException('This test did not perform any assertions'); $result->addUseless(new FailEvent($this, $e, $time)); } if ($eventType === Events::TEST_SUCCESS) { $result->addSuccessful($this); $this->fire($eventType, new TestEvent($this, $time)); } else { $this->fire($eventType, new FailEvent($this, $e, $time)); } } else { $this->callTestEndHooks($status, $time, $e); } $this->fire(Events::TEST_AFTER, new TestEvent($this, $time)); $this->eventDispatcher->dispatch(new TestEvent($this, $time), Events::TEST_END); } /** * Return false by default, the Unit-specific TestCaseWrapper implements this properly as it supports the PHPUnit * test override `->expectNotToPerformAssertions()`. */ protected function doesNotPerformAssertions(): bool { return false; } public function getResultAggregator(): ResultAggregator { if ($this->resultAggregator === null) { throw new \LogicException('ResultAggregator is not set'); } return $this->resultAggregator; } /** * This class represents exactly one test */ public function count(): int { return 1; } /** * Should a test be skipped (can be set from hooks) */ protected function ignore(bool $ignored): void { $this->ignored = $ignored; } public function numberOfAssertionsPerformed(): int { return $this->getNumAssertions(); } protected function fire(string $eventType, TestEvent $event): void { if ($this->eventDispatcher === null) { throw new RuntimeException('EventDispatcher must be injected before running test'); } $test = $event->getTest(); if ($test instanceof TestInterface) { foreach ($test->getMetadata()->getGroups() as $group) { $this->eventDispatcher->dispatch($event, $eventType . '.' . $group); } } $this->eventDispatcher->dispatch($event, $eventType); } private function callTestEndHooks(string $status, float $time, ?Throwable $e): void { foreach (array_reverse($this->hooks) as $hook) { if ($hook === 'codeCoverage' && !$this->collectCodeCoverage) { continue; } if (method_exists($this, $hook . 'End')) { $this->{$hook . 'End'}($status, $time, $e); } } } private function checkConditionalAsserts(ResultAggregator $result): void { if (!method_exists($this, 'getScenario')) { return; } $lastFailure = $result->getLastFailure(); if ($lastFailure === null) { return; } if (Descriptor::getTestSignatureUnique($lastFailure->getTest()) !== Descriptor::getTestSignatureUnique($this)) { return; } foreach ($this->getScenario()?->getSteps() ?? [] as $step) { if ($step->hasFailed()) { $result->popLastFailure(); throw $lastFailure->getFail(); } } } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Lib\ModuleContainer; use Codeception\Util\Template; use Exception; use function codecept_debug; use function ucfirst; class TryTo extends Assertion implements GeneratedStep { public function run(ModuleContainer $container = null): bool { $this->isTry = true; try { parent::run($container); } catch (Exception $e) { codecept_debug("Failed to perform: {$e->getMessage()}, skipping..."); return false; } return true; } public static function getTemplate(Template $template): ?Template { $action = $template->getVar('action'); if ((str_starts_with($action, 'have')) || (str_starts_with($action, 'am'))) { return null; // dont try on conditions } if (str_starts_with($action, 'wait')) { return null; // dont try on waiters } if (str_starts_with($action, 'grab')) { return null; // dont on grabbers } $conditionalDoc = "* [!] Test won't be stopped on fail. Error won't be logged \n " . $template->getVar('doc'); return $template ->place('doc', $conditionalDoc) ->place('action', 'tryTo' . ucfirst($action)) ->place('return', 'return ') ->place('return_type', ': bool') ->place('step', 'TryTo'); } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Step as CodeceptionStep; class Assertion extends CodeceptionStep { } <?php namespace Codeception\Step; use Codeception\Util\Template; interface GeneratedStep { public static function getTemplate(Template $template): ?Template; } <?php declare(strict_types=1); namespace Codeception\Step\Argument; use Stringable; class PasswordArgument implements FormattedOutput, Stringable { public function __construct(private string $password) { } /** * {@inheritdoc} */ public function getOutput(): string { return '******'; } /** * {@inheritdoc} */ public function __toString(): string { return $this->password; } } <?php namespace Codeception\Step\Argument; /** * Implemented in Step arguments where literal values need to be modified in test execution output (e.g. passwords). */ interface FormattedOutput { /** * Returns the argument's value formatted for output. */ public function getOutput(): string; /** * Returns the argument's literal value. */ public function __toString(): string; } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Lib\ModuleContainer; use Codeception\Util\Template; use Exception; use function codecept_debug; use function ucfirst; use function usleep; class Retry extends Assertion implements GeneratedStep { protected static string $methodTemplate = <<<EOF /** * [!] Method is generated. * * {{doc}} * * Retry number and interval set by \$I->retry(); * * @see \{{module}}::{{method}}() */ public function {{action}}({{params}}) { \$retryNum = isset(\$this->retryNum) ? \$this->retryNum : 1; \$retryInterval = isset(\$this->retryInterval) ? \$this->retryInterval : 200; return \$this->getScenario()->runStep(new \Codeception\Step\Retry('{{method}}', func_get_args(), \$retryNum, \$retryInterval)); } EOF; public function __construct($action, array $arguments, private int $retryNum, private int $retryInterval) { $this->action = $action; $this->arguments = $arguments; } public function run(ModuleContainer $container = null) { $retry = 0; $interval = $this->retryInterval; while (true) { try { $this->isTry = $retry < $this->retryNum; return parent::run($container); } catch (Exception $e) { ++$retry; if (!$this->isTry) { throw $e; } codecept_debug("Retrying #{$retry} in {$interval}ms"); usleep($interval * 1000); $interval *= 2; } } } public static function getTemplate(Template $template): ?Template { $action = $template->getVar('action'); if ((str_starts_with($action, 'have')) || (str_starts_with($action, 'am'))) { return null; // dont retry conditions } if (str_starts_with($action, 'wait')) { return null; // dont retry waiters } $doc = "* Executes {$action} and retries on failure."; return (new Template(self::$methodTemplate)) ->place('method', $template->getVar('method')) ->place('module', $template->getVar('module')) ->place('params', $template->getVar('params')) ->place('doc', $doc) ->place('action', 'retry' . ucfirst($action)); } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Lib\ModuleContainer; use Codeception\Step as CodeceptionStep; use function mb_strcut; class Comment extends CodeceptionStep { public function __toString(): string { return $this->getAction(); } public function toString(int $maxLength): string { return mb_strcut((string)$this, 0, $maxLength, 'utf-8'); } public function getHtml(string $highlightColor = '#732E81'): string { return '<strong>' . $this->getAction() . '</strong>'; } public function getPhpCode(int $maxLength): string { return '// ' . $this->getAction(); } public function run(ModuleContainer $container = null): void { // don't do anything, let's rest } public function getPrefix(): string { return ''; } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Step as CodeceptionStep; class Action extends CodeceptionStep { } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Lib\ModuleContainer; use Codeception\Step as CodeceptionStep; use PHPUnit\Framework\IncompleteTestError; class Incomplete extends CodeceptionStep { public function run(ModuleContainer $container = null): void { throw new IncompleteTestError($this->getAction()); } public function __toString(): string { return $this->getAction(); } } <?php declare(strict_types=1); namespace Codeception\Step; use Closure; use Codeception\Lib\ModuleContainer; use Codeception\Step as CodeceptionStep; class Executor extends CodeceptionStep { protected Closure $callable; public function __construct(Closure $callable, array $arguments = []) { parent::__construct('execute callable function', []); $this->callable = $callable; } public function run(ModuleContainer $container = null) { $callable = $this->callable; return $callable(); } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Exception\ConditionalAssertionFailed; use Codeception\Lib\ModuleContainer; use Codeception\Util\Template; use PHPUnit\Framework\AssertionFailedError; use function preg_replace; use function str_replace; use function ucfirst; class ConditionalAssertion extends Assertion implements GeneratedStep { public function run(ModuleContainer $container = null): void { try { parent::run($container); } catch (AssertionFailedError $e) { throw new ConditionalAssertionFailed($e->getMessage(), $e->getCode(), $e); } } public function getAction(): string { $action = 'can' . ucfirst($this->action); return (string)preg_replace('#^canDont#', 'cant', $action); } public function getHumanizedAction(): string { return $this->humanize($this->action . ' ' . $this->getHumanizedArguments()); } public static function getTemplate(Template $template): ?Template { $action = $template->getVar('action'); if ((!str_starts_with($action, 'see')) && (!str_starts_with($action, 'dontSee'))) { return null; } $conditionalDoc = "* [!] Conditional Assertion: Test won't be stopped on fail\n " . $template->getVar('doc'); $prefix = 'can'; if (str_starts_with($action, 'dontSee')) { $prefix = 'cant'; $action = str_replace('dont', '', $action); } return $template ->place('doc', $conditionalDoc) ->place('action', $prefix . ucfirst($action)) ->place('step', 'ConditionalAssertion'); } public function match(string $name): bool { return str_starts_with($name, 'see') || str_starts_with($name, 'dontSee'); } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Lib\ModuleContainer; use Codeception\Step as CodeceptionStep; use PHPUnit\Framework\SkippedTestError; use PHPUnit\Framework\SkippedWithMessageException; use PHPUnit\Runner\Version as PHPUnitVersion; class Skip extends CodeceptionStep { public function run(ModuleContainer $container = null): void { $skipMessage = $this->getAction(); if (PHPUnitVersion::series() < 10) { throw new SkippedTestError($skipMessage); } throw new SkippedWithMessageException($skipMessage); } public function __toString(): string { return $this->getAction(); } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Lib\ModuleContainer; use Codeception\Step as CodeceptionStep; use function array_pop; use function end; use function is_string; use function str_replace; class Meta extends CodeceptionStep { public function run(ModuleContainer $container = null): void { } public function setTraceInfo(string $file, int $line): void { $this->file = $file; $this->line = $line; } public function setPrefix(string $actor): void { $this->prefix = $actor; } public function getArgumentsAsString(int $maxLength = self::DEFAULT_MAX_LENGTH): string { $argumentBackup = $this->arguments; $lastArgAsString = ''; $lastArg = end($this->arguments); if (is_string($lastArg) && str_contains($lastArg, "\n")) { $lastArgAsString = "\r\n " . str_replace("\n", "\n ", $lastArg); array_pop($this->arguments); } $result = parent::getArgumentsAsString($maxLength) . $lastArgAsString; $this->arguments = $argumentBackup; return $result; } public function setFailed(bool $failed): void { $this->failed = $failed; } } <?php declare(strict_types=1); namespace Codeception\Step; use Codeception\Step as CodeceptionStep; class Condition extends CodeceptionStep { } <?php declare(strict_types=1); namespace Codeception; use Codeception\Event\FailEvent; use Codeception\Test\Test; class ResultAggregator { /** * @var bool Stop execution of test suite if this property is true */ private bool $stop = false; /** * @var FailEvent[] */ private array $failures = []; /** * @var FailEvent[] */ private array $errors = []; /** * @var FailEvent[] */ private array $warnings = []; /** * @var FailEvent[] */ private array $useless = []; /** * @var FailEvent[] */ private array $skipped = []; /** * @var FailEvent[] */ private array $incomplete = []; private int $count = 0; private int $successful = 0; private int $assertions = 0; public function stop(): void { $this->stop = true; } public function shouldStop(): bool { return $this->stop; } public function addTest(Test $test): void { $this->count++; } public function addSuccessful(Test $test): void { $this->successful++; } public function addFailure(FailEvent $failEvent): void { $this->failures[] = $failEvent; } public function addError(FailEvent $failEvent): void { $this->errors[] = $failEvent; } public function addWarning(FailEvent $failEvent): void { $this->warnings[] = $failEvent; } public function addSkipped(FailEvent $failEvent): void { $this->skipped[] = $failEvent; } public function addIncomplete(FailEvent $failEvent): void { $this->incomplete[] = $failEvent; } public function addUseless(FailEvent $failEvent): void { $this->useless[] = $failEvent; } public function addToAssertionCount(int $count): void { $this->assertions += $count; } /** * @return FailEvent[] */ public function failures(): array { return $this->failures; } /** * @return FailEvent[] */ public function errors(): array { return $this->errors; } /** * @return FailEvent[] */ public function useless(): array { return $this->useless; } /** * @return FailEvent[] */ public function incomplete(): array { return $this->incomplete; } /** * @return FailEvent[] */ public function skipped(): array { return $this->skipped; } public function wasSuccessful(): bool { return ( $this->errorCount() + $this->failureCount() + $this->warningCount() ) === 0; } public function wasSuccessfulIgnoringWarnings(): bool { return ($this->errorCount() + $this->failureCount()) === 0; } /** * @deprecated replaced by wasSuccessfulAndNoTestIsUselessOrSkippedOrIncomplete */ public function wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete(): bool { return $this->wasSuccessfulAndNoTestIsUselessOrSkippedOrIncomplete(); } public function wasSuccessfulAndNoTestIsUselessOrSkippedOrIncomplete(): bool { return $this->wasSuccessful() && ($this->uselessCount() + $this->skippedCount() + $this->incompleteCount()) === 0; } public function testCount(): int { return $this->count; } public function successfulCount(): int { return $this->successful; } public function assertionCount(): int { return $this->assertions; } public function skippedCount(): int { return count($this->skipped); } public function incompleteCount(): int { return count($this->incomplete); } public function errorCount(): int { return count($this->errors); } public function failureCount(): int { return count($this->failures); } public function warningCount(): int { return count($this->warnings); } public function uselessCount(): int { return count($this->useless); } public function popLastFailure(): ?FailEvent { return array_pop($this->failures); } public function getLastFailure(): ?FailEvent { return end($this->failures) ?: null; } } <?php declare(strict_types=1); namespace Codeception; use Codeception\Event\FailEvent; use Codeception\Event\SuiteEvent; use Codeception\Event\TestEvent; use Codeception\Test\Descriptor; use Codeception\Test\Interfaces\Dependent; use Codeception\Test\Test; use Codeception\Test\TestCaseWrapper; use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SkippedTestError; use PHPUnit\Framework\SkippedWithMessageException; use PHPUnit\Runner\Version; use PHPUnit\TextUI\Configuration\Registry; use Symfony\Component\EventDispatcher\EventDispatcher; use function count; class Suite { /** * @var Array<string, Module> */ protected array $modules = []; protected ?string $baseName = null; private bool $reportUselessTests = false; private bool $backupGlobals = false; private bool $beStrictAboutChangesToGlobalState = false; private bool $disallowTestOutput = false; private bool $collectCodeCoverage = false; /** * @var Test[] */ private array $tests = []; public function __construct(private EventDispatcher $dispatcher, private string $name = '') { } public function getName(): string { return $this->name; } public function reportUselessTests(bool $enabled): void { $this->reportUselessTests = $enabled; } public function backupGlobals(bool $enabled): void { $this->backupGlobals = $enabled; } public function beStrictAboutChangesToGlobalState(bool $enabled): void { $this->beStrictAboutChangesToGlobalState = $enabled; } public function disallowTestOutput(bool $enabled): void { $this->disallowTestOutput = $enabled; } public function collectCodeCoverage(bool $enabled): void { $this->collectCodeCoverage = $enabled; } public function run(ResultAggregator $result): void { if (count($this->tests) === 0) { return; } $this->dispatcher->dispatch(new SuiteEvent($this), 'suite.start'); foreach ($this->tests as $test) { if ($result->shouldStop()) { break; } $this->dispatcher->dispatch(new TestEvent($test), Events::TEST_START); if ($test instanceof TestInterface) { if ($test->getMetadata()->isBlocked()) { $result->addTest($test); $skip = $test->getMetadata()->getSkip(); if ($skip !== null) { if (class_exists(SkippedWithMessageException::class)) { $exception = new SkippedWithMessageException($skip); } else { $exception = new SkippedTestError($skip); } $failEvent = new FailEvent($test, $exception, 0); $result->addSkipped($failEvent); $this->dispatcher->dispatch($failEvent, Events::TEST_SKIPPED); } $incomplete = $test->getMetadata()->getIncomplete(); if ($incomplete !== null) { $exception = new IncompleteTestError($incomplete); $failEvent = new FailEvent($test, $exception, 0); $result->addIncomplete($failEvent); $this->dispatcher->dispatch($failEvent, Events::TEST_INCOMPLETE); } $this->dispatcher->dispatch(new TestEvent($test, 0), Events::TEST_END); continue; } } if ($test instanceof TestCaseWrapper) { $testCase = $test->getTestCase(); if (Version::series() < 10) { $testCase->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); $testCase->setBackupGlobals($this->backupGlobals); } } $test->setEventDispatcher($this->dispatcher); $test->reportUselessTests($this->reportUselessTests); $test->collectCodeCoverage($this->collectCodeCoverage); $test->realRun($result); } } public function reorderDependencies(): void { $tests = []; foreach ($this->tests as $test) { $tests = array_merge($tests, $this->getDependencies($test)); } $queue = []; $hashes = []; foreach ($tests as $test) { if (in_array(spl_object_hash($test), $hashes, true)) { continue; } $hashes[] = spl_object_hash($test); $queue[] = $test; } $this->tests = $queue; } protected function getDependencies(Test $test): array { if (!$test instanceof Dependent) { return [$test]; } $tests = []; foreach ($test->fetchDependencies() as $requiredTestName) { $required = $this->findMatchedTest($requiredTestName); if ($required === null) { continue; } $tests = array_merge($tests, $this->getDependencies($required)); } $tests[] = $test; return $tests; } protected function findMatchedTest(string $testSignature): ?Test { foreach ($this->tests as $test) { $signature = Descriptor::getTestSignature($test); if ($signature === $testSignature) { return $test; } } return null; } /** * @return Array<string,Module> */ public function getModules(): array { return $this->modules; } /** * @param Array<string,Module> $modules */ public function setModules(array $modules): void { $this->modules = $modules; } public function getBaseName(): string { return $this->baseName; } public function setBaseName(string $baseName): void { $this->baseName = $baseName; } protected function fire(string $eventType, TestEvent $event): void { $test = $event->getTest(); if ($test instanceof TestInterface) { foreach ($test->getMetadata()->getGroups() as $group) { $this->dispatcher->dispatch($event, $eventType . '.' . $group); } } $this->dispatcher->dispatch($event, $eventType); } public function addTest(Test $test): void { $this->tests [] = $test; } /** * @return Test[] */ public function getTests(): array { return $this->tests; } public function getTestCount(): int { return count($this->tests); } public function initPHPUnitConfiguration(): void { $cliParameters = []; if ($this->backupGlobals) { $cliParameters [] = '--globals-backup'; } if ($this->beStrictAboutChangesToGlobalState) { $cliParameters [] = '--strict-global-state'; } if ($this->disallowTestOutput) { $cliParameters [] = '--disallow-test-output'; } $cliConfiguration = (new \PHPUnit\TextUI\CliArguments\Builder())->fromParameters($cliParameters, []); $xmlConfiguration = \PHPUnit\TextUI\XmlConfiguration\DefaultConfiguration::create(); Registry::init($cliConfiguration, $xmlConfiguration); } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Event\FailEvent; use Codeception\Event\StepEvent; use Codeception\Event\SuiteEvent; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Exception\ThrowableWrapper; use Codeception\Suite; use Codeception\TestInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function array_reverse; class Module implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::TEST_BEFORE => 'before', Events::TEST_AFTER => 'after', Events::STEP_BEFORE => 'beforeStep', Events::STEP_AFTER => 'afterStep', Events::TEST_FAIL => 'failed', Events::TEST_ERROR => 'failed', Events::SUITE_BEFORE => 'beforeSuite', Events::SUITE_AFTER => 'afterSuite' ]; /** * @param \Codeception\Module[] $modules */ public function __construct(protected array $modules = []) { } public function beforeSuite(SuiteEvent $event): void { $suite = $event->getSuite(); if (!$suite instanceof Suite) { return; } $this->modules = $suite->getModules(); foreach ($this->modules as $module) { $module->_beforeSuite($event->getSettings()); } } public function afterSuite(): void { foreach (array_reverse($this->modules) as $module) { $module->_afterSuite(); } } public function before(TestEvent $event): void { if (!$event->getTest() instanceof TestInterface) { return; } foreach ($this->modules as $module) { $module->_before($event->getTest()); } } public function after(TestEvent $event): void { if (!$event->getTest() instanceof TestInterface) { return; } foreach (array_reverse($this->modules) as $module) { $module->_after($event->getTest()); $module->_resetConfig(); } } public function failed(FailEvent $event): void { if (!$event->getTest() instanceof TestInterface) { return; } foreach (array_reverse($this->modules) as $module) { $exception = $event->getFail(); if (!$exception instanceof \Exception) { /** * @TODO Change _failed parameter to \Throwable in the next major version */ $exception = new ThrowableWrapper($exception); } $module->_failed($event->getTest(), $exception); } } public function beforeStep(StepEvent $event): void { foreach ($this->modules as $module) { $module->_beforeStep($event->getStep()); } } public function afterStep(StepEvent $event): void { foreach (array_reverse($this->modules) as $module) { $module->_afterStep($event->getStep()); } } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Configuration; use Codeception\Event\SuiteEvent; use Codeception\Events; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class Bootstrap implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::SUITE_INIT => 'loadBootstrap', ]; public function loadBootstrap(SuiteEvent $event): void { $settings = $event->getSettings(); if (!isset($settings['bootstrap'])) { return; } Configuration::loadBootstrap($settings['bootstrap'], $settings['path']); } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\ResultAggregator; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class FailFast implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, array<string|int>> */ protected static array $events = [ Events::TEST_FAIL => ['stopOnFail', 128], Events::TEST_ERROR => ['stopOnFail', 128], ]; private int $failureCount = 0; public function __construct(private int $stopFailureCount, private ResultAggregator $resultAggregator) { } public function stopOnFail(TestEvent $e): void { $this->failureCount++; if ($this->failureCount >= $this->stopFailureCount) { $this->resultAggregator->stop(); } } } <?php declare(strict_types=1); declare(ticks=1); namespace Codeception\Subscriber; use Codeception\Event\SuiteEvent; use Codeception\Events; use Codeception\ResultAggregator; use RuntimeException; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function function_exists; use function pcntl_async_signals; use function pcntl_signal; class GracefulTermination implements EventSubscriberInterface { /** * @var string */ public const SIGNAL_FUNC = 'pcntl_signal'; /** * @var string */ public const ASYNC_SIGNAL_HANDLING_FUNC = 'pcntl_async_signals'; public function __construct(private ResultAggregator $resultAggregator) { } public function handleSuite(SuiteEvent $event): void { if (function_exists(self::ASYNC_SIGNAL_HANDLING_FUNC)) { pcntl_async_signals(true); } if (function_exists(self::SIGNAL_FUNC)) { pcntl_signal(SIGTERM, function (): void { $this->terminate(); }); pcntl_signal(SIGINT, function (): void { $this->terminate(); }); } } public function terminate(): void { $this->resultAggregator->stop(); throw new RuntimeException( "\n\n---------------------------\nTESTS EXECUTION TERMINATED\n---------------------------\n" ); } /** * @return array<string, string> */ public static function getSubscribedEvents(): array { if (!function_exists(self::SIGNAL_FUNC)) { return []; } return [Events::SUITE_BEFORE => 'handleSuite']; } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Event\SuiteEvent; use Codeception\Events; use Codeception\Test\Test; use Codeception\Test\TestCaseWrapper; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function call_user_func; use function is_callable; class BeforeAfterTest implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string|int[]|string[]> */ protected static array $events = [ Events::SUITE_BEFORE => 'beforeClass', Events::SUITE_AFTER => ['afterClass', 100] ]; public function beforeClass(SuiteEvent $event): void { foreach ($event->getSuite()->getTests() as $test) { $this->executeMethods($test, $test->getMetadata()->getBeforeClassMethods()); } } public function afterClass(SuiteEvent $event): void { foreach ($event->getSuite()->getTests() as $test) { $this->executeMethods($test, $test->getMetadata()->getAfterClassMethods()); } } private function executeMethods(Test $test, array $methods): void { if ($test instanceof TestCaseWrapper) { $test = $test->getTestCase(); } foreach ($methods as $method) { if (is_callable([$test, $method])) { call_user_func([$test, $method]); } } } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Event\SuiteEvent; use Codeception\Events; use Codeception\Lib\Console\Output; use Codeception\Lib\Notification; use Codeception\Subscriber\Shared\StaticEventsTrait; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class Deprecation implements EventSubscriberInterface { use StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::SUITE_AFTER => 'afterSuite', ]; private Output $output; /** * @param array<string, mixed> $options */ public function __construct(array $options) { $this->output = new Output($options); } public function afterSuite(SuiteEvent $event): void { $messages = Notification::all(); if (count($messages) === 0) { return; } foreach (array_count_values($messages) as $message => $count) { if ($count > 1) { $message = $count . 'x ' . $message; } $this->output->notification($message); } $this->output->writeln(''); } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Exception\Deprecation; use Codeception\Exception\Error; use Codeception\Exception\Notice; use Codeception\Exception\Warning; use Codeception\Event\SuiteEvent; use Codeception\Events; use Codeception\Lib\Notification; use PHPUnit\Framework\Error\Deprecated as PHPUnit9Deprecation; use PHPUnit\Framework\Error\Error as PHPUnit9Error; use PHPUnit\Framework\Error\Notice as PHPUnit9Notice; use PHPUnit\Framework\Error\Warning as PHPUnit9Warning; use PHPUnit\Runner\Version as PHPUnitVersion; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler as SymfonyDeprecationErrorHandler; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function call_user_func; use function class_exists; use function count; use function error_get_last; use function error_reporting; use function getenv; use function in_array; use function is_array; use function register_shutdown_function; use function restore_error_handler; use function set_error_handler; use function sprintf; class ErrorHandler implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::SUITE_BEFORE => 'handle', Events::SUITE_AFTER => 'onFinish' ]; /** * @var bool $stopped to keep shutdownHandler from possible looping. */ private bool $stopped = false; /** * @var bool $initialized to avoid double error handler substitution */ private bool $initialized = false; private bool $deprecationsInstalled = false; /** * @var callable|null */ private $oldHandler; private bool $suiteFinished = false; /** * @var int Stores bitmask for errors */ private int $errorLevel; private bool $convertDeprecationsToExceptions = false; public function __construct() { $this->errorLevel = E_ALL & ~E_STRICT & ~E_DEPRECATED; } public function onFinish(SuiteEvent $event): void { $this->suiteFinished = true; } public function handle(SuiteEvent $event): void { $settings = $event->getSettings(); if ($settings['error_level']) { $this->errorLevel = eval("return {$settings['error_level']};"); } error_reporting($this->errorLevel); if ($settings['convert_deprecations_to_exceptions']) { $this->convertDeprecationsToExceptions = true; } if ($this->initialized) { return; } // We must register shutdown function before deprecation error handler to restore previous error handler // and silence DeprecationErrorHandler yelling about 'THE ERROR HANDLER HAS CHANGED!' register_shutdown_function([$this, 'shutdownHandler']); $this->registerDeprecationErrorHandler(); $this->oldHandler = set_error_handler([$this, 'errorHandler']); $this->initialized = true; } public function errorHandler(int $errNum, string $errMsg, string $errFile, int $errLine, array $context = []): bool { if ((E_USER_DEPRECATED === $errNum || E_DEPRECATED === $errNum) && !$this->convertDeprecationsToExceptions) { $this->handleDeprecationError($errNum, $errMsg, $errFile, $errLine, $context); return true; } if ((error_reporting() & $errNum) === 0) { // This error code is not included in error_reporting return false; } if (str_contains($errMsg, 'Cannot modify header information')) { return false; } if (PHPUnitVersion::series() < 10) { throw match ($errNum) { E_DEPRECATED, E_USER_DEPRECATED => new PHPUnit9Deprecation($errMsg, $errNum, $errFile, $errLine), E_NOTICE, E_STRICT, E_USER_NOTICE => new PHPUnit9Notice($errMsg, $errNum, $errFile, $errLine), E_WARNING, E_USER_WARNING => new PHPUnit9Warning($errMsg, $errNum, $errFile, $errLine), default => new PHPUnit9Error($errMsg, $errNum, $errFile, $errLine), }; } else { $errMsg .= ' at ' . $errFile . ':' . $errLine; throw match ($errNum) { E_DEPRECATED, E_USER_DEPRECATED => new Deprecation($errMsg, $errNum, $errFile, $errLine), E_NOTICE, E_STRICT, E_USER_NOTICE => new Notice($errMsg, $errNum, $errFile, $errLine), E_WARNING, E_USER_WARNING => new Warning($errMsg, $errNum, $errFile, $errLine), default => new Error($errMsg, $errNum, $errFile, $errLine), }; } } public function shutdownHandler(): void { if ($this->deprecationsInstalled) { restore_error_handler(); } if ($this->stopped) { return; } $this->stopped = true; $error = error_get_last(); if ( !$this->suiteFinished && ( $error === null || !in_array($error['type'], [E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR]) ) ) { echo "\n\n\nCOMMAND DID NOT FINISH PROPERLY.\n"; exit(125); } if (!is_array($error)) { return; } if (error_reporting() === 0) { return; } // not fatal if (!in_array($error['type'], [E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR])) { return; } echo "\n\n\nFATAL ERROR. TESTS NOT FINISHED.\n"; echo sprintf("%s \nin %s:%d\n", $error['message'], $error['file'], $error['line']); } private function registerDeprecationErrorHandler(): void { if (class_exists('\Symfony\Bridge\PhpUnit\DeprecationErrorHandler') && 'disabled' !== getenv('SYMFONY_DEPRECATIONS_HELPER')) { // DeprecationErrorHandler only will be installed if array('PHPUnit\Util\ErrorHandler', 'handleError') // is installed or no other error handlers are installed. // So we will remove Symfony\Component\Debug\ErrorHandler if it's installed. $old = set_error_handler('var_dump'); restore_error_handler(); if ( $old && is_array($old) && count($old) > 0 && $old[0] instanceof \Symfony\Component\Debug\ErrorHandler ) { restore_error_handler(); } $this->deprecationsInstalled = true; SymfonyDeprecationErrorHandler::register(getenv('SYMFONY_DEPRECATIONS_HELPER')); } } private function handleDeprecationError(int $type, string $message, string $file, int $line, array $context): void { if (($this->errorLevel & $type) === 0) { return; } if ($this->deprecationsInstalled && $this->oldHandler) { call_user_func($this->oldHandler, $type, $message, $file, $line, $context); return; } Notification::deprecate("{$message}", "{$file}:{$line}"); } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Lib\Di; use Codeception\Test\Cest; use Codeception\Test\Unit; use Codeception\TestInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class PrepareTest implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::TEST_BEFORE => 'prepare', ]; protected array $modules = []; public function prepare(TestEvent $event): void { $test = $event->getTest(); if (!$test instanceof TestInterface) { return; } $prepareMethods = $test->getMetadata()->getParam('prepare'); if (!$prepareMethods) { return; } /** @var Di $di */ $di = $test->getMetadata()->getService('di'); foreach ($prepareMethods as $method) { if ($test instanceof Cest) { $di->injectDependencies($test->getTestInstance(), $method); } if ($test instanceof Unit) { $di->injectDependencies($test, $method); } } } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Configuration; use Codeception\Event\SuiteEvent; use Codeception\Events; use Codeception\Exception\ConfigurationException; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function class_exists; use function is_array; use function key; use function reset; class ExtensionLoader implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::MODULE_INIT => 'registerSuiteExtensions', Events::SUITE_AFTER => 'stopSuiteExtensions' ]; /** * @var array<string, mixed> */ protected array $config = []; /** * @var array<string, mixed> */ protected array $options = []; /** * @var array<class-string, EventSubscriberInterface> */ protected array $globalExtensions = []; /** * @var array<class-string, EventSubscriberInterface> */ protected array $suiteExtensions = []; public function __construct(protected EventDispatcher $dispatcher) { $this->config = Configuration::config(); } /** * @param array<string, mixed> $options * @throws ConfigurationException */ public function bootGlobalExtensions(array $options): void { $this->options = $options; $this->globalExtensions = $this->bootExtensions($this->config); } public function registerGlobalExtensions(): void { foreach ($this->globalExtensions as $extension) { $this->dispatcher->addSubscriber($extension); } } public function registerSuiteExtensions(SuiteEvent $event): void { $suiteConfig = $event->getSettings(); $extensions = $this->bootExtensions($suiteConfig); $this->suiteExtensions = []; foreach ($extensions as $extension) { $extensionClass = $extension::class; if (isset($this->globalExtensions[$extensionClass])) { continue; // already globally enabled } $this->dispatcher->addSubscriber($extension); $this->suiteExtensions[$extensionClass] = $extension; } } public function stopSuiteExtensions(): void { foreach ($this->suiteExtensions as $extension) { $this->dispatcher->removeSubscriber($extension); } $this->suiteExtensions = []; } /** * @param array<string, mixed> $config * @return array<class-string, EventSubscriberInterface> * @throws ConfigurationException */ protected function bootExtensions(array $config): array { $extensions = []; foreach ($config['extensions']['enabled'] as $extensionClass) { if (is_array($extensionClass)) { $extensionClass = key($extensionClass); } if (!class_exists($extensionClass)) { throw new ConfigurationException( "Class `{$extensionClass}` is not defined. Autoload it or include into " . "'_bootstrap.php' file of 'tests' directory" ); } $extensionConfig = $this->getExtensionConfig($extensionClass, $config); $extension = new $extensionClass($extensionConfig, $this->options); if (!$extension instanceof EventSubscriberInterface) { throw new ConfigurationException( "Class {$extensionClass} is not an EventListener. Please create it as Extension or GroupObject." ); } $extensions[$extension::class] = $extension; } return $extensions; } /** * @param array<string, mixed> $config * @return array<string, mixed> */ private function getExtensionConfig(string $extension, array $config): array { $extensionConfig = $config['extensions']['config'][$extension] ?? []; if (!isset($config['extensions']['enabled'])) { return $extensionConfig; } if (!is_array($config['extensions']['enabled'])) { return $extensionConfig; } foreach ($config['extensions']['enabled'] as $enabledExtensionsConfig) { if (!is_array($enabledExtensionsConfig)) { continue; } $enabledExtension = key($enabledExtensionsConfig); if ($enabledExtension === $extension) { $enabledExtensionConfig = reset($enabledExtensionsConfig); if (!is_array($enabledExtensionConfig)) { return $extensionConfig; } return Configuration::mergeConfigs($enabledExtensionConfig, $extensionConfig); } } return $extensionConfig; } } <?php declare(strict_types=1); namespace Codeception\Subscriber\Shared; trait StaticEventsTrait { public static function getSubscribedEvents(): array { return static::$events; } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Configuration; use Codeception\Event\SuiteEvent; use Codeception\Events; use Codeception\Lib\Generator\Actions; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function codecept_debug; use function fclose; use function fgets; use function file_exists; use function file_put_contents; use function fopen; use function is_writable; use function mkdir; use function preg_match; class AutoRebuild implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::SUITE_INIT => 'updateActor' ]; public function updateActor(SuiteEvent $event): void { $settings = $event->getSettings(); if (!$settings['actor']) { codecept_debug('actor is empty'); return; // no actor } $modules = $event->getSuite()->getModules(); $actorActionsFile = Configuration::supportDir() . '_generated' . DIRECTORY_SEPARATOR . $settings['actor'] . 'Actions.php'; if (!file_exists($actorActionsFile)) { codecept_debug("Generating {$settings['actor']}Actions..."); $this->generateActorActions($actorActionsFile, $settings); return; } // load actor class to see hash $handle = @fopen($actorActionsFile, "r"); if ($handle && is_writable($actorActionsFile)) { $line = @fgets($handle); if (preg_match('#\[STAMP] ([a-f0-9]*)#', $line, $matches)) { $hash = $matches[1]; $currentHash = Actions::genHash($modules, $settings); // regenerate actor class when hashes do not match if ($hash != $currentHash) { codecept_debug("Rebuilding {$settings['actor']}..."); @fclose($handle); $this->generateActorActions($actorActionsFile, $settings); return; } } @fclose($handle); } } protected function generateActorActions(string $actorActionsFile, array $settings): void { if (!file_exists(Configuration::supportDir() . '_generated')) { @mkdir(Configuration::supportDir() . '_generated'); } $actionsGenerator = new Actions($settings); $generated = $actionsGenerator->produce(); @file_put_contents($actorActionsFile, $generated); } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Event\FailEvent; use Codeception\Event\PrintResultEvent; use Codeception\Event\StepEvent; use Codeception\Event\SuiteEvent; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Exception\UselessTestException; use Codeception\Lib\Console\Message; use Codeception\Lib\Console\MessageFactory; use Codeception\Lib\Console\Output; use Codeception\ResultAggregator; use Codeception\Step; use Codeception\Step\Comment; use Codeception\Step\ConditionalAssertion; use Codeception\Step\Meta; use Codeception\Subscriber\Shared\StaticEventsTrait; use Codeception\Suite; use Codeception\Test\Descriptor; use Codeception\Test\Interfaces\ScenarioDriven; use Codeception\TestInterface; use Codeception\Util\Debug; use Codeception\Util\StackTraceFilter; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\IncompleteTestError; use PHPUnit\Framework\SelfDescribing; use PHPUnit\Framework\SkippedTest; use SebastianBergmann\Timer\Duration; use SebastianBergmann\Timer\ResourceUsageFormatter; use SebastianBergmann\Timer\Timer; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function array_map; use function array_merge; use function array_reverse; use function array_shift; use function codecept_relative_path; use function count; use function exec; use function getenv; use function implode; use function number_format; use function preg_match; use function preg_replace; use function round; use function sprintf; use function strlen; use function strtoupper; use function substr; use function ucfirst; class Console implements EventSubscriberInterface { use StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::SUITE_AFTER => 'afterSuite', Events::TEST_START => 'startTest', Events::TEST_END => 'endTest', Events::STEP_BEFORE => 'beforeStep', Events::TEST_SUCCESS => 'testSuccess', Events::TEST_FAIL => 'testFail', Events::TEST_ERROR => 'testError', Events::TEST_INCOMPLETE => 'testIncomplete', Events::TEST_SKIPPED => 'testSkipped', Events::TEST_WARNING => 'testWarning', Events::TEST_USELESS => 'testUseless', Events::TEST_FAIL_PRINT => 'printFail', Events::RESULT_PRINT_AFTER => 'afterResult', ]; protected ?Meta $metaStep = null; protected ?Message $message = null; protected bool $steps = true; protected bool $debug = false; protected bool $ansi = true; protected bool $silent = false; protected ?SelfDescribing $printedTest = null; protected bool $rawStackTrace = false; protected int $traceLength = 5; protected ?int $width = null; protected Output $output; protected string $namespace = ''; /** * @var array<string, string> */ protected array $chars = ['success' => '+', 'fail' => 'x', 'of' => ':']; /** * @var array<string, int|bool|null> */ protected array $options = [ 'debug' => false, 'ansi' => false, 'steps' => true, 'verbosity' => 0, 'xml' => null, 'phpunit-xml' => null, 'html' => null, 'no-artifacts' => false, ]; protected MessageFactory $messageFactory; private Timer $timer; private bool $firstDefectType = true; /** * @param array<string, mixed> $options */ public function __construct(array $options) { $this->timer = new Timer(); $this->timer->start(); $this->prepareOptions($options); $this->output = new Output($options); $this->messageFactory = new MessageFactory($this->output); if ($this->debug) { Debug::setOutput($this->output); } $this->detectWidth(); if ($this->options['ansi'] && !$this->isWin()) { $this->chars['success'] = '✔'; $this->chars['fail'] = '✖'; } } // triggered for scenario based tests: cept, cest public function beforeSuite(SuiteEvent $event): void { $this->namespace = ""; $settings = $event->getSettings(); if (isset($settings['namespace'])) { $this->namespace = $settings['namespace']; } $this->message("%s Tests (%d) ") ->with(ucfirst($event->getSuite()->getBaseName()), $event->getSuite()->getTestCount()) ->style('bold') ->width($this->width, '-') ->prepend("\n") ->writeln(); if ($event->getSuite() instanceof Suite) { $message = $this->message( implode( ', ', array_map( fn ($module) => $module->_getName(), $event->getSuite()->getModules() ) ) ); $message->style('info') ->prepend('Modules: ') ->writeln(OutputInterface::VERBOSITY_VERBOSE); } $this->message()->width($this->width, '-')->writeln(OutputInterface::VERBOSITY_VERBOSE); } // triggered for all tests public function startTest(TestEvent $event): void { $test = $event->getTest(); $this->printedTest = $test; $this->message = null; if (!$this->output->isInteractive() && !$this->isDetailed($test)) { return; } $this->writeCurrentTest($test); if ($this->isDetailed($test)) { $this->output->writeln(''); $this->message(Descriptor::getTestSignature($test)) ->style('info') ->prepend('Signature: ') ->writeln(); $this->message(codecept_relative_path(Descriptor::getTestFullName($test))) ->style('info') ->prepend('Test: ') ->writeln(); if ($this->steps) { $this->message('Scenario --')->style('comment')->writeln(); $this->output->waitForDebugOutput = false; } } } public function afterResult(PrintResultEvent $event): void { $result = $event->getResult(); $this->printHeader($result); $verbose = $this->options['verbosity'] >= OutputInterface::VERBOSITY_VERBOSE; $outputFormatter = $this->output->getFormatter(); $outputFormatter->setStyle('warning', new OutputFormatterStyle('black', 'yellow')); $outputFormatter->setStyle('success', new OutputFormatterStyle('black', 'green')); $this->printDefects($result->errors(), 'error'); $this->printDefects($result->failures(), 'failure'); $this->printDefects($result->useless(), 'useless test'); if ($verbose) { $this->printDefects($result->incomplete(), 'incomplete test'); $this->printDefects($result->skipped(), 'skipped test'); } $this->printFooter($event); if ($result->skippedCount() + $result->incompleteCount() > 0 && !$verbose) { $this->output->writeln("run with `-v` to get more info about skipped or incomplete tests"); } } protected function printHeader(ResultAggregator $result): void { if ($result->testCount() > 0) { $this->printResourceUsage($this->timer->stop()); } } private function printResourceUsage(Duration $duration): void { $formatter = new ResourceUsageFormatter(); $this->message($formatter->resourceUsage($duration))->writeln(); } /** * @param FailEvent[] $defects * @param string $type */ private function printDefects(array $defects, string $type): void { $count = count($defects); if ($count == 0) { return; } if ($this->firstDefectType) { $this->firstDefectType = false; } else { $this->message("\n---------")->writeln(); } $this->message('')->writeln(); $this->message( sprintf( "There %s %d %s%s:", ($count == 1) ? 'was' : 'were', $count, $type, ($count == 1) ? '' : 's' ) )->writeln(); $i = 1; foreach ($defects as $defect) { $this->printFail($defect, $i++); } } protected function printFooter(PrintResultEvent $event): void { $result = $event->getResult(); $testCount = $result->testCount(); $assertionCount = $result->assertionCount(); $this->message('')->writeln(); if ($testCount === 0) { $this->message('No tests executed!')->style('warning')->writeln(); return; } if ($result->wasSuccessfulAndNoTestIsUselessOrSkippedOrIncomplete()) { $message = sprintf( 'OK (%d test%s, %d assertion%s)', $testCount, $testCount === 1 ? '' : 's', $assertionCount, $assertionCount === 1 ? '' : 's' ); $this->message($message)->style('success')->writeln(); return; } $style = 'error'; if ($result->wasSuccessful()) { $style = 'warning'; $this->message('OK, but incomplete, skipped, or useless tests!')->style($style)->writeln(); } elseif ($result->errorCount()) { $this->message('ERRORS!')->style($style)->writeln(); } elseif ($result->failureCount()) { $this->message('FAILURES!')->style($style)->writeln(); } elseif ($result->warningCount()) { $style = 'warning'; $this->message('WARNINGS!')->style($style)->writeln(); } $counts = [ sprintf("Tests: %s", $testCount), sprintf("Assertions: %s", $assertionCount), ]; if ($result->errorCount() > 0) { $counts [] = sprintf("Errors: %s", $result->errorCount()); } if ($result->failureCount() > 0) { $counts [] = sprintf("Failures: %s", $result->failureCount()); } if ($result->warningCount() > 0) { $counts [] = sprintf("Warnings: %s", $result->warningCount()); } if ($result->skippedCount() > 0) { $counts [] = sprintf("Skipped: %s", $result->skippedCount()); } if ($result->incompleteCount() > 0) { $counts [] = sprintf("Incomplete: %s", $result->incompleteCount()); } if ($result->uselessCount() > 0) { $counts [] = sprintf("Useless: %s", $result->uselessCount()); } $this->message(implode(', ', $counts) . '.')->style($style)->writeln(); } public function testSuccess(TestEvent $event): void { if ($this->isDetailed($event->getTest())) { $this->message('PASSED')->center(' ')->style('ok')->append("\n")->writeln(); return; } $this->writelnFinishedTest($event, $this->message($this->chars['success'])->style('ok')); } public function endTest(TestEvent $event): void { $this->metaStep = null; $this->printedTest = null; } public function testWarning(TestEvent $event): void { if ($this->isDetailed($event->getTest())) { $this->message('WARNING')->center(' ')->style('pending')->append("\n")->writeln(); return; } $this->writelnFinishedTest($event, $this->message('W')->style('pending')); } public function testFail(FailEvent $event): void { if ($this->isDetailed($event->getTest())) { $this->message('FAIL')->center(' ')->style('fail')->append("\n")->writeln(); return; } $this->writelnFinishedTest($event, $this->message($this->chars['fail'])->style('fail')); } public function testError(FailEvent $event): void { if ($this->isDetailed($event->getTest())) { $this->message('ERROR')->center(' ')->style('fail')->append("\n")->writeln(); return; } $this->writelnFinishedTest($event, $this->message('E')->style('fail')); } public function testSkipped(FailEvent $event): void { if ($this->isDetailed($event->getTest())) { $msg = $event->getFail()->getMessage(); $this->message('SKIPPED')->append($msg !== '' ? ": {$msg}" : '')->center(' ')->style('pending')->writeln(); return; } $this->writelnFinishedTest($event, $this->message('S')->style('pending')); } public function testIncomplete(FailEvent $event): void { if ($this->isDetailed($event->getTest())) { $msg = $event->getFail()->getMessage(); $this->message('INCOMPLETE')->append($msg !== '' ? ": {$msg}" : '')->center(' ')->style('pending')->writeln(); return; } $this->writelnFinishedTest($event, $this->message('I')->style('pending')); } public function testUseless(FailEvent $event): void { $this->writelnFinishedTest($event, $this->message('U')->style('pending')); } protected function isDetailed($test): bool { if (!$test instanceof ScenarioDriven) { return false; } return $this->steps; } public function beforeStep(StepEvent $event): void { if (!$this->steps || !$event->getTest() instanceof ScenarioDriven) { return; } $metaStep = $event->getStep()->getMetaStep(); if ($metaStep && $this->metaStep != $metaStep) { $this->message(' ' . $metaStep->getPrefix()) ->style('bold') ->append($metaStep->__toString()) ->writeln(); } $this->metaStep = $metaStep; $this->printStep($event->getStep()); } private function printStep(Step $step): void { if ($step instanceof Comment && $step->__toString() == '') { return; // don't print empty comments } $msg = $this->message(' '); if ($this->metaStep) { $msg->append(' '); } $msg->append($step->getPrefix()); $prefixLength = $msg->getLength(); if (!$this->metaStep) { $msg->style('bold'); } $maxLength = $this->width - $prefixLength; $msg->append(OutputFormatter::escape($step->toString($maxLength))); if ($this->metaStep) { $msg->style('info'); } $msg->writeln(); } public function afterSuite(SuiteEvent $event): void { $this->message()->width($this->width, '-')->writeln(); } public function printFail(FailEvent $event, int $eventNumber): void { $failedTest = $event->getTest(); $fail = $event->getFail(); $this->output->write($eventNumber . ") "); $this->writeCurrentTest($failedTest, false); $this->output->writeln(''); // Clickable `editor_url`: if (isset($this->options['editor_url']) && is_string($this->options['editor_url'])) { $filePath = $failedTest->getFilename(); $line = 1; foreach ($fail->getTrace() as $trace) { if (isset($trace['file']) && $filePath === $trace['file'] && isset($trace['line'])) { $line = $trace['line']; } } $message = str_replace(['%%file%%', '%%line%%'], [$filePath, $line], $this->options['editor_url']); } else { $message = Descriptor::getTestFullName($failedTest); } $testStyle = 'error'; if ( $fail instanceof SkippedTest || $fail instanceof IncompleteTestError || $fail instanceof UselessTestException ) { $testStyle = 'warning'; } $this->message(' Test ')->style($testStyle) ->append($message) ->write(); if ($failedTest instanceof ScenarioDriven) { $this->printScenarioFail($failedTest, $fail); $this->printReports($failedTest); return; } $this->printException($fail); $this->printExceptionTrace($fail); } public function printReports(TestInterface $failedTest): void { if ($this->options['no-artifacts']) { return; } $reports = $failedTest->getMetadata()->getReports(); if (!empty($reports)) { $this->output->writeln('<comment>Artifacts:</comment>'); $this->output->writeln(''); } foreach ($reports as $type => $report) { $type = ucfirst($type); $this->output->writeln("{$type}: <debug>{$report}</debug>"); } } public function printException($exception, string $cause = null): void { if ($exception instanceof SkippedTest || $exception instanceof IncompleteTestError) { if ($exception->getMessage() !== '') { $this->message(OutputFormatter::escape($exception->getMessage()))->prepend("\n")->writeln(); } return; } $class = $exception::class; if (str_starts_with($class, 'Codeception\Exception')) { $class = substr($class, strlen('Codeception\Exception\\')); } $this->output->writeln(''); $message = $this->message(OutputFormatter::escape($exception->getMessage())); if ($exception instanceof ExpectationFailedException) { $comparisonFailure = $exception->getComparisonFailure(); if ($comparisonFailure !== null) { $message->append($this->messageFactory->prepareComparisonFailureMessage($comparisonFailure)); } } $isFailure = $exception instanceof AssertionFailedError || $class === ExpectationFailedException::class || $class === AssertionFailedError::class; if (!$isFailure) { $message->prepend("[{$class}] ")->block('error'); } if ($isFailure && $cause) { $cause = OutputFormatter::escape(ucfirst($cause)); $message->prepend("<error> Step </error> {$cause}\n<error> Fail </error> "); } $message->writeln(); } public function printScenarioFail(ScenarioDriven $failedTest, $fail): void { $failedStep = (string)$failedTest->getScenario()->getMetaStep(); if ($failedStep === '') { foreach (array_reverse($failedTest->getScenario()->getSteps()) as $step) { if ($step->hasFailed()) { $failedStep = (string)$step; break; } } } $this->printException($fail, $failedStep); $this->printScenarioTrace($failedTest); if ($this->output->getVerbosity() == OutputInterface::VERBOSITY_DEBUG) { $this->printExceptionTrace($fail); return; } if (!$fail instanceof AssertionFailedError) { $this->printExceptionTrace($fail); } } public function printExceptionTrace($exception): void { static $limit = 10; if ( $exception instanceof SkippedTest || $exception instanceof IncompleteTestError || $exception instanceof UselessTestException ) { return; } if ($this->rawStackTrace) { $this->message(OutputFormatter::escape(StackTraceFilter::getFilteredStacktrace($exception, true, false)))->writeln(); return; } $trace = StackTraceFilter::getFilteredStacktrace($exception, false); $i = 0; foreach ($trace as $step) { if ($i >= $limit) { break; } ++$i; $message = $this->message((string)$i)->prepend('#')->width(4); if (!isset($step['file'])) { foreach (['class', 'type', 'function'] as $info) { if (!isset($step[$info])) { continue; } $message->append($step[$info]); } $message->writeln(); continue; } // Clickable `editor_url`: if (isset($this->options['editor_url']) && is_string($this->options['editor_url'])) { $lineString = str_replace(['%%file%%', '%%line%%'], [$step['file'], $step['line']], $this->options['editor_url']); } else { $lineString = $step['file'] . ':' . $step['line']; } $message->append($lineString); $message->writeln(); } $prev = $exception->getPrevious(); if ($prev) { $this->printExceptionTrace($prev); } } public function printScenarioTrace(ScenarioDriven $failedTest): void { $trace = array_reverse($failedTest->getScenario()->getSteps()); $length = count($trace); $stepNumber = $length; if ($length === 0) { return; } $this->message("\nScenario Steps:\n")->style('comment')->writeln(); foreach ($trace as $step) { /** @var Step $step */ if (!$step->__toString()) { continue; } $message = $this ->message((string)$stepNumber) ->prepend(' ') ->width(strlen((string)$length)) ->append(". "); $message->append(OutputFormatter::escape($step->getPhpCode($this->width - $message->getLength()))); if ($step->hasFailed()) { $message->style('bold'); } if (!$step instanceof Comment) { $filePath = $step->getFilePath(); if ($filePath) { // Clickable `editor_url`: if (isset($this->options['editor_url']) && is_string($this->options['editor_url'])) { $lineString = str_replace(['%%file%%', '%%line%%'], [codecept_absolute_path($step->getFilePath()), $step->getLineNumber()], $this->options['editor_url']); } else { $lineString = $step->getFilePath() . ':' . $step->getLineNumber(); } $message->append(" at <info>$lineString</info>"); } } --$stepNumber; $message->writeln(); if (($length - $stepNumber - 1) >= $this->traceLength) { break; } } $this->output->writeln(""); } public function detectWidth(): int { $this->width = 60; if ( !$this->isWin() && (PHP_SAPI === "cli") && (getenv('TERM')) && (getenv('TERM') != 'unknown') ) { // try to get terminal width from ENV variable (bash), see also https://github.com/Codeception/Codeception/issues/3788 if (getenv('COLUMNS')) { $this->width = (int)getenv('COLUMNS'); } else { $this->width = (int)(`command -v tput >> /dev/null 2>&1 && tput cols`) - 2; } } elseif ($this->isWin() && (PHP_SAPI === "cli")) { exec('mode con', $output); if (isset($output[4])) { preg_match('#^ +.* +(\d+)$#', $output[4], $matches); if (!empty($matches[1])) { $this->width = (int)$matches[1]; } } } return $this->width; } private function isWin(): bool { return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; } protected function writeCurrentTest(SelfDescribing $test, bool $inProgress = true): void { $prefix = ($this->output->isInteractive() && !$this->isDetailed($test) && $inProgress) ? '- ' : ''; $testString = Descriptor::getTestAsString($test); $testString = preg_replace('#^([^:]+):\s#', "<focus>$1{$this->chars['of']}</focus> ", $testString); $this ->message($testString) ->prepend($prefix) ->write(); } protected function writelnFinishedTest(TestEvent $event, Message $result): void { /** @var SelfDescribing $test */ $test = $event->getTest(); if ($this->isDetailed($test)) { return; } if ($this->output->isInteractive()) { $this->output->write("\x0D"); } $result->append(' ')->write(); $this->writeCurrentTest($test, false); if (method_exists($test, 'getScenario')) { $numFails = count( array_filter( $test->getScenario()?->getSteps() ?? [], function (Step $step) { return $step->hasFailed() && $step instanceof ConditionalAssertion; } ) ); $conditionalFailsMessage = ""; if ($numFails == 1) { $conditionalFailsMessage = "[F]"; } elseif ($numFails !== 0) { $conditionalFailsMessage = "{$numFails}x[F]"; } if ($conditionalFailsMessage !== '') { $conditionalFailsMessage = " <error>{$conditionalFailsMessage}</error> "; $this->message($conditionalFailsMessage)->write(); } } $this->writeTimeInformation($event); $this->output->writeln(''); } private function message(string $string = ''): Message { return $this->messageFactory->message($string); } protected function writeTimeInformation(TestEvent $event): void { $time = $event->getTime(); if ($time !== 0.0) { $this ->message(number_format(round($time, 2), 2)) ->prepend('(') ->append('s)') ->style('info') ->write(); } } private function prepareOptions(array $options): void { $this->options = array_merge($this->options, $options); $this->debug = $this->options['debug'] || $this->options['verbosity'] >= OutputInterface::VERBOSITY_VERY_VERBOSE; $this->steps = $this->debug || $this->options['steps']; $this->rawStackTrace = ($this->options['verbosity'] === OutputInterface::VERBOSITY_DEBUG); } } <?php declare(strict_types=1); namespace Codeception\Subscriber; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Test\Descriptor; use Codeception\Test\Interfaces\Dependent; use Codeception\TestInterface; use PHPUnit\Framework\SelfDescribing; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function in_array; class Dependencies implements EventSubscriberInterface { use Shared\StaticEventsTrait; /** * @var array<string, string> */ protected static array $events = [ Events::TEST_START => 'testStart', Events::TEST_SUCCESS => 'testSuccess' ]; /** * @var string[] */ protected array $successfulTests = []; public function testStart(TestEvent $event): void { $test = $event->getTest(); if (!$test instanceof Dependent) { return; } $testSignatures = $test->fetchDependencies(); foreach ($testSignatures as $signature) { if (!in_array($signature, $this->successfulTests) && $test instanceof TestInterface) { $test->getMetadata()->setSkip("This test depends on {$signature} to pass"); return; } } } public function testSuccess(TestEvent $event): void { $test = $event->getTest(); $this->successfulTests[] = Descriptor::getTestSignature($test); } } <?php declare(strict_types=1); namespace Codeception\Coverage\Subscriber; use Codeception\Configuration; use Codeception\Coverage\Filter; use Codeception\Coverage\PhpCodeCoverageFactory; use Codeception\Event\PrintResultEvent; use Codeception\Events; use Codeception\Exception\ConfigurationException; use Codeception\Lib\Console\Output; use Codeception\Subscriber\Shared\StaticEventsTrait; use PHPUnit\Runner\Version as PHPUnitVersion; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; use SebastianBergmann\CodeCoverage\Report\Cobertura as CoberturaReport; use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlFacadeReport; use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; use SebastianBergmann\CodeCoverage\Report\Text as TextReport; use SebastianBergmann\CodeCoverage\Report\Thresholds; use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlFacadeReport; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function array_merge; use function class_exists; use function file_put_contents; use function sprintf; use function strpos; class Printer implements EventSubscriberInterface { use StaticEventsTrait; /** * @var array<string, string> */ public static array $events = [ Events::RESULT_PRINT_AFTER => 'printResult' ]; protected array $settings = [ 'enabled' => true, 'low_limit' => 35, 'high_limit' => 70, 'show_uncovered' => false, 'show_only_summary' => false ]; public static CodeCoverage $coverage; protected string $logDir; public function __construct(protected array $options, private Output $output) { $this->logDir = Configuration::outputDir(); $this->settings = array_merge($this->settings, Configuration::config()['coverage']); self::$coverage = PhpCodeCoverageFactory::build(); // Apply filter $filter = new Filter(self::$coverage); $filter->whiteList(Configuration::config()); $filter->blackList(Configuration::config()); } protected function absolutePath(string $path): string { if ((str_starts_with($path, '/')) || (strpos($path, ':') === 1)) { // absolute path return $path; } return $this->logDir . $path; } public function printResult(PrintResultEvent $event): void { if (!$this->settings['enabled']) { $this->output->write("\nCodeCoverage is disabled in `codeception.yml` config\n"); return; } if (!$this->options['quiet']) { $this->printConsole(); } $this->output->write("Remote CodeCoverage reports are not printed to console\n"); $this->printPHP(); $this->output->write("\n"); if ($this->options['coverage-html']) { $this->printHtml(); $this->output->write("HTML report generated in {$this->options['coverage-html']}\n"); } if ($this->options['coverage-xml']) { $this->printXml(); $this->output->write("XML report generated in {$this->options['coverage-xml']}\n"); } if ($this->options['coverage-text']) { $this->printText(); $this->output->write("Text report generated in {$this->options['coverage-text']}\n"); } if ($this->options['coverage-crap4j']) { $this->printCrap4j(); $this->output->write("Crap4j report generated in {$this->options['coverage-crap4j']}\n"); } if ($this->options['coverage-cobertura']) { $this->printCobertura(); $this->output->write("Cobertura report generated in {$this->options['coverage-cobertura']}\n"); } if ($this->options['coverage-phpunit']) { $this->printPHPUnit(); $this->output->write("PHPUnit report generated in {$this->options['coverage-phpunit']}\n"); } } protected function printConsole(): void { if (PHPUnitVersion::series() < 10) { $writer = new TextReport( $this->settings['low_limit'], $this->settings['high_limit'], $this->settings['show_uncovered'], $this->settings['show_only_summary'] ); } else { $writer = new TextReport( Thresholds::from( $this->settings['low_limit'], $this->settings['high_limit'], ), $this->settings['show_uncovered'], $this->settings['show_only_summary'] ); } $this->output->write($writer->process(self::$coverage, $this->options['colors'])); } protected function printHtml(): void { if (PHPUnitVersion::series() < 10) { $writer = new HtmlFacadeReport( $this->settings['low_limit'], $this->settings['high_limit'], sprintf( ', <a target="_top" href="/newspapers?url=https://codeception.com">Codeception</a> and <a target="_top" href="/newspapers?url=https://phpunit.de/">PHPUnit %s</a>', PHPUnitVersion::id() ) ); } else { $writer = new HtmlFacadeReport( sprintf( ', <a target="_top" href="/newspapers?url=https://codeception.com">Codeception</a> and <a target="_top" href="/newspapers?url=https://phpunit.de/">PHPUnit %s</a>', PHPUnitVersion::id() ), null, Thresholds::from($this->settings['low_limit'], $this->settings['high_limit']), ); } $writer->process(self::$coverage, $this->absolutePath($this->options['coverage-html'])); } protected function printXml(): void { $writer = new CloverReport(); $writer->process(self::$coverage, $this->absolutePath($this->options['coverage-xml'])); } protected function printPHP(): void { $writer = new PhpReport(); $writer->process(self::$coverage, $this->absolutePath($this->options['coverage'])); } protected function printText(): void { if (PHPUnitVersion::series() < 10) { $writer = new TextReport( $this->settings['low_limit'], $this->settings['high_limit'], $this->settings['show_uncovered'], $this->settings['show_only_summary'] ); } else { $writer = new TextReport( Thresholds::from( $this->settings['low_limit'], $this->settings['high_limit'], ), $this->settings['show_uncovered'], $this->settings['show_only_summary'] ); } file_put_contents( $this->absolutePath($this->options['coverage-text']), $writer->process(self::$coverage, false) ); } protected function printCrap4j(): void { $writer = new Crap4jReport(); $writer->process(self::$coverage, $this->absolutePath($this->options['coverage-crap4j'])); } protected function printCobertura(): void { $writer = new CoberturaReport(); $writer->process(self::$coverage, $this->absolutePath($this->options['coverage-cobertura'])); } protected function printPHPUnit(): void { $writer = new XmlFacadeReport(PHPUnitVersion::id()); $writer->process(self::$coverage, $this->absolutePath($this->options['coverage-phpunit'])); } } <?php declare(strict_types=1); namespace Codeception\Coverage\Subscriber; use Codeception\Coverage\Filter; use Codeception\Coverage\PhpCodeCoverageFactory; use Codeception\Coverage\SuiteSubscriber; use Codeception\Event\SuiteEvent; use Codeception\Events; use Codeception\Exception\ConfigurationException; use Codeception\Exception\ModuleException; use Codeception\Lib\Interfaces\Remote; use Codeception\Stub; use Exception; use PHPUnit\Runner\CodeCoverage as PHPUnitCodeCoverage; use PHPUnit\Runner\Version as PHPUnitVersion; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; /** * Collects code coverage from unit and functional tests. * Results from all suites are merged. */ class Local extends SuiteSubscriber { /** * @var array<string, string> */ public static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::SUITE_AFTER => 'afterSuite', ]; protected ?Remote $module = null; protected function isEnabled(): bool { return !$this->module instanceof Remote && $this->settings['enabled']; } /** * @throws ConfigurationException|ModuleException|Exception */ public function beforeSuite(SuiteEvent $event): void { $this->applySettings($event->getSettings()); $this->module = $this->getServerConnectionModule($event->getSuite()->getModules()); if (!$this->isEnabled()) { return; } $event->getSuite()->collectCodeCoverage(true); Filter::setup($this->coverage) ->whiteList($this->filters) ->blackList($this->filters); } public function afterSuite(SuiteEvent $event): void { if (!$this->isEnabled()) { return; } $codeCoverage = PhpCodeCoverageFactory::build(); PhpCodeCoverageFactory::clear(); $this->mergeToPrint($codeCoverage); } } <?php declare(strict_types=1); namespace Codeception\Coverage\Subscriber; use Codeception\Configuration; use Codeception\Event\SuiteEvent; use Codeception\Util\FileSystem; use PharData; use function file_put_contents; use function is_dir; use function mkdir; use function strtr; use function sys_get_temp_dir; use function tempnam; use function unlink; /** * When collecting code coverage on remote server * data is retrieved over HTTP and not merged with the local code coverage results. * * Class RemoteServer * @package Codeception\Coverage\Subscriber */ class RemoteServer extends LocalServer { public function isEnabled(): bool { return $this->module && $this->settings['remote'] && $this->settings['enabled']; } public function afterSuite(SuiteEvent $event): void { if (!$this->isEnabled()) { return; } $suite = strtr($event->getSuite()->getName(), ['\\' => '.']); if ($this->options['coverage-xml']) { $this->retrieveAndPrintXml($suite); } if ($this->options['coverage-html']) { $this->retrieveAndPrintHtml($suite); } if ($this->options['coverage-crap4j']) { $this->retrieveAndPrintCrap4j($suite); } if ($this->options['coverage-cobertura']) { $this->retrieveAndPrintCobertura($suite); } if ($this->options['coverage-phpunit']) { $this->retrieveAndPrintPHPUnit($suite); } } protected function retrieveAndPrintHtml(string $suite): void { $tempFile = tempnam(sys_get_temp_dir(), 'C3') . '.tar'; file_put_contents($tempFile, $this->c3Request('html')); $destDir = Configuration::outputDir() . $suite . '.remote.coverage'; if (is_dir($destDir)) { FileSystem::doEmptyDir($destDir); } else { mkdir($destDir, 0777, true); } $pharData = new PharData($tempFile); $pharData->extractTo($destDir); unlink($tempFile); } protected function retrieveAndPrintXml(string $suite): void { $destFile = Configuration::outputDir() . $suite . '.remote.coverage.xml'; file_put_contents($destFile, $this->c3Request('clover')); } protected function retrieveAndPrintCrap4j(string $suite): void { $destFile = Configuration::outputDir() . $suite . '.remote.crap4j.xml'; file_put_contents($destFile, $this->c3Request('crap4j')); } protected function retrieveAndPrintCobertura(string $suite): void { $destFile = Configuration::outputDir() . $suite . '.remote.cobertura.xml'; file_put_contents($destFile, $this->c3Request('cobertura')); } protected function retrieveAndPrintPHPUnit(string $suite): void { $tempFile = tempnam(sys_get_temp_dir(), 'C3') . '.tar'; file_put_contents($tempFile, $this->c3Request('phpunit')); $destDir = Configuration::outputDir() . $suite . '.remote.coverage-phpunit'; if (is_dir($destDir)) { FileSystem::doEmptyDir($destDir); } else { mkdir($destDir, 0777, true); } $pharData = new PharData($tempFile); $pharData->extractTo($destDir); unlink($tempFile); } } <?php declare(strict_types=1); namespace Codeception\Coverage\Subscriber; use Codeception\Configuration; use Codeception\Coverage\SuiteSubscriber; use Codeception\Event\StepEvent; use Codeception\Event\SuiteEvent; use Codeception\Event\TestEvent; use Codeception\Events; use Codeception\Exception\ModuleException; use Codeception\Exception\RemoteException; use Codeception\Lib\Interfaces\Web as WebInterface; use Codeception\Lib\Notification; use Codeception\Module\WebDriver as WebDriverModule; use Facebook\WebDriver\Exception\NoSuchAlertException; use RuntimeException; use SebastianBergmann\CodeCoverage\CodeCoverage; use function array_filter; use function array_key_exists; use function array_replace_recursive; use function codecept_debug; use function file_exists; use function file_get_contents; use function is_array; use function json_encode; use function parse_url; use function preg_match; use function rtrim; use function str_replace; use function stream_context_create; use function unserialize; use function usleep; /** * When collecting code coverage data from local server HTTP requests are sent to c3.php file. * Coverage Collection is started by sending cookies/headers. * Result is taken from the local file and merged with local code coverage results. * * Class LocalServer * @package Codeception\Coverage\Subscriber */ class LocalServer extends SuiteSubscriber { // headers /** * @var string */ public const COVERAGE_HEADER = 'X-Codeception-CodeCoverage'; /** * @var string */ public const COVERAGE_HEADER_ERROR = 'X-Codeception-CodeCoverage-Error'; /** * @var string */ public const COVERAGE_HEADER_CONFIG = 'X-Codeception-CodeCoverage-Config'; /** * @var string */ public const COVERAGE_HEADER_SUITE = 'X-Codeception-CodeCoverage-Suite'; // cookie names /** * @var string */ public const COVERAGE_COOKIE = 'CODECEPTION_CODECOVERAGE'; /** * @var string */ public const COVERAGE_COOKIE_ERROR = 'CODECEPTION_CODECOVERAGE_ERROR'; protected string $suiteName = ''; protected array $c3Access = [ 'http' => [ 'method' => "GET", 'header' => '' ] ]; protected ?WebInterface $module = null; /** * @var array<string, string> */ public static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::TEST_BEFORE => 'beforeTest', Events::STEP_AFTER => 'afterStep', Events::SUITE_AFTER => 'afterSuite', ]; protected function isEnabled(): bool { return $this->module && !$this->settings['remote'] && $this->settings['enabled']; } public function beforeSuite(SuiteEvent $event): void { $this->module = $this->getServerConnectionModule($event->getSuite()->getModules()); $this->applySettings($event->getSettings()); if (!$this->isEnabled()) { return; } $this->suiteName = $event->getSuite()->getBaseName(); if ($this->settings['remote_config']) { $this->addC3AccessHeader(self::COVERAGE_HEADER_CONFIG, $this->settings['remote_config']); $knock = $this->c3Request('clear'); if ($knock === false) { throw new RemoteException( ' CodeCoverage Error. Check the file "c3.php" is included in your application. We tried to access "/c3/report/clear" but this URI was not accessible. You can review actual error messages in c3tmp dir. ' ); } } } public function beforeTest(TestEvent $event): void { if (!$this->isEnabled()) { return; } $this->startCoverageCollection($event->getTest()->getName()); } public function afterStep(StepEvent $event): void { if (!$this->isEnabled()) { return; } $this->fetchErrors(); } public function afterSuite(SuiteEvent $event): void { if (!$this->isEnabled()) { return; } // wait for all running tests to finish $blockfilename = Configuration::outputDir() . 'c3tmp/block_report'; if (file_exists($blockfilename) && filesize($blockfilename) !== 0) { $retries = 120; // 30 sec total while (file_get_contents($blockfilename) !== '0' && --$retries >= 0) { usleep(250_000); // 0.25 sec } if (file_get_contents($blockfilename) !== '0' && $retries === -1) { Notification::warning( 'Timeout: Some coverage data is not included in the coverage report.', '', ); } } $coverageFile = Configuration::outputDir() . 'c3tmp/codecoverage.serialized'; $retries = 5; while (!file_exists($coverageFile) && --$retries >= 0) { $seconds = (int)(0.5 * 1_000_000); // 0.5 sec usleep($seconds); } if (!file_exists($coverageFile)) { if (file_exists(Configuration::outputDir() . 'c3tmp/error.txt')) { throw new RuntimeException(file_get_contents(Configuration::outputDir() . 'c3tmp/error.txt')); } throw new RuntimeException('Code coverage file ' . $coverageFile . ' does not exist'); } $contents = file_get_contents($coverageFile); $coverage = @unserialize($contents); if ($coverage === false) { return; } $this->preProcessCoverage($coverage) ->mergeToPrint($coverage); } /** * Allows Translating Remote Paths To Local (IE: When Using Docker) */ protected function preProcessCoverage(CodeCoverage $coverage): self { //Only Process If Work Directory Set if ($this->settings['work_dir'] === null) { return $this; } $workDir = rtrim($this->settings['work_dir'], '/\\') . DIRECTORY_SEPARATOR; $projectDir = Configuration::projectDir(); $coverageData = $coverage->getData(true); //We only want covered files, not all whitelisted ones. codecept_debug("Replacing all instances of {$workDir} with {$projectDir}"); foreach ($coverageData as $path => $datum) { unset($coverageData[$path]); $path = str_replace($workDir, $projectDir, $path); $coverageData[$path] = $datum; } $coverage->setData($coverageData); return $this; } protected function c3Request(string $action): string|false { $this->addC3AccessHeader(self::COVERAGE_HEADER, 'remote-access'); $context = stream_context_create($this->c3Access); $c3Url = $this->settings['c3_url'] ?: $this->module->_getUrl(); $contents = file_get_contents($c3Url . '/c3/report/' . $action, false, $context); $okHeaders = array_filter( $http_response_header, fn ($h) => preg_match('#^HTTP(.*?)\s200#', $h) ); if (empty($okHeaders)) { throw new RemoteException("Request was not successful. See response header: " . $http_response_header[0]); } if ($contents === false) { $this->getRemoteError($http_response_header); } return $contents; } protected function startCoverageCollection($testName): void { $value = [ 'CodeCoverage' => $testName, 'CodeCoverage_Suite' => $this->suiteName, 'CodeCoverage_Config' => $this->settings['remote_config'] ]; $value = json_encode($value, JSON_THROW_ON_ERROR); if ($this->module instanceof WebDriverModule) { $this->module->amOnPage('/'); } $cookieDomain = $this->settings['cookie_domain'] ?? null; if (!$cookieDomain) { $c3Url = parse_url($this->settings['c3_url'] ?: $this->module->_getUrl()); // we need to separate coverage cookies by host; we can't separate cookies by port. $cookieDomain = $c3Url['host'] ?? 'localhost'; } $cookieParams = []; if ($cookieDomain !== 'localhost') { $cookieParams['domain'] = $cookieDomain; } $this->module->setCookie(self::COVERAGE_COOKIE, $value, $cookieParams); // putting in configuration ensures the cookie is used for all sessions of a MultiSession test $cookies = $this->module->_getConfig('cookies'); if (!$cookies || !is_array($cookies)) { $cookies = []; } $found = false; foreach ($cookies as &$cookie) { if (!is_array($cookie) || !array_key_exists('Name', $cookie) || !array_key_exists('Value', $cookie)) { // \Codeception\Lib\InnerBrowser will complain about this continue; } if ($cookie['Name'] === self::COVERAGE_COOKIE) { $found = true; $cookie['Value'] = $value; break; } } unset($cookie); if (!$found) { $cookies[] = [ 'Name' => self::COVERAGE_COOKIE, 'Value' => $value ]; } $this->module->_setConfig(['cookies' => $cookies]); } protected function fetchErrors(): void { // Calling grabCookie() while an alert is present dismisses the alert // @see https://github.com/Codeception/Codeception/issues/1485 if ($this->module instanceof WebDriverModule) { try { $alert = $this->module->webDriver->switchTo()->alert(); $alert->getText(); // If this succeeds an alert is present, abort return; } catch (NoSuchAlertException) { // No alert present, continue } } try { $error = $this->module->grabCookie(self::COVERAGE_COOKIE_ERROR); } catch (ModuleException) { // when a new session is started we can't get cookies because there is no // current page, but there can be no code coverage error either $error = null; } if (!empty($error)) { $this->module->resetCookie(self::COVERAGE_COOKIE_ERROR); throw new RemoteException($error); } } protected function getRemoteError(array $headers): void { foreach ($headers as $header) { if (str_starts_with($header, self::COVERAGE_HEADER_ERROR)) { throw new RemoteException($header); } } } protected function addC3AccessHeader(string $header, string $value): void { $headerString = "{$header}: {$value}\r\n"; if (!str_contains($this->c3Access['http']['header'], $headerString)) { $this->c3Access['http']['header'] .= $headerString; } } protected function applySettings(array $settings): void { parent::applySettings($settings); if (isset($settings['coverage']['remote_context_options'])) { $this->c3Access = array_replace_recursive($this->c3Access, $settings['coverage']['remote_context_options']); } } } <?php declare(strict_types=1); namespace Codeception\Coverage; use Codeception\Configuration; use Codeception\Exception\ConfigurationException; use Codeception\Exception\ModuleException; use PHPUnit\Runner\Version as PHPUnitVersion; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Filter as PhpUnitFilter; use Symfony\Component\Finder\Exception\DirectoryNotFoundException; use Symfony\Component\Finder\Finder; use function array_pop; use function explode; use function implode; use function is_array; use function iterator_to_array; use function str_replace; class Filter { protected static ?self $codeceptionFilter = null; protected ?PhpUnitFilter $phpUnitFilter = null; public function __construct(protected ?CodeCoverage $phpCodeCoverage) { $this->phpUnitFilter = $this->phpCodeCoverage->filter(); } public static function setup(CodeCoverage $phpCoverage): self { self::$codeceptionFilter = new self($phpCoverage); return self::$codeceptionFilter; } /** * @throws ConfigurationException */ public function whiteList(array $config): self { $filter = $this->phpUnitFilter; if (!isset($config['coverage'])) { return $this; } $coverage = $config['coverage']; if (!isset($coverage['whitelist'])) { $coverage['whitelist'] = []; if (isset($coverage['include'])) { $coverage['whitelist']['include'] = $coverage['include']; } if (isset($coverage['exclude'])) { $coverage['whitelist']['exclude'] = $coverage['exclude']; } } if (PHPUnitVersion::series() >= 11) { return $this->newWhiteList($coverage['whitelist']); } if (isset($coverage['whitelist']['include'])) { if (!is_array($coverage['whitelist']['include'])) { throw new ConfigurationException('Error parsing yaml. Config `whitelist: include:` should be an array'); } foreach ($coverage['whitelist']['include'] as $fileOrDir) { $finder = !str_contains($fileOrDir, '*') ? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir] : $this->matchWildcardPattern($fileOrDir); foreach ($finder as $file) { $filter->includeFile((string)$file); } } } if (isset($coverage['whitelist']['exclude'])) { if (!is_array($coverage['whitelist']['exclude'])) { throw new ConfigurationException('Error parsing yaml. Config `whitelist: exclude:` should be an array'); } foreach ($coverage['whitelist']['exclude'] as $fileOrDir) { try { $finder = !str_contains($fileOrDir, '*') ? [Configuration::projectDir() . DIRECTORY_SEPARATOR . $fileOrDir] : $this->matchWildcardPattern($fileOrDir); foreach ($finder as $file) { $filter->excludeFile((string)$file); } } catch (DirectoryNotFoundException) { continue; } } } return $this; } private function newWhiteList(array $whitelist): self { $include = $whitelist['include'] ?? []; $exclude = $whitelist['exclude'] ?? []; if (!is_array($include)) { throw new ConfigurationException('Error parsing yaml. Config `whitelist: include:` should be an array'); } if (!is_array($exclude)) { throw new ConfigurationException('Error parsing yaml. Config `whitelist: exclude:` should be an array'); } if (count($exclude) === 0 && count($include) === 0) { return $this; } if (count($include) === 0) { $include = [ Configuration::projectDir() . DIRECTORY_SEPARATOR . '*' ]; } $allIncludedFiles = []; foreach ($include as $fileOrDir) { $finder = !str_contains($fileOrDir, '*') ? $this->matchFileOrDirectory($fileOrDir) : $this->matchWildcardPattern($fileOrDir); $allIncludedFiles += iterator_to_array($finder->getIterator()); } $allExcludedFiles = []; foreach ($exclude as $fileOrDir) { try { $finder = !str_contains($fileOrDir, '*') ? $this->matchFileOrDirectory($fileOrDir) : $this->matchWildcardPattern($fileOrDir); $allExcludedFiles += iterator_to_array($finder->getIterator()); } catch (DirectoryNotFoundException) { continue; } } $coveredFiles = array_diff($allIncludedFiles, $allExcludedFiles); foreach ($coveredFiles as $coveredFile) { $this->phpUnitFilter->includeFile((string)$coveredFile); } return $this; } /** * @throws ModuleException */ public function blackList(array $config): self { if (isset($config['coverage']['blacklist'])) { throw new ModuleException($this, 'The blacklist functionality has been removed from PHPUnit 5,' . ' please remove blacklist section from configuration.'); } return $this; } private function matchFileOrDirectory(string $fileOrDir): Finder { $fullPath = Configuration::projectDir() . $fileOrDir; $finder = Finder::create(); if (is_dir($fullPath)) { $finder->in($fullPath); $finder->name('*.php'); } else { $finder->in(dirname($fullPath)); $finder->name(basename($fullPath)); } $finder->ignoreVCS(true)->files(); return $finder; } protected function matchWildcardPattern(string $pattern): Finder { $finder = Finder::create(); $fileOrDir = str_replace('\\', '/', $pattern); $parts = explode('/', $fileOrDir); $file = array_pop($parts); if ($file === '*') { $file = '*.php'; } $finder->name($file); if ($parts !== []) { $lastPath = array_pop($parts); if ($lastPath === '*') { $finder->in(Configuration::projectDir() . implode('/', $parts)); } else { $finder->in(Configuration::projectDir() . implode('/', [...$parts, $lastPath])); } } $finder->ignoreVCS(true)->files(); return $finder; } public function getFilter(): PhpUnitFilter { return $this->phpUnitFilter; } } <?php declare(strict_types=1); namespace Codeception\Coverage; use Codeception\Configuration; use Codeception\Coverage\Subscriber\Printer; use Codeception\Exception\ConfigurationException; use Codeception\Lib\Interfaces\Remote as RemoteInterface; use Codeception\Subscriber\Shared\StaticEventsTrait; use Exception; use PHPUnit\Framework\CodeCoverageException; use SebastianBergmann\CodeCoverage\CodeCoverage; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function array_keys; abstract class SuiteSubscriber implements EventSubscriberInterface { use StaticEventsTrait; protected array $defaultSettings = [ 'enabled' => false, 'remote' => false, 'local' => false, 'xdebug_session' => 'codeception', 'remote_config' => null, 'show_uncovered' => false, 'c3_url' => null, 'work_dir' => null, 'cookie_domain' => null, 'path_coverage' => false, 'strict_covers_annotation' => false, 'ignore_deprecated_code' => false, 'disable_code_coverage_ignore' => false, ]; protected array $settings = []; protected array $filters = []; protected array $modules = []; protected ?CodeCoverage $coverage = null; protected string $logDir; public static array $events = []; abstract protected function isEnabled(); /** * SuiteSubscriber constructor. * * @throws ConfigurationException */ public function __construct(protected array $options = []) { $this->logDir = Configuration::outputDir(); } /** * @throws Exception */ protected function applySettings(array $settings): void { try { $this->coverage = PhpCodeCoverageFactory::build(); } catch (CodeCoverageException $e) { throw new Exception( 'XDebug is required to collect CodeCoverage. Please install xdebug extension and enable it in php.ini', $e->getCode(), $e ); } $this->filters = $settings; $this->settings = $this->defaultSettings; $keys = array_keys($this->defaultSettings); foreach ($keys as $key) { if (isset($settings['coverage'][$key])) { $this->settings[$key] = $settings['coverage'][$key]; } } if ($this->settings['strict_covers_annotation']) { $this->coverage->enableCheckForUnintentionallyCoveredCode(); } if ($this->settings['ignore_deprecated_code']) { $this->coverage->ignoreDeprecatedCode(); } else { $this->coverage->doNotIgnoreDeprecatedCode(); } if ($this->settings['disable_code_coverage_ignore']) { $this->coverage->disableAnnotationsForIgnoringCode(); } else { $this->coverage->enableAnnotationsForIgnoringCode(); } if ($this->settings['show_uncovered']) { $this->coverage->includeUncoveredFiles(); } else { $this->coverage->excludeUncoveredFiles(); } } protected function getServerConnectionModule(array $modules): ?RemoteInterface { foreach ($modules as $module) { if ($module instanceof RemoteInterface) { return $module; } } return null; } protected function mergeToPrint(CodeCoverage $coverage): void { Printer::$coverage->merge($coverage); } } <?php declare(strict_types=1); namespace Codeception\Coverage; use Codeception\Configuration; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Driver\Selector; use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; class PhpCodeCoverageFactory { private static ?CodeCoverage $instance = null; public static function build(): CodeCoverage { if (self::$instance !== null) { return self::$instance; } $coverageConfiguration = Configuration::config()['coverage']; $pathCoverage = $coverageConfiguration['path_coverage'] ?? false; $filter = new CodeCoverageFilter(); if ($pathCoverage) { $driver = (new Selector())->forLineAndPathCoverage($filter); } else { $driver = (new Selector())->forLineCoverage($filter); } self::$instance = new CodeCoverage($driver, $filter); return self::$instance; } public static function clear(): void { self::$instance = null; } } <?php declare(strict_types=1); namespace Codeception; use Codeception\Configuration as Config; use Codeception\Event\SuiteEvent; use Codeception\Exception\ModuleRequireException; use Codeception\Lib\Console\Output; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use function array_keys; use function array_merge; use function is_array; /** * A base class for all Codeception Extensions and GroupObjects * * Available Properties: * * * config: current extension configuration * * options: passed running options * */ abstract class Extension implements EventSubscriberInterface { /** * @var array<int|string, mixed> */ protected array $config = []; protected Output $output; protected array $globalConfig = []; /** * @var array<string, Module> */ private array $modules = []; public function __construct(array $config, protected array $options) { $this->config = array_merge($this->config, $config); $this->output = new Output($options); $this->_initialize(); } public static function getSubscribedEvents(): array { if (!isset(static::$events)) { return [Events::SUITE_INIT => 'receiveModuleContainer']; } if (isset(static::$events[Events::SUITE_INIT])) { if (!is_array(static::$events[Events::SUITE_INIT])) { static::$events[Events::SUITE_INIT] = [[static::$events[Events::SUITE_INIT]]]; } static::$events[Events::SUITE_INIT][] = ['receiveModuleContainer']; } else { static::$events[Events::SUITE_INIT] = 'receiveModuleContainer'; } return static::$events; } public function receiveModuleContainer(SuiteEvent $event): void { $this->modules = $event->getSuite()->getModules(); } /** * Pass config variables that should be injected into global config. */ public function _reconfigure(array $config = []): void { Configuration::append($config); } /** * You can do all preparations here. No need to override constructor. * Also you can skip calling `_reconfigure` if you don't need to. */ public function _initialize(): void { $this->_reconfigure(); // hook for BC only. } /** * @param string|iterable $messages The message as an iterable of strings or a single string */ protected function write(iterable|string $messages): void { if (!$this->options['silent'] && $messages) { $this->output->write($messages); } } /** * @param string|iterable $messages The message as an iterable of strings or a single string */ protected function writeln(iterable|string $messages): void { if (!$this->options['silent'] && $messages) { $this->output->writeln($messages); } } public function hasModule(string $name): bool { return isset($this->modules[$name]); } /** * @return string[] */ public function getCurrentModuleNames(): array { return array_keys($this->modules); } public function getModule(string $name): Module { if (!$this->hasModule($name)) { throw new ModuleRequireException($name, 'module is not enabled'); } return $this->modules[$name]; } public function getTestsDir(): string { return Config::testsDir(); } public function getLogDir(): string { return Config::outputDir(); } public function getDataDir(): string { return Config::dataDir(); } public function getRootDir(): string { return Config::projectDir(); } public function getGlobalConfig(): array { return Config::config(); } } <?php declare(strict_types=1); namespace Codeception\Lib; class Notification { /** * @var string[] */ protected static array $messages = []; public static function warning(string $message, string $location): void { self::$messages[] = 'WARNING: ' . self::formatMessage($message, $location); } public static function deprecate(string $message, string $location = ''): void { self::$messages[] = 'DEPRECATION: ' . self::formatMessage($message, $location); } private static function formatMessage(string $message, string $location = ''): string { if ($location !== '') { return "<bold>{$message}</bold> <info>{$location}</info>"; } return $message; } /** * @return string[] */ public static function all(): array { $messages = self::$messages; self::$messages = []; return $messages; } } <?php declare(strict_types=1); namespace Codeception\Lib; use Codeception\Exception\TestParseException; use Codeception\Scenario; use Codeception\Step\Action; use Codeception\Step\Comment; use Codeception\Test\Metadata; use Exception; use ParseError; class Parser { protected string $code; public function __construct(protected Scenario $scenario, protected Metadata $metadata) { } public function prepareToRun(string $code): void { $this->parseFeature($code); $this->parseScenarioOptions($code); } public function parseFeature(string $code): void { $matches = []; $code = $this->stripComments($code); $res = preg_match("#\\\$I->wantTo\\(\\s*?['\"](.*?)['\"]\\s*?\\);#", $code, $matches); if ($res) { $this->scenario->setFeature($matches[1]); return; } $res = preg_match("#\\\$I->wantToTest\\(['\"](.*?)['\"]\\);#", $code, $matches); if ($res) { $this->scenario->setFeature("test " . $matches[1]); } } public function parseScenarioOptions(string $code): void { $this->metadata->setParamsFromAnnotations($this->matchComments($code)); } public function parseSteps(string $code): void { // parse per line $friends = []; $lines = explode("\n", $code); $isFriend = false; foreach ($lines as $line) { // friends if (preg_match("#\\\$I->haveFriend\\((.*?)\\);#", $line, $matches)) { $friends[] = trim($matches[1], '\'"'); } // friend's section start if (preg_match("#\\\$(.*?)->does\\(#", $line, $matches)) { $friend = $matches[1]; if (!in_array($friend, $friends)) { continue; } $isFriend = true; $this->addCommentStep("\n----- {$friend} does -----"); continue; } // actions if (preg_match("#\\\$I->(.*)\\((.*?)\\);#", $line, $matches)) { $this->addStep($matches); } // friend's section ends if ($isFriend && str_contains($line, '}')) { $this->addCommentStep("-------- back to me\n"); $isFriend = false; } } } protected function addStep(array $matches): void { [$m, $action, $params] = $matches; if (in_array($action, ['wantTo', 'wantToTest'])) { return; } $this->scenario->addStep(new Action($action, explode(',', $params))); } protected function addCommentStep(string $comment): void { $this->scenario->addStep(new Comment($comment, [])); } public static function load(string $file): void { try { self::includeFile($file); } catch (ParseError $e) { throw new TestParseException($file, $e->getMessage(), $e->getLine()); } catch (Exception) { // file is valid otherwise } } /** * @return string[] */ public static function getClassesFromFile(string $file): array { $sourceCode = file_get_contents($file); $classes = []; $tokens = token_get_all($sourceCode, TOKEN_PARSE); $tokenCount = count($tokens); $namespace = ''; for ($i = 0; $i < $tokenCount; ++$i) { if ($tokens[$i][0] === T_NAMESPACE) { $namespace = ''; for ($j = $i + 1; $j < $tokenCount; ++$j) { if ($tokens[$j] === '{' || $tokens[$j] === ';') { break; } if ($tokens[$j][0] === T_STRING || $tokens[$j][0] === T_NAME_QUALIFIED) { $namespace .= $tokens[$j][1] . '\\'; } } } if ($tokens[$i][0] === T_CLASS) { // class at the beginning of file if (!isset($tokens[$i - 2])) { $classes[] = $namespace . $tokens[$i + 2][1]; continue; } // new class if ($tokens[$i - 2][0] === T_NEW) { continue; } // :: class if ($tokens[$i - 1][0] === T_WHITESPACE && $tokens[$i - 2][0] === T_DOUBLE_COLON) { continue; } // ::class if ($tokens[$i - 1][0] === T_DOUBLE_COLON) { continue; } // class{ if (isset($tokens[$i + 1]) && ($tokens[$i + 1] === '{')) { continue; } // class { if (isset($tokens[$i + 2]) && $tokens[$i + 1][0] === T_WHITESPACE && $tokens[$i + 2] === '{') { continue; } $classes[] = $namespace . $tokens[$i + 2][1]; } } $tokens = null; gc_mem_caches(); return $classes; } /* * Include in different scope to prevent included file from affecting $file variable */ private static function includeFile(string $file): void { include_once $file; } protected function stripComments(string $code): string { $code = preg_replace('#//.*?$#m', '', $code); // remove inline comments return preg_replace('#/*\*.*?\*/#ms', '', $code); // remove block comment } protected function matchComments(string $code): string { $matches = []; $comments = ''; $hasLineComment = preg_match_all('#//(.*?)$#m', $code, $matches); if ($hasLineComment) { foreach ($matches[1] as $line) { $comments .= $line . "\n"; } } $hasBlockComment = preg_match('#/*\*(.*?)\*/#ms', $code, $matches); if ($hasBlockComment) { $comments .= $matches[1] . "\n"; } return $comments; } } <?php declare(strict_types=1); namespace Codeception\Lib; use Codeception\Configuration; use Codeception\Exception\ConfigurationException; use Codeception\Exception\InjectionException; use Codeception\Exception\ModuleConflictException; use Codeception\Exception\ModuleException; use Codeception\Exception\ModuleRequireException; use Codeception\Lib\Interfaces\ConflictsWithModule; use Codeception\Lib\Interfaces\DependsOnModule; use Codeception\Lib\Interfaces\PartedModule; use Codeception\Module; use Codeception\Util\Annotation; use ReflectionClass; use ReflectionException; use ReflectionMethod; /** * Class ModuleContainer * @package Codeception\Lib */ class ModuleContainer { /** * @var string */ public const MODULE_NAMESPACE = '\\Codeception\\Module\\'; /** * @var int */ public const MAXIMUM_LEVENSHTEIN_DISTANCE = 5; /** * @var array<string, string> */ public static array $packages = [ 'AMQP' => 'codeception/module-amqp', 'Apc' => 'codeception/module-apc', 'Asserts' => 'codeception/module-asserts', 'Cli' => 'codeception/module-cli', 'DataFactory' => 'codeception/module-datafactory', 'Db' => 'codeception/module-db', 'Doctrine' => "codeception/module-doctrine", 'Filesystem' => 'codeception/module-filesystem', 'FTP' => 'codeception/module-ftp', 'Laravel' => 'codeception/module-laravel', 'Lumen' => 'codeception/module-lumen', 'Memcache' => 'codeception/module-memcache', 'MongoDb' => 'codeception/module-mongodb', 'Phalcon' => 'codeception/module-phalcon', 'PhpBrowser' => 'codeception/module-phpbrowser', 'Queue' => 'codeception/module-queue', 'Redis' => 'codeception/module-redis', 'REST' => 'codeception/module-rest', 'Sequence' => 'codeception/module-sequence', 'SOAP' => 'codeception/module-soap', 'Symfony' => 'codeception/module-symfony', 'WebDriver' => "codeception/module-webdriver", 'Yii2' => "codeception/module-yii2", 'ZendExpressive' => 'codeception/module-zendexpressive', 'ZF2' => 'codeception/module-zf2', ]; /** * @var array<string,Module> */ private array $modules = []; private array $active = []; private array $actions = []; public function __construct(private Di $di, private array $config) { $this->di->set($this); } /** * Create a module. * * @throws ConfigurationException * @throws InjectionException * @throws ModuleException * @throws ModuleRequireException * @throws ReflectionException */ public function create(string $moduleName, bool $active = true): ?object { $this->active[$moduleName] = $active; $moduleClass = $this->getModuleClass($moduleName); if (!class_exists($moduleClass)) { if (isset(self::$packages[$moduleName])) { $package = self::$packages[$moduleName]; throw new ConfigurationException("Module {$moduleName} is not installed.\nUse Composer to install corresponding package:\n\ncomposer require {$package} --dev"); } throw new ConfigurationException("Module {$moduleName} could not be found and loaded"); } $config = $this->getModuleConfig($moduleName); if (empty($config) && !$active) { // For modules that are a dependency of other modules we want to skip the validation of the config. // This config validation is performed in \Codeception\Module::__construct(). // Explicitly setting $config to null skips this validation. $config = null; } $this->modules[$moduleName] = $this->di->instantiate($moduleClass, [$this, $config], 'false'); $module = $this->modules[$moduleName]; if ($this->moduleHasDependencies($module)) { $this->injectModuleDependencies($moduleName, $module); } // If module is not active its actions should not be included in the actor class $actions = $active ? $this->getActionsForModule($module, $config) : []; foreach ($actions as $action) { $this->actions[$action] = $moduleName; } return $module; } /** * Does a module have dependencies? */ private function moduleHasDependencies(Module $module): bool { if (!$module instanceof DependsOnModule) { return false; } return (bool)$module->_depends(); } /** * Get the actions of a module. * * @return string[] */ private function getActionsForModule(Module $module, array $config): array { $reflectionClass = new ReflectionClass($module); // Only public methods can be actions $methods = $reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC); // Should this module be loaded partially? $configuredParts = null; if ($module instanceof PartedModule && isset($config['part'])) { $configuredParts = is_array($config['part']) ? $config['part'] : [$config['part']]; } $actions = []; foreach ($methods as $method) { if ($this->includeMethodAsAction($module, $method, $configuredParts)) { $actions[] = $method->name; } } return $actions; } /** * Should a method be included as an action? */ private function includeMethodAsAction(Module $module, ReflectionMethod $method, array $configuredParts = null): bool { // Filter out excluded actions if ($module::$excludeActions && in_array($method->name, $module::$excludeActions)) { return false; } // Keep only the $onlyActions if they are specified if ($module::$onlyActions && !in_array($method->name, $module::$onlyActions)) { return false; } // Do not include inherited actions if the static $includeInheritedActions property is set to false. // However, if an inherited action is also specified in the static $onlyActions property // it should be included as an action. if ( !$module::$includeInheritedActions && !in_array($method->name, $module::$onlyActions) && $method->getDeclaringClass()->getName() != $module::class ) { return false; } // Do not include hidden methods, methods with a name starting with an underscore if (str_starts_with($method->name, '_')) { return false; } // If a part is configured for the module, only include actions from that part if ($configuredParts) { $moduleParts = Annotation::forMethod($module, $method->name)->fetchAll('part'); if (!array_uintersect($moduleParts, $configuredParts, 'strcasecmp')) { return false; } } return true; } /** * Is the module a helper? */ private function isHelper(string $moduleName): bool { return str_contains($moduleName, '\\'); } /** * Get the fully qualified class name for a module. */ private function getModuleClass(string $moduleName): string { if ($this->isHelper($moduleName)) { return $moduleName; } return self::MODULE_NAMESPACE . $moduleName; } /** * Is a module instantiated in this ModuleContainer? */ public function hasModule(string $moduleName): bool { return isset($this->modules[$moduleName]); } /** * Get a module from this ModuleContainer. * * @throws ModuleException */ public function getModule(string $moduleName): Module { if (!$this->hasModule($moduleName)) { $this->throwMissingModuleExceptionWithSuggestion(__CLASS__, $moduleName); } return $this->modules[$moduleName]; } public function throwMissingModuleExceptionWithSuggestion(string $className, string $moduleName): void { $suggestedModuleNameInfo = $this->getModuleSuggestion($moduleName); throw new ModuleException($className, "Module {$moduleName} couldn't be connected" . $suggestedModuleNameInfo); } protected function getModuleSuggestion(string $missingModuleName): string { $shortestLevenshteinDistance = null; $suggestedModuleName = null; foreach (array_keys($this->modules) as $moduleName) { $levenshteinDistance = levenshtein($missingModuleName, $moduleName); if ($shortestLevenshteinDistance === null || $levenshteinDistance <= $shortestLevenshteinDistance) { $shortestLevenshteinDistance = $levenshteinDistance; $suggestedModuleName = $moduleName; } } if ($suggestedModuleName !== null && $shortestLevenshteinDistance <= self::MAXIMUM_LEVENSHTEIN_DISTANCE) { return " (did you mean '{$suggestedModuleName}'?)"; } return ''; } /** * Get the module for an action. * * @return Module|null */ public function moduleForAction(string $action) { if (!isset($this->actions[$action])) { return null; } return $this->modules[$this->actions[$action]]; } /** * Get all actions. * * @return array An array with actions as keys and module names as values. */ public function getActions(): array { return $this->actions; } /** * Get all modules. * * @return array An array with module names as keys and modules as values. */ public function all(): array { return $this->modules; } /** * Mock a module in this ModuleContainer. */ public function mock(string $moduleName, object $mock): void { $this->modules[$moduleName] = $mock; } /** * Inject the dependencies of a module. * * @throws ModuleException * @throws ModuleRequireException */ private function injectModuleDependencies(string $moduleName, DependsOnModule $module): void { $this->checkForMissingDependencies($moduleName, $module); if (!method_exists($module, '_inject')) { throw new ModuleException($module, 'Module requires method _inject to be defined to accept dependencies'); } $dependencies = array_map(fn ($dependency): ?object => $this->create($dependency, false), $this->getConfiguredDependencies($moduleName)); call_user_func_array([$module, '_inject'], $dependencies); } /** * Check for missing dependencies. * * @throws ModuleException|ModuleRequireException */ private function checkForMissingDependencies(string $moduleName, $module): void { $dependencies = $this->getModuleDependencies($module); $configuredDependenciesCount = count($this->getConfiguredDependencies($moduleName)); if ($configuredDependenciesCount < count($dependencies)) { $missingDependency = array_keys($dependencies)[$configuredDependenciesCount]; $message = sprintf( "\nThis module depends on %s\n\n\n%s", $missingDependency, $this->getErrorMessageForDependency($module, $missingDependency) ); throw new ModuleRequireException($moduleName, $message); } } /** * Get the dependencies of a module. * * @throws ModuleException */ private function getModuleDependencies(DependsOnModule $module): array { $depends = $module->_depends(); if (!$depends) { return []; } if (!is_array($depends)) { $message = sprintf("Method _depends of module '%s' must return an array", $module::class); throw new ModuleException($module, $message); } return $depends; } /** * Get the configured dependencies for a module. */ private function getConfiguredDependencies(string $moduleName): array { $config = $this->getModuleConfig($moduleName); if (!isset($config['depends'])) { return []; } return is_array($config['depends']) ? $config['depends'] : [$config['depends']]; } /** * Get the error message for a module dependency that is missing. */ private function getErrorMessageForDependency(DependsOnModule $module, string $missingDependency): string { $depends = $module->_depends(); return $depends[$missingDependency]; } /** * Get the configuration for a module. * * A module with name $moduleName can be configured at two paths in a configuration file: * - modules.config.$moduleName * - modules.enabled.$moduleName * * This method checks both locations for configuration. If there is configuration at both locations * this method merges them, where the configuration at modules.enabled.$moduleName takes precedence * over modules.config.$moduleName if the same parameters are configured at both locations. */ private function getModuleConfig(string $moduleName): array { $config = $this->config['modules']['config'][$moduleName] ?? []; if (!isset($this->config['modules']['enabled'])) { return $config; } if (!is_array($this->config['modules']['enabled'])) { return $config; } foreach ($this->config['modules']['enabled'] as $enabledModuleConfig) { if (!is_array($enabledModuleConfig)) { continue; } $enabledModuleName = key($enabledModuleConfig); if ($enabledModuleName === $moduleName) { $moduleConfig = reset($enabledModuleConfig); if (!is_array($moduleConfig)) { return $config; } return Configuration::mergeConfigs($moduleConfig, $config); } } return $config; } /** * Check if there are conflicting modules in this ModuleContainer. * * @throws ModuleConflictException */ public function validateConflicts(): void { $canConflict = []; foreach ($this->modules as $moduleName => $module) { $parted = $module instanceof PartedModule && $module->_getConfig('part'); if ($this->active[$moduleName] && !$parted) { $canConflict[] = $module; } } foreach ($canConflict as $module) { foreach ($canConflict as $otherModule) { $this->validateConflict($module, $otherModule); } } } /** * Check if the modules passed as arguments to this method conflict with each other. * * @throws ModuleConflictException */ private function validateConflict(Module $module, Module $otherModule): void { if ($module === $otherModule || !$module instanceof ConflictsWithModule) { return; } $conflicts = $this->normalizeConflictSpecification($module->_conflicts()); if ($otherModule instanceof $conflicts) { throw new ModuleConflictException($module, $otherModule); } } /** * Normalize the return value of ConflictsWithModule::_conflicts() to a class name. * This is necessary because it can return a module name instead of the name of a class or interface. * * @return class-string|Module|string */ private function normalizeConflictSpecification(string $conflicts) { if (interface_exists($conflicts) || class_exists($conflicts)) { return $conflicts; } if ($this->hasModule($conflicts)) { return $this->getModule($conflicts); } return $conflicts; } } <?php namespace Codeception\Lib; use Psy\Shell; use Psy\Configuration; class PauseShell { public const LOG_FILE = '.pause.log'; private Configuration $psyConf; public function __construct() { $relativeLogFilePath = codecept_relative_path(codecept_output_dir(self::LOG_FILE)); $this->psyConf = new Configuration([ 'prompt' => '>> ', 'startupMessage' => "<warning>Execution PAUSED</warning> All commands will be saved to $relativeLogFilePath" ]); $this->psyConf->setHistoryFile(codecept_output_dir(self::LOG_FILE)); $this->psyConf->setHistorySize(1000); } public function addMessage(string $message): self { $this->psyConf->setStartupMessage($this->psyConf->getStartupMessage() . "\n" . $message); return $this; } public function getShell(): Shell { return new Shell($this->psyConf); } } <?php declare(strict_types=1); namespace Codeception\Lib\Console; class ReplHistory { protected string $outputFile; protected array $stashedCommands = []; protected static ?self $instance = null; private function __construct() { $this->outputFile = codecept_output_dir('stashed-commands'); if (file_exists($this->outputFile)) { unlink($this->outputFile); } } public static function getInstance(): ReplHistory { if (static::$instance == null) { static::$instance = new self(); } return static::$instance; } public function add($command): void { $this->stashedCommands[] = $command; } public function getAll(): array { return $this->stashedCommands; } public function clear(): void { $this->stashedCommands = []; } public function save(): void { if (empty($this->stashedCommands)) { return; } file_put_contents($this->outputFile, implode("\n", $this->stashedCommands) . "\n", FILE_APPEND); codecept_debug("Stashed commands have been saved to {$this->outputFile}"); $this->clear(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Console; use SebastianBergmann\Comparator\ComparisonFailure; use SebastianBergmann\Diff\Differ; use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; class DiffFactory { public function createDiff(ComparisonFailure $failure): string { return $this->getDiff($failure->getExpectedAsString(), $failure->getActualAsString()); } private function getDiff(string $expected = '', string $actual = ''): string { if (!$actual && !$expected) { return ''; } $differ = new Differ(new UnifiedDiffOutputBuilder('')); return $differ->diff($expected, $actual); } } <?php declare(strict_types=1); namespace Codeception\Lib\Console; use SebastianBergmann\Comparator\ComparisonFailure; class MessageFactory { protected DiffFactory $diffFactory; protected Colorizer $colorizer; public function __construct(private Output $output) { $this->diffFactory = new DiffFactory(); $this->colorizer = new Colorizer(); } public function message(string $text = ''): Message { return new Message($text, $this->output); } public function prepareComparisonFailureMessage(ComparisonFailure $failure): string { $diff = $this->diffFactory->createDiff($failure); if ($diff === '') { return ''; } $diff = $this->colorizer->colorize($diff); return "\n<comment>- Expected</comment> | <info>+ Actual</info>\n{$diff}"; } } <?php declare(strict_types=1); namespace Codeception\Lib\Console; use Exception; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Helper\FormatterHelper as SymfonyFormatterHelper; use Symfony\Component\Console\Output\ConsoleOutput; class Output extends ConsoleOutput { /** * @var array<string, int|bool> */ protected array $config = [ 'colors' => true, 'verbosity' => self::VERBOSITY_NORMAL, 'interactive' => true ]; public SymfonyFormatterHelper $formatHelper; public bool $waitForDebugOutput = true; protected bool $isInteractive = false; public function __construct(array $config) { $this->config = array_merge($this->config, $config); // enable interactive output mode for CLI $this->isInteractive = $this->config['interactive'] && isset($_SERVER['TERM']) && PHP_SAPI == 'cli' && $_SERVER['TERM'] != 'linux'; $formatter = new OutputFormatter($this->config['colors']); $formatter->setStyle('default', new OutputFormatterStyle()); $formatter->setStyle('bold', new OutputFormatterStyle(null, null, ['bold'])); $formatter->setStyle('focus', new OutputFormatterStyle('magenta', null, ['bold'])); $formatter->setStyle('ok', new OutputFormatterStyle('green', null, ['bold'])); $formatter->setStyle('error', new OutputFormatterStyle('white', 'red', ['bold'])); $formatter->setStyle('fail', new OutputFormatterStyle('red', null, ['bold'])); $formatter->setStyle('pending', new OutputFormatterStyle('yellow', null, ['bold'])); $formatter->setStyle('debug', new OutputFormatterStyle('cyan')); $formatter->setStyle('comment', new OutputFormatterStyle('yellow')); $formatter->setStyle('info', new OutputFormatterStyle('green')); $this->formatHelper = new SymfonyFormatterHelper(); parent::__construct($this->config['verbosity'], $this->config['colors'], $formatter); } public function isInteractive(): bool { return $this->isInteractive; } protected function clean(string $message): string { // clear json serialization return str_replace('\/', '/', $message); } public function debug(mixed $message): void { if ($this->waitForDebugOutput) { $this->writeln(''); $this->waitForDebugOutput = false; } if (!is_string($message)) { dump($message); return; } $message = $this->clean($message); $message = OutputFormatter::escape($message); $this->writeln("<debug> {$message}</debug>"); } public function message($message): Message { $message = sprintf(...func_get_args()); return new Message($message, $this); } public function exception(Exception $exception): void { $class = $exception::class; $this->writeln(""); $this->writeln(sprintf('(![ %s ]!)', $class)); $this->writeln($exception->getMessage()); $this->writeln(""); } public function notification(string $message): void { $this->writeln("<comment>{$message}</comment>"); } } <?php declare(strict_types=1); namespace Codeception\Lib\Console; use Stringable; use Symfony\Component\Console\Output\OutputInterface; class Message implements Stringable { public function __construct(protected string $message, protected ?Output $output = null) { } public function with($param): self { $args = array_merge([$this->message], func_get_args()); $this->message = sprintf(...$args); return $this; } public function style(string $name): self { $this->message = sprintf('<%s>%s</%s>', $name, $this->message, $name); return $this; } public function width(int $length, string $char = ' '): self { $messageLength = $this->getLength(); if ($messageLength < $length) { $this->message .= str_repeat($char, $length - $messageLength); } return $this; } public function cut(int $length): self { $this->message = mb_substr($this->message, 0, $length, 'utf-8'); return $this; } public function write(int $verbose = OutputInterface::VERBOSITY_NORMAL): void { if ($verbose > $this->output->getVerbosity()) { return; } $this->output->write($this->message); } public function writeln(int $verbose = OutputInterface::VERBOSITY_NORMAL): void { if ($verbose > $this->output->getVerbosity()) { return; } $this->output->writeln($this->message); } public function prepend(Message|string $string): self { if ($string instanceof Message) { $string = $string->getMessage(); } $this->message = $string . $this->message; return $this; } public function append(Message|string $string): self { if ($string instanceof Message) { $string = $string->getMessage(); } $this->message .= $string; return $this; } public function apply(callable $func): self { $this->message = call_user_func($func, $this->message); return $this; } public function center(string $char): self { $this->message = $char . $this->message . $char; return $this; } public function getMessage(): string { return $this->message; } public function block(string $style): self { $this->message = $this->output->formatHelper->formatBlock($this->message, $style, true); return $this; } public function getLength(bool $includeTags = false): int { return mb_strwidth($includeTags ? $this->message : strip_tags($this->message), 'utf-8'); } public static function ucfirst(string $text): string { return mb_strtoupper(mb_substr($text, 0, 1, 'utf-8'), 'utf-8') . mb_substr($text, 1, null, 'utf-8'); } public function __toString(): string { return $this->message; } } <?php declare(strict_types=1); namespace Codeception\Lib\Console; use Symfony\Component\Console\Formatter\OutputFormatter; class Colorizer { public function colorize(string $string = ''): string { $fp = fopen('php://memory', 'r+'); fwrite($fp, $string); rewind($fp); $colorizedMessage = ''; while ($line = fgets($fp)) { $char = $line[0]; $line = OutputFormatter::escape(trim($line)); switch ($char) { case '+': $line = "<info>{$line}</info>"; break; case '-': $line = "<comment>{$line}</comment>"; break; } $colorizedMessage .= $line . "\n"; } return trim($colorizedMessage); } } <?php namespace Codeception\Lib\Interfaces; use Doctrine\ORM\EntityManagerInterface; interface DoctrineProvider { public function _getEntityManager(): EntityManagerInterface; } <?php namespace Codeception\Lib\Interfaces; /** * Interface PartedModule * * Module implementing this interface can be loaded partly. * Parts can be defined by marking methods with `@part` annotations. * Part of modules can be loaded by specifying part (or several parts) in config: * * ``` * modules: * enabled: [MyModule] * config: * MyModule: * part: usefulActions * ``` * * * @package Codeception\Lib\Interfaces */ interface PartedModule { public function _parts(): array; } <?php namespace Codeception\Lib\Interfaces; interface RequiresPackage { /** * Returns list of classes and corresponding packages required for this module */ public function _requires(): array; } <?php namespace Codeception\Lib\Interfaces; interface ActiveRecord extends ORM { public function haveRecord(string $model, array $attributes = []); public function seeRecord(string $model, array $attributes = []): void; public function dontSeeRecord(string $model, array $attributes = []): void; public function grabRecord(string $model, array $attributes = []); } <?php namespace Codeception\Lib\Interfaces; interface ORM { } <?php namespace Codeception\Lib\Interfaces; interface DataMapper extends ORM, DoctrineProvider { public function haveInRepository(string $entity, array $data); public function seeInRepository(string $entity, array $params = []): void; public function dontSeeInRepository(string $entity, array $params = []): void; public function grabFromRepository(string $entity, string $field, array $params = []); } <?php namespace Codeception\Lib\Interfaces; interface ConflictsWithModule { /** * Returns class name or interface of module which can conflict with current. */ public function _conflicts(): string; } <?php namespace Codeception\Lib\Interfaces; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * If class implementing this interface is subscribed to event dispatcher * it replaces default Console Subscriber */ interface ConsolePrinter extends EventSubscriberInterface { } <?php namespace Codeception\Lib\Interfaces; interface DependsOnModule { /** * Specifies class or module which is required for current one. * * This method should return array with key as class name and value as error message * [className => errorMessage] */ public function _depends(): array; } <?php namespace Codeception\Lib\Interfaces; /** * Modules for API testing */ interface API { } <?php declare(strict_types=1); namespace Codeception\Lib; use Codeception\Exception\ConfigurationException; use Dotenv\Dotenv as PhpDotenv; use Dotenv\Repository\RepositoryBuilder; use Exception; use SimpleXMLElement; use Symfony\Component\Dotenv\Dotenv as SymfonyDotenv; use Symfony\Component\Yaml\Yaml; use function class_exists; use function codecept_absolute_path; use function codecept_relative_path; use function extension_loaded; use function file_exists; use function file_get_contents; use function method_exists; use function parse_ini_file; use function preg_match; class ParamsLoader { /** * @param array<mixed>|string $paramStorage * @return array<mixed> * @throws ConfigurationException */ public static function load(array|string $paramStorage): array { if (is_array($paramStorage)) { return $paramStorage; } if ($paramStorage === 'env' || $paramStorage === 'environment') { return self::loadEnvironmentVars(); } $paramsFile = codecept_absolute_path($paramStorage); if (!file_exists($paramsFile)) { throw new ConfigurationException("Params file {$paramsFile} not found"); } try { if (preg_match('#\.ya?ml$#', $paramStorage)) { return self::loadYamlFile($paramsFile); } if (preg_match('#\.ini$#', $paramStorage)) { return self::loadIniFile($paramsFile); } if (preg_match('#\.php$#', $paramStorage)) { return self::loadPhpFile($paramsFile); } if (preg_match('#(\.env(\.|$))#', $paramStorage)) { return self::loadDotEnvFile($paramsFile); } if (preg_match('#\.xml$#', $paramStorage)) { return self::loadXmlFile($paramsFile); } } catch (Exception $e) { throw new ConfigurationException("Failed loading params from {$paramStorage}\n" . $e->getMessage()); } throw new ConfigurationException("Params can't be loaded from `{$paramStorage}`."); } /** * @return array<mixed> * @throws ConfigurationException */ private static function loadIniFile(string $file): array { $params = parse_ini_file($file); return self::validateParams($params, $file); } /** * @return array<mixed> * @throws ConfigurationException */ private static function loadPhpFile(string $file): array { $params = require $file; return self::validateParams($params, $file); } /** * @return array<mixed> * @throws ConfigurationException */ private static function loadYamlFile(string $file): array { $params = Yaml::parse(self::getFileContents($file)); $params = self::validateParams($params, $file); if (isset($params['parameters'])) { // Symfony style $params = self::validateParams($params['parameters'], $file); ; } return self::validateParams($params, $file); } /** * @return array<mixed> * @throws ConfigurationException */ private static function loadXmlFile(string $file): array { if (!extension_loaded('simplexml')) { throw new ConfigurationException('`simplexml` extension is required to parse .xml files.'); } $paramsToArray = function (SimpleXMLElement $params) use (&$paramsToArray): array { $a = []; foreach ($params as $param) { $key = isset($param['key']) ? (string)$param['key'] : $param->getName(); $type = isset($param['type']) ? (string)$param['type'] : 'string'; $value = (string)$param; $a[$key] = match ($type) { 'bool', 'boolean', 'int', 'integer', 'float', 'double' => settype($value, $type), 'constant' => constant($value), 'collection' => $paramsToArray($param), default => (string) $param, }; } return $a; }; $simpleXMLElement = simplexml_load_file($file); if ($simpleXMLElement === false) { throw new ConfigurationException("Params can't be loaded from `{$file}`."); } $params = $paramsToArray($simpleXMLElement); return self::validateParams($params, $file); } /** * @return array<mixed> * @throws ConfigurationException */ private static function loadDotEnvFile(string $file): array { // vlucas/phpdotenv if ( class_exists(PhpDotenv::class) && class_exists(RepositoryBuilder::class) && method_exists(RepositoryBuilder::class, 'createWithDefaultAdapters') ) { $repository = RepositoryBuilder::createWithDefaultAdapters()->make(); $dotenv = PhpDotenv::create($repository, codecept_root_dir(), codecept_relative_path($file)); return $dotenv->load(); } // symfony/dotenv if (class_exists(SymfonyDotenv::class)) { $symfonyDotEnv = new SymfonyDotenv(); $values = $symfonyDotEnv->parse(self::getFileContents($file), $file); $symfonyDotEnv->populate($values); return $values; } throw new ConfigurationException( "`vlucas/phpdotenv:5.*` or `symfony/dotenv` library is required to parse .env files.\n" . "Please install it via composer, e.g.: composer require vlucas/phpdotenv" ); } /** * @return array<mixed> */ private static function loadEnvironmentVars(): array { return $_SERVER; } /** * @throws ConfigurationException */ private static function getFileContents(string $file): string { $contents = file_get_contents($file); if ($contents === false) { throw new ConfigurationException("Params can't be loaded from `{$file}`."); } return $contents; } /** * @return array<mixed> * @throws ConfigurationException */ private static function validateParams(mixed $params, string $file): array { if (!is_array($params)) { throw new ConfigurationException("Params can't be loaded from `{$file}`."); } return $params; } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Util\Shared\Namespaces; use Codeception\Util\Template; class Helper { use Namespaces; use Classname; protected string $template = <<<EOF <?php declare(strict_types=1); {{namespace}} // here you can define custom actions // all public methods declared in helper class will be available in \$I class {{name}} extends \\Codeception\\Module { } EOF; public function __construct(protected array $settings, protected string $name) { } public function produce(): string { return (new Template($this->template)) ->place('namespace', $this->getNamespaceHeader($this->supportNamespace() . 'Helper\\' . $this->name)) ->place('name', $this->getShortClassName($this->name)) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Exception\ConfigurationException; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Util\Shared\Namespaces; use Codeception\Util\Template; class Cest { use Classname; use Namespaces; protected string $template = <<<EOF <?php {{namespace}} class {{name}}Cest { public function _before({{actor}} \$I) { } // tests public function tryToTest({{actor}} \$I) { } } EOF; protected ?string $name; public function __construct(string $className, protected array $settings) { $this->name = $this->removeSuffix($className, 'Cest'); } public function produce(): string { $actor = $this->settings['actor']; if (!$actor) { throw new ConfigurationException("Cest can't be created for suite without an actor. Add `actor: SomeTester` to suite config"); } $namespaceHeader = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst($this->settings['suite']) . '\\' . $this->name); if ($namespaceHeader) { $namespaceHeader .= "\nuse " . $this->supportNamespace() . $actor . ";"; } return (new Template($this->template)) ->place('name', $this->getShortClassName($this->name)) ->place('namespace', $namespaceHeader) ->place('actor', $actor) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Util\Shared\Namespaces; use Codeception\Util\Template; class Group { use Namespaces; use Classname; protected string $template = <<<EOF <?php declare(strict_types=1); namespace {{namespace}}; use \Codeception\Event\TestEvent; /** * Group class is Codeception Extension which is allowed to handle to all internal events. * This class itself can be used to listen events for test execution of one particular group. * It may be especially useful to create fixtures data, prepare server, etc. * * INSTALLATION: * * To use this group extension, include it to "extensions" option of global Codeception config. */ class {{class}} extends \Codeception\Platform\Group { public static \$group = '{{groupName}}'; public function _before(TestEvent \$e) { } public function _after(TestEvent \$e) { } } EOF; protected string $namespace; public function __construct(protected array $settings, protected string $name) { $this->settings = $settings; $this->name = $name; $this->namespace = $this->getNamespaceString($this->supportNamespace() . '\\Group\\' . $name); } public function produce(): string { return (new Template($this->template)) ->place('class', ucfirst($this->name)) ->place('name', $this->name) ->place('namespace', $this->namespace) ->place('groupName', strtolower($this->name)) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Behat\Gherkin\Node\StepNode; use Codeception\Test\Loader\Gherkin; use Codeception\Util\Template; use Symfony\Component\Finder\Finder; class GherkinSnippets { protected string $template = <<<EOF /** * @{{type}} {{text}} */ public function {{methodName}}({{params}}) { throw new \PHPUnit\Framework\IncompleteTestError("Step `{{text}}` is not defined"); } EOF; /** * @var string[] */ protected array $snippets = []; /** * @var string[] */ protected array $processed = []; /** * @var string[] */ protected array $features = []; public function __construct(array $settings, $test = null) { $loader = new Gherkin($settings); $pattern = $loader->getPattern(); $path = $settings['path']; if (!empty($test)) { $path = $settings['path'] . '/' . $test; if (preg_match($pattern, $test)) { $path = dirname($path); $pattern = basename($test); } } $finder = Finder::create() ->files() ->sortByName() ->in($path) ->followLinks() ->name($pattern); foreach ($finder as $file) { $pathname = str_replace("//", "/", $file->getPathname()); $loader->loadTests($pathname); } $availableSteps = $loader->getSteps(); $allSteps = []; foreach ($availableSteps as $stepGroup) { $allSteps = array_merge($allSteps, $stepGroup); } foreach ($loader->getTests() as $test) { $steps = $test->getScenarioNode()->getSteps(); if ($test->getFeatureNode()->hasBackground()) { $steps = array_merge($steps, $test->getFeatureNode()->getBackground()->getSteps()); } foreach ($steps as $step) { $matched = false; $text = $step->getText(); if (self::stepHasPyStringArgument($step)) { // pretend it is inline argument $text .= ' ""'; } foreach (array_keys($allSteps) as $pattern) { if (preg_match($pattern, $text)) { $matched = true; break; } } if (!$matched) { $this->addSnippet($step); $file = str_ireplace($settings['path'], '', $test->getFeatureNode()->getFile()); if (!in_array($file, $this->features)) { $this->features[] = $file; } } } } } public function addSnippet(StepNode $step): void { $args = []; $pattern = $step->getText(); // match numbers (not in quotes) if (preg_match_all('#([\d.])(?=([^"]*"[^"]*")*[^"]*$)#', $pattern, $matches)) { foreach ($matches[1] as $num => $param) { ++$num; $args[] = '$num' . $num; $pattern = str_replace($param, ":num{$num}", $pattern); } } // match quoted string if (preg_match_all('#"(.*?)"#', $pattern, $matches)) { foreach ($matches[1] as $num => $param) { ++$num; $args[] = '$arg' . $num; $pattern = str_replace('"' . $param . '"', ":arg{$num}", $pattern); } } // Has multiline argument at the end of step? if (self::stepHasPyStringArgument($step)) { $num = count($args) + 1; $pattern .= " :arg{$num}"; $args[] = '$arg' . $num; } if (in_array($pattern, $this->processed)) { return; } $methodName = preg_replace('#(\s+?|\'|\"|\W)#', '', ucwords(preg_replace('#"(.*?)"|\d+#', '', $step->getText()))); if (empty($methodName)) { $methodName = 'step_' . substr(sha1($pattern), 0, 9); } $this->snippets[] = (new Template($this->template)) ->place('type', $step->getKeywordType()) ->place('text', $pattern) ->place('methodName', lcfirst($methodName)) ->place('params', implode(', ', $args)) ->produce(); $this->processed[] = $pattern; } /** * @return string[] */ public function getSnippets(): array { return $this->snippets; } /** * @return string[] */ public function getFeatures(): array { return $this->features; } public static function stepHasPyStringArgument(StepNode $step): bool { if ($step->hasArguments()) { $stepArgs = $step->getArguments(); if ($stepArgs[count($stepArgs) - 1]->getNodeType() == "PyString") { return true; } } return false; } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Util\Shared\Namespaces; use Codeception\Util\Template; class PageObject { use Namespaces; use Classname; protected string $template = <<<EOF <?php declare(strict_types=1); namespace {{namespace}}; class {{class}} { /** * Declare UI map for this page here. CSS or XPath allowed. * public \$usernameField = '#username'; * public \$formSubmitButton = "#mainForm input[type=submit]"; */ {{actions}} } EOF; protected string $actionsTemplate = <<<EOF /** * @var \\{{actorClass}}; */ protected \${{actor}}; public function __construct(\\{{actorClass}} \$I) { \$this->{{actor}} = \$I; // you can inject other page objects here as well } EOF; protected string $actions = ''; protected string $name; protected string $namespace; public function __construct(protected array $settings, string $name) { $this->name = $this->getShortClassName($name); $this->namespace = $this->getNamespaceString($this->supportNamespace() . '\\Page\\' . $name); } public function produce(): string { return (new Template($this->template)) ->place('namespace', $this->namespace) ->place('actions', $this->produceActions()) ->place('class', $this->name) ->produce(); } protected function produceActions(): string { if (!isset($this->settings['actor'])) { return ''; // global pageobject } $actor = lcfirst($this->settings['actor']); $actorClass = ltrim($this->supportNamespace() . $this->settings['actor'], '\\'); return (new Template($this->actionsTemplate)) ->place('actorClass', $actorClass) ->place('actor', $actor) ->place('pageObject', $this->name) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Codecept; use Codeception\Configuration; use Codeception\Lib\Di; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Lib\ModuleContainer; use Codeception\Step\GeneratedStep; use Codeception\Util\ReflectionHelper; use Codeception\Util\Template; use Exception; use InvalidArgumentException; use ReflectionAttribute; use ReflectionClass; use ReflectionException; use ReflectionIntersectionType; use ReflectionMethod; use ReflectionNamedType; use ReflectionType; use ReflectionUnionType; use function implode; use function sprintf; class Actions { use Classname; public Di $di; public ModuleContainer $moduleContainer; protected string $template = <<<EOF <?php //[STAMP] {{hash}} // phpcs:ignoreFile namespace {{namespace}}_generated; // This class was automatically generated by build task // You should not change it manually as it will be overwritten on next build trait {{name}}Actions { /** * @return \Codeception\Scenario */ abstract protected function getScenario(); {{methods}} } EOF; protected string $methodTemplate = <<<EOF /** * [!] Method is generated. Documentation taken from corresponding module. * {{doc}} * @see \{{module}}::{{method}}() */{{attributes}} public function {{action}}({{params}}){{return_type}} { {{return}}\$this->getScenario()->runStep(new \Codeception\Step\{{step}}('{{method}}', func_get_args())); } EOF; protected string $name; protected array $settings = []; protected array $modules = []; protected array $actions = []; protected int $numMethods = 0; /** * @var GeneratedStep[] */ protected array $generatedSteps = []; public function __construct(array $settings) { $this->name = $settings['actor']; $this->settings = $settings; $this->di = new Di(); $modules = Configuration::modules($this->settings); $this->moduleContainer = new ModuleContainer($this->di, $settings); foreach ($modules as $moduleName) { $this->moduleContainer->create($moduleName); } $this->modules = $this->moduleContainer->all(); $this->actions = $this->moduleContainer->getActions(); $this->generatedSteps = (array)$settings['step_decorators']; } public function produce(): string { $namespace = trim($this->supportNamespace(), '\\'); $methods = []; $code = []; foreach ($this->actions as $action => $moduleName) { if (in_array($action, $methods)) { continue; } $class = new ReflectionClass($this->modules[$moduleName]); $method = $class->getMethod($action); $code[] = $this->addMethod($method); $methods[] = $action; ++$this->numMethods; } return (new Template($this->template)) ->place('namespace', $namespace !== '' ? $namespace . '\\' : '') ->place('hash', self::genHash($this->modules, $this->settings)) ->place('name', $this->name) ->place('methods', implode("\n\n ", $code)) ->produce(); } protected function addMethod(ReflectionMethod $refMethod): string { $class = $refMethod->getDeclaringClass(); $params = $this->getParamsString($refMethod); $module = $class->getName(); $body = ''; $doc = $this->addDoc($class, $refMethod); $doc = str_replace('/**', '', (string)$doc); $doc = trim(str_replace('*/', '', $doc)); if ($doc === '') { $doc = "*"; } $returnType = $this->createReturnTypeHint($refMethod); if (count($refMethod->getAttributes()) > 0) { $attributes = "\n " . $this->getAttributesString($refMethod); } $methodTemplate = (new Template($this->methodTemplate)) ->place('module', $module) ->place('method', $refMethod->name) ->place('attributes', $attributes ?? '') ->place('return_type', $returnType) ->place('return', ($returnType === ': void' || $returnType === ': never') ? '' : 'return ') ->place('params', $params); if (str_starts_with($refMethod->name, 'see')) { $type = 'Assertion'; } elseif (str_starts_with($refMethod->name, 'am')) { $type = 'Condition'; } else { $type = 'Action'; } $body .= $methodTemplate ->place('doc', $doc) ->place('action', $refMethod->name) ->place('step', $type) ->produce(); // add auto generated steps foreach (array_unique($this->generatedSteps) as $generator) { if (!is_callable([$generator, 'getTemplate'])) { throw new Exception("Wrong configuration for generated steps. {$generator} doesn't implement \Codeception\Step\GeneratedStep interface"); } $template = call_user_func([$generator, 'getTemplate'], clone $methodTemplate); if ($template) { $body .= $template->produce(); } } return $body; } protected function getAttributesString(ReflectionMethod $refMethod): string { $attributes = []; foreach ($refMethod->getAttributes() as $attribute) { $attributes[] = $this->stringifyAttribute($attribute); } return implode("\n ", $attributes); } protected function getParamsString(ReflectionMethod $refMethod): string { $params = []; foreach ($refMethod->getParameters() as $param) { $type = ''; $reflectionType = $param->getType(); if ($reflectionType !== null) { $type = $this->stringifyType($reflectionType, $refMethod->getDeclaringClass()) . ' '; } $attributes = ''; foreach ($param->getAttributes() as $attribute) { $attributes .= $this->stringifyAttribute($attribute); } if ($attributes !== '') { $attributes .= ' '; } if ($param->isOptional()) { $params[] = $attributes . $type . '$' . $param->name . ' = ' . ReflectionHelper::getDefaultValue($param); } else { $params[] = $attributes . $type . '$' . $param->name; } } return implode(', ', $params); } /** * @throws ReflectionException */ protected function addDoc(ReflectionClass $class, ReflectionMethod $refMethod): string|false { $doc = $refMethod->getDocComment(); if (!$doc) { $interfaces = $class->getInterfaces(); foreach ($interfaces as $interface) { $i = new ReflectionClass($interface->name); if ($i->hasMethod($refMethod->name)) { $doc = $i->getMethod($refMethod->name)->getDocComment(); break; } } } if (!$doc && $class->getParentClass()) { $parent = new ReflectionClass($class->getParentClass()->name); if ($parent->hasMethod($refMethod->name)) { return $parent->getMethod($refMethod->name)->getDocComment(); } return $doc; } return $doc; } public static function genHash(array $modules, array $settings): string { $actions = []; foreach ($modules as $moduleName => $module) { $actions[$moduleName] = get_class_methods($module::class); } return md5(Codecept::VERSION . serialize($actions) . serialize($settings['modules']) . implode(',', (array)$settings['step_decorators'])); } public function getNumMethods(): int { return $this->numMethods; } private function createReturnTypeHint(ReflectionMethod $refMethod): string { $returnType = $refMethod->getReturnType(); if (!$returnType instanceof ReflectionType) { return ''; } return ': ' . $this->stringifyType($returnType, $refMethod->getDeclaringClass()); } private function stringifyType(ReflectionType $type, ReflectionClass $moduleClass): string { if ($type instanceof ReflectionUnionType) { return $this->stringifyNamedTypes($type->getTypes(), $moduleClass, '|'); } elseif ($type instanceof ReflectionIntersectionType) { return $this->stringifyNamedTypes($type->getTypes(), $moduleClass, '&'); } elseif ($type instanceof ReflectionNamedType) { return sprintf( '%s%s', ($type->allowsNull() && $type->getName() !== 'mixed') ? '?' : '', self::stringifyNamedType($type, $moduleClass) ); } else { throw new InvalidArgumentException('Unsupported type class: ' . $type::class); } } /** * @param ReflectionNamedType[] $types */ private function stringifyNamedTypes(array $types, ReflectionClass $moduleClass, string $separator): string { $strings = []; foreach ($types as $type) { $strings[] = self::stringifyNamedType($type, $moduleClass); } return implode($separator, $strings); } public static function stringifyNamedType(ReflectionNamedType $type, ReflectionClass $moduleClass): string { $typeName = $type->getName(); if ($typeName === 'self') { $typeName = $moduleClass->getName(); } elseif ($typeName === 'parent') { $typeName = $moduleClass->getParentClass()->getName(); } return sprintf( '%s%s', $type->isBuiltin() ? '' : '\\', $typeName ); } private function stringifyAttribute(ReflectionAttribute $attribute): string { try { $refClass = new ReflectionClass($attribute->getName()); $name = sprintf('%s%s', $refClass->isUserDefined() ? '\\' : '', $attribute->getName()); } catch (ReflectionException) { // If we can't get the class then just return what we've been given. $name = $attribute->getName(); } $arguments = $attribute->getArguments(); // Strip the wrapping array brackets so parameters aren't converted to arrays. $args = substr(ReflectionHelper::phpEncodeValue($arguments), 1, -1); return '#[' . $name . '(' . $args . ')]'; } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Util\Template; class Feature { protected string $template = <<<EOF Feature: {{name}} In order to ... As a ... I need to ... Scenario: try {{name}} EOF; public function __construct(protected string $name) { } public function produce(): string { return (new Template($this->template)) ->place('name', $this->name) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Configuration; use Codeception\Lib\Di; use Codeception\Lib\Friend; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Lib\ModuleContainer; use Codeception\Util\ReflectionHelper; use Codeception\Util\Template; use ReflectionClass; use ReflectionMethod; class Actor { use Classname; public Di $di; public ModuleContainer $moduleContainer; protected string $template = <<<EOF <?php declare(strict_types=1); {{hasNamespace}} /** * Inherited Methods {{inheritedMethods}} * * @SuppressWarnings(PHPMD) */ class {{actor}} extends \Codeception\Actor { use _generated\{{actor}}Actions; /** * Define custom actions here */ } EOF; protected string $inheritedMethodTemplate = ' * @method {{return}} {{method}}({{params}})'; protected array $modules = []; protected array $actions = []; public function __construct(protected array $settings) { $this->di = new Di(); $this->moduleContainer = new ModuleContainer($this->di, $settings); $modules = Configuration::modules($this->settings); foreach ($modules as $moduleName) { $this->moduleContainer->create($moduleName); } $this->modules = $this->moduleContainer->all(); $this->actions = $this->moduleContainer->getActions(); } public function produce(): string { $namespace = trim($this->supportNamespace(), '\\'); return (new Template($this->template)) ->place('hasNamespace', $namespace !== '' ? "\nnamespace {$namespace};" : '') ->place('actor', $this->settings['actor']) ->place('inheritedMethods', $this->prependAbstractActorDocBlocks()) ->produce(); } protected function prependAbstractActorDocBlocks(): string { $inherited = []; $class = new ReflectionClass(\Codeception\Actor::class); $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { if ($method->name == '__call') { continue; } // skipping magic if ($method->name == '__construct') { continue; } // skipping magic $returnType = 'void'; if ($method->name == 'haveFriend') { $returnType = Friend::class; } $params = $this->getParamsString($method); $inherited[] = (new Template($this->inheritedMethodTemplate)) ->place('method', $method->name) ->place('params', $params) ->place('return', $returnType) ->produce(); } return implode("\n", $inherited); } protected function getParamsString(ReflectionMethod $refMethod): string { $params = []; foreach ($refMethod->getParameters() as $param) { if ($param->isOptional()) { $params[] = '$' . $param->name . ' = ' . ReflectionHelper::getDefaultValue($param); } else { $params[] = '$' . $param->name; } } return implode(', ', $params); } public function getActorName() { return $this->settings['actor']; } /** * @return string[] */ public function getModules(): array { return array_keys($this->modules); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Exception\ConfigurationException; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Util\Shared\Namespaces; use Codeception\Util\Template; class StepObject { use Namespaces; use Classname; protected string $template = <<<EOF <?php declare(strict_types=1); namespace {{namespace}}; class {{name}} extends {{actorClass}} { {{actions}} } EOF; protected string $actionTemplate = <<<EOF public function {{action}}() { \$I = \$this; } EOF; protected string $name; protected string $actions = ''; public string $namespace; public function __construct(protected array $settings, string $name) { $this->name = $this->getShortClassName($name); $this->namespace = $this->getNamespaceString($this->supportNamespace() . 'Step\\' . $name); } public function produce(): string { $actor = $this->settings['actor']; if (!$actor) { throw new ConfigurationException("Steps can't be created for suite without an actor"); } $extended = '\\' . ltrim($this->supportNamespace() . $actor, '\\'); return (new Template($this->template)) ->place('namespace', $this->namespace) ->place('name', $this->name) ->place('actorClass', $extended) ->place('actions', $this->actions) ->produce(); } public function createAction($action): void { $this->actions .= (new Template($this->actionTemplate)) ->place('action', $action) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator\Shared; trait Classname { protected function removeSuffix(string $classname, string $suffix): string { $classname = preg_replace('#\.php$#', '', $classname); return preg_replace("#{$suffix}$#", '', $classname); } protected function supportNamespace(): string { if (!isset($this->settings)) { return "\\"; } $namespace = ""; if ($this->settings['namespace']) { $namespace .= '\\' . $this->settings['namespace']; } if (isset($this->settings['support_namespace'])) { $namespace .= '\\' . $this->settings['support_namespace']; } return trim($namespace, '\\') . '\\'; } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Util\Shared\Namespaces; use Codeception\Util\Template; class Snapshot { use Namespaces; use Classname; protected string $template = <<<EOF <?php declare(strict_types=1); namespace {{namespace}}; class {{name}} extends \\Codeception\\Snapshot { {{actions}} protected function fetchData() { // TODO: return a value which will be used for snapshot } } EOF; protected string $actionsTemplate = <<<EOF /** * @var \\{{actorClass}}; */ protected \${{actor}}; public function __construct(\\{{actorClass}} \$I) { \$this->{{actor}} = \$I; } EOF; protected string $namespace; protected string $name; public function __construct(protected array $settings, string $name) { $this->name = $this->getShortClassName($name); $this->namespace = $this->getNamespaceString($this->supportNamespace() . 'Snapshot\\' . $name); } public function produce(): string { return (new Template($this->template)) ->place('namespace', $this->namespace) ->place('actions', $this->produceActions()) ->place('name', $this->name) ->produce(); } protected function produceActions(): string { if (!isset($this->settings['actor'])) { return ''; // no actor in suite } $actor = lcfirst($this->settings['actor']); $actorClass = rtrim($this->supportNamespace(), '\\') . $this->settings['actor']; return (new Template($this->actionsTemplate)) ->place('actorClass', $actorClass) ->place('actor', $actor) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib\Generator; use Codeception\Configuration; use Codeception\Lib\Generator\Shared\Classname; use Codeception\Util\Shared\Namespaces; use Codeception\Util\Template; class Test { use Namespaces; use Classname; protected string $template = <<<EOF <?php {{namespace}} class {{name}}Test extends \Codeception\Test\Unit { {{tester}} protected function _before() { } // tests public function testSomeFeature() { } } EOF; protected string $testerTemplate = <<<EOF protected {{actorClass}} \${{actor}}; EOF; protected string $name; public function __construct(protected array $settings, string $name) { $this->name = $this->removeSuffix($name, 'Test'); } public function produce(): string { $actor = $this->settings['actor']; $ns = $this->getNamespaceHeader($this->settings['namespace'] . '\\' . ucfirst($this->settings['suite']) . '\\' . $this->name); if ($ns) { $ns .= "\nuse " . $this->supportNamespace() . $actor . ";"; } $tester = ''; if ($this->settings['actor']) { $tester = (new Template($this->testerTemplate)) ->place('actorClass', $actor) ->place('actor', lcfirst(Configuration::config()['actor_suffix'])) ->produce(); } return (new Template($this->template)) ->place('namespace', $ns) ->place('name', $this->getShortClassName($this->name)) ->place('tester', $tester) ->produce(); } } <?php declare(strict_types=1); namespace Codeception\Lib; use Codeception\Configuration; use Codeception\Exception\ConfigurationException; use Codeception\Test\Gherkin; use Codeception\Test\Test; use Codeception\Util\PathResolver; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; use function realpath; /** * Loads information for groups from external sources (config, filesystem) */ class GroupManager { protected array $testsInGroups = []; protected string $rootDir; public function __construct(protected array $configuredGroups) { $this->rootDir = Configuration::baseDir(); $this->loadGroupsByPattern(); $this->loadConfiguredGroupSettings(); } /** * proceeds group names with asterisk: * * ``` * "tests/_log/g_*" => [ * "tests/_log/group_1", * "tests/_log/group_2", * "tests/_log/group_3", * ] * ``` */ protected function loadGroupsByPattern(): void { foreach ($this->configuredGroups as $group => $pattern) { if (!str_contains($group, '*')) { continue; } $path = dirname($pattern); if (!PathResolver::isPathAbsolute($pattern)) { $path = $this->rootDir . $path; } $files = Finder::create()->files() ->name(basename($pattern)) ->sortByName() ->in($path); foreach ($files as $file) { /** @var SplFileInfo $file * */ $prefix = str_replace('*', '', $group); $pathPrefix = str_replace('*', '', basename($pattern)); $groupName = $prefix . str_replace($pathPrefix, '', $file->getRelativePathname()); $this->configuredGroups[$groupName] = dirname($pattern) . DIRECTORY_SEPARATOR . $file->getRelativePathname(); } unset($this->configuredGroups[$group]); } } protected function loadConfiguredGroupSettings(): void { foreach ($this->configuredGroups as $group => $tests) { $this->testsInGroups[$group] = []; if (is_array($tests)) { foreach ($tests as $test) { $file = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $test); $this->testsInGroups[$group][] = $this->normalizeFilePath($file, $group); } continue; } $path = $tests; if (!codecept_is_path_absolute($tests)) { $path = $this->rootDir . $tests; } if (is_file($path)) { $handle = @fopen($path, "r"); if ($handle) { while (($test = fgets($handle, 4096)) !== false) { // if the current line is blank then we need to move to the next line // otherwise the current codeception directory becomes part of the group // which causes every single test to run if (trim($test) === '') { continue; } $file = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], trim($test)); $this->testsInGroups[$group][] = $this->normalizeFilePath($file, $group); } fclose($handle); } } } } private function normalizeFilePath(string $file, string $group): string { $pathParts = explode(':', $file); if (codecept_is_path_absolute($file)) { if ($file[0] === '/' && count($pathParts) > 1) { //take segment before first : $this->checkIfFileExists($pathParts[0], $group); return sprintf('%s:%s', realpath($pathParts[0]), $pathParts[1]); } elseif (count($pathParts) > 2) { //on Windows take segment before second : $fullPath = $pathParts[0] . ':' . $pathParts[1]; $this->checkIfFileExists($fullPath, $group); return sprintf('%s:%s', realpath($fullPath), $pathParts[2]); } $this->checkIfFileExists($file, $group); return realpath($file); } elseif (!str_contains($file, ':')) { $dirtyPath = $this->rootDir . $file; $this->checkIfFileExists($dirtyPath, $group); return realpath($dirtyPath); } $dirtyPath = $this->rootDir . $pathParts[0]; $this->checkIfFileExists($dirtyPath, $group); return sprintf('%s:%s', realpath($dirtyPath), $pathParts[1]); } private function checkIfFileExists(string $path, string $group): void { if (!file_exists($path)) { throw new ConfigurationException('GroupManager: File or directory ' . $path . ' set in ' . $group . ' group does not exist'); } } public function groupsForTest(Test $test): array { $filename = realpath($test->getFileName()); $testName = $test->getName(); $groups = $test->getMetadata()->getGroups(); foreach ($this->testsInGroups as $group => $tests) { foreach ($tests as $testPattern) { if ($filename == $testPattern) { $groups[] = $group; } if (str_starts_with($filename . ':' . $testName, (string)$testPattern)) { $groups[] = $group; } if ( $test instanceof Gherkin && mb_strtolower($filename . ':' . $test->getMetadata()->getFeature()) === mb_strtolower($testPattern) ) { $groups[] = $group; } } } return array_unique($groups); } } <?php declare(strict_types=1); namespace Codeception\Lib\Actor\Shared; trait Retry { protected int $retryNum = 1; protected int $retryInterval = 100; /** * Configure number of retries and initial interval. * Interval will be doubled on each unsuccessful execution. * * Use with \$I->retryXXX() methods; */ public function retry(int $num, int $interval = 200): void { $this->retryNum = $num; $this->retryInterval = $interval; } } <?php declare(strict_types=1); namespace Codeception\Lib\Actor\Shared; use Codeception\Scenario; trait Comment { abstract protected function getScenario(): Scenario; public function expectTo(string $prediction): self { return $this->comment('I expect to ' . $prediction); } public function expect(string $prediction): self { return $this->comment('I expect ' . $prediction); } public function amGoingTo(string $argumentation): self { return $this->comment('I am going to ' . $argumentation); } public function am(string $role): self { $role = trim($role); if (stripos('aeiou', (string)$role[0]) !== false) { return $this->comment('As an ' . $role); } return $this->comment('As a ' . $role); } public function lookForwardTo(string $achieveValue): self { return $this->comment('So that I ' . $achieveValue); } public function comment(string $description): self { $this->getScenario()->comment($description); return $this; } } <?php declare(strict_types=1); namespace Codeception\Lib\Actor\Shared; use Codeception\Lib\Friend as LibFriend; use Codeception\Scenario; trait Friend { protected array $friends = []; abstract protected function getScenario(): Scenario; public function haveFriend(string $name, string $actorClass = null): LibFriend { if (!isset($this->friends[$name])) { $actor = $actorClass === null ? $this : new $actorClass($this->getScenario()); $this->friends[$name] = new LibFriend($name, $actor, $this->getScenario()->current('modules')); } return $this->friends[$name]; } } <?php declare(strict_types=1); namespace Codeception\Lib\Actor\Shared; use Codeception\Command\Console; use Codeception\Lib\PauseShell; use Codeception\Util\Debug; use Psy\Shell; use Psy\Configuration; trait Pause { public function pause(array $vars = []): void { if (!Debug::isEnabled()) { return; } $psy = (new PauseShell()) ->addMessage('$I-> to launch commands') ->addMessage('$this-> to access current test') ->addMessage('exit to exit') ->getShell(); $vars['I'] = $this; $psy->setScopeVariables($vars); $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); if (!$backtrace[1]['object'] instanceof Console) { // set the scope of test class $psy->setBoundObject($backtrace[1]['object']); } $psy->run(); } } <?php declare(strict_types=1); namespace Codeception\Lib; use Codeception\Actor; use Codeception\Exception\TestRuntimeException; use Codeception\Lib\Interfaces\MultiSession; class Friend { protected array $data = []; protected array $multiSessionModules = []; public function __construct(protected string $name, protected Actor $actor, array $modules = []) { $this->multiSessionModules = array_filter($modules, fn ($m): bool => $m instanceof MultiSession); if (empty($this->multiSessionModules)) { throw new TestRuntimeException("No multisession modules used. Can't instantiate friend"); } } public function does($closure) { $currentUserData = []; foreach ($this->multiSessionModules as $module) { $name = $module->_getName(); $currentUserData[$name] = $module->_backupSession(); if (empty($this->data[$name])) { $module->_initializeSession(); $this->data[$name] = $module->_backupSession(); continue; } $module->_loadSession($this->data[$name]); } $this->actor->comment(strtoupper("{$this->name} does ---")); $ret = $closure($this->actor); $this->actor->comment(strtoupper("--- {$this->name} finished")); foreach ($this->multiSessionModules as $module) { $name = $module->_getName(); $this->data[$name] = $module->_backupSession(); $module->_loadSession($currentUserData[$name]); } return $ret; } public function isGoingTo(string $argumentation): void { $this->actor->amGoingTo($argumentation); } public function expects(string $prediction): void { $this->actor->expect($prediction); } public function expectsTo(string $prediction): void { $this->actor->expectTo($prediction); } public function leave(): void { foreach ($this->multiSessionModules as $module) { if (isset($this->data[$module->_getName()])) { $module->_closeSession($this->data[$module->_getName()]); } } } } <?php declare(strict_types=1); namespace Codeception\Lib; use Codeception\Exception\InjectionException; use Codeception\Util\ReflectionHelper; use Exception; use ReflectionClass; use ReflectionException; use ReflectionMethod; use ReflectionObject; class Di { /** * @var string */ public const DEFAULT_INJECT_METHOD_NAME = '_inject'; /** * @var object[] */ protected array $container = []; protected ?Di $fallback = null; public function __construct(Di $fallback = null) { $this->fallback = $fallback; } public function get(string $className): ?object { // normalize namespace $className = ltrim($className, '\\'); return $this->container[$className] ?? null; } public function set(object $class): void { $this->container[$class::class] = $class; } /** * @param string $injectMethodName Method which will be invoked after object creation; * Resolved dependencies will be passed to it as arguments * @throws InjectionException|ReflectionException */ public function instantiate( string $className, array $constructorArgs = null, string $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME ): ?object { // normalize namespace $className = ltrim($className, '\\'); // get class from container if (isset($this->container[$className])) { if ($this->container[$className] instanceof $className) { return $this->container[$className]; } throw new InjectionException("Failed to resolve cyclic dependencies for class '{$className}'"); } // get class from parent container if ($this->fallback && ($class = $this->fallback->get($className))) { return $class; } $this->container[$className] = false; // flag that object is being instantiated $reflectedClass = new ReflectionClass($className); if (!$reflectedClass->isInstantiable()) { return null; } $reflectedConstructor = $reflectedClass->getConstructor(); if (is_null($reflectedConstructor)) { $object = new $className(); } else { try { if (!$constructorArgs) { $constructorArgs = $this->prepareArgs($reflectedConstructor); } } catch (Exception $e) { throw new InjectionException("Failed to create instance of '{$className}'. " . $e->getMessage()); } $object = $reflectedClass->newInstanceArgs($constructorArgs); } if ($injectMethodName !== '') { $this->injectDependencies($object, $injectMethodName); } $this->container[$className] = $object; return $object; } /** * @param string $injectMethodName Method which will be invoked with resolved dependencies as its arguments * @throws InjectionException|ReflectionException */ public function injectDependencies(object $object, string $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME, array $defaults = []): void { $reflectedObject = new ReflectionObject($object); $reflectionObjectHasMethod = $reflectedObject->hasMethod($injectMethodName); if (!$reflectionObjectHasMethod) { return; } $reflectedMethod = $reflectedObject->getMethod($injectMethodName); try { $args = $this->prepareArgs($reflectedMethod, $defaults); } catch (Exception $e) { $msg = $e->getMessage(); if ($e->getPrevious() !== null) { // injection failed because PHP code is invalid. See #3869 $msg .= '; ' . $e->getPrevious(); } throw new InjectionException( "Failed to inject dependencies in instance of '{$reflectedObject->name}'. {$msg}" ); } if (!$reflectedMethod->isPublic()) { $reflectedMethod->setAccessible(true); } $reflectedMethod->invokeArgs($object, $args); } protected function prepareArgs(ReflectionMethod $method, array $defaults = []): array { $args = []; $parameters = $method->getParameters(); foreach ($parameters as $k => $parameter) { $dependency = ReflectionHelper::getClassFromParameter($parameter); if (is_null($dependency)) { if ($parameter->isVariadic()) { continue; } if (!$parameter->isOptional()) { if (!isset($defaults[$k])) { throw new InjectionException("Parameter '{$parameter->name}' must have default value."); } $args[] = $defaults[$k]; continue; } $args[] = $parameter->getDefaultValue(); } else { $arg = $this->instantiate($dependency); if (is_null($arg)) { if ($parameter->isVariadic()) { continue; } throw new InjectionException("Failed to resolve dependency '{$dependency}'."); } $args[] = $arg; } } return $args; } } <?php declare(strict_types=1); namespace Codeception\Lib\Connector\Shared; /** * Converts BrowserKit\Request's request parameters and files into PHP-compatible structure * * @see https://bugs.php.net/bug.php?id=25589 * @see https://bugs.php.net/bug.php?id=25589 * * @package Codeception\Lib\Connector */ trait PhpSuperGlobalsConverter { /** * Rearrange files array to be compatible with PHP $_FILES superglobal structure * @see https://bugs.php.net/bug.php?id=25589 */ protected function remapFiles(array $requestFiles): array { $files = $this->rearrangeFiles($requestFiles); return $this->replaceSpaces($files); } /** * Escape high-level variable name with dots, underscores and other "special" chars * to be compatible with PHP "bug" * @see https://bugs.php.net/bug.php?id=40000 */ protected function remapRequestParameters(array $parameters): array { return $this->replaceSpaces($parameters); } private function rearrangeFiles(array $requestFiles): array { $files = []; foreach ($requestFiles as $name => $info) { if (!is_array($info)) { continue; } /** * If we have a form with fields like * ``` * <input type="file" name="foo" /> * <input type="file" name="foo[bar]" /> * ``` * then only array variable will be used while simple variable will be ignored in php $_FILES * (eg $_FILES = [ * foo => [ * tmp_name => [ * 'bar' => 'asdf' * ], * //... * ] * ] * ) * (notice there is no entry for file "foo", only for file "foo[bar]" * this will check if current element contains inner arrays within it's keys * so we can ignore element itself and only process inner files */ $hasInnerArrays = count(array_filter($info, 'is_array')); if ($hasInnerArrays || !isset($info['tmp_name'])) { $inner = $this->remapFiles($info); foreach ($inner as $innerName => $innerInfo) { /** * Convert from ['a' => ['tmp_name' => '/tmp/test.txt'] ] * to ['tmp_name' => ['a' => '/tmp/test.txt'] ] */ $innerInfo = array_map( fn ($v) => [$innerName => $v], $innerInfo ); if (empty($files[$name])) { $files[$name] = []; } $files[$name] = array_replace_recursive($files[$name], $innerInfo); } } else { $files[$name] = $info; } } return $files; } /** * Replace spaces and dots and other chars in high-level query parameters for * compatibility with PHP bug (or not a bug) * @see https://bugs.php.net/bug.php?id=40000 * * @param array $parameters Array of request parameters to be converted */ private function replaceSpaces(array $parameters): array { $qs = http_build_query($parameters); parse_str($qs, $output); return $output; } } <?php declare(strict_types=1); namespace Codeception\Event; use Codeception\ResultAggregator; use Symfony\Contracts\EventDispatcher\Event; class PrintResultEvent extends Event { public function __construct(protected ResultAggregator $result) { } public function getResult(): ResultAggregator { return $this->result; } } <?php declare(strict_types=1); namespace Codeception\Event; use Codeception\Test\Test; use Symfony\Contracts\EventDispatcher\Event; class TestEvent extends Event { /** * @param float|null $time Time taken */ public function __construct(protected Test $test, protected ?float $time = 0) { } public function getTime(): float { return $this->time; } public function getTest(): Test { return $this->test; } } <?php declare(strict_types=1); namespace Codeception\Event; use Codeception\Test\Test; use Throwable; class FailEvent extends TestEvent { public function __construct(Test $test, private Throwable $fail, ?float $time) { parent::__construct($test, $time); } public function getFail(): Throwable { return $this->fail; } } <?php declare(strict_types=1); namespace Codeception\Event; use Codeception\ResultAggregator; use Codeception\Suite; use Symfony\Contracts\EventDispatcher\Event; class SuiteEvent extends Event { public function __construct(protected ?Suite $suite = null, protected array $settings = []) { } public function getSuite(): ?Suite { return $this->suite; } public function getSettings(): array { return $this->settings; } } <?php declare(strict_types=1); namespace Codeception\Event; use Codeception\Step; use Codeception\TestInterface; use Symfony\Contracts\EventDispatcher\Event; class StepEvent extends Event { public function __construct(protected TestInterface $test, protected Step $step) { } public function getStep(): Step { return $this->step; } public function getTest(): TestInterface { return $this->test; } } <?php declare(strict_types=1); namespace Codeception; use Codeception\Coverage\Subscriber\Local; use Codeception\Coverage\Subscriber\LocalServer; use Codeception\Coverage\Subscriber\Printer as CoveragePrinter; use Codeception\Coverage\Subscriber\RemoteServer; use Codeception\Event\PrintResultEvent; use Codeception\Exception\ConfigurationException; use Codeception\Lib\Console\Output; use Codeception\Lib\Interfaces\ConsolePrinter; use Codeception\Lib\Notification; use Codeception\Reporter\HtmlReporter; use Codeception\Reporter\JUnitReporter; use Codeception\Reporter\PhpUnitReporter; use Codeception\Reporter\ReportPrinter; use Codeception\Subscriber\AutoRebuild; use Codeception\Subscriber\BeforeAfterTest; use Codeception\Subscriber\Bootstrap; use Codeception\Subscriber\Console; use Codeception\Subscriber\Dependencies; use Codeception\Subscriber\Deprecation; use Codeception\Subscriber\ErrorHandler; use Codeception\Subscriber\ExtensionLoader; use Codeception\Subscriber\FailFast; use Codeception\Subscriber\GracefulTermination; use Codeception\Subscriber\Module; use Codeception\Subscriber\PrepareTest; use Symfony\Component\EventDispatcher\EventDispatcher; class Codecept { /** * @var string */ public const VERSION = '5.1.2'; protected ResultAggregator $resultAggregator; protected EventDispatcher $dispatcher; protected ExtensionLoader $extensionLoader; protected array $options = [ 'silent' => false, 'debug' => false, 'steps' => false, 'html' => false, 'xml' => false, 'phpunit-xml' => false, 'no-redirect' => true, 'report' => false, 'colors' => false, 'coverage' => false, 'coverage-xml' => false, 'coverage-html' => false, 'coverage-text' => false, 'coverage-crap4j' => false, 'coverage-cobertura' => false, 'coverage-phpunit' => false, 'groups' => null, 'excludeGroups' => null, 'filter' => null, 'shard' => null, 'env' => null, 'fail-fast' => 0, 'ansi' => true, 'verbosity' => 1, 'interactive' => true, 'no-rebuild' => false, 'quiet' => false, ]; protected array $config = []; protected array $extensions = []; private Output $output; public function __construct(array $options = []) { $this->resultAggregator = new ResultAggregator(); $this->dispatcher = new EventDispatcher(); $this->extensionLoader = new ExtensionLoader($this->dispatcher); $baseOptions = $this->mergeOptions($options); $this->extensionLoader->bootGlobalExtensions($baseOptions); // extensions may override config $this->config = Configuration::config(); $this->options = $this->mergeOptions($options); // options updated from config $this->output = new Output($this->options); $this->registerSubscribers(); } /** * Merges given options with default values and current configuration * * @throws ConfigurationException */ protected function mergeOptions(array $options): array { $config = Configuration::config(); $baseOptions = array_merge($this->options, $config['settings']); return array_merge($baseOptions, $options); } public function registerSubscribers(): void { // required $this->dispatcher->addSubscriber(new GracefulTermination($this->resultAggregator)); $this->dispatcher->addSubscriber(new ErrorHandler()); $this->dispatcher->addSubscriber(new Dependencies()); $this->dispatcher->addSubscriber(new Bootstrap()); $this->dispatcher->addSubscriber(new PrepareTest()); $this->dispatcher->addSubscriber(new Module()); $this->dispatcher->addSubscriber(new BeforeAfterTest()); // optional if (!$this->options['no-rebuild']) { $this->dispatcher->addSubscriber(new AutoRebuild()); } if ($this->options['fail-fast'] > 0) { $this->dispatcher->addSubscriber(new FailFast($this->options['fail-fast'], $this->resultAggregator)); } if ($this->options['coverage']) { $this->dispatcher->addSubscriber(new Local($this->options)); $this->dispatcher->addSubscriber(new LocalServer($this->options)); $this->dispatcher->addSubscriber(new RemoteServer($this->options)); $this->dispatcher->addSubscriber(new CoveragePrinter($this->options, $this->output)); } if ($this->options['report']) { $this->dispatcher->addSubscriber(new ReportPrinter($this->options)); } $this->dispatcher->addSubscriber($this->extensionLoader); $this->extensionLoader->registerGlobalExtensions(); if (!$this->options['silent'] && !$this->isConsolePrinterSubscribed()) { $this->dispatcher->addSubscriber(new Console($this->options)); } $this->dispatcher->addSubscriber(new Deprecation($this->options)); $this->registerReporters(); } private function isConsolePrinterSubscribed(): bool { foreach ($this->dispatcher->getListeners() as $event => $listeners) { foreach ($listeners as $listener) { if ($listener instanceof ConsolePrinter) { return true; } if (is_array($listener) && $listener[0] instanceof ConsolePrinter) { return true; } } } return false; } private function registerReporters(): void { if (isset($this->config['reporters'])) { Notification::warning( "'reporters' option is not supported! Custom reporters must be reimplemented as extensions.", '' ); } if ($this->options['html']) { $this->dispatcher->addSubscriber( new HtmlReporter($this->options, $this->output) ); } if ($this->options['xml']) { $this->dispatcher->addSubscriber( new JUnitReporter($this->options, $this->output) ); } if ($this->options['phpunit-xml']) { $this->dispatcher->addSubscriber( new PhpUnitReporter($this->options, $this->output) ); } } private function absolutePath(string $path): string { if ((str_starts_with($path, '/')) or (strpos($path, ':') === 1)) { // absolute path return $path; } return Configuration::outputDir() . $path; } public function run(string $suite, string $test = null, array $config = null): void { ini_set( 'memory_limit', $this->config['settings']['memory_limit'] ?? '1024M' ); $config = $config ?: Configuration::config(); $config = Configuration::suiteSettings($suite, $config); $selectedEnvironments = $this->options['env']; if (!$selectedEnvironments || empty($config['env'])) { $this->runSuite($config, $suite, $test); return; } // Iterate over all unique environment sets and runs the given suite with each of the merged configurations. foreach (array_unique($selectedEnvironments) as $envList) { $envSet = explode(',', $envList); $suiteEnvConfig = $config; // contains a list of the environments used in this suite configuration env set. $envConfigs = []; foreach ($envSet as $currentEnv) { // The $settings['env'] actually contains all parsed configuration files as a // filename => filecontents key-value array. If there is no configuration file for the // $currentEnv the merge will be skipped. if (!array_key_exists($currentEnv, $config['env'])) { return; } // Merge configuration consecutively with already build configuration if (is_array($config['env'][$currentEnv])) { $suiteEnvConfig = Configuration::mergeConfigs($suiteEnvConfig, $config['env'][$currentEnv]); } $envConfigs[] = $currentEnv; } $suiteEnvConfig['current_environment'] = implode(',', $envConfigs); $suiteToRun = $suite; if (!empty($envList)) { $suiteToRun .= ' (' . implode(', ', $envSet) . ')'; } $this->runSuite($suiteEnvConfig, $suiteToRun, $test); } } public function runSuite(array $settings, string $suite, string $test = null): void { $settings['shard'] = $this->options['shard']; $suiteManager = new SuiteManager($this->dispatcher, $suite, $settings, $this->options); $suiteManager->initialize(); srand($this->options['seed']); $suiteManager->loadTests($test); srand(); $suiteManager->run($this->resultAggregator); } public static function versionString(): string { return 'Codeception PHP Testing Framework v' . self::VERSION; } public function printResult(): void { $this->dispatcher->dispatch(new PrintResultEvent($this->resultAggregator), Events::RESULT_PRINT_AFTER); } public function getResultAggregator(): ResultAggregator { return $this->resultAggregator; } public function getOptions(): array { return $this->options; } public function getDispatcher(): EventDispatcher { return $this->dispatcher; } } <tr> <td class="stepName {error}" ><p onclick="showHide('{id}', this)"><span class="toggle">+</span> {metaStep}</p> </td> </tr> <tr> <td class="nostyle"> <table border="0" width="100%" class="substeps scenarioStepsTable" id="stepContainer{id}"> {steps} </table> </td> </tr> <tr class="scenarioRow {scenarioStatus}"> <td> <p class="{scenarioStatus}" onclick="showHide('{id}', this)">{toggle} {name} <span style="color: #34495e; font-size: 70%;">{time}s</span></p> </td> </tr> <tr class="scenarioRow {scenarioStatus}"> <td> <table border="0" width="100%" class="{scenarioStatus} scenarioStepsTable" id="stepContainer{id}"> {steps} {failure} {png} {html} </table> </td> </tr> <tr > <td class="error"> {fail} </td> </tr><html> <head> <title>Test results
{header} {scenarios}

Summary

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

{suite} Tests

{name} {status} ({time})

*/ protected static array $events = [ Events::TEST_SUCCESS => 'testSuccess', Events::TEST_FAIL => 'testFailure', Events::TEST_ERROR => 'testError', Events::TEST_INCOMPLETE => 'testIncomplete', Events::TEST_SKIPPED => 'testSkipped', Events::TEST_WARNING => 'testWarning', Events::TEST_USELESS => 'testUseless', Events::RESULT_PRINT_AFTER => 'afterResult', ]; private Output $output; private int $successfulCount = 0; private int $errorCount = 0; private int $failureCount = 0; private int $warningCount = 0; private int $skippedCount = 0; private int $incompleteCount = 0; private int $uselessCount = 0; public function __construct(array $options) { $this->output = new Output($options); } private function message(string $string = ''): Message { return $this->output->message($string); } public function testSuccess(TestEvent $event): void { $this->printTestResult($event->getTest(), 'Ok'); $this->successfulCount++; } public function testError(FailEvent $event): void { $this->printTestResult($event->getTest(), 'ERROR'); $this->errorCount++; } public function testFailure(FailEvent $event): void { $this->printTestResult($event->getTest(), "\033[41;37mFAIL\033[0m"); $this->failureCount++; } public function testWarning(FailEvent $event): void { $this->printTestResult($event->getTest(), 'WARNING'); $this->warningCount++; } public function testSkipped(FailEvent $event): void { $this->printTestResult($event->getTest(), 'Skipped'); $this->skippedCount++; } public function testIncomplete(FailEvent $event): void { $this->printTestResult($event->getTest(), 'Incomplete'); $this->incompleteCount++; } public function testUseless(FailEvent $event): void { $this->printTestResult($event->getTest(), 'Useless'); $this->uselessCount++; } private function printTestResult(Test $test, string $status): void { $name = Descriptor::getTestAsString($test); if (strlen($name) > 75) { $name = substr($name, 0, 70); } $this->message($name) ->width(75, '.') ->append($status) ->writeln(); } public function afterResult(PrintResultEvent $event): void { $counts = [ sprintf("Successful: %s", $this->successfulCount) ]; $failedCount = $this->errorCount + $this->errorCount + $this->warningCount; if ($failedCount > 0) { $counts [] = sprintf("Failed: %s", $failedCount); } if ($this->incompleteCount > 0) { $counts [] = sprintf("Incomplete: %s", $this->incompleteCount); } if ($this->skippedCount > 0) { $counts [] = sprintf("Skipped: %s", $this->skippedCount); } if ($this->uselessCount > 0) { $counts [] = sprintf("Useless: %s", $this->uselessCount); } $this->output->writeln("\nCodeception Results"); $this->output->writeln(implode('. ', $counts) . '.'); } } getTest(); if (method_exists($test, 'getFileName')) { $filename = $test->getFileName(); } else { $reflector = new ReflectionClass($test); $filename = $reflector->getFileName(); } if ($filename !== $this->currentFile) { if ($this->currentFile !== null) { parent::afterSuite(new SuiteEvent()); } //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->testSuiteUseless[self::FILE_LEVEL] = 0; $this->testSuiteLevel = self::FILE_LEVEL; $this->currentFile = $filename; $currentFileSuiteElement = $this->document->createElement('testsuite'); if ($test instanceof Reported) { $reportFields = $test->getReportFields(); $class = $reportFields['class'] ?? $reportFields['name']; $currentFileSuiteElement->setAttribute('name', $class); } else { $currentFileSuiteElement->setAttribute('name', $test::class); } $currentFileSuiteElement->setAttribute('file', $filename); $this->testSuites[self::SUITE_LEVEL]->appendChild($currentFileSuiteElement); $this->testSuites[self::FILE_LEVEL] = $currentFileSuiteElement; } parent::startTest($event); } /** * Cleans the mess caused by test suite manipulation in startTest */ public function afterSuite(SuiteEvent $event): void { $suite = $event->getSuite(); if ($suite->getName()) { if ($this->currentFile) { //close last file in the test suite parent::afterSuite(new SuiteEvent($suite)); $this->currentFile = null; } $this->testSuiteLevel = self::SUITE_LEVEL; } parent::afterSuite($event); } } */ protected static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::SUITE_AFTER => 'afterSuite', Events::TEST_START => 'startTest', Events::TEST_END => 'endTest', Events::TEST_FAIL => 'testFailure', Events::TEST_ERROR => 'testError', Events::TEST_INCOMPLETE => 'testSkipped', Events::TEST_SKIPPED => 'testSkipped', Events::TEST_USELESS => 'testUseless', Events::TEST_WARNING => 'testWarning', Events::RESULT_PRINT_AFTER => 'afterResult', ]; protected string $reportFileParam = 'xml'; protected string $reportName = 'JUNIT XML'; protected bool $isStrict = false; /** * @var string[] */ protected array $strictAttributes = ['file', 'name', 'class']; protected DOMDocument $document; protected DOMElement $root; /** * @var DOMElement[] */ protected array $testSuites = []; /** * @var int[] */ protected array $testSuiteTests = [0]; /** * @var int[] */ protected array $testSuiteAssertions = [0]; /** * @var int[] */ protected array $testSuiteErrors = [0]; /** * @var int[] */ protected array $testSuiteFailures = [0]; /** * @var int[] */ protected array $testSuiteSkipped = [0]; /** * @var int[] */ protected array $testSuiteUseless = [0]; /** * @var int[] */ protected array $testSuiteTimes = [0]; protected int $testSuiteLevel = 0; protected ?DOMElement $currentTestCase = null; private string $reportFile; public function __construct(array $options, private Output $output) { $this->reportFile = $options[$this->reportFileParam]; if (!codecept_is_path_absolute($this->reportFile)) { $this->reportFile = codecept_output_dir($this->reportFile); } codecept_debug(sprintf("Printing %s report to %s", $this->reportName, $this->reportFile)); $this->isStrict = $options['strict_xml']; $this->document = new DOMDocument('1.0', 'UTF-8'); $this->document->formatOutput = true; $this->root = $this->document->createElement('testsuites'); $this->document->appendChild($this->root); } public function afterResult(PrintResultEvent $event): void { file_put_contents($this->reportFile, $this->document->saveXML()); $this->output->message( "- %s report generated in file://%s", $this->reportName, $this->reportFile )->writeln(); } public function beforeSuite(SuiteEvent $event): void { $suite = $event->getSuite(); $testSuite = $this->document->createElement('testsuite'); $testSuite->setAttribute('name', $suite->getName()); 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->testSuiteSkipped[$this->testSuiteLevel] = 0; $this->testSuiteUseless[$this->testSuiteLevel] = 0; $this->testSuiteTimes[$this->testSuiteLevel] = 0; } public function afterSuite(SuiteEvent $event): void { $this->testSuites[$this->testSuiteLevel]->setAttribute( 'tests', (string)$this->testSuiteTests[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'assertions', (string)$this->testSuiteAssertions[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'errors', (string)$this->testSuiteErrors[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'failures', (string)$this->testSuiteFailures[$this->testSuiteLevel] ); $this->testSuites[$this->testSuiteLevel]->setAttribute( 'skipped', (string)$this->testSuiteSkipped[$this->testSuiteLevel] ); if (!$this->isStrict) { $this->testSuites[$this->testSuiteLevel]->setAttribute( 'useless', (string)$this->testSuiteUseless[$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->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel]; $this->testSuiteUseless[$this->testSuiteLevel - 1] += $this->testSuiteUseless[$this->testSuiteLevel]; $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; } $this->testSuiteLevel--; } public function startTest(TestEvent $event): void { $test = $event->getTest(); $this->currentTestCase = $this->document->createElement('testcase'); foreach ($test->getReportFields() as $attr => $value) { if ($this->isStrict and !in_array($attr, $this->strictAttributes)) { continue; } $this->currentTestCase->setAttribute($attr, $value); } } public function endTest(TestEvent $event): void { $test = $event->getTest(); $time = $event->getTime(); $this->currentTestCase->setAttribute( 'time', sprintf('%F', $time) ); $numAssertions = $test->numberOfAssertionsPerformed(); $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; $this->currentTestCase->setAttribute( 'assertions', (string)$numAssertions ); $testOutput = ''; if ($test instanceof TestCaseWrapper) { $testCase = $test->getTestCase(); $phpunitVersion = PHPUnitVersion::series(); if (version_compare($phpunitVersion, '11.0', '>=')) { if (!$testCase->expectsOutput()) { $testOutput = $testCase->output(); } } elseif (version_compare($phpunitVersion, '10.3', '>=')) { if (!$testCase->expectsOutput()) { $testOutput = $testCase->getActualOutputForAssertion(); } } else { if (!$testCase->hasExpectationOnOutput()) { $testOutput = $testCase->getActualOutputForAssertion(); } } } if ($testOutput !== '') { $systemOut = $this->document->createElement( 'system-out', Xml::prepareString($testOutput) ); $this->currentTestCase->appendChild($systemOut); } $this->testSuites[$this->testSuiteLevel]->appendChild( $this->currentTestCase ); $this->testSuiteTests[$this->testSuiteLevel]++; $this->testSuiteTimes[$this->testSuiteLevel] += $time; $this->currentTestCase = null; } /** * @throws InvalidArgumentException * @throws ReflectionException */ public function testError(FailEvent $event): void { $this->doAddFault($event->getTest(), $event->getFail(), 'error'); $this->testSuiteErrors[$this->testSuiteLevel]++; } /** * @throws InvalidArgumentException * @throws ReflectionException */ public function testWarning(FailEvent $event): void { $this->doAddFault($event->getTest(), $event->getFail(), 'warning'); $this->testSuiteFailures[$this->testSuiteLevel]++; } /** * @throws InvalidArgumentException * @throws ReflectionException */ public function testFailure(FailEvent $event): void { $this->doAddFault($event->getTest(), $event->getFail(), 'failure'); $this->testSuiteFailures[$this->testSuiteLevel]++; } public function testSkipped(FailEvent $event): void { if ($this->currentTestCase === null) { return; } $skipped = $this->document->createElement('skipped'); $this->currentTestCase->appendChild($skipped); $this->testSuiteSkipped[$this->testSuiteLevel]++; } public function testUseless(FailEvent $event): void { if ($this->currentTestCase === null) { return; } $error = $this->document->createElement('error', 'Useless Test'); $this->currentTestCase->appendChild($error); $this->testSuiteUseless[$this->testSuiteLevel]++; } /** * Method which generalizes addError() and addFailure() * * @throws InvalidArgumentException * @throws ReflectionException */ private function doAddFault(Test $test, Throwable $t, string $type): void { if ($this->currentTestCase === null) { return; } if ($test instanceof TestCaseWrapper) { $buffer = str_replace(': ', '::test', $test->toString()) . "\n"; } elseif ($test instanceof SelfDescribing) { $buffer = $test->toString() . "\n"; } else { $buffer = ''; } if (PHPUnitVersion::series() < 10) { $exceptionString = TestFailure::exceptionToString($t); } else { $exceptionString = ThrowableToStringMapper::map($t); } $buffer .= $exceptionString . "\n" . StackTraceFilter::getFilteredStacktrace($t); $fault = $this->document->createElement( $type, Xml::prepareString($buffer) ); $fault->setAttribute('type', $t::class); $this->currentTestCase->appendChild($fault); } } */ protected static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::RESULT_PRINT_AFTER => 'afterResult', Events::TEST_SUCCESS => 'testSuccess', Events::TEST_FAIL => 'testFailure', Events::TEST_ERROR => 'testError', Events::TEST_INCOMPLETE => 'testIncomplete', Events::TEST_SKIPPED => 'testSkipped', Events::TEST_USELESS => 'testUseless', Events::TEST_WARNING => 'testWarning', ]; protected int $id = 0; protected string $scenarios = ''; protected string $templatePath; private string $reportFile; private Timer $timer; public function __construct(array $options, private Output $output) { $this->reportFile = $options['html']; if (!codecept_is_path_absolute($this->reportFile)) { $this->reportFile = codecept_output_dir($this->reportFile); } codecept_debug(sprintf("Printing HTML report to %s", $this->reportFile)); $this->templatePath = sprintf( '%s%stemplate%s', __DIR__, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR ); $this->timer = new Timer(); $this->timer->start(); } public function beforeSuite(SuiteEvent $event): void { $suite = $event->getSuite(); if (!$suite->getName()) { return; } $suiteTemplate = new Template( $this->templatePath . 'suite.html' ); $suiteTemplate->setVar(['suite' => ucfirst($suite->getName())]); $this->scenarios .= $suiteTemplate->render(); } public function testSuccess(TestEvent $event): void { $this->printTestEvent($event, 'scenarioSuccess'); } public function testError(FailEvent $event): void { $this->printTestEvent($event, 'scenarioFailed'); } public function testFailure(FailEvent $event): void { $this->printTestEvent($event, 'scenarioFailed'); } public function testWarning(FailEvent $event): void { $this->printTestEvent($event, 'scenarioSuccess'); } public function testSkipped(FailEvent $event): void { $this->printTestEvent($event, 'scenarioSkipped'); } public function testIncomplete(FailEvent $event): void { $this->printTestEvent($event, 'scenarioIncomplete'); } public function testUseless(FailEvent $event): void { $this->printTestEvent($event, 'scenarioUseless'); } public function printTestEvent(TestEvent $event, string $scenarioStatus): void { $failure = ''; $test = $event->getTest(); if ($event instanceof FailEvent) { $failTemplate = new Template( $this->templatePath . 'fail.html' ); $failTemplate->setVar(['fail' => nl2br($event->getFail()->getMessage())]); $failure = $failTemplate->render() . PHP_EOL; } $steps = []; if ($test instanceof ScenarioDriven) { $steps = $test->getScenario()->getSteps(); } $stepsBuffer = ''; $subStepsRendered = []; foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); $subStepsRendered[$key][] = $this->renderStep($step); } } foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); if (! empty($subStepsRendered[$key])) { $subStepsBuffer = implode('', $subStepsRendered[$key]); unset($subStepsRendered[$key]); $stepsBuffer .= $this->renderSubsteps($step->getMetaStep(), $subStepsBuffer); } } else { $stepsBuffer .= $this->renderStep($step); } } $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)), ENT_QUOTES | ENT_SUBSTITUTE); $testString = preg_replace('~^([\s\w\\\]+):\s~', '$1 » ', $testString); $scenarioTemplate = new Template( $this->templatePath . 'scenario.html' ); $scenarioTemplate->setVar( [ 'id' => ++$this->id, 'name' => $testString, 'scenarioStatus' => $scenarioStatus, 'steps' => $stepsBuffer, 'toggle' => $toggle, 'failure' => $failure, 'png' => $png, 'html' => $html, 'time' => round($event->getTime(), 2), ] ); $this->scenarios .= $scenarioTemplate->render(); } public function printTestResult(Test $test, float $time, string $scenarioStatus): void { //keep this method for backwards compatibility, remove in Codeception 6.0 trigger_error( __METHOD__ . ' is deprecated, please use printTestEvent instead', E_USER_DEPRECATED, ); $steps = []; if ($test instanceof ScenarioDriven) { $steps = $test->getScenario()->getSteps(); } $stepsBuffer = ''; $subStepsRendered = []; foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); $subStepsRendered[$key][] = $this->renderStep($step); } } foreach ($steps as $step) { $metaStep = $step->getMetaStep(); if ($metaStep) { $key = $this->getMetaStepKey($metaStep); if (! empty($subStepsRendered[$key])) { $subStepsBuffer = implode('', $subStepsRendered[$key]); unset($subStepsRendered[$key]); $stepsBuffer .= $this->renderSubsteps($step->getMetaStep(), $subStepsBuffer); } } else { $stepsBuffer .= $this->renderStep($step); } } $scenarioTemplate = new Template( $this->templatePath . 'scenario.html' ); $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)), ENT_QUOTES | ENT_SUBSTITUTE); $testString = preg_replace('~^([\s\w\\\]+):\s~', '$1 » ', $testString); $scenarioTemplate->setVar( [ 'id' => ++$this->id, 'name' => $testString, 'scenarioStatus' => $scenarioStatus, 'steps' => $stepsBuffer, 'toggle' => $toggle, 'failure' => '', 'png' => $png, 'html' => $html, 'time' => round($time, 2) ] ); $this->scenarios .= $scenarioTemplate->render(); } private function getMetaStepKey(Meta $metaStep): string { $key = ''; $filePath = $metaStep->getFilePath(); if ($filePath !== null) { $key = $filePath; $lineNumber = $metaStep->getLineNumber(); if ($lineNumber !== null) { $key .= ':' . $lineNumber; } } return $key . $metaStep->getAction(); } protected function renderStep(Step $step): string { $stepTemplate = new Template($this->templatePath . 'step.html'); $stepTemplate->setVar(['action' => $step->getHtml(), 'error' => $step->hasFailed() ? 'failedStep' : '']); return $stepTemplate->render(); } protected function renderSubsteps(Meta $metaStep, string $substepsBuffer): string { $metaTemplate = new Template($this->templatePath . 'substeps.html'); $metaTemplate->setVar(['metaStep' => $metaStep->getHtml(), 'error' => $metaStep->hasFailed() ? 'failedStep' : '', 'steps' => $substepsBuffer, 'id' => uniqid()]); return $metaTemplate->render(); } public function afterResult(PrintResultEvent $event): void { $timeTaken = $this->timer->stop()->asString(); $result = $event->getResult(); $scenarioHeaderTemplate = new Template( $this->templatePath . 'scenario_header.html' ); $status = $result->wasSuccessfulIgnoringWarnings() ? 'OK' : 'FAILED'; $scenarioHeaderTemplate->setVar( [ 'name' => 'Codeception Results', 'status' => $status, 'time' => $timeTaken ] ); $header = $scenarioHeaderTemplate->render(); $scenariosTemplate = new Template( $this->templatePath . 'scenarios.html' ); $scenariosTemplate->setVar( [ 'header' => $header, 'scenarios' => $this->scenarios, 'successfulScenarios' => $result->successfulCount(), 'failedScenarios' => $result->failureCount(), 'skippedScenarios' => $result->skippedCount(), 'incompleteScenarios' => $result->incompleteCount(), 'uselessScenarios' => $result->uselessCount(), ] ); file_put_contents($this->reportFile, $scenariosTemplate->render()); $this->output->message( "- HTML report generated in file://%s", $this->reportFile )->writeln(); } } scenario; } /** * This method is used by Cept format to add description to test output * * It can be used by Cest format too. * It doesn't do anything when called, but it is parsed by Parser before execution * * @see \Codeception\Lib\Parser::parseFeature */ public function wantTo(string $text): void { } public function wantToTest(string $text): void { } public function __call(string $method, array $arguments) { $class = $this::class; throw new RuntimeException("Call to undefined method {$class}::{$method}"); } /** * Lazy-execution given anonymous function */ public function execute(Closure $callable): self { $this->scenario->addStep(new Executor($callable, [])); $callable(); return $this; } } settings = $settings; $this->dispatcher = $dispatcher; $this->di = new Di(); $this->groupManager = new GroupManager($settings['groups']); $this->moduleContainer = new ModuleContainer($this->di, $settings); $modules = Configuration::modules($this->settings); foreach ($modules as $moduleName) { $this->moduleContainer->create($moduleName); } $this->moduleContainer->validateConflicts(); if (isset($settings['current_environment'])) { $this->env = $settings['current_environment']; } $this->testFilter = new Filter( $options['groups'] ?? null, $options['excludeGroups'] ?? null, $options['filter'] ?? null, ); $this->suite = $this->createSuite($name); } public function initialize(): void { $this->dispatcher->dispatch(new Event\SuiteEvent($this->suite, $this->settings), Events::MODULE_INIT); foreach ($this->moduleContainer->all() as $module) { $module->_initialize(); } if ($this->settings['actor'] && !file_exists(Configuration::supportDir() . $this->settings['actor'] . '.php')) { throw new Exception\ConfigurationException( $this->settings['actor'] . " class doesn't exist in suite folder.\nRun the 'build' command to generate it" ); } $this->dispatcher->dispatch(new Event\SuiteEvent($this->suite, $this->settings), Events::SUITE_INIT); ini_set('xdebug.show_exception_trace', '0'); // Issue https://github.com/symfony/symfony/issues/7646 } public function loadTests(string $path = null): void { $testLoader = new Loader($this->settings); $testLoader->loadTests($path); $tests = $testLoader->getTests(); if ($this->settings['shuffle']) { shuffle($tests); } foreach ($tests as $test) { $this->addToSuite($test); } $this->suite->reorderDependencies(); } protected function addToSuite(Test $test): void { if (!$this->testFilter->isNameAccepted($test)) { return; } $this->configureTest($test); $this->checkEnvironmentExists($test); if (!$this->isExecutedInCurrentEnvironment($test)) { return; // skip tests from other environments } $groups = $this->groupManager->groupsForTest($test); if (!$this->testFilter->isGroupAccepted($test, $groups)) { return; } $this->suite->addTest($test); if (!empty($groups) && $test instanceof TestInterface) { $test->getMetadata()->setGroups($groups); } } protected function createSuite(string $name): Suite { if ($this->settings['namespace']) { $name = $this->settings['namespace'] . '.' . $name; } $suite = new Suite($this->dispatcher, $name); $suite->setBaseName(preg_replace('#\s.+$#', '', $name)); // replace everything after space (env name) $suite->setModules($this->moduleContainer->all()); $suite->reportUselessTests((bool)($this->settings['report_useless_tests'] ?? false)); $suite->backupGlobals((bool)($this->settings['backup_globals'] ?? false)); $suite->beStrictAboutChangesToGlobalState((bool)($this->settings['be_strict_about_changes_to_global_state'] ?? false)); $suite->disallowTestOutput((bool)($this->settings['disallow_test_output'] ?? false)); if (PHPUnitVersion::series() >= 10) { $suite->initPHPUnitConfiguration(); } return $suite; } public function run(ResultAggregator $resultAggregator): void { $this->dispatcher->dispatch(new Event\SuiteEvent($this->suite, $this->settings), Events::SUITE_BEFORE); try { unset($GLOBALS['app']); // hook for not to serialize globals $this->suite->run($resultAggregator); } finally { $this->dispatcher->dispatch(new Event\SuiteEvent($this->suite, $this->settings), Events::SUITE_AFTER); } } public function getSuite(): Suite { return $this->suite; } public function getModuleContainer(): ModuleContainer { return $this->moduleContainer; } protected function checkEnvironmentExists(TestInterface $test): void { $envs = $test->getMetadata()->getEnv(); if (empty($envs)) { return; } if (!isset($this->settings['env'])) { Notification::warning("Environments are not configured", Descriptor::getTestFullName($test)); return; } $listedEnvironments = explode(',', implode(',', $envs)); foreach ($listedEnvironments as $env) { if (!array_key_exists($env, $this->settings['env'])) { Notification::warning("Environment {$env} was not configured but used in test", Descriptor::getTestFullName($test)); } } } protected function isExecutedInCurrentEnvironment(TestInterface $test): bool { $envs = $test->getMetadata()->getEnv(); if (empty($envs)) { return true; } $currentEnvironments = explode(',', $this->env); foreach ($envs as $envList) { $envList = explode(',', $envList); if (count($envList) == count(array_intersect($currentEnvironments, $envList))) { return true; } } return false; } protected function configureTest(TestInterface $test): void { $di = clone($this->di); $test->getMetadata()->setServices([ 'di' => $di, 'dispatcher' => $this->dispatcher, 'modules' => $this->moduleContainer ]); $test->getMetadata()->setCurrent([ 'actor' => $this->getActorClassName(), 'env' => $this->env, 'modules' => $this->moduleContainer->all() ]); if ($test instanceof TestCaseWrapper) { $di->set(new Scenario($test)); $testCase = $test->getTestCase(); if ($testCase instanceof Unit) { $testCase->setMetadata($test->getMetadata()); } } if ($test instanceof ScenarioDriven) { $test->preload(); } } } metadata = $test->getMetadata(); $this->test = $test; } public function setFeature(string $feature): void { $this->metadata->setFeature($feature); } public function getFeature(): string { return $this->metadata->getFeature(); } public function getGroups(): array { return $this->metadata->getGroups(); } public function current(?string $key) { return $this->metadata->getCurrent($key); } /** * @throws InjectionException */ public function runStep(Step $step): mixed { $step->saveTrace(); if ($this->metaStep instanceof Meta) { $step->setMetaStep($this->metaStep); } $this->steps[] = $step; $result = null; $dispatcher = $this->metadata->getService('dispatcher'); $dispatcher->dispatch(new StepEvent($this->test, $step), Events::STEP_BEFORE); try { $result = $step->run($this->metadata->getService('modules')); } catch (ConditionalAssertionFailed $f) { $testResult = $this->test->getResultAggregator(); $failEvent = new FailEvent(clone($this->test), $f, 0); $testResult->addFailure($failEvent); } finally { $dispatcher->dispatch(new StepEvent($this->test, $step), Events::STEP_AFTER); } $step->executed = true; return $result; } public function addStep(Step $step): void { $this->steps[] = $step; } /** * Returns the steps of this scenario. * * @return Step[] */ public function getSteps(): array { return $this->steps; } public function getHtml(): string { $text = ''; foreach ($this->getSteps() as $step) { if ($step->getName() !== 'Comment') { $text .= $step->getHtml() . '
'; } else { $text .= trim($step->getHumanizedArguments(), '"') . '
'; } } $text = str_replace(['"\'', '\'"'], ["'", "'"], $text); return "

" . mb_strtoupper('I want to ' . $this->getFeature(), 'utf-8') . "

" . $text; } public function getText(): string { $text = ''; foreach ($this->getSteps() as $step) { $text .= $step->getPrefix() . "{$step} \r\n"; } $text = trim(str_replace(['"\'', '\'"'], ["'", "'"], $text)); return mb_strtoupper('I want to ' . $this->getFeature(), 'utf-8') . "\r\n\r\n" . $text . "\r\n\r\n"; } public function comment(string $comment): void { $this->runStep(new Comment($comment, [])); } public function skip(string $message = ''): void { if (PHPUnitVersion::series() < 10) { throw new SkippedTestError($message); } throw new SkippedWithMessageException($message); } public function incomplete(string $message = ''): void { throw new IncompleteTestError($message); } public function setMetaStep(?Meta $metaStep): void { $this->metaStep = $metaStep; } public function getMetaStep(): ?Meta { return $this->metaStep; } } '_before', Events::TEST_AFTER . '.' . static::$group => '_after', ]; } return array_merge($events, $inheritedEvents); } } readCustomCommandsFromConfig(); } catch (ConfigurationException $e) { if ($e->getCode() === 404) { return; } $this->renderExceptionWrapper($e, new ConsoleOutput()); exit(1); } catch (Exception $e) { $this->renderExceptionWrapper($e, new ConsoleOutput()); exit(1); } } public function renderExceptionWrapper(Exception $exception, OutputInterface $output): void { if (method_exists(BaseApplication::class, 'renderException')) { //Symfony 5 parent::renderException($exception, $output); } else { parent::renderThrowable($exception, $output); } } /** * Search custom commands and register them. * * @throws ConfigurationException */ protected function readCustomCommandsFromConfig(): void { $this->getCoreArguments(); // Maybe load outside config file $config = Configuration::config(); if (empty($config['extensions']['commands'])) { return; } foreach ($config['extensions']['commands'] as $commandClass) { $commandName = $this->getCustomCommandName($commandClass); $this->add(new $commandClass($commandName)); } } /** * Validate and get the name of the command * * @param class-string $commandClass A class that implement the `\Codeception\CustomCommandInterface`. * @throws ConfigurationException */ protected function getCustomCommandName(string $commandClass): string { if (!class_exists($commandClass)) { throw new ConfigurationException("Extension: Command class {$commandClass} not found"); } $interfaces = class_implements($commandClass); if (!in_array(CustomCommandInterface::class, $interfaces)) { throw new ConfigurationException("Extension: Command {$commandClass} must implement " . "the interface `Codeception\\CustomCommandInterface`"); } return $commandClass::getCommandName(); } /** * To cache Class ArgvInput * * @inheritDoc */ public function run(InputInterface $input = null, OutputInterface $output = null): int { if ($input === null) { $input = $this->getCoreArguments(); } if (!ini_get('register_argc_argv')) { throw new ConfigurationException('register_argc_argv must be set to On for running Codeception'); } return parent::run($input, $output); } /** * Add global a --config option. */ protected function getDefaultInputDefinition(): InputDefinition { $inputDefinition = parent::getDefaultInputDefinition(); $inputDefinition->addOption( new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config') ); return $inputDefinition; } /** * Search for --config Option and if found will be loaded * * example: * -c file.yml|dir * -cfile.yml|dir * --config file.yml|dir * --config=file.yml|dir */ protected function getCoreArguments(): SymfonyArgvInput { if ($this->coreArguments !== null) { return $this->coreArguments; } $argvWithoutConfig = []; if (isset($_SERVER['argv'])) { $argv = $_SERVER['argv']; for ($i = 0; $i < count($argv); ++$i) { if (preg_match('#^(?:-([^c-]*)?c|--config(?:=|$))(.*)$#', $argv[$i], $match)) { if (!empty($match[2])) { //same index $this->preloadConfiguration($match[2]); } elseif (isset($argv[$i + 1])) { //next index $this->preloadConfiguration($argv[++$i]); } if (!empty($match[1])) { $argvWithoutConfig[] = "-" . $match[1]; //rest commands } continue; } $argvWithoutConfig[] = $argv[$i]; } } return $this->coreArguments = new SymfonyArgvInput($argvWithoutConfig); } /** * Pre load Configuration, the config option is use. * * @param string $configFile Path to Configuration * @throws ConfigurationException */ protected function preloadConfiguration(string $configFile): void { try { Configuration::config($configFile); } catch (ConfigurationException $e) { if ($e->getCode() == 404) { throw new ConfigurationException("Your configuration file `{$configFile}` could not be found.", 405); } throw $e; } } } |null Current configuration */ protected static ?array $config = null; /** * @var array environmental files configuration cache */ protected static array $envConfig = []; /** * @var string|null Directory containing main configuration file. * @see self::projectDir() */ protected static ?string $dir = null; /** * @var string|null Directory of a base configuration file for the project with includes. * @see self::projectDir() */ protected static ?string $baseDir = null; /** * @var string Current project output directory. */ protected static ?string $outputDir = null; /** * @var string|null Current project data directory. This directory is used to hold * sql dumps and other things needed for current project tests. */ protected static ?string $dataDir = null; /** * @var string|null Directory with test support files like Actors, Helpers, PageObjects, etc */ protected static ?string $supportDir = null; /** * @var string|null Directory containing environment configuration files. */ protected static ?string $envsDir = null; /** * @var string|null Directory containing tests and suites of the current project. */ protected static ?string $testsDir = null; public static bool $lock = false; /** * @var array */ public static array $defaultConfig = [ 'actor_suffix' => 'Tester', 'support_namespace' => null, 'namespace' => '', 'include' => [], 'paths' => [], 'extends' => null, 'suites' => [], 'modules' => [], 'extensions' => [ 'enabled' => [], 'config' => [], 'commands' => [], ], 'groups' => [], 'bootstrap' => false, 'settings' => [ 'colors' => true, 'bootstrap' => false, 'strict_xml' => false, 'lint' => true, 'backup_globals' => true, 'report_useless_tests' => false, 'be_strict_about_changes_to_global_state' => false, 'shuffle' => false, ], 'coverage' => [], 'params' => [], 'gherkin' => [] ]; /** * @var array */ public static array $defaultSuiteSettings = [ 'actor' => null, 'modules' => [ 'enabled' => [], 'config' => [], 'depends' => [] ], 'step_decorators' => ConditionalAssertion::class, 'path' => null, 'extends' => null, 'namespace' => null, 'groups' => [], 'formats' => [], 'shuffle' => false, 'extensions' => [ // suite extensions 'enabled' => [], 'config' => [], ], 'error_level' => 'E_ALL & ~E_STRICT & ~E_DEPRECATED', 'convert_deprecations_to_exceptions' => false, ]; /** * @var array|null */ protected static ?array $params = null; /** * Loads global config file which is `codeception.yml` by default. * When config is already loaded - returns it. * * @return array * @throws ConfigurationException */ public static function config(string $configFile = null): array { if (!$configFile && self::$config) { return self::$config; } if (self::$config && self::$lock) { return self::$config; } if ($configFile === null) { $configFile = getcwd() . DIRECTORY_SEPARATOR . 'codeception.yml'; } if (is_dir($configFile)) { $configFile = $configFile . DIRECTORY_SEPARATOR . 'codeception.yml'; } $dir = realpath(dirname($configFile)); if ($dir !== false) { self::$dir = $dir; $configDistFile = $dir . DIRECTORY_SEPARATOR . 'codeception.dist.yml'; // set the one default base directory for included setup if (!self::$baseDir) { self::$baseDir = $dir; } } if (!file_exists($configFile) && (!isset($configDistFile) || !file_exists($configDistFile))) { throw new ConfigurationException("Configuration file could not be found.\nRun `bootstrap` to initialize Codeception.", 404); } // Preload config to retrieve params such that they are applied to codeception config file below $tempConfig = self::$defaultConfig; $distConfigContents = ''; if (isset($configDistFile) && file_exists($configDistFile)) { $distConfigContents = file_get_contents($configDistFile); if ($distConfigContents === false) { throw new ConfigurationException("Failed to read {$configDistFile}"); } $tempConfig = self::mergeConfigs($tempConfig, self::getConfFromContents($distConfigContents, $configDistFile)); } $configContents = ''; if (file_exists($configFile)) { $configContents = file_get_contents($configFile); if ($configContents === false) { throw new ConfigurationException("Failed to read {$configFile}"); } $tempConfig = self::mergeConfigs($tempConfig, self::getConfFromContents($configContents, $configFile)); } self::prepareParams($tempConfig); // load config using params $config = self::$defaultConfig; if (isset($configDistFile) && $distConfigContents !== '') { $config = self::mergeConfigs(self::$defaultConfig, self::getConfFromContents($distConfigContents, $configDistFile)); } if ($configContents !== '') { $config = self::mergeConfigs($config, self::getConfFromContents($configContents, $configFile)); } if ($config == self::$defaultConfig) { throw new ConfigurationException("Configuration file is invalid"); } // we check for the "extends" key in the yml file if (isset($config['extends'])) { // and now we search for the file $presetFilePath = codecept_absolute_path($config['extends']); if (file_exists($presetFilePath)) { // and merge it with our configuration file $config = self::mergeConfigs(self::getConfFromFile($presetFilePath), $config); } } self::$config = $config; if (!isset($config['paths']['support']) && isset($config['paths']['helpers'])) { $config['paths']['support'] = $config['paths']['helpers']; } if (!isset($config['paths']['output'])) { throw new ConfigurationException('Output path is not defined by key "paths: output"'); } self::$outputDir = $config['paths']['output']; // fill up includes with wildcard expansions $config['include'] = self::expandWildcardedIncludes($config['include']); // config without tests, for inclusion of other configs if (!empty($config['include'])) { self::$config = $config; if (!isset($config['paths']['tests'])) { return $config; } } if (!isset($config['paths']['tests'])) { throw new ConfigurationException( 'Tests directory is not defined in Codeception config by key "paths: tests:"' ); } if (!isset($config['paths']['data'])) { throw new ConfigurationException('Data path is not defined Codeception config by key "paths: data"'); } if (!isset($config['paths']['support'])) { throw new ConfigurationException('Helpers path is not defined by key "paths: support"'); } self::$dataDir = $config['paths']['data']; self::$supportDir = $config['paths']['support']; self::$testsDir = $config['paths']['tests']; if (isset($config['paths']['envs'])) { self::$envsDir = $config['paths']['envs']; } Autoload::addNamespace(self::$config['namespace'] . '\\' . self::$config['support_namespace'], self::supportDir()); self::loadBootstrap($config['bootstrap'], self::testsDir()); self::loadSuites(); return $config; } /** * @throws ConfigurationException */ public static function loadBootstrap(string|false $bootstrap, string $path): void { if (!$bootstrap) { return; } $bootstrap = PathResolver::isPathAbsolute($bootstrap) ? $bootstrap : rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $bootstrap; if (!file_exists($bootstrap)) { throw new ConfigurationException("Bootstrap file {$bootstrap} can't be loaded"); } require_once $bootstrap; } protected static function loadSuites(): void { $suites = Finder::create() ->files() ->name('*.{suite,suite.dist}.yml') ->in(self::$dir . DIRECTORY_SEPARATOR . self::$testsDir) ->depth('< 1') ->sortByName(); self::$suites = []; foreach (array_keys(self::$config['suites']) as $suite) { self::$suites[$suite] = $suite; } /** @var SplFileInfo $suite */ foreach ($suites as $suite) { preg_match('#(.*?)(\.suite|\.suite\.dist)\.yml#', $suite->getFilename(), $matches); self::$suites[$matches[1]] = $matches[1]; } } /** * Returns suite configuration. Requires suite name and global config used (Configuration::config) * * @param array $config * @return array * @throws Exception */ public static function suiteSettings(string $suite, array $config): array { // cut namespace name from suite name if ($suite != $config['namespace'] && str_starts_with($suite, $config['namespace'])) { $suite = ltrim(substr($suite, strlen($config['namespace'])), '.'); } if (!in_array($suite, self::$suites)) { throw new ConfigurationException("Suite {$suite} was not loaded"); } // load global config $globalConf = $config['settings']; foreach (['modules', 'coverage', 'support_namespace', 'namespace', 'groups', 'env', 'gherkin', 'extensions'] as $key) { if (isset($config[$key])) { $globalConf[$key] = $config[$key]; } } $settings = self::mergeConfigs(self::$defaultSuiteSettings, $globalConf); // load suite config $settings = self::loadSuiteConfig($suite, $config['paths']['tests'], $settings); // load from environment configs if (isset($config['paths']['envs'])) { $envConf = self::loadEnvConfigs(self::$dir . DIRECTORY_SEPARATOR . $config['paths']['envs']); $settings = self::mergeConfigs($settings, $envConf); } if (!$settings['path']) { // take a suite path from its name $settings['path'] = $suite; } $config['paths']['tests'] = str_replace('/', DIRECTORY_SEPARATOR, $config['paths']['tests']); $settings['path'] = self::$dir . DIRECTORY_SEPARATOR . $config['paths']['tests'] . DIRECTORY_SEPARATOR . $settings['path'] . DIRECTORY_SEPARATOR; $settings['suite'] = $suite; $settings['suite_namespace'] = $settings['namespace'] . '\\' . $suite; return $settings; } /** * Loads environments configuration from set directory * * @param string $path Path to the directory * @return array */ protected static function loadEnvConfigs(string $path): array { if (isset(self::$envConfig[$path])) { return self::$envConfig[$path]; } if (!is_dir($path)) { self::$envConfig[$path] = []; return self::$envConfig[$path]; } $envFiles = Finder::create() ->files() ->name('*.yml') ->in($path) ->depth('< 2'); $envConfig = []; /** @var SplFileInfo $envFile */ foreach ($envFiles as $envFile) { $env = str_replace(['.dist.yml', '.yml'], '', $envFile->getFilename()); $envConfig[$env] = []; $envPath = $path; if ($envFile->getRelativePath() !== '') { $envPath .= DIRECTORY_SEPARATOR . $envFile->getRelativePath(); } foreach (['.dist.yml', '.yml'] as $suffix) { $envConf = self::getConfFromFile($envPath . DIRECTORY_SEPARATOR . $env . $suffix, []); $envConfig[$env] = self::mergeConfigs($envConfig[$env], $envConf); } } self::$envConfig[$path] = ['env' => $envConfig]; return self::$envConfig[$path]; } /** * Loads configuration from Yaml data * * @param string $contents Yaml config file contents * @param string $filename which is supposed to be loaded * @return array * @throws ConfigurationException */ protected static function getConfFromContents(string $contents, string $filename = '(.yml)'): array { if (self::$params) { // replace '%var%' with encoded value $singleQuoteTemplate = new Template($contents, "'%", "%'", 'json_encode'); $singleQuoteTemplate->setVars(self::$params); $contents = $singleQuoteTemplate->produce(); // replace "%var%" with encoded value $doubleQuoteTemplate = new Template($contents, '"%', '%"', 'json_encode'); $doubleQuoteTemplate->setVars(self::$params); $contents = $doubleQuoteTemplate->produce(); // replace %var% with string value as is $plainTemplate = new Template($contents, '%', '%'); $plainTemplate->setVars(self::$params); $contents = $plainTemplate->produce(); } try { $conf = Yaml::parse($contents); } catch (ParseException $exception) { throw new ConfigurationException( sprintf( "Error loading Yaml config from `%s`\n \n%s\nRead more about Yaml format https://goo.gl/9UPuEC", $filename, $exception->getMessage() ) ); } if ($conf === null) { throw new ConfigurationException("Configuration file {$filename} is empty."); } if (!is_array($conf)) { throw new ConfigurationException("Configuration file {$filename} is invalid."); } return $conf; } /** * Loads configuration from Yaml file or returns given value if the file doesn't exist * * @param array $nonExistentValue Value used if filename is not found * @return array * @throws ConfigurationException */ protected static function getConfFromFile(string $filename, array $nonExistentValue = []): array { if (file_exists($filename)) { $yaml = file_get_contents($filename); if ($yaml === false) { throw new ConfigurationException("Failed to read {$filename}"); } return self::getConfFromContents($yaml, $filename); } return $nonExistentValue; } /** * @return string[] */ public static function suites(): array { return self::$suites; } /** * Return list of enabled modules according suite config. * * @param array $settings Suite settings * @return string[] */ public static function modules(array $settings): array { return array_filter( array_map( fn ($m) => is_array($m) ? key($m) : $m, $settings['modules']['enabled'], array_keys($settings['modules']['enabled']) ), function ($m) use ($settings): bool { if (!isset($settings['modules']['disabled'])) { return true; } return !in_array($m, $settings['modules']['disabled']); } ); } public static function isExtensionEnabled(string $extensionName): bool { return isset(self::$config['extensions']['enabled']) && in_array($extensionName, self::$config['extensions']['enabled']); } /** * Returns current path to `_data` dir. * Use it to store database fixtures, sql dumps, or other files required by your tests. */ public static function dataDir(): string { return self::$dir . DIRECTORY_SEPARATOR . self::$dataDir . DIRECTORY_SEPARATOR; } /** * Return current path to `_helpers` dir. * Helpers are custom modules. */ public static function supportDir(): string { return self::$dir . DIRECTORY_SEPARATOR . self::$supportDir . DIRECTORY_SEPARATOR; } /** * Returns actual path to current `_output` dir. * Use it in Helpers or Groups to save result or temporary files. * * @throws ConfigurationException */ public static function outputDir(): string { if (self::$outputDir === '') { throw new ConfigurationException("Path for output not specified. Please, set output path in global config"); } $dir = self::$outputDir . DIRECTORY_SEPARATOR; if (!codecept_is_path_absolute($dir)) { $dir = self::$dir . DIRECTORY_SEPARATOR . $dir; } if (!file_exists($dir)) { @mkdir($dir, 0777, true); } if (!is_writable($dir)) { @chmod($dir, 0777); } if (!is_writable($dir)) { throw new ConfigurationException( "Path for output is not writable. Please, set appropriate access mode for output path: {$dir}" ); } return $dir; } /** * Returns path to the root of your project. * Basically returns path to current `codeception.yml` loaded. * Use this method instead of `__DIR__`, `getcwd()` or anything else. */ public static function projectDir(): string { return self::$dir . DIRECTORY_SEPARATOR; } /** * Returns path to the base dir for config which consists with included setup * Returns path to `codeception.yml` which was executed. * If config doesn't have "include" section the result is the same as `projectDir()` */ public static function baseDir(): string { return self::$baseDir . DIRECTORY_SEPARATOR; } /** * Returns path to tests directory */ public static function testsDir(): string { return self::$dir . DIRECTORY_SEPARATOR . self::$testsDir . DIRECTORY_SEPARATOR; } /** * Return current path to `_envs` dir. * Use it to store environment specific configuration. */ public static function envsDir(): string { if (self::$envsDir === '') { return ''; } return self::$dir . DIRECTORY_SEPARATOR . self::$envsDir . DIRECTORY_SEPARATOR; } /** * Is this a meta-configuration file that just points to other `codeception.yml`? * If so, it may have no tests by itself. */ public static function isEmpty(): bool { return !(bool)self::$testsDir; } /** * Adds parameters to config * @param array $config * @return array */ public static function append(array $config = []): array { self::$config = self::mergeConfigs(self::$config ?? [], $config); if (isset(self::$config['paths']['output'])) { self::$outputDir = self::$config['paths']['output']; } if (isset(self::$config['paths']['data'])) { self::$dataDir = self::$config['paths']['data']; } if (isset(self::$config['paths']['support'])) { self::$supportDir = self::$config['paths']['support']; } if (isset(self::$config['paths']['tests'])) { self::$testsDir = self::$config['paths']['tests']; } return self::$config; } /** * @param array $a1 * @param array $a2 * @return array */ public static function mergeConfigs(array $a1, array $a2): array { // for sequential arrays if (isset($a1[0], $a2[0])) { return array_values(array_unique(array_merge_recursive($a2, $a1), SORT_REGULAR)); } // for associative arrays $res = []; foreach ($a2 as $k2 => $v2) { if (!isset($a1[$k2]) || !is_array($a1[$k2])) { // if no such key $res[$k2] = $v2; unset($a1[$k2]); continue; } if (is_array($v2)) { $res[$k2] = self::mergeConfigs($a1[$k2], $v2); unset($a1[$k2]); } } foreach ($a1 as $k1 => $v1) { // only single elements here left $res[$k1] = $v1; } return $res; } /** * Loads config from *.dist.suite.yml and *.suite.yml * * @param array $settings * @return array * @throws ConfigurationException */ protected static function loadSuiteConfig(string $suite, string $path, array $settings): array { if (isset(self::$config['suites'][$suite])) { // bundled config return self::mergeConfigs($settings, self::$config['suites'][$suite]); } $suiteDir = self::$dir . DIRECTORY_SEPARATOR . $path; $suiteDistConf = self::getConfFromFile($suiteDir . DIRECTORY_SEPARATOR . "{$suite}.suite.dist.yml", []); $suiteConf = self::getConfFromFile($suiteDir . DIRECTORY_SEPARATOR . "{$suite}.suite.yml", []); // now we check the suite config file, if a extends key is defined if (isset($suiteConf['extends'])) { $presetFilePath = codecept_is_path_absolute($suiteConf['extends']) ? $suiteConf['extends'] // If path is absolute – use it : realpath($suiteDir . DIRECTORY_SEPARATOR . $suiteConf['extends']); // Otherwise try to locate it in the suite dir if ($presetFilePath === false) { throw new ConfigurationException( sprintf("Configuration file %s does not exist", $suiteConf['extends']) ); } if (file_exists($presetFilePath)) { $settings = self::mergeConfigs(self::getConfFromFile($presetFilePath, []), $settings); } } $settings = self::mergeConfigs($settings, $suiteDistConf); return self::mergeConfigs($settings, $suiteConf); } /** * Replaces wildcarded items in include array with real paths. * * @param string[] $includes * @return string[] * @throws ConfigurationException */ protected static function expandWildcardedIncludes(array $includes): array { if (empty($includes)) { return $includes; } $expandedIncludes = []; foreach ($includes as $include) { $expandedIncludes = array_merge($expandedIncludes, self::expandWildcardsFor($include)); } return $expandedIncludes; } /** * Finds config files in given wildcarded include path. * Returns the expanded paths or the original if not a wildcard. * * @return string[] * @throws ConfigurationException */ protected static function expandWildcardsFor(string $include): array { if (1 !== preg_match('#[?.*]#', $include)) { return [$include,]; } try { $configFiles = Finder::create()->files() ->name('/codeception(\.dist\.yml|\.yml)/') ->in(self::$dir . DIRECTORY_SEPARATOR . $include); } catch (InvalidArgumentException) { throw new ConfigurationException( "Configuration file(s) could not be found in \"{$include}\"." ); } $paths = []; foreach ($configFiles as $file) { $paths[] = codecept_relative_path($file->getPath()); } return array_unique($paths); } /** * @param array $settings * @throws ConfigurationException */ private static function prepareParams(array $settings): void { self::$params = []; foreach ($settings['params'] as $paramStorage) { static::$params = array_merge(self::$params, ParamsLoader::load($paramStorage)); } } } setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'Suite name or snapshot name)'), new InputArgument('snapshot', InputArgument::OPTIONAL, 'Name of snapshot'), ]); parent::configure(); } public function getDescription(): string { return 'Generates empty Snapshot class'; } public function execute(InputInterface $input, OutputInterface $output): int { $suite = (string)$input->getArgument('suite'); $class = $input->getArgument('snapshot'); if (!$class) { $class = $suite; $suite = ''; } $conf = $suite ? $this->getSuiteConfig($suite) : $this->getGlobalConfig(); if ($suite) { $suite = DIRECTORY_SEPARATOR . ucfirst($suite); } $path = $this->createDirectoryFor(Configuration::supportDir() . 'Snapshot' . $suite, $class); $filename = $path . $this->getShortClassName($class) . '.php'; $output->writeln($filename); $snapshot = new SnapshotGenerator($conf, ucfirst($suite) . '\\' . $class); $res = $this->createFile($filename, $snapshot->produce()); if (!$res) { $output->writeln("Snapshot {$filename} already exists"); return 1; } $output->writeln("Snapshot was created in {$filename}"); return 0; } } setDefinition([ new InputArgument('env', InputArgument::REQUIRED, 'Environment name'), ]); } public function getDescription(): string { return 'Generates empty environment config'; } public function execute(InputInterface $input, OutputInterface $output): int { $config = $this->getGlobalConfig(); if (Configuration::envsDir() === '') { throw new ConfigurationException( "Path for environments configuration is not set.\n" . "Please specify envs path in your `codeception.yml`\n \n" . "envs: tests/_envs" ); } $relativePath = $config['paths']['envs']; $env = $input->getArgument('env'); $file = "{$env}.yml"; $path = $this->createDirectoryFor($relativePath, $file); $saved = $this->createFile($path . $file, "# `{$env}` environment config goes here"); if ($saved) { $output->writeln("{$env} config was created in {$relativePath}/{$file}"); return 0; } else { $output->writeln("File {$relativePath}/{$file} already exists"); return 1; } } } setDefinition( [ new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'), new InputArgument('test', InputArgument::OPTIONAL, 'tests to be loaded'), ] ); parent::configure(); } public function getDescription(): string { return 'Prints step-by-step scenario-driven test or a feature'; } public function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = $input->getArgument('suite'); $test = $input->getArgument('test'); $config = $this->getGlobalConfig(); ini_set( 'memory_limit', $config['settings']['memory_limit'] ?? '1024M' ); if (!Configuration::isEmpty() && !$test && str_starts_with($suite, (string)$config['paths']['tests'])) { [, $suite, $test] = $this->matchTestFromFilename($suite, $config['paths']['tests']); } $settings = $this->getSuiteConfig($suite); $eventDispatcher = new EventDispatcher(); $eventDispatcher->addSubscriber(new ConsolePrinter([ 'colors' => (!$input->hasParameterOption('--no-ansi') xor $input->hasParameterOption('ansi')), 'steps' => true, 'verbosity' => OutputInterface::VERBOSITY_VERBOSE, ])); $eventDispatcher->addSubscriber(new BootstrapLoader()); $suiteManager = new SuiteManager($eventDispatcher, $suite, $settings, []); $moduleContainer = $suiteManager->getModuleContainer(); foreach (Configuration::modules($settings) as $module) { $this->mockModule($module, $moduleContainer); } $suiteManager->loadTests($test); $tests = $suiteManager->getSuite()->getTests(); $eventDispatcher->dispatch(new SuiteEvent($suiteManager->getSuite(), $settings), Events::SUITE_INIT); $eventDispatcher->dispatch(new SuiteEvent($suiteManager->getSuite(), $settings), Events::SUITE_BEFORE); foreach ($tests as $test) { if ($test instanceof Test && $test instanceof ScenarioDriven) { $this->dryRunTest($output, $eventDispatcher, $test); } } $eventDispatcher->dispatch(new SuiteEvent($suiteManager->getSuite()), Events::SUITE_AFTER); return 0; } protected function matchTestFromFilename($filename, $testsPath) { $filename = str_replace(['//', '\/', '\\'], '/', $filename); $res = preg_match("#^{$testsPath}/(.*?)/(.*)$#", $filename, $matches); if (!$res) { throw new InvalidArgumentException("Test file can't be matched"); } return $matches; } protected function dryRunTest(OutputInterface $output, EventDispatcher $eventDispatcher, Test $test): void { $eventDispatcher->dispatch(new TestEvent($test), Events::TEST_START); $eventDispatcher->dispatch(new TestEvent($test), Events::TEST_BEFORE); try { $test->test(); } catch (Exception) { } $eventDispatcher->dispatch(new TestEvent($test), Events::TEST_AFTER); $eventDispatcher->dispatch(new TestEvent($test), Events::TEST_END); if ($test->getMetadata()->isBlocked()) { $output->writeln(''); if ($skip = $test->getMetadata()->getSkip()) { $output->writeln(" SKIPPED " . $skip); } if ($incomplete = $test->getMetadata()->getIncomplete()) { $output->writeln(" INCOMPLETE " . $incomplete); } } $output->writeln(''); } private function mockModule(string $moduleName, ModuleContainer $moduleContainer): void { $module = $moduleContainer->getModule($moduleName); $class = new \ReflectionClass($module); $methodResults = []; foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { if ($method->isConstructor()) { continue; } $methodResults[$method->getName()] = $this->getDefaultResultForMethod($class, $method); } $moduleContainer->mock($moduleName, Stub::makeEmpty($module, $methodResults)); } private function getDefaultResultForMethod(\ReflectionClass $class, ReflectionMethod $method): mixed { $returnType = $method->getReturnType(); if ($returnType === null || $returnType->allowsNull()) { return null; } if ($returnType instanceof ReflectionUnionType) { return $this->getDefaultValueOfUnionType($returnType); } if ($returnType instanceof ReflectionIntersectionType) { return $this->returnDefaultValueForIntersectionType($returnType); } if ($returnType->isBuiltin()) { return $this->getDefaultValueForBuiltinType($returnType); } $typeName = Actions::stringifyNamedType($returnType, $class); return Stub::makeEmpty($typeName); } private function getDefaultValueForBuiltinType(ReflectionNamedType $returnType): mixed { return match ($returnType->getName()) { 'mixed', 'void' => null, 'string' => '', 'int' => 0, 'float' => 0.0, 'bool' => false, 'array' => [], 'resource' => fopen('data://text/plain;base64,', 'r'), default => throw new Exception('Unsupported return type ' . $returnType->getName()), }; } private function getDefaultValueOfUnionType(ReflectionUnionType $returnType): mixed { $unionTypes = $returnType->getTypes(); foreach ($unionTypes as $type) { if ($type->isBuiltin()) { return $this->getDefaultValueForBuiltinType($type); } } return Stub::makeEmpty($unionTypes[0]); } private function returnDefaultValueForIntersectionType(ReflectionIntersectionType $returnType): mixed { $extends = null; $implements = []; foreach ($returnType->getTypes() as $type) { if (class_exists($type->getName())) { $extends = $type; } else { $implements [] = $type; } } $className = uniqid('anonymous_class_'); $code = "abstract class $className"; if ($extends !== null) { $code .= " extends \\$extends"; } if (count($implements) > 0) { $code .= ' implements ' . implode(', ', $implements); } $code .= ' {}'; eval($code); return Stub::makeEmpty($className); } } setDefinition( [ new InputArgument('path', InputArgument::OPTIONAL, 'custom installation dir', null), new InputOption( 'namespace', 's', InputOption::VALUE_OPTIONAL, 'Namespace to add for actor classes and helpers' ), new InputOption('actor', 'a', InputOption::VALUE_OPTIONAL, 'Custom actor instead of Tester'), new InputOption('empty', 'e', InputOption::VALUE_NONE, "Don't create standard suites") ] ); } public function getDescription(): string { return "Creates default test suites and generates all required files"; } public function execute(InputInterface $input, OutputInterface $output): int { $bootstrap = new BootstrapTemplate($input, $output); if ($input->getArgument('path')) { $bootstrap->initDir($input->getArgument('path')); } $bootstrap->setup(); return 0; } } setDefinition( [ new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'), new InputArgument('test', InputArgument::OPTIONAL, 'test to be scanned'), new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), ] ); parent::configure(); } public function getDescription(): string { return 'Fetches empty steps from feature files of suite and prints code snippets for them'; } public function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = $input->getArgument('suite'); $test = $input->getArgument('test'); $config = $this->getSuiteConfig($suite); $generator = new GherkinSnippetsGenerator($config, $test); $snippets = $generator->getSnippets(); if (empty($snippets)) { $output->writeln(" All Gherkin steps are defined. Exiting... "); return 0; } $output->writeln(" Snippets found in: "); $features = $generator->getFeatures(); foreach ($features as $feature) { $output->writeln(" - {$feature} "); } $output->writeln(" Generated Snippets: "); $output->writeln(" ----------------------------------------- "); foreach ($snippets as $snippet) { $output->writeln($snippet); } $output->writeln(" ----------------------------------------- "); $output->writeln(sprintf(' %d snippets proposed', count($snippets))); $output->writeln(" Copy generated snippets to {$config['actor']} or a specific Gherkin context "); return 0; } } setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'Either suite name or page object name)'), new InputArgument('page', InputArgument::OPTIONAL, 'Page name of pageobject to represent'), ]); parent::configure(); } public function getDescription(): string { return 'Generates empty PageObject class'; } public function execute(InputInterface $input, OutputInterface $output): int { $suite = (string)$input->getArgument('suite'); $class = $input->getArgument('page'); if (!$class) { $class = $suite; $suite = ''; } $conf = $suite ? $this->getSuiteConfig($suite) : $this->getGlobalConfig(); if ($suite) { $suite = DIRECTORY_SEPARATOR . ucfirst($suite); } $path = $this->createDirectoryFor(Configuration::supportDir() . 'Page' . $suite, $class); $filename = $path . $this->getShortClassName($class) . '.php'; $output->writeln($filename); $pageObject = new PageObjectGenerator($conf, ucfirst($suite) . '\\' . $class); $res = $this->createFile($filename, $pageObject->produce()); if (!$res) { $output->writeln("PageObject {$filename} already exists"); return 1; } $output->writeln("PageObject was created in {$filename}"); return 0; } } cleanProjectsRecursively($output, $projectDir); $output->writeln("Done"); return 0; } private function cleanProjectsRecursively(OutputInterface $output, string $projectDir): void { $config = Configuration::config($projectDir); $logDir = Configuration::outputDir(); $output->writeln("Cleaning up output " . $logDir . "..."); FileSystem::doEmptyDir($logDir); $subProjects = $config['include']; foreach ($subProjects as $subProject) { $subProjectDir = $projectDir . $subProject; $this->cleanProjectsRecursively($output, $subProjectDir); } } } setDefinition([ new InputArgument('suite', InputArgument::OPTIONAL, 'suite to be tested'), new InputArgument('test', InputArgument::OPTIONAL, 'test to be run'), new InputOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values'), new InputOption('ext', 'e', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Run with extension enabled'), new InputOption('report', '', InputOption::VALUE_NONE, 'Show output in compact style'), new InputOption('html', '', InputOption::VALUE_OPTIONAL, 'Generate html with results', 'report.html'), new InputOption('xml', '', InputOption::VALUE_OPTIONAL, 'Generate JUnit XML Log', 'report.xml'), new InputOption('phpunit-xml', '', InputOption::VALUE_OPTIONAL, 'Generate PhpUnit XML Log', 'phpunit-report.xml'), new InputOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output'), new InputOption( 'no-colors', '', InputOption::VALUE_NONE, 'Force no colors in output (useful to override config file)' ), new InputOption('silent', '', InputOption::VALUE_NONE, 'Only outputs suite names and final results'), new InputOption('steps', '', InputOption::VALUE_NONE, 'Show steps in output'), new InputOption('debug', 'd', InputOption::VALUE_NONE, 'Show debug and scenario output'), new InputOption('shard', '', InputOption::VALUE_REQUIRED, 'Execute subset of tests to run tests on different machine. To split tests on 3 machines to run with shards: 1/3, 2/3, 3/3'), new InputOption('filter', '', InputOption::VALUE_REQUIRED, 'Filter tests by name'), new InputOption('grep', '', InputOption::VALUE_REQUIRED, 'Filter tests by name (alias to --filter)'), new InputOption('bootstrap', '', InputOption::VALUE_OPTIONAL, 'Execute custom PHP script before running tests. Path can be absolute or relative to current working directory', false), new InputOption('no-redirect', '', InputOption::VALUE_NONE, 'Do not redirect to Composer-installed version in vendor/codeception'), new InputOption( 'coverage', '', InputOption::VALUE_OPTIONAL, 'Run with code coverage' ), new InputOption( 'coverage-html', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage HTML report in path' ), new InputOption( 'coverage-xml', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage XML report in file' ), new InputOption( 'coverage-text', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage text report in file' ), new InputOption( 'coverage-crap4j', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage report in Crap4J XML format' ), new InputOption( 'coverage-cobertura', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage report in Cobertura XML format' ), new InputOption( 'coverage-phpunit', '', InputOption::VALUE_OPTIONAL, 'Generate CodeCoverage PHPUnit report in path' ), new InputOption('no-exit', '', InputOption::VALUE_NONE, "Don't finish with exit code"), new InputOption( 'group', 'g', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Groups of tests to be executed' ), new InputOption( 'skip', 's', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Skip selected suites' ), new InputOption( 'skip-group', 'x', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Skip selected groups' ), new InputOption( 'env', '', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Run tests in selected environments.' ), new InputOption('fail-fast', 'f', InputOption::VALUE_OPTIONAL, 'Stop after nth failure'), new InputOption('no-rebuild', '', InputOption::VALUE_NONE, 'Do not rebuild actor classes on start'), new InputOption( 'seed', '', InputOption::VALUE_REQUIRED, 'Define random seed for shuffle setting' ), new InputOption('no-artifacts', '', InputOption::VALUE_NONE, "Don't report about artifacts"), ]); parent::configure(); } public function getDescription(): string { return 'Runs the test suites'; } /** * Executes Run * * @throws ConfigurationException|ParseException */ public function execute(InputInterface $input, OutputInterface $output): int { $this->ensurePhpExtIsAvailable('CURL'); $this->ensurePhpExtIsAvailable('mbstring'); $this->options = $input->getOptions(); $this->output = $output; if ($this->options['bootstrap']) { Configuration::loadBootstrap($this->options['bootstrap'], getcwd()); } $config = $this->getGlobalConfig(); $config = $this->addRuntimeOptionsToCurrentConfig($config); if (!$this->options['colors']) { $this->options['colors'] = $config['settings']['colors']; } if (!$this->options['silent']) { $this->output->writeln( Codecept::versionString() . ' https://stand-with-ukraine.pp.ua' ); if ($this->options['seed']) { $this->output->writeln( "Running with seed: " . $this->options['seed'] . "\n" ); } } if ($this->options['debug']) { $this->output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); } $userOptions = array_intersect_key($this->options, array_flip($this->passedOptionKeys($input))); $userOptions = array_merge( $userOptions, $this->booleanOptions($input, [ 'xml' => 'report.xml', 'phpunit-xml' => 'phpunit-report.xml', 'html' => 'report.html', 'coverage' => 'coverage.serialized', 'coverage-xml' => 'coverage.xml', 'coverage-html' => 'coverage', 'coverage-text' => 'coverage.txt', 'coverage-crap4j' => 'crap4j.xml', 'coverage-cobertura' => 'cobertura.xml', 'coverage-phpunit' => 'coverage-phpunit']) ); $userOptions['verbosity'] = $this->output->getVerbosity(); $userOptions['interactive'] = !$input->hasParameterOption(['--no-interaction', '-n']); $userOptions['ansi'] = (!$input->hasParameterOption('--no-ansi') xor $input->hasParameterOption('ansi')); $userOptions['seed'] = $this->options['seed'] ? (int)$this->options['seed'] : rand(); if ($this->options['no-colors'] || !$userOptions['ansi']) { $userOptions['colors'] = false; } if ($this->options['group']) { $userOptions['groups'] = $this->options['group']; } if ($this->options['skip-group']) { $userOptions['excludeGroups'] = $this->options['skip-group']; } if ($this->options['coverage-xml'] || $this->options['coverage-html'] || $this->options['coverage-text'] || $this->options['coverage-crap4j'] || $this->options['coverage-phpunit']) { $this->options['coverage'] = true; } if (!$userOptions['ansi'] && $input->getOption('colors')) { $userOptions['colors'] = true; // turn on colors even in non-ansi mode if strictly passed } // array key will exist if fail-fast option is used if (array_key_exists('fail-fast', $userOptions)) { $userOptions['fail-fast'] = (int)$this->options['fail-fast'] ?: 1; } $suite = $input->getArgument('suite'); $test = $input->getArgument('test'); if ($this->options['group']) { $this->output->writeln(sprintf("[Groups] %s ", implode(', ', $this->options['group']))); } if ($input->getArgument('test')) { $this->options['steps'] = true; } if (!$test) { // Check if suite is given and is in an included path if (!empty($suite) && !empty($config['include'])) { $isIncludeTest = false; // Remember original projectDir $projectDir = Configuration::projectDir(); foreach ($config['include'] as $include) { // Find if the suite begins with an include path if (str_starts_with($suite, (string)$include)) { // Use include config $config = Configuration::config($projectDir . $include); $config = $this->addRuntimeOptionsToCurrentConfig($config); if (!empty($this->options['override'])) { $config = $this->overrideConfig($this->options['override']); } if (!isset($config['paths']['tests'])) { throw new RuntimeException( sprintf("Included '%s' has no tests path configured", $include) ); } $testsPath = $include . DIRECTORY_SEPARATOR . $config['paths']['tests']; try { [, $suite, $test] = $this->matchTestFromFilename($suite, $testsPath); $isIncludeTest = true; } catch (InvalidArgumentException) { // Incorrect include match, continue trying to find one continue; } } else { $result = $this->matchSingleTest($suite, $config); if ($result) { [, $suite, $test] = $result; } } } // Restore main config if (!$isIncludeTest) { $config = $this->addRuntimeOptionsToCurrentConfig( Configuration::config($projectDir) ); } } elseif (!empty($suite)) { $result = $this->matchSingleTest($suite, $config); if ($result) { [, $suite, $test] = $result; } } } $filter = $input->getOption('filter') ?? $input->getOption('grep') ?? null; if ($test) { $userOptions['filter'] = $this->matchFilteredTestName($test); } elseif ( $suite && !$this->isWildcardSuiteName($suite) && !$this->isSuiteInMultiApplication($suite) ) { $userOptions['filter'] = $this->matchFilteredTestName($suite); } if (isset($userOptions['filter']) && $filter) { throw new InvalidOptionException("--filter and --grep can't be used with a test name"); } elseif ($filter) { $userOptions['filter'] = $filter; } if ($this->options['shard']) { $this->output->writeln( "[Shard {$userOptions['shard']}] Running subset of tests" ); } if (!$this->options['silent'] && $config['settings']['shuffle']) { $this->output->writeln( "[Seed] " . $userOptions['seed'] . "" ); } $this->codecept = new Codecept($userOptions); if ($suite && $test) { $this->codecept->run($suite, $test, $config); } // Run all tests of given suite or all suites if (!$test) { $didPassCliSuite = !empty($suite); $rawSuites = $didPassCliSuite ? explode(',', $suite) : Configuration::suites(); /** @var string[] $mainAppSuites */ $mainAppSuites = []; /** @var array $appSpecificSuites */ $appSpecificSuites = []; /** @var string[] $wildcardSuites */ $wildcardSuites = []; foreach ($rawSuites as $rawSuite) { if ($this->isWildcardSuiteName($rawSuite)) { $wildcardSuites[] = explode('*::', $rawSuite)[1]; continue; } if ($this->isSuiteInMultiApplication($rawSuite)) { $appAndSuite = explode('::', $rawSuite); $appSpecificSuites[$appAndSuite[0]][] = $appAndSuite[1]; continue; } $mainAppSuites[] = $rawSuite; } if ([] !== $mainAppSuites) { $this->executed = $this->runSuites($mainAppSuites, $this->options['skip']); } if (!empty($wildcardSuites) && ! empty($appSpecificSuites)) { $this->output->writeLn('Wildcard options can not be combined with specific suites of included apps.'); return 2; } if ( !empty($config['include']) && (!$didPassCliSuite || !empty($wildcardSuites) || !empty($appSpecificSuites)) ) { $currentDir = Configuration::projectDir(); $includedApps = $config['include']; if (!empty($appSpecificSuites)) { $includedApps = array_intersect($includedApps, array_keys($appSpecificSuites)); } $this->runIncludedSuites( $includedApps, $currentDir, $appSpecificSuites, $wildcardSuites ); } if ($this->executed === 0) { throw new RuntimeException( sprintf("Suite '%s' could not be found", implode(', ', $rawSuites)) ); } } $this->codecept->printResult(); if ($this->options['shard']) { $this->output->writeln( "[Shard {$userOptions['shard']}] Merge this result with other shards to see the complete report" ); } if (!$input->getOption('no-exit') && !$this->codecept->getResultAggregator()->wasSuccessfulIgnoringWarnings()) { exit(1); } return 0; } protected function matchSingleTest($suite, $config): ?array { // Workaround when codeception.yml is inside tests directory and tests path is set to "." // @see https://github.com/Codeception/Codeception/issues/4432 if (isset($config['paths']['tests']) && $config['paths']['tests'] === '.' && !preg_match('#^\.[/\\\]#', $suite)) { $suite = './' . $suite; } // running a single test when suite has a configured path if (isset($config['suites'])) { foreach ($config['suites'] as $s => $suiteConfig) { if (!isset($suiteConfig['path'])) { continue; } $testsPath = $config['paths']['tests'] . DIRECTORY_SEPARATOR . $suiteConfig['path']; if ($suiteConfig['path'] === '.') { $testsPath = $config['paths']['tests']; } if (preg_match("#^{$testsPath}/(.*?)$#", $suite, $matches)) { $matches[2] = $matches[1]; $matches[1] = $s; return $matches; } } } if (!Configuration::isEmpty()) { // Run single test without included tests if (str_starts_with($suite, (string)$config['paths']['tests'])) { return $this->matchTestFromFilename($suite, $config['paths']['tests']); } // Run single test from working directory $realTestDir = (string)realpath(Configuration::testsDir()); $cwd = (string)getcwd(); if (str_starts_with($realTestDir, $cwd)) { $file = $suite; if (str_contains($file, ':')) { [$file] = explode(':', $suite, -1); } $realPath = $cwd . DIRECTORY_SEPARATOR . $file; if (file_exists($realPath) && str_starts_with($realPath, $realTestDir)) { //only match test if file is in tests directory return $this->matchTestFromFilename( $cwd . DIRECTORY_SEPARATOR . $suite, $realTestDir ); } } } return null; } /** * Runs included suites recursively * * @param string[] $suites * @param array $filterAppSuites An array keyed by included app name where values are suite names to run. * @param string[] $filterSuitesByWildcard A list of suite names (applies to all included apps) * @throws ConfigurationException */ protected function runIncludedSuites( array $suites, string $parentDir, array $filterAppSuites = [], array $filterSuitesByWildcard = [], ) { $defaultConfig = Configuration::config(); $absolutePath = Configuration::projectDir(); foreach ($suites as $relativePath) { $currentDir = rtrim($parentDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $relativePath; $config = Configuration::config($currentDir); if (!empty($defaultConfig['groups'])) { $groups = array_map(fn ($g) => $absolutePath . $g, $defaultConfig['groups']); Configuration::append(['groups' => $groups]); } $suites = Configuration::suites(); if (!empty($filterSuitesByWildcard)) { $suites = array_intersect($suites, $filterSuitesByWildcard); } if (isset($filterAppSuites[$relativePath])) { $suites = array_intersect($suites, $filterAppSuites[$relativePath]); } $namespace = $this->currentNamespace(); $this->output->writeln( "\n\n[{$namespace}]: tests from {$currentDir}\n" ); $this->executed += $this->runSuites($suites, $this->options['skip']); if (!empty($config['include'])) { $this->runIncludedSuites($config['include'], $currentDir); } } } protected function currentNamespace(): string { $config = Configuration::config(); if (!$config['namespace']) { throw new RuntimeException( "Can't include into runner suite without a namespace;\n" . "Please add `namespace` section into included codeception.yml file" ); } return $config['namespace']; } /** * @param string[] $suites * @param string[] $skippedSuites * @return int Number of executed test suites */ protected function runSuites(array $suites, array $skippedSuites = []): int { $executed = 0; foreach ($suites as $suite) { if (in_array($suite, $skippedSuites)) { continue; } if (!in_array($suite, Configuration::suites())) { continue; } $this->codecept->run($suite); ++$executed; } return $executed; } /** * @return string[] */ protected function matchTestFromFilename(string $filename, string $testsPath): array { $filter = ''; if (str_contains($filename, ':')) { if ((PHP_OS === 'Windows' || PHP_OS === 'WINNT') && $filename[1] === ':') { // match C:\... [$drive, $path, $filter] = explode(':', $filename, 3); $filename = $drive . ':' . $path; } else { [$filename, $filter] = explode(':', $filename, 2); } if ($filter !== '') { $filter = ':' . $filter; } } $testsPath = str_replace(['//', '\/', '\\'], '/', $testsPath); $filename = str_replace(['//', '\/', '\\'], '/', $filename); if (rtrim($filename, '/') === $testsPath) { //codecept run tests return ['', '', $filter]; } $res = preg_match("#^{$testsPath}/(.*?)(?>/(.*))?$#", $filename, $matches); if (!$res) { throw new InvalidArgumentException("Test file can't be matched"); } if (!isset($matches[2])) { $matches[2] = ''; } if ($filter !== '') { $matches[2] .= $filter; } return $matches; } private function matchFilteredTestName(string &$path): ?string { $testParts = explode(':', $path, 2); if (count($testParts) > 1) { [$path, $filter] = $testParts; // use carat to signify start of string like in normal regex // phpunit --filter matches against the fully qualified method name, so tests actually begin with : $caratPos = strpos($filter, '^'); if ($caratPos !== false) { $filter = substr_replace($filter, ':', $caratPos, 1); } return $filter; } return null; } /** * @return string[] */ protected function passedOptionKeys(ArgvInput $input): array { $options = []; $request = (string)$input; $tokens = explode(' ', $request); foreach ($tokens as $token) { $token = preg_replace('#=.*#', '', $token); // strip = from options if (empty($token)) { continue; } if ($token == '--') { break; // there should be no options after ' -- ', only arguments } if (str_starts_with($token, '--')) { $options[] = substr($token, 2); } elseif ($token[0] === '-') { $shortOption = substr($token, 1); $options[] = $this->getDefinition()->getOptionForShortcut($shortOption)->getName(); } } return $options; } /** * @return array */ protected function booleanOptions(ArgvInput $input, array $options = []): array { $values = []; $request = (string)$input; foreach ($options as $option => $defaultValue) { if (strpos($request, sprintf('--%s', $option))) { $values[$option] = $input->getOption($option) ?: $defaultValue; } else { $values[$option] = false; } } return $values; } /** * @throws Exception */ private function ensurePhpExtIsAvailable(string $ext): void { if (!extension_loaded(strtolower($ext))) { throw new Exception( "Codeception requires \"{$ext}\" extension installed to make tests run\n" . "If you are not sure, how to install \"{$ext}\", please refer to StackOverflow\n\n" . "Notice: PHP for Apache/Nginx and CLI can have different php.ini files.\n" . "Please make sure that your PHP you run from console has \"{$ext}\" enabled." ); } } private function isWildcardSuiteName(string $suiteName): bool { return str_starts_with($suiteName, '*::'); } private function isSuiteInMultiApplication(string $suiteName): bool { return str_contains($suiteName, '::'); } private function addRuntimeOptionsToCurrentConfig(array $config): array { // update config from options if (count($this->options['override'])) { $config = $this->overrideConfig($this->options['override']); } // enable extensions if ($this->options['ext']) { $config = $this->enableExtensions($this->options['ext']); } return $config; } } setDefinition([ new InputArgument('group', InputArgument::REQUIRED, 'Group class name'), ]); } public function getDescription(): string { return 'Generates Group subscriber'; } public function execute(InputInterface $input, OutputInterface $output): int { $config = $this->getGlobalConfig(); $groupInputArgument = (string)$input->getArgument('group'); $class = ucfirst($groupInputArgument); $path = $this->createDirectoryFor(Configuration::supportDir() . 'Group' . DIRECTORY_SEPARATOR, $class); $filename = $path . $class . '.php'; $group = new GroupGenerator($config, $groupInputArgument); $res = $this->createFile($filename, $group->produce()); if (!$res) { $output->writeln("Group {$filename} already exists"); return 1; } $output->writeln("Group extension was created in {$filename}"); $output->writeln( 'To use this group extension, include it to "extensions" option of global Codeception config.' ); return 0; } } setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'suite where tests will be put'), new InputArgument('class', InputArgument::REQUIRED, 'test name'), ]); } public function getDescription(): string { return 'Generates empty Cest file in suite'; } public function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); $class = $input->getArgument('class'); $config = $this->getSuiteConfig($suite); $className = $this->getShortClassName($class); $path = $this->createDirectoryFor($config['path'], $class); $filename = $this->completeSuffix($className, 'Cest'); $filename = $path . $filename; if (file_exists($filename)) { $output->writeln("Test {$filename} already exists"); return 1; } $cest = new CestGenerator($class, $config); $res = $this->createFile($filename, $cest->produce()); if (!$res) { $output->writeln("Test {$filename} already exists"); return 1; } $output->writeln("Test was created in {$filename}"); return 0; } } setDefinition( [ new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'), new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), ] ); parent::configure(); } public function getDescription(): string { return 'Prints all defined feature steps'; } public function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = $input->getArgument('suite'); $config = $this->getSuiteConfig($suite); $config['describe_steps'] = true; $loader = new GherkinLoader($config); $steps = $loader->getSteps(); foreach ($steps as $name => $context) { $table = new Table($output); $table->setHeaders(['Step', 'Implementation']); $output->writeln("Steps from {$name} context:"); foreach ($context as $step => $callable) { if (count($callable) < 2) { continue; } $method = $callable[0] . '::' . $callable[1]; $table->addRow([$step, $method]); } $table->render(); } if (!isset($table)) { $output->writeln("No steps are defined, start creating them by running gherkin:snippets"); } return 0; } } setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'suite from which texts should be generated'), new InputOption('path', 'p', InputOption::VALUE_REQUIRED, 'Use specified path as destination instead of default'), new InputOption('single-file', '', InputOption::VALUE_NONE, 'Render all scenarios to only one file'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Specify output format: html or text (default)', 'text'), ]); parent::configure(); } public function getDescription(): string { return 'Generates text representation for all scenarios'; } protected function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); $suiteConf = $this->getSuiteConfig($suite); $path = $input->getOption('path') ? $input->getOption('path') : Configuration::dataDir() . 'scenarios'; $format = $input->getOption('format'); @mkdir($path); if (!is_writable($path)) { throw new ConfigurationException( "Path {$path} is not writable. Please, set valid permissions for folder to store scenarios." ); } $path = $path . DIRECTORY_SEPARATOR . $suite; if (!$input->getOption('single-file')) { @mkdir($path); } $suiteManager = new SuiteManager(new EventDispatcher(), $suite, $suiteConf, []); if ($suiteConf['bootstrap'] && file_exists($suiteConf['path'] . $suiteConf['bootstrap'])) { require_once $suiteConf['path'] . $suiteConf['bootstrap']; } $tests = $this->getTests($suiteManager); $scenarios = ""; foreach ($tests as $test) { if (!$test instanceof ScenarioDriven || !$test instanceof Descriptive) { continue; } $feature = $test->getScenarioText($format); $name = $this->underscore(basename($test->getFileName(), '.php')); // create separate file for each test in Cest if ($test instanceof Cest && !$input->getOption('single-file')) { $name .= '.' . $this->underscore($test->getTestMethod()); } if ($input->getOption('single-file')) { $scenarios .= $feature; $output->writeln("* {$name} rendered"); } else { $feature = $this->decorate($feature, $format); $this->createFile($path . DIRECTORY_SEPARATOR . $name . $this->formatExtension($format), $feature, true); $output->writeln("* {$name} generated"); } } if ($input->getOption('single-file')) { $this->createFile($path . $this->formatExtension($format), $this->decorate($scenarios, $format), true); } return 0; } protected function decorate(string $text, string $format): string { if ($format === 'html') { return "$text"; } return $text; } protected function getTests($suiteManager) { $suiteManager->loadTests(); return $suiteManager->getSuite()->getTests(); } protected function formatExtension(string $format): string { if ($format === 'html') { return '.html'; } return '.txt'; } private function underscore(string $name): string { $name = preg_replace('#([A-Z]+)([A-Z][a-z])#', '\\1_\\2', $name); $name = preg_replace('#([a-z\d])([A-Z])#', '\\1_\\2', $name); $name = str_replace(['/', '\\'], ['.', '.'], $name); $name = preg_replace('#_Cept$#', '', $name); return preg_replace('#_Cest$#', '', $name); } } setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'Suite for StepObject'), new InputArgument('step', InputArgument::REQUIRED, 'StepObject name'), new InputOption('silent', '', InputOption::VALUE_NONE, 'skip verification question'), ]); } public function getDescription(): string { return 'Generates empty StepObject class'; } public function execute(InputInterface $input, OutputInterface $output): int { $suite = (string)$input->getArgument('suite'); $step = $input->getArgument('step'); $config = $this->getSuiteConfig($suite); $class = $this->getShortClassName($step); $path = $this->createDirectoryFor(Configuration::supportDir() . 'Step' . DIRECTORY_SEPARATOR . ucfirst($suite), $step); /** * @var QuestionHelper */ $dialog = $this->getHelperSet()->get('question'); $filename = $path . $class . '.php'; $helper = $this->getHelper('question'); $question = new Question("Add action to StepObject class (ENTER to exit): "); $stepObject = new StepObjectGenerator($config, ucfirst($suite) . '\\' . $step); if (!$input->getOption('silent')) { do { $question = new Question('Add action to StepObject class (ENTER to exit): ', null); $action = $dialog->ask($input, $output, $question); if ($action) { $stepObject->createAction($action); } } while ($action); } $res = $this->createFile($filename, $stepObject->produce()); if (!$res) { $output->writeln("StepObject {$filename} already exists"); return 1; } $output->writeln("StepObject was created in {$filename}"); return 0; } } setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'suite to be tested'), new InputArgument('feature', InputArgument::REQUIRED, 'feature to be generated'), new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), ]); } public function getDescription(): string { return 'Generates empty feature file in suite'; } public function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); $filename = $input->getArgument('feature'); $config = $this->getSuiteConfig($suite); $this->createDirectoryFor($config['path'], $filename); $feature = new Feature(basename($filename)); if (!preg_match('#\.feature$#', $filename)) { $filename .= '.feature'; } $fullPath = rtrim($config['path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename; $res = $this->createFile($fullPath, $feature->produce()); if (!$res) { $output->writeln("Feature {$filename} already exists"); return 1; } $output->writeln("Feature was created in {$fullPath}"); return 0; } } */ class SelfUpdate extends Command { /** * @var string */ public const NAME = 'Codeception'; /** * @var string */ public const GITHUB_REPO = 'Codeception/Codeception'; /** * @var string */ public const PHAR_URL = 'https://codeception.com/php80/'; /** * Holds the current script filename. */ protected string $filename; /** * {@inheritdoc} */ protected function configure(): void { $this->filename = $_SERVER['argv'][0] ?? Phar::running(false); $this ->setAliases(['selfupdate']) ->setDescription( sprintf( 'Upgrade %s to the latest version', $this->filename ) ); parent::configure(); } protected function getCurrentVersion(): string { return Codecept::VERSION; } /** * {@inheritdoc} */ public function execute(InputInterface $input, OutputInterface $output): int { $currentVersion = $this->getCurrentVersion(); $output->writeln( sprintf( '%s version %s', self::NAME, $currentVersion ) ); $url = self::PHAR_URL; $updater = new Updater(null, false); $updater->getStrategy()->setPharUrl($url . 'codecept.phar'); $updater->getStrategy()->setVersionUrl($url . 'codecept.version'); try { if ($updater->hasUpdate()) { $output->writeln("\nUpdating..."); $updater->update(); $output->writeln( sprintf("\n%s has been updated.\n", $this->filename) ); } else { $output->writeln('You are already using the latest version.'); } } catch (Exception $e) { $output->writeln( sprintf( "\n%s\n", $e->getMessage() ) ); return 1; } return 0; } } getFormatter()->setStyle('notice', new OutputFormatterStyle('white', 'green', ['bold'])); $output->getFormatter()->setStyle('bold', new OutputFormatterStyle(null, null, ['bold'])); $output->getFormatter()->setStyle('warning', new OutputFormatterStyle(null, 'yellow', ['bold'])); $output->getFormatter()->setStyle('debug', new OutputFormatterStyle('cyan')); } } settings['actor'])) { return null; } $namespace = ""; if ($this->settings['namespace']) { $namespace .= '\\' . $this->settings['namespace']; } if (isset($this->settings['support_namespace'])) { $namespace .= '\\' . $this->settings['support_namespace']; } $namespace = rtrim($namespace, '\\') . '\\'; return $namespace . $this->settings['actor']; } private function getActor($test) { $actorClass = $this->getActorClassName(); return $actorClass ? new $actorClass(new Scenario($test)) : null; } } getGlobalConfig()); } protected function getGlobalConfig(string $conf = null): array { return Configuration::config($conf); } /** * @return string[] */ protected function getSuites($conf = null): array { return Configuration::suites(); } protected function overrideConfig($configOptions): array { $updatedConfig = []; foreach ($configOptions as $option) { $keys = explode(': ', $option); if (count($keys) < 2) { throw new InvalidArgumentException('--override should have config passed as "key: value"'); } $value = array_pop($keys); $yaml = ''; for ($ind = 0; count($keys); $ind += 2) { $yaml .= "\n" . str_repeat(' ', $ind) . array_shift($keys) . ': '; } $yaml .= $value; try { $config = Yaml::parse($yaml); } catch (ParseException $e) { throw new \Codeception\Exception\ParseException("Overridden config can't be parsed: \n{$yaml}\n" . $e->getParsedLine()); } $updatedConfig = array_merge_recursive($updatedConfig, $config); } return Configuration::append($updatedConfig); } protected function enableExtensions($extensions): array { $config = ['extensions' => ['enabled' => []]]; foreach ($extensions as $name) { if (!class_exists($name)) { $className = 'Codeception\\Extension\\' . ucfirst($name); if (!class_exists($className)) { throw new InvalidOptionException("Extension {$name} can't be loaded (tried by {$name} and {$className})"); } $config['extensions']['enabled'][] = $className; continue; } $config['extensions']['enabled'][] = $name; } return Configuration::append($config); } } setDefinition([ new InputArgument('name', InputArgument::REQUIRED, 'helper name'), ]); } public function getDescription(): string { return 'Generates new helper'; } public function execute(InputInterface $input, OutputInterface $output): int { $name = ucfirst((string)$input->getArgument('name')); $config = $this->getGlobalConfig(); $path = $this->createDirectoryFor(Configuration::supportDir() . 'Helper', $name); $filename = $path . $this->getShortClassName($name) . '.php'; $res = $this->createFile($filename, (new Helper($config, $name))->produce()); if ($res) { $output->writeln("Helper {$filename} created"); return 0; } else { $output->writeln("Error creating helper {$filename}"); return 1; } } } api + ApiTester * * `codecept g:suite integration Code` -> integration + CodeTester * * `codecept g:suite frontend Front` -> frontend + FrontTester * */ class GenerateSuite extends Command { use Shared\FileSystemTrait; use Shared\ConfigTrait; use Shared\StyleTrait; protected function configure(): void { $this->setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'suite to be generated'), new InputArgument('actor', InputArgument::OPTIONAL, 'name of new actor class'), ]); } public function getDescription(): string { return 'Generates new test suite'; } public function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); $suite = ucfirst((string)$input->getArgument('suite')); $actor = $input->getArgument('actor'); if ($this->containsInvalidCharacters($suite)) { $output->writeln("Suite name '{$suite}' contains invalid characters. ([A-Za-z0-9_])."); return 1; } $config = $this->getGlobalConfig(); if (!$actor) { $actor = $suite . $config['actor_suffix']; } $dir = Configuration::testsDir(); if (file_exists($dir . $suite . '.suite.yml')) { throw new Exception("Suite configuration file '{$suite}.suite.yml' already exists."); } $this->createDirectoryFor($dir . $suite); if ($config['settings']['bootstrap']) { // generate bootstrap file $this->createFile( $dir . $suite . DIRECTORY_SEPARATOR . $config['settings']['bootstrap'], "createFile( $dir . $suite . '.suite.yml', $yamlSuiteConfig = (new Template($yamlSuiteConfigTemplate)) ->place('actor', $actor) ->place('suite_namespace', $config['namespace'] . '\\' . $suite) ->produce() ); Configuration::append(Yaml::parse($yamlSuiteConfig)); $actorGenerator = new ActorGenerator(Configuration::config()); $content = $actorGenerator->produce(); $file = $this->createDirectoryFor( Configuration::supportDir(), $actor ) . $this->getShortClassName($actor); $file .= '.php'; $this->createFile($file, $content); $output->writeln("Actor " . $actor . " was created in {$file}"); $output->writeln("Suite config {$suite}.suite.yml was created."); $output->writeln(' '); $output->writeln("Next steps:"); $output->writeln("1. Edit {$suite}.suite.yml to enable modules for this suite"); $output->writeln("2. Create first test with generate:cest testName ( or test|cept) command"); $output->writeln("3. Run tests of this suite with codecept run {$suite} command"); $output->writeln("Suite {$suite} generated"); return 0; } private function containsInvalidCharacters(string $suite): bool { return (bool)preg_match('#[^A-Za-z0-9_]#', $suite); } } setDefinition( [ new InputArgument('suite', InputArgument::REQUIRED, 'suite where tests will be put'), new InputArgument('class', InputArgument::REQUIRED, 'class name'), ] ); parent::configure(); } public function getDescription(): string { return 'Generates empty unit test file in suite'; } public function execute(InputInterface $input, OutputInterface $output): int { $suite = $input->getArgument('suite'); $class = $input->getArgument('class'); $config = $this->getSuiteConfig($suite); $className = $this->getShortClassName($class); $path = $this->createDirectoryFor($config['path'], $class); $filename = $this->completeSuffix($className, 'Test'); $filename = $path . $filename; $test = new TestGenerator($config, $class); $res = $this->createFile($filename, $test->produce()); if (!$res) { $output->writeln("Test {$filename} already exists"); return 1; } $output->writeln("Test was created in {$filename}"); return 0; } } setDefinition([ new InputArgument('suite', InputArgument::REQUIRED, 'suite to be executed'), new InputOption('colors', '', InputOption::VALUE_NONE, 'Use colors in output'), ]); parent::configure(); } public function getDescription(): string { return 'Launches interactive test console'; } public function execute(InputInterface $input, OutputInterface $output): int { $suiteName = $input->getArgument('suite'); $this->output = $output; $config = Configuration::config(); $settings = Configuration::suiteSettings($suiteName, $config); $options = $input->getOptions(); $options['debug'] = true; $options['silent'] = true; $options['interactive'] = false; $options['colors'] = true; Debug::setOutput(new Output($options)); $this->codecept = new Codecept($options); $eventDispatcher = $this->codecept->getDispatcher(); $suiteManager = new SuiteManager($eventDispatcher, $suiteName, $settings, []); $suiteManager->initialize(); $this->suite = $suiteManager->getSuite(); $moduleContainer = $suiteManager->getModuleContainer(); $this->actions = array_keys($moduleContainer->getActions()); $this->test = new Cept('', ''); $this->test->getMetadata()->setServices([ 'dispatcher' => $eventDispatcher, 'modules' => $moduleContainer ]); $scenario = new Scenario($this->test); if (!$settings['actor']) { throw new ConfigurationException("Interactive shell can't be started without an actor"); } if (isset($config['namespace']) && $config['namespace'] !== '') { $settings['actor'] = $config['namespace'] . '\\Support\\' . $settings['actor']; } $actor = $settings['actor']; $I = new $actor($scenario); $this->listenToSignals(); $output->writeln("Interactive console started for suite {$suiteName}"); $output->writeln("Try Codeception commands without writing a test"); $suiteEvent = new SuiteEvent($this->suite, $settings); $eventDispatcher->dispatch($suiteEvent, Events::SUITE_INIT); $eventDispatcher->dispatch(new TestEvent($this->test), Events::TEST_PARSED); $eventDispatcher->dispatch(new TestEvent($this->test), Events::TEST_BEFORE); if (is_string($settings['bootstrap']) && file_exists($settings['bootstrap'])) { require $settings['bootstrap']; } $I->pause(); $eventDispatcher->dispatch(new TestEvent($this->test), Events::TEST_AFTER); $eventDispatcher->dispatch(new SuiteEvent($this->suite), Events::SUITE_AFTER); $output->writeln("Bye-bye!"); return 0; } protected function listenToSignals(): void { if (function_exists('pcntl_signal')) { declare(ticks=1); pcntl_signal(SIGINT, SIG_IGN); pcntl_signal(SIGTERM, SIG_IGN); } } } addHandler(new ConsoleCompletion( $suiteCommand, 'suite', ConsoleCompletionInterface::TYPE_ARGUMENT, Configuration::suites() )); } $handler->addHandlers([ new ShellPathCompletion( ConsoleCompletionInterface::ALL_COMMANDS, 'path', ConsoleCompletionInterface::TYPE_ARGUMENT ), new ShellPathCompletion( ConsoleCompletionInterface::ALL_COMMANDS, 'test', ConsoleCompletionInterface::TYPE_ARGUMENT ), ]); } protected function execute(InputInterface $input, OutputInterface $output): int { if ($input->getOption('generate-hook') && $input->getOption('use-vendor-bin')) { global $argv; $argv[0] = 'vendor/bin/' . basename($argv[0]); } parent::execute($input, $output); return 0; } protected function createDefinition(): SymfonyInputDefinition { $definition = parent::createDefinition(); $definition->addOption(new InputOption( 'use-vendor-bin', null, InputOption::VALUE_NONE, 'Use the vendor bin for autocompletion.' )); return $definition; } } setDescription('BASH completion hook.') ->setHidden(true) // Hide from listing ->setHelp(<<composer require stecman/symfony-console-completion END); } protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln("Install optional stecman/symfony-console-completion"); return 0; } } setDefinition( [ new InputArgument('template', InputArgument::REQUIRED, 'Init template for the setup'), new InputOption('path', null, InputOption::VALUE_REQUIRED, 'Change current directory', null), new InputOption('namespace', null, InputOption::VALUE_OPTIONAL, 'Namespace to add for actor classes and helpers', null), ] ); } public function getDescription(): string { return "Creates test suites by a template"; } public function execute(InputInterface $input, OutputInterface $output): int { $template = (string)$input->getArgument('template'); if (class_exists($template)) { $className = $template; } else { $className = 'Codeception\Template\\' . ucfirst($template); if (!class_exists($className)) { throw new Exception("Template from a {$className} can't be loaded; Init can't be executed"); } } $initProcess = new $className($input, $output); if (!$initProcess instanceof InitTemplate) { throw new Exception("{$className} is not a valid template"); } if ($path = $input->getOption('path')) { $initProcess->initDir($path); } $initProcess->setup(); return 0; } } setDefinition( [ new InputArgument('suite', InputArgument::OPTIONAL, 'to show suite configuration'), new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'), new InputOption('override', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Override config values'), ] ); parent::configure(); } public function getDescription(): string { return 'Validates and prints config to screen'; } public function execute(InputInterface $input, OutputInterface $output): int { $this->addStyles($output); if ($suite = $input->getArgument('suite')) { $output->write("Validating {$suite} config... "); $config = $this->getSuiteConfig($suite); $output->writeln("Ok"); $output->writeln("------------------------------\n"); $output->writeln("{$suite} Suite Config:\n"); $output->writeln($this->formatOutput($config)); return 0; } $output->write("Validating global config... "); $config = $this->getGlobalConfig(); $output->writeln($input->getOption('override')); if (!empty($input->getOption('override'))) { $config = $this->overrideConfig($input->getOption('override')); } $suites = Configuration::suites(); $output->writeln("Ok"); $output->writeln("------------------------------\n"); $output->writeln("Codeception Config:\n"); $output->writeln($this->formatOutput($config)); $output->writeln('Directories:'); $output->writeln("codecept_root_dir() " . codecept_root_dir()); $output->writeln("codecept_output_dir() " . codecept_output_dir()); $output->writeln("codecept_data_dir() " . codecept_data_dir()); $output->writeln(''); $output->writeln("Available suites: " . implode(', ', $suites)); foreach ($suites as $suite) { $output->write("Validating suite {$suite}... "); $this->getSuiteConfig($suite); $output->writeln('Ok'); } $output->writeln("Execute codecept config:validate [] to see config for a suite"); return 0; } protected function formatOutput($config): ?string { $output = print_r($config, true); return preg_replace('#\[(.*?)] =>#', "$1 =>", $output); } } output = $output; $this->buildActorsForConfig(); return 0; } private function buildActor(array $settings): bool { $actorGenerator = new ActorGenerator($settings); $this->output->writeln( '' . Configuration::config()['namespace'] . '\\' . $actorGenerator->getActorName() . " includes modules: " . implode(', ', $actorGenerator->getModules()) ); $content = $actorGenerator->produce(); $file = $this->createDirectoryFor( Configuration::supportDir(), $settings['actor'] ) . $this->getShortClassName($settings['actor']); $file .= '.php'; return $this->createFile($file, $content); } private function buildActions(array $settings): bool { $actionsGenerator = new ActionsGenerator($settings); $content = $actionsGenerator->produce(); $this->output->writeln( " -> {$settings['actor']}Actions.php generated successfully. " . $actionsGenerator->getNumMethods() . " methods added" ); $file = $this->createDirectoryFor(Configuration::supportDir() . '_generated', $settings['actor']); $file .= $this->getShortClassName($settings['actor']) . 'Actions.php'; return $this->createFile($file, $content, true); } private function buildSuiteActors(): void { $suites = $this->getSuites(); if (!empty($suites)) { $this->output->writeln("Building Actor classes for suites: " . implode(', ', $suites) . ''); } foreach ($suites as $suite) { $settings = $this->getSuiteConfig($suite); if (!$settings['actor']) { continue; // no actor } $this->buildActions($settings); $actorBuilt = $this->buildActor($settings); if ($actorBuilt) { $this->output->writeln("{$settings['actor']}.php created."); } } } protected function buildActorsForConfig($configFile = null): void { $config = $this->getGlobalConfig($configFile); $dir = Configuration::projectDir(); $this->buildSuiteActors(); foreach ($config['include'] as $subConfig) { $this->output->writeln("\nIncluded Configuration: {$subConfig}"); $this->buildActorsForConfig($dir . DIRECTORY_SEPARATOR . $subConfig); } } } 'davert']); * Fixtures::get('user1'); * Fixtures::exists('user1'); * ``` */ class Fixtures { protected static array $fixtures = []; public static function add(string $name, $data): void { self::$fixtures[$name] = $data; } public static function get(string $name) { if (!self::exists($name)) { throw new RuntimeException("{$name} not found in fixtures"); } return self::$fixtures[$name]; } public static function cleanup(string $name = ''): void { if (self::exists($name)) { unset(self::$fixtures[$name]); return; } self::$fixtures = []; } public static function exists(string $name): bool { return isset(self::$fixtures[$name]); } } setAccessible(true); return $property->getValue($object); } /** * Set a private property of an object. * * @throws ReflectionException */ public static function setPrivateProperty(object $object, string $property, $value, string $class = null): void { if (is_null($class)) { $class = $object; } $property = new ReflectionProperty($class, $property); $property->setAccessible(true); $property->setValue($object, $value); } /** * Invoke a private method of an object. * * @throws ReflectionException */ public static function invokePrivateMethod(?object $object, string $method, array $args = [], string $class = null): mixed { if (is_null($class)) { $class = $object; } $method = new ReflectionMethod($class, $method); $method->setAccessible(true); return $method->invokeArgs($object, $args); } /** * Returns class name without namespace * * (does not use reflection actually) */ public static function getClassShortName(object $object): string { $path = explode('\\', get_class($object)); return array_pop($path); } public static function getClassFromParameter(ReflectionParameter $parameter): ?string { $type = $parameter->getType(); if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) { return null; } $typeString = $type->getName(); if ($typeString === 'self') { return $parameter->getDeclaringClass()->getName(); } if ($typeString === 'parent') { return $parameter->getDeclaringClass()->getParentClass()->getName(); } return $typeString; } /** * Infer default parameter from the reflection object and format it as PHP (code) string */ public static function getDefaultValue(ReflectionParameter $parameter): string { if ($parameter->isDefaultValueAvailable()) { if (method_exists($parameter, 'isDefaultValueConstant') && $parameter->isDefaultValueConstant()) { $constName = $parameter->getDefaultValueConstantName(); if (str_contains($constName, '::')) { [$class, $const] = explode('::', $constName); if (in_array($class, ['self', 'static'])) { $constName = '\\' . $parameter->getDeclaringClass()->getName() . '::' . $const; } elseif (!str_starts_with($class, '\\')) { $constName = '\\' . $constName; } } return $constName; } return self::phpEncodeValue($parameter->getDefaultValue()); } $type = $parameter->getType(); // Default to 'null' if explicitly allowed or there is no specific type hint. if (!$type || $type->allowsNull() || !$type instanceof ReflectionNamedType || !$type->isBuiltin()) { return 'null'; } // Default value should match the parameter type if 'null' is NOT allowed. return match ($type->getName()) { 'string' => "''", 'array' => '[]', 'boolean' => 'false', 'int', 'integer', 'float', 'double', 'number', 'numeric' => '0', default => 'null', }; } public static function phpEncodeValue(mixed $value): string { if (is_array($value)) { return self::phpEncodeArray($value); } if (is_string($value)) { return json_encode($value, JSON_THROW_ON_ERROR); } return var_export($value, true); } /** * Recursively PHP encode an array */ public static function phpEncodeArray(array $array): string { $isPlainArray = fn (array $value): bool => ($value === []) || ( (array_keys($value) === range(0, count($value) - 1)) && ([] === array_filter(array_keys($value), 'is_string')) ); if ($isPlainArray($array)) { return '[' . implode(', ', array_map([self::class, 'phpEncodeValue'], $array)) . ']'; } $values = array_map( fn ($key): string => self::phpEncodeValue($key) . ' => ' . self::phpEncodeValue($array[$key]), array_keys($array) ); return '[' . implode(', ', $values) . ']'; } } debug($message); } public static function isEnabled(): bool { return (bool)self::$output; } public static function pause(array $vars = []): void { if (!self::isEnabled()) { return; } $pauseShell = new PauseShell(); $psy = $pauseShell->getShell(); $psy->setScopeVariables($vars); $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3); foreach ($backtrace as $backtraceStep) { $class = $backtraceStep['class'] ?? null; $fn = $backtraceStep['function'] ?? null; if ($class === Debug::class && $fn === 'pause') { continue; } if ($fn === 'codecept_pause' && !$class) { continue; } if (!isset($backtraceStep['object'])) { continue; } $pauseShell->addMessage('Use $this-> to access current object'); $psy->setBoundObject($backtraceStep['object']); break; } $psy->run(); } public static function confirm($question) { if (!self::$output) { return; } $questionHelper = new QuestionHelper(); return $questionHelper->ask(new ArgvInput(), self::$output, new ConfirmationQuestion($question)); } } respectively */ private static function fsCaseStrCmp(string $str1, string $str2, string $dirSep = DIRECTORY_SEPARATOR): int { $cmpFn = self::isWindows($dirSep) ? 'strcasecmp' : 'strcmp'; return $cmpFn($str1, $str2); } /** * What part of this path (leftmost 0-3 characters) what * it is absolute relative to: * * On Unix: * This is simply '/' for an absolute path or * '' for a relative path * * On Windows this is more complicated: * If the first two characters are a letter followed * by a ':', this indicates that the path is * on a specific device. * With or without a device specified, a path MAY * start with a '\\' to indicate an absolute path * on the device or '' to indicate a path relative * to the device's CWD * * @return array */ private static function getPathAbsolutenessPrefix(string $path, string $dirSep = DIRECTORY_SEPARATOR): array { $devLetterPrefixPattern = ''; if (self::isWindows($dirSep)) { $devLetterPrefixPattern = '([A-Za-z]:|)'; } $matches = []; if (!preg_match('/^' . $devLetterPrefixPattern . preg_quote($dirSep, '/') . '?/', $path, $matches)) { // This should match, even if it matches 0 characters throw new ConfigurationException("INTERNAL ERROR: This must be a regex problem."); } return [ 'wholePrefix' => $matches[0], // The optional device letter followed by the optional $dirSep 'devicePrefix' => self::isWindows($dirSep) ? $matches[1] : '']; } /** * Are we in a Windows style filesystem? */ private static function isWindows(string $dirSep = DIRECTORY_SEPARATOR): bool { return ($dirSep == '\\'); } public static function isPathAbsolute(string $path): bool { if (DIRECTORY_SEPARATOR === '/') { return substr($path, 0, 1) === DIRECTORY_SEPARATOR; } return preg_match('#^[A-Z]:(?![^/\\\])#i', $path) === 1; } } isDir()) { rmdir((string)$path); } else { unlink((string)$path); } } } public static function deleteDir(string $dir): bool { if (!file_exists($dir)) { return true; } if (!is_dir($dir) || is_link($dir)) { return @unlink($dir); } if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { $dir = str_replace('/', '\\', $dir); exec('rd /s /q "' . $dir . '"'); return true; } foreach (scandir($dir) as $item) { if ($item === '.' || $item === '..') { continue; } if (!self::deleteDir($dir . DIRECTORY_SEPARATOR . $item)) { chmod($dir . DIRECTORY_SEPARATOR . $item, 0777); if (!self::deleteDir($dir . DIRECTORY_SEPARATOR . $item)) { return false; } } } return @rmdir($dir); } public static function copyDir(string $src, string $dst): void { $dir = opendir($src); @mkdir($dst); while (false !== ($file = readdir($dir))) { if (($file != '.') && ($file != '..')) { if (is_dir($src . DIRECTORY_SEPARATOR . $file)) { self::copyDir($src . DIRECTORY_SEPARATOR . $file, $dst . DIRECTORY_SEPARATOR . $file); } else { copy($src . DIRECTORY_SEPARATOR . $file, $dst . DIRECTORY_SEPARATOR . $file); } } } closedir($dir); } } ClassName $mapped_file = self::loadMappedFile('\\', $relative_class); if ($mapped_file) { return $mapped_file; } } return false; } /** * Load the mapped file for a namespace prefix and relative class. * * @param string $prefix The namespace prefix. * @param string $relativeClass The relative class name. * @return string|false Boolean false if no mapped file can be loaded, or the name of the mapped file that was loaded. */ protected static function loadMappedFile(string $prefix, string $relativeClass): string|false { if (!isset(self::$map[$prefix])) { return false; } foreach (self::$map[$prefix] as $baseDir) { $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php'; // 'static' is for testing purposes if (static::requireFile($file)) { return $file; } } return false; } protected static function requireFile($file): bool { if (file_exists($file)) { require_once $file; return true; } return false; } } fetch('guy'); * Annotation::forClass('MyTestCase')->method('testData')->fetch('depends'); * Annotation::forClass('MyTestCase')->method('testData')->fetchAll('depends'); * ``` */ public static function forClass(object|string $class): self { if (is_object($class)) { $class = get_class($class); } if (!isset(static::$reflectedClasses[$class])) { static::$reflectedClasses[$class] = new ReflectionClass($class); } return new self(static::$reflectedClasses[$class]); } public static function forMethod(object|string $class, string $method): self { return self::forClass($class)->method($method); } /** * Parses raw comment for annotations */ public static function fetchAnnotationsFromDocblock(string $annotation, string $docblock): array { if (preg_match_all(sprintf(self::$regex, $annotation), $docblock, $matched)) { return $matched[1]; } return []; } /** * Fetches all available annotations */ public static function fetchAllAnnotationsFromDocblock(string $docblock): array { $annotations = []; if (!preg_match_all(sprintf(self::$regex, '(\w+)'), $docblock, $matched)) { return $annotations; } foreach ($matched[1] as $k => $annotation) { if (!isset($annotations[$annotation])) { $annotations[$annotation] = []; } $annotations[$annotation][] = $matched[2][$k]; } return $annotations; } public function __construct(ReflectionClass $reflectionClass) { $this->currentReflectedItem = $reflectionClass; $this->reflectedClass = $reflectionClass; } public function method(string $method): self { $this->currentReflectedItem = $this->reflectedClass->getMethod($method); return $this; } public function fetch(string $annotation): ?string { $attr = $this->attribute($annotation); if ($attr) { $arguments = $attr->getArguments(); if (count($arguments) === 0) { return ''; } return $arguments[0]; } $docBlock = (string)$this->currentReflectedItem->getDocComment(); if (preg_match(sprintf(self::$regex, $annotation), $docBlock, $matched)) { return $matched[1]; } return null; } public function fetchAll(string $annotation): array { $attr = $this->attribute($annotation); if ($attr) { if (!$attr->isRepeated()) { return $attr->getArguments(); } $attrs = $this->attributes(); if ($annotation === 'example') { $annotation = 'examples'; // we renamed this annotation } $name = ucfirst($annotation); $attrs = array_filter($attrs, fn ($a) => $a->getName() === "Codeception\\Attribute\\$name"); if ($annotation === 'examples') { return array_map(fn (\ReflectionAttribute $a) => $a->getArguments(), $attrs); } return array_merge(...array_map(fn (\ReflectionAttribute $a) => $a->getArguments(), $attrs)); } $docBlock = (string)$this->currentReflectedItem->getDocComment(); if (preg_match_all(sprintf(self::$regex, $annotation), $docBlock, $matched)) { return $matched[1]; } return []; } public function attributes(): array { $attrs = $this->currentReflectedItem->getAttributes(); $attrs = array_filter($attrs); $attrs = array_filter($attrs, fn (\ReflectionAttribute $a) => str_starts_with($a->getName(), 'Codeception\\Attribute\\')); return $attrs; } public function attribute($name): ?\ReflectionAttribute { $attrs = $this->attributes(); if ($name === 'example') { $name = 'examples'; // we renamed this annotation } $name = ucfirst($name); $attrs = array_filter($attrs, fn ($a) => $a->getName() === "Codeception\\Attribute\\$name"); if (empty($attrs)) { return null; } return reset($attrs); } public function raw(): string|false { return $this->currentReflectedItem->getDocComment(); } /** * Returns an associative array value of annotation * Either JSON or Doctrine-annotation style allowed * Returns null if not a valid array data */ public static function arrayValue(string $annotation): ?array { $annotation = trim($annotation); $openingBrace = substr($annotation, 0, 1); // json-style data format if (in_array($openingBrace, ['{', '['])) { return json_decode($annotation, true, 512, JSON_THROW_ON_ERROR); } // doctrine-style data format if ($openingBrace === '(') { preg_match_all('#(\w+)\s*?=\s*?"(.*?)"\s*?[,)]#', $annotation, $matches, PREG_SET_ORDER); $data = []; foreach ($matches as $item) { $data[$item[1]] = $item[2]; } return $data; } return null; } } getPrevious() ? $e->getPrevious()->getTrace() : $e->getTrace(); $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(array $step): bool { if (!isset($step['class'])) { return false; } $className = $step['class']; foreach (self::$filteredClassesPattern as $filteredClassName) { if (str_starts_with($className, $filteredClassName)) { return true; } } return false; } protected static function fileIsFiltered(array $step): bool { if (!isset($step['file'])) { return false; } if (str_contains($step['file'], 'codecept.phar/')) { return true; } if (str_contains($step['file'], 'vendor' . DIRECTORY_SEPARATOR . 'phpunit')) { return true; } if (str_contains($step['file'], 'vendor' . DIRECTORY_SEPARATOR . 'codeception')) { return true; } $modulePath = 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR . 'Module'; if (str_contains($step['file'], $modulePath)) { return false; // don`t filter modules } if (str_contains($step['file'], 'src' . DIRECTORY_SEPARATOR . 'Codeception' . DIRECTORY_SEPARATOR)) { return true; } return false; } private static function frameExists(array $trace, string $file, int $line): bool { foreach ($trace as $frame) { if ( isset($frame['file']) && $frame['file'] == $file && isset($frame['line']) && $frame['line'] == $line ) { return true; } } return false; } } posts->comments->count(); * ``` */ class Maybe implements ArrayAccess, Iterator, JsonSerializable { protected int $position = 0; protected mixed $val = null; protected ?bool $assocArray = null; public function __construct(mixed $val = null) { $this->val = $val; if (is_array($this->val)) { $this->assocArray = $this->isAssocArray($this->val); } $this->position = 0; } private function isAssocArray(array $arr): bool { return array_keys($arr) !== range(0, count($arr) - 1); } public function __toString(): string { if ($this->val === null) { return '?'; } return (string)$this->val; } public function __get(string $key): Maybe { if ($this->val === null) { return new Maybe(); } if (is_object($this->val) && (isset($this->val->{$key}) || property_exists($this->val, $key))) { return $this->val->{$key}; } return $this->val->key; } public function __set(string $key, $val) { if ($this->val === null) { return; } if (is_object($this->val)) { $this->val->{$key} = $val; return; } $this->val->key = $val; } public function __call(string $method, array $args) { if ($this->val === null) { return new Maybe(); } return call_user_func_array([$this->val, $method], $args); } public function __clone() { if (is_object($this->val)) { $this->val = clone $this->val; } } public function __unset(string $key) { if (is_object($this->val) && (isset($this->val->{$key}) || property_exists($this->val, $key))) { unset($this->val->{$key}); } } public function offsetExists(mixed $offset): bool { if (is_array($this->val) || ($this->val instanceof ArrayAccess)) { return isset($this->val[$offset]); } return false; } public function offsetGet(mixed $offset): Maybe { if (is_array($this->val) || $this->val instanceof ArrayAccess) { return $this->val[$offset]; } return new Maybe(); } public function offsetSet(mixed $offset, mixed $value): void { if (is_array($this->val) || ($this->val instanceof ArrayAccess)) { $this->val[$offset] = $value; } } public function offsetUnset(mixed $offset): void { if (is_array($this->val) || ($this->val instanceof ArrayAccess)) { unset($this->val[$offset]); } } public function value() { $val = $this->val; if (is_array($val)) { foreach ($val as $k => $v) { if ($v instanceof self) { $v = $v->value(); } $val[$k] = $v; } } return $val; } /** * Return the current element * @link https://php.net/manual/en/iterator.current.php * @return mixed Can return any type. */ public function current(): mixed { if (!is_array($this->val)) { return null; } if ($this->assocArray) { $keys = array_keys($this->val); return $this->val[$keys[$this->position]]; } return $this->val[$this->position]; } /** * Move forward to next element * @link https://php.net/manual/en/iterator.next.php * @return void Any returned value is ignored. */ public function next(): void { ++$this->position; } /** * Return the key of the current element * @link https://php.net/manual/en/iterator.key.php * @return int|string|null scalar on success, or null on failure. */ public function key(): mixed { if ($this->assocArray) { $keys = array_keys($this->val); return $keys[$this->position]; } return $this->position; } /** * Checks if current position is valid * @link https://php.net/manual/en/iterator.valid.php * @return bool The return value will be casted to boolean and then evaluated. * Returns true on success or false on failure. */ public function valid(): bool { if (!is_array($this->val)) { return false; } if ($this->assocArray) { $keys = array_keys($this->val); return isset($keys[$this->position]); } return isset($this->val[$this->position]); } /** * Rewind the Iterator to the first element * @link https://php.net/manual/en/iterator.rewind.php * @return void Any returned value is ignored. */ public function rewind(): void { if (is_array($this->val)) { $this->assocArray = $this->isAssocArray($this->val); } $this->position = 0; } /** * Specify data which should be serialized to JSON * @link https://php.net/manual/en/jsonserializable.jsonserialize.php * @return mixed data which can be serialized by json_encode(), * which is a value of any type other than a resource. */ public function jsonSerialize(): mixed { return $this->value(); } } breakParts($class); return array_pop($namespaces); } protected function getNamespaceString(string $class): string { $namespaces = $this->getNamespaces($class); return implode('\\', $namespaces); } protected function getNamespaceHeader(string $class): string { $str = $this->getNamespaceString($class); if (!$str) { return ""; } return "\nnamespace {$str};\n"; } protected function getNamespaces(string $class): array { $namespaces = $this->breakParts($class); array_pop($namespaces); return array_filter($namespaces, 'strlen'); } } vars[$var] = $val; return $this; } /** * Sets all template vars */ public function setVars(array $vars): void { $this->vars = $vars; } public function getVar(string $name) { if (isset($this->vars[$name])) { return $this->vars[$name]; } return null; } /** * Fills up template string with placed variables. */ public function produce(): string { $result = $this->template; $regex = sprintf('~%s([\w\.]+)%s~m', $this->placeholderStart, $this->placeholderEnd); $matched = preg_match_all($regex, $result, $matches, PREG_SET_ORDER); if (!$matched) { return $result; } foreach ($matches as $match) { // fill in placeholders $placeholder = $match[1]; $value = $this->vars; foreach (explode('.', trim($placeholder, '\'"')) as $segment) { if (is_array($value) && array_key_exists($segment, $value)) { $value = $value[$segment]; } else { continue 2; } } if ($this->encoderFunction !== null) { $value = ($this->encoderFunction)($value); } elseif (!is_string($value)) { $value = (string)$value; } $result = str_replace($this->placeholderStart . $placeholder . $this->placeholderEnd, $value, $result); } return $result; } } click('do')->click('undo'); * ActionSequence::build()->click('do')->click('undo'); * ``` * * @method $this see($text, $selector = null) * @method $this dontSee($text, $selector = null) * @method $this seeElement($selector, $attributes = []) * @method $this dontSeeElement($selector, $attributes = []) * @method $this click($link, $context = null) * @method $this wait($timeout) * @method $this waitForElementChange($element, Closure $callback, $timeout = 30) * @method $this waitForElement($element, $timeout = 10) * @method $this waitForElementVisible($element, $timeout = 10) * @method $this waitForElementNotVisible($element, $timeout = 10) * @method $this waitForText($text, $timeout = 10, $selector = null) * @method $this submitForm($selector, array $params, $button = null) * @method $this seeLink($text, $url = null) * @method $this dontSeeLink($text, $url = null) * @method $this seeCheckboxIsChecked($checkbox) * @method $this dontSeeCheckboxIsChecked($checkbox) * @method $this seeInField($field, $value) * @method $this dontSeeInField($field, $value) * @method $this seeInFormFields($formSelector, array $params) * @method $this dontSeeInFormFields($formSelector, array $params) * @method $this selectOption($select, $option) * @method $this checkOption($option) * @method $this uncheckOption($option) * @method $this fillField($field, $value) * @method $this attachFile($field, $filename) * @method $this seeNumberOfElements($selector, $expected) * @method $this seeOptionIsSelected($selector, $optionText) * @method $this dontSeeOptionIsSelected($selector, $optionText) */ class ActionSequence { /** * @var Action[] */ protected array $actions = []; /** * Creates an instance */ public static function build(): self { return new self(); } public function __call(string $action, array $arguments): self { $this->addAction($action, $arguments); return $this; } protected function addAction(string $action, $arguments): void { if (!is_array($arguments)) { $arguments = [$arguments]; } $this->actions[] = new Action($action, $arguments); } /** * Creates action sequence from associative array, * where key is action, and value is action arguments */ public function fromArray(array $actions): self { foreach ($actions as $action => $arguments) { $this->addAction($action, $arguments); } return $this; } /** * Returns a list of logged actions as associative array * * @return Action[] */ public function getActions(): array { return $this->actions; } /** * Executes sequence of action as methods of passed object. */ public function run(object $context): void { foreach ($this->actions as $step) { codecept_debug("- {$step}"); try { call_user_func_array([$context, $step->getAction()], $step->getArguments()); } catch (Exception $exception) { $class = get_class($exception); // rethrow exception for a specific action throw new $class($exception->getMessage() . "\nat {$step}"); } } } public function __toString(): string { $actionsLog = []; foreach ($this->actions as $step) { $args = str_replace('"', "'", $step->getArgumentsAsString(20)); $actionsLog[] = $step->getAction() . ": {$args}"; } return implode(', ', $actionsLog); } } addStyles($output); $this->output = $output; } /** * Change the directory where Codeception should be installed. */ public function initDir(string $workDir): void { $this->checkInstalled($workDir); $this->sayInfo("Initializing Codeception in {$workDir}"); $this->createDirectoryFor($workDir); chdir($workDir); $this->workDir = $workDir; } /** * Override this class to create customized setup. * * @return mixed */ abstract public function setup(); /** * ```php * ask('select the browser of your choice', 'firefox'); * * // propose firefox or chrome possible options * $this->ask('select the browser of your choice', ['firefox', 'chrome']); * * // ask true/false question * $this->ask('do you want to proceed (y/n)', true); * ``` * * @return mixed|string */ protected function ask(string $question, string|bool|array $answer = null): mixed { $question = "? {$question}"; $dialog = new QuestionHelper(); if (is_array($answer)) { $question .= " (" . $answer[0] . ") "; return $dialog->ask($this->input, $this->output, new ChoiceQuestion($question, $answer, 0)); } if (is_bool($answer)) { $question .= " (y/n) "; return $dialog->ask($this->input, $this->output, new ConfirmationQuestion($question, $answer)); } if (is_string($answer)) { $question .= " ({$answer})"; } return $dialog->ask($this->input, $this->output, new Question("{$question} ", $answer)); } /** * Print a message to console. * * ```php * say('Welcome to Setup'); * ``` */ protected function say(string $message = ''): void { $this->output->writeln($message); } /** * Print a successful message */ protected function saySuccess(string $message): void { $this->say(" {$message} "); } /** * Print error message */ protected function sayError(string $message): void { $this->say(" {$message} "); } /** * Print warning message */ protected function sayWarning(string $message): void { $this->say(" {$message} "); } /** * Print info message */ protected function sayInfo(string $message): void { $this->say(" {$message}"); } /** * Create a helper class inside a directory */ protected function createHelper(string $name, string $directory, array $settings = []): void { $file = $this->createDirectoryFor( $dir = $directory . DIRECTORY_SEPARATOR . "Helper", "{$name}.php" ) . "{$name}.php"; $gen = new Helper($settings, $name); // generate helper $this->createFile( $file, $gen->produce() ); require_once $file; $this->sayInfo("{$name} helper has been created in {$dir}"); } /** * Create an empty directory and add a placeholder file into it */ protected function createEmptyDirectory(string $dir): void { $this->createDirectoryFor($dir); $this->createFile($dir . DIRECTORY_SEPARATOR . '.gitkeep', ''); } protected function gitIgnore(string $path): void { file_put_contents($path . DIRECTORY_SEPARATOR . self::GIT_IGNORE, "*\n!" . self::GIT_IGNORE . "\n"); } protected function checkInstalled(string $dir = '.'): void { if (file_exists($dir . DIRECTORY_SEPARATOR . 'codeception.yml') || file_exists($dir . DIRECTORY_SEPARATOR . 'codeception.dist.yml')) { throw new Exception("Codeception is already installed in this directory"); } } /** * Create an Actor class and generate actions for it. * Requires a suite config as array in 3rd parameter. * @param array $suiteConfig */ protected function createActor(string $name, string $directory, array $suiteConfig): void { $file = $this->createDirectoryFor( $directory, $name ) . $this->getShortClassName($name); $file .= '.php'; $suiteConfig['namespace'] = $this->namespace; $config = Configuration::mergeConfigs(Configuration::$defaultSuiteSettings, $suiteConfig); $actorGenerator = new Actor($config); $content = $actorGenerator->produce(); $this->createFile($file, $content); $this->sayInfo("{$name} actor has been created in {$directory}"); $actionsGenerator = new Actions($config); $content = $actionsGenerator->produce(); $generatedDir = $directory . DIRECTORY_SEPARATOR . '_generated'; $this->createDirectoryFor($generatedDir, 'Actions.php'); $this->createFile($generatedDir . DIRECTORY_SEPARATOR . $actorGenerator->getActorName() . 'Actions.php', $content); $this->sayInfo("Actions have been loaded"); } protected function addModulesToComposer(array $modules): ?int { $packages = ModuleContainer::$packages; $section = null; if (!file_exists('composer.json')) { $this->say(''); $this->sayWarning('Can\'t locate composer.json, please add following packages into "require-dev" section of composer.json:'); $this->say(''); foreach (array_unique($modules) as $module) { if (!isset($packages[$module])) { continue; } $package = $packages[$module]; $this->say(sprintf('"%s": "%s"', $package, "*")); $composer[$section][$package] = "*"; } $this->say(''); return null; } $composer = json_decode(file_get_contents('composer.json'), true, 512, JSON_THROW_ON_ERROR); if ($composer === null) { throw new Exception("Invalid composer.json file. JSON can't be decoded"); } $section = null; if (isset($composer['require']) && isset($composer['require']['codeception/codeception'])) { $section = 'require'; } if (isset($composer['require-dev']) && isset($composer['require-dev']['codeception/codeception'])) { $section = 'require-dev'; } if ($section === null) { $section = 'require'; } $packageCounter = 0; foreach (array_unique($modules) as $module) { if (!isset($packages[$module])) { continue; } $package = $packages[$module]; if (isset($composer[$section][$package])) { continue; } $this->sayInfo("Adding {$package} for {$module} to composer.json"); $composer[$section][$package] = "*"; ++$packageCounter; } file_put_contents('composer.json', json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); if ($packageCounter !== 0) { $this->say("{$packageCounter} new packages added to {$section}"); } if ($packageCounter && $this->ask('composer.json updated. Do you want to run "composer update"?', true)) { $this->sayInfo('Running composer update'); exec('composer update', $output, $status); if ($status !== 0) { $this->sayInfo('Composer installation failed. Please check composer.json and try to run "composer update" manually'); return null; } if (!empty($composer['config']['vendor_dir'])) { $this->updateComposerClassMap($composer['config']['vendor_dir']); } else { $this->updateComposerClassMap(); } } return $packageCounter; } private function updateComposerClassMap(string $vendorDir = 'vendor'): void { $loader = require $vendorDir . '/autoload.php'; $classMap = require $vendorDir . '/composer/autoload_classmap.php'; $loader->addClassMap($classMap); $map = require $vendorDir . '/composer/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } } } file = $traceLine['file']; $this->line = $traceLine['line']; $this->addMetaStep($traceLine, $stack); } private function isTestFile(string $file) { return preg_match('#[^\\' . DIRECTORY_SEPARATOR . '](Cest|Cept|Test).php$#', $file); } public function getName(): string { $class = explode('\\', __CLASS__); return end($class); } public function getAction(): string { return $this->action; } public function getFilePath(): ?string { if ($this->file) { return codecept_relative_path($this->file); } return null; } public function getLineNumber(): ?int { if ($this->line) { return $this->line; } return null; } public function hasFailed(): bool { return $this->failed; } public function getArguments(): array { return $this->arguments; } public function getArgumentsAsString(int $maxLength = self::DEFAULT_MAX_LENGTH): string { $arguments = $this->arguments; $argumentCount = count($arguments); $totalLength = $argumentCount - 1; // count separators before adding length of individual arguments foreach ($arguments as $key => $argument) { $stringifiedArgument = $this->stringifyArgument($argument); $arguments[$key] = $stringifiedArgument; $totalLength += mb_strlen($stringifiedArgument, 'utf-8'); } if ($totalLength > $maxLength && $maxLength > 0) { //sort arguments from shortest to longest uasort($arguments, function ($arg1, $arg2): int { $length1 = mb_strlen($arg1, 'utf-8'); $length2 = mb_strlen($arg2, 'utf-8'); if ($length1 === $length2) { return 0; } return ($length1 < $length2) ? -1 : 1; }); $allowedLength = floor(($maxLength - $argumentCount + 1) / $argumentCount); $lengthRemaining = $maxLength; $argumentsRemaining = $argumentCount; foreach ($arguments as $key => $argument) { --$argumentsRemaining; if (mb_strlen($argument, 'utf-8') > $allowedLength) { $arguments[$key] = mb_substr($argument, 0, (int)$allowedLength - 4, 'utf-8') . '...' . mb_substr($argument, -1, 1, 'utf-8'); $lengthRemaining -= ($allowedLength + 1); } else { $lengthRemaining -= (mb_strlen($argument, 'utf-8') + 1); //recalculate allowed length because this argument was short if ($argumentsRemaining > 0) { $allowedLength = floor(($lengthRemaining - $argumentsRemaining + 1) / $argumentsRemaining); } } } //restore original order of arguments ksort($arguments); } return implode(',', $arguments); } protected function stringifyArgument(mixed $argument): string { if (is_string($argument)) { return '"' . strtr($argument, ["\n" => '\n', "\r" => '\r', "\t" => ' ']) . '"'; } elseif (is_resource($argument)) { $argument = (string)$argument; } elseif (is_array($argument)) { foreach ($argument as $key => $value) { if (is_object($value)) { $argument[$key] = $this->getClassName($value); } } } elseif (is_object($argument)) { if ($argument instanceof FormattedOutput) { $argument = $argument->getOutput(); } elseif (method_exists($argument, '__toString')) { $argument = (string)$argument; } elseif ($argument::class === 'Facebook\WebDriver\WebDriverBy') { $argument = Locator::humanReadableString($argument); } elseif ($argument instanceof Constraint) { $argument = $argument->toString(); } else { $argument = $this->getClassName($argument); } } $arg_str = json_encode($argument, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); return str_replace('\"', '"', $arg_str); } protected function getClassName(object $argument): string { if ($argument instanceof Closure) { return Closure::class; } elseif ($argument instanceof MockObject) { $parentClass = get_parent_class($argument); $reflection = new \ReflectionClass($argument); if ($parentClass !== false) { return $this->formatClassName($parentClass); } $interfaces = $reflection->getInterfaceNames(); foreach ($interfaces as $interface) { if (str_starts_with($interface, 'PHPUnit\\')) { continue; } if (str_starts_with($interface, 'Codeception\\')) { continue; } return $this->formatClassName($interface); } } return $this->formatClassName($argument::class); } protected function formatClassName(string $classname): string { return trim($classname, "\\"); } public function getPhpCode(int $maxLength): string { $result = "\${$this->prefix}->" . $this->getAction() . '('; $maxLength = $maxLength - mb_strlen($result, 'utf-8') - 1; return $result . ($this->getHumanizedArguments($maxLength) . ')'); } public function getMetaStep(): ?MetaStep { return $this->metaStep; } public function __toString(): string { $humanizedAction = $this->humanize($this->getAction()); return $humanizedAction . ' ' . $this->getHumanizedArguments(); } public function toString(int $maxLength): string { $humanizedAction = $this->humanize($this->getAction()); $maxLength = $maxLength - mb_strlen($humanizedAction, 'utf-8') - 1; return $humanizedAction . ' ' . $this->getHumanizedArguments($maxLength); } public function getHtml(string $highlightColor = '#732E81'): string { if (empty($this->arguments)) { return sprintf('%s %s', ucfirst($this->prefix), $this->humanize($this->getAction())); } return sprintf('%s %s %s', ucfirst($this->prefix), htmlspecialchars($this->humanize($this->getAction()), ENT_QUOTES | ENT_SUBSTITUTE), $highlightColor, htmlspecialchars($this->getHumanizedArguments(0), ENT_QUOTES | ENT_SUBSTITUTE)); } public function getHumanizedActionWithoutArguments(): string { return $this->humanize($this->getAction()); } public function getHumanizedArguments(int $maxLength = self::DEFAULT_MAX_LENGTH): string { return $this->getArgumentsAsString($maxLength); } protected function clean(string $text): string { return str_replace('\/', '', $text); } protected function humanize(string $text): string { $text = preg_replace('#([A-Z]+)([A-Z][a-z])#', '\\1 \\2', $text); $text = preg_replace('#([a-z\d])([A-Z])#', '\\1 \\2', $text); $text = preg_replace('#\bdont\b#', "don't", $text); return mb_strtolower($text, 'UTF-8'); } /** * @return mixed */ public function run(ModuleContainer $container = null) { $this->executed = true; if ($container === null) { return null; } $activeModule = $container->moduleForAction($this->action); if (!is_callable([$activeModule, $this->action])) { throw new RuntimeException("Action '{$this->action}' can't be called"); } try { $res = call_user_func_array([$activeModule, $this->action], $this->arguments); } catch (Exception $e) { if ($this->isTry) { throw $e; } $this->failed = true; $this->getMetaStep()?->setFailed(true); throw $e; } return $res; } /** * If steps are combined into one method they can be reproduced as meta-step. * We are using stack trace to analyze if steps were called from test, if not - they were called from meta-step. */ protected function addMetaStep(array $step, array $stack): void { if (($this->isTestFile($this->file)) || ($step['class'] == Scenario::class)) { return; } $i = count($stack) - self::STACK_POSITION - 1; // get into test file and retrieve its actual call while (isset($stack[$i])) { $step = $stack[$i]; --$i; if (!isset($step['file']) || !isset($step['function']) || !isset($step['class'])) { continue; } if (!$this->isTestFile($step['file'])) { continue; } // in case arguments were passed by reference, copy args array to ensure dereference. array_values() does not dereference values $this->metaStep = new Step\Meta($step['function'], array_map(fn ($i) => $i, array_values($step['args']))); $this->metaStep->setTraceInfo($step['file'], $step['line']); // page objects or other classes should not be included with "I" if (!in_array(Actor::class, class_parents($step['class']))) { if (isset($step['object'])) { $this->metaStep->setPrefix($step['object']::class . ':'); return; } $this->metaStep->setPrefix($step['class'] . ':'); } return; } } public function setMetaStep(?MetaStep $metaStep): void { $this->metaStep = $metaStep; } public function getPrefix(): string { return $this->prefix . ' '; } } assertSame($this->dataSet, $data, "Snapshot doesn't match real data"); } /** * Loads data set from file. */ protected function load(): void { if (!file_exists($this->getFileName())) { return; } $fileContents = file_get_contents($this->getFileName()); if ($this->saveAsJson) { $fileContents = json_decode($fileContents, false, 512, JSON_THROW_ON_ERROR); } $this->dataSet = $fileContents; if (!$this->dataSet) { throw new ContentNotFound("Loaded snapshot is empty"); } } /** * Saves data set to file */ protected function save(): void { $fileContents = $this->dataSet; if ($this->saveAsJson) { $fileContents = json_encode($fileContents, JSON_THROW_ON_ERROR); } file_put_contents($this->getFileName(), $fileContents); } /** * If no filename is defined, generates one from class name */ protected function getFileName(): string { if (!$this->fileName) { $this->fileName = preg_replace('#\W#', '.', $this::class) . '.' . $this->extension; } return codecept_data_dir() . $this->fileName; } /** * Performs assertion for data sets */ public function assert(): void { // fetch data $data = $this->fetchData(); if (!$data) { throw new ContentNotFound("Fetched snapshot is empty."); } $this->load(); if (!$this->dataSet) { $this->printDebug('Snapshot is empty. Updating snapshot...'); $this->dataSet = $data; $this->save(); return; } try { $this->assertData($data); $this->printDebug('Data matches snapshot'); } catch (AssertionFailedError $exception) { $this->printDebug('Snapshot assertion failed'); if (!is_bool($this->refresh)) { $confirm = Debug::confirm('Should we update snapshot with fresh data? (Y/n) '); } else { $confirm = $this->refresh; } if ($confirm) { $this->dataSet = $data; $this->save(); $this->printDebug('Snapshot data updated'); return; } if ($this->showDiff) { throw $exception; } $this->fail($exception->getMessage()); } } /** * Force update snapshot data. */ public function shouldRefreshSnapshot(bool $refresh = true): void { $this->refresh = $refresh; } /** * Show detailed diff if snapshot test fails */ public function shouldShowDiffOnFail(bool $showDiff = true): void { $this->showDiff = $showDiff; } /** * json_encode/json_decode the snapshot data on storing/reading. */ public function shouldSaveAsJson(bool $saveAsJson = true): void { $this->saveAsJson = $saveAsJson; } /** * Set the snapshot file extension. * By default it will be stored as `.json`. * * The file extension will not perform any formatting in the data, * it is only used as the snapshot file extension. */ public function setSnapshotFileExtension(string $fileExtension = 'json'): void { $this->extension = $fileExtension; } private function printDebug(string $message): void { Debug::debug($this::class . ': ' . $message); } } file = $file; $this->line = $line; } } message = $extension . "\n\n" . $this->message; } } getMessage(), $throwable->getCode(), $throwable->getFile(), $throwable->getLine() ); } } message = "Remote Application Error:\n" . $this->message; } } message = "Couldn't parse test '{$fileName}'"; if ($line !== null) { $this->message .= " on line {$line}"; } if ($errors) { $this->message .= PHP_EOL . $errors; } } } message = "[{$module}] module requirements not met --\n \n" . $this->message; } } message = "{$module} module conflicts with {$conflicted}\n\n--\n" . "This usually happens when you enable two modules with the same actions but with different backends.\n" . "For instance, you can't run PhpBrowser, WebDriver, Laravel5 modules in one suite,\n" . "as they implement similar methods but use different drivers to execute them.\n" . "You can load a part of module (like: ORM) to avoid conflict.\n" . $additional; } } message = $module . " module is not configured!\n \n" . $this->message; } } module = $module; parent::__construct($message); $this->message = "{$module}: {$this->message}"; } } _setUp(); } } protected function tearDown(): void { if (method_exists($this, '_tearDown')) { $this->_tearDown(); } } public static function setUpBeforeClass(): void { if (method_exists(get_called_class(), '_setUpBeforeClass')) { static::_setUpBeforeClass(); } } public static function tearDownAfterClass(): void { if (method_exists(get_called_class(), '_tearDownAfterClass')) { static::_tearDownAfterClass(); } } public function expectExceptionMessageRegExp(string $regularExpression): void { $this->expectExceptionMessageMatches($regularExpression); } public static function assertRegExp(string $pattern, string $string, string $message = ''): void { parent::assertMatchesRegularExpression($pattern, $string, $message); } public static function assertNotRegExp(string $pattern, string $string, string $message = ''): void { parent::assertDoesNotMatchRegularExpression($pattern, $string, $message); } public static function assertFileNotExists(string $filename, string $message = ''): void { parent::assertFileDoesNotExist($filename, $message); } } add(new Codeception\Command\Build('build')); $app->add(new Codeception\Command\Run('run')); $app->add(new Codeception\Command\Init('init')); $app->add(new Codeception\Command\Console('console')); $app->add(new Codeception\Command\Bootstrap('bootstrap')); $app->add(new Codeception\Command\GenerateCest('generate:cest')); $app->add(new Codeception\Command\GenerateTest('generate:test')); $app->add(new Codeception\Command\GenerateSuite('generate:suite')); $app->add(new Codeception\Command\GenerateHelper('generate:helper')); $app->add(new Codeception\Command\GenerateScenarios('generate:scenarios')); $app->add(new Codeception\Command\Clean('clean')); $app->add(new Codeception\Command\GenerateGroup('generate:groupobject')); $app->add(new Codeception\Command\GeneratePageObject('generate:pageobject')); $app->add(new Codeception\Command\GenerateStepObject('generate:stepobject')); $app->add(new Codeception\Command\GenerateSnapshot('generate:snapshot')); $app->add(new Codeception\Command\GenerateEnvironment('generate:environment')); $app->add(new Codeception\Command\GenerateFeature('generate:feature')); $app->add(new Codeception\Command\GherkinSnippets('gherkin:snippets')); $app->add(new Codeception\Command\GherkinSteps('gherkin:steps')); $app->add(new Codeception\Command\DryRun('dry-run')); $app->add(new Codeception\Command\ConfigValidate('config:validate')); // Suggests package if (class_exists('Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand')) { $app->add(new Codeception\Command\Completion()); } else { $app->add(new Codeception\Command\CompletionFallback()); } $app->registerCustomCommands(); // add only if within a phar archive. if ('phar:' === substr(__FILE__, 0, 5)) { $app->add(new Codeception\Command\SelfUpdate('self-update')); } $app->run(); }); */ protected static array $events = [ Events::SUITE_BEFORE => 'runBefore' ]; private array $processes = []; public function _initialize(): void { if (!class_exists(Process::class)) { throw new ExtensionException($this, 'symfony/process package is required'); } } public function runBefore(): void { $this->runProcesses(); $this->processMonitoring(); } private function runProcesses(): void { foreach ($this->config as $item) { if (is_array($item)) { $currentCommand = array_shift($item); $followingCommands = $item; } else { $currentCommand = $item; $followingCommands = []; } $process = $this->runProcess($currentCommand); $this->addProcessToMonitoring($process, $followingCommands); } } private function runProcess(string $command): Process { $this->output->debug('[RunBefore] Starting ' . $command); $process = Process::fromShellCommandline($command, $this->getRootDir()); $process->start(); return $process; } /** * @param string[] $followingCommands */ private function addProcessToMonitoring(Process $process, array $followingCommands): void { $this->processes[] = [ 'instance' => $process, 'following' => $followingCommands ]; } private function removeProcessFromMonitoring(int $index): void { unset($this->processes[$index]); } private function processMonitoring(): void { while (count($this->processes) !== 0) { $this->checkProcesses(); sleep(1); } } private function checkProcesses(): void { foreach ($this->processes as $index => $process) { /** * @var Process $processInstance */ $processInstance = $process['instance']; if (!$this->isRunning($processInstance)) { if (!$processInstance->isSuccessful()) { $this->output->debug('[RunBefore] Failed ' . $processInstance->getCommandLine()); $this->output->writeln('' . $processInstance->getErrorOutput() . ''); exit(1); } $this->output->debug('[RunBefore] Completed ' . $processInstance->getCommandLine()); $this->runFollowingCommand($process['following']); $this->removeProcessFromMonitoring($index); } } } /** * @param string[] $followingCommands */ private function runFollowingCommand(array $followingCommands): void { if ($followingCommands !== []) { $process = $this->runProcess(array_shift($followingCommands)); $this->addProcessToMonitoring($process, $followingCommands); } } private function isRunning(Process $process): bool { return $process->isRunning(); } } login(); * $I->amOnUrl('https://codeception.com'); * } * ``` * */ class Recorder extends Extension { protected array $config = [ 'delete_successful' => true, 'module' => 'WebDriver', 'template' => null, 'animate_slides' => true, 'ignore_steps' => [], 'success_color' => 'success', 'failure_color' => 'danger', 'error_color' => 'dark', 'delete_orphaned' => false, 'include_microseconds' => false, ]; protected string $template = << Recorder Result EOF; protected string $indicatorTemplate = << EOF; protected string $indexTemplate = << Recorder Results Index

Record #{{seed}}

    {{records}}
EOF; protected string $slidesTemplate = <<
EOF; public static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::SUITE_AFTER => 'afterSuite', Events::TEST_BEFORE => 'before', Events::TEST_ERROR => 'persist', Events::TEST_FAIL => 'persist', Events::TEST_SUCCESS => 'cleanup', Events::STEP_AFTER => 'afterStep', ]; protected ?\Codeception\Module $webDriverModule = null; protected ?string $dir = null; protected array $slides = []; protected int $stepNum = 0; protected ?string $seed = null; protected array $seeds = []; protected array $recordedTests = []; protected array $skipRecording = []; protected array $errorMessages = []; protected bool $colors = false; protected bool $ansi = false; protected array $timeStamps = []; private ?string $dateFormat = null; public function beforeSuite(): void { $this->webDriverModule = null; if (!$this->hasModule($this->config['module'])) { $this->writeln('Recorder is disabled, no available modules'); return; } $this->seed = uniqid(); $this->seeds[] = $this->seed; $this->webDriverModule = $this->getModule($this->config['module']); $this->skipRecording = []; $this->errorMessages = []; $this->dateFormat = $this->config['include_microseconds'] ? 'Y-m-d\TH:i:s.uP' : DATE_ATOM; $this->ansi = !isset($this->options['no-ansi']); $this->colors = !isset($this->options['no-colors']); if (!$this->webDriverModule instanceof ScreenshotSaver) { throw new ExtensionException( $this, 'You should pass module which implements ' . ScreenshotSaver::class . ' interface' ); } $this->writeln( sprintf( '⏺ Recording ⏺ step-by-step screenshots will be saved to %s', codecept_output_dir() ) ); $this->writeln("Directory Format: record_{$this->seed}_{filename}_{testname} ----"); } public function afterSuite(): void { if (!$this->webDriverModule) { return; } $links = ''; if (!empty($this->slides)) { foreach ($this->recordedTests as $suiteName => $suite) { $links .= "
  • {$suiteName}
    • "; foreach ($suite as $fileName => $tests) { $links .= "
    • {$fileName}
      • "; foreach ($tests as $test) { $links .= in_array($test['path'], $this->skipRecording, true) ? "
      • config['error_color']}\">{$test['name']}
      • \n" : '
      • {$test['name']}
      • \n"; } $links .= '
      '; } $links .= '
'; } $indexHTML = (new Template($this->indexTemplate)) ->place('seed', $this->seed) ->place('records', $links) ->produce(); try { file_put_contents(codecept_output_dir() . 'records.html', $indexHTML); } catch (Exception $exception) { $this->writeln( "⏺ An exception occurred while saving records.html: {$exception->getMessage()}" ); } $this->writeln('⏺ Records saved into: file://' . codecept_output_dir() . 'records.html'); } foreach ($this->errorMessages as $message) { $this->writeln($message); } } public function before(TestEvent $event): void { if (!$this->webDriverModule) { return; } $this->dir = null; $this->stepNum = 0; $this->slides = []; $this->timeStamps = []; $this->dir = codecept_output_dir() . "record_{$this->seed}_{$this->getTestName($event)}"; $testPath = codecept_relative_path(Descriptor::getTestFullName($event->getTest())); try { !is_dir($this->dir) && !mkdir($this->dir) && !is_dir($this->dir); } catch (Exception $exception) { $this->skipRecording[] = $testPath; $this->appendErrorMessage( $testPath, "⏺ An exception occurred while creating directory: {$this->dir}" ); } } public function cleanup(TestEvent $event): void { if ($this->config['delete_orphaned']) { $recordingDirectories = []; $directories = new DirectoryIterator(codecept_output_dir()); // getting a list of currently present recording directories foreach ($directories as $directory) { preg_match('/^record_(.*?)_[^\n]+.php_[^\n]+$/', $directory->getFilename(), $match); if (isset($match[1])) { $recordingDirectories[$match[1]][] = codecept_output_dir() . $directory->getFilename(); } } // removing orphaned recording directories foreach (array_diff(array_keys($recordingDirectories), $this->seeds) as $orphanedSeed) { foreach ($recordingDirectories[$orphanedSeed] as $orphanedDirectory) { FileSystem::deleteDir($orphanedDirectory); } } } if (!$this->webDriverModule || !$this->dir) { return; } if (!$this->config['delete_successful']) { $this->persist($event); return; } // deleting successfully executed tests FileSystem::deleteDir($this->dir); } public function persist(TestEvent $event): void { if (!$this->webDriverModule) { return; } $indicatorHtml = ''; $slideHtml = ''; $testName = $this->getTestName($event); $testPath = codecept_relative_path(Descriptor::getTestFullName($event->getTest())); $dir = codecept_output_dir() . "record_{$this->seed}_$testName"; $status = 'success'; if (strcasecmp($this->dir ?? '', $dir) !== 0) { $filename = str_pad('0', 3, '0', STR_PAD_LEFT) . '.png'; try { !is_dir($dir) && !mkdir($dir) && !is_dir($dir); $this->dir = $dir; } catch (Exception $exception) { $this->skipRecording[] = $testPath; $this->appendErrorMessage( $testPath, "⏺ An exception occurred while creating directory: {$dir}" ); } $this->slides = []; $this->timeStamps = []; $this->slides[$filename] = new Step\Action('encountered an unexpected error prior to the test execution'); $this->timeStamps[$filename] = (new DateTime())->format($this->dateFormat); $status = 'error'; try { if ($this->webDriverModule->webDriver === null) { throw new ExtensionException($this, 'Failed to save screenshot as webDriver is not set'); } $this->webDriverModule->webDriver->takeScreenshot($this->dir . DIRECTORY_SEPARATOR . $filename); } catch (Exception $exception) { $this->appendErrorMessage( $testPath, "⏺ Unable to capture a screenshot for {$testPath}/before" ); } } if (!in_array($testPath, $this->skipRecording, true)) { foreach ($this->slides as $i => $step) { /** @var Step $step */ if ($step->hasFailed()) { $status = 'failure'; } $indicatorHtml .= (new Template($this->indicatorTemplate)) ->place('step', (int)$i) ->place('isActive', (int)$i ? '' : 'active') ->produce(); $slideHtml .= (new Template($this->slidesTemplate)) ->place('image', $i) ->place('caption', $step->getHtml('#3498db')) ->place('isActive', (int)$i ? '' : 'active') ->place('isError', $status === 'success' ? '' : 'error') ->place('timeStamp', $this->timeStamps[$i]) ->produce(); } $html = (new Template($this->template)) ->place('indicators', $indicatorHtml) ->place('slides', $slideHtml) ->place('feature', ucfirst($event->getTest()->getFeature())) ->place('test', Descriptor::getTestSignature($event->getTest())) ->place('carousel_class', $this->config['animate_slides'] ? ' slide' : '') ->produce(); $indexFile = $this->dir . DIRECTORY_SEPARATOR . 'index.html'; $environment = $event->getTest()->getMetadata()->getCurrent('env') ?: ''; $suite = ucfirst(basename(dirname($event->getTest()->getMetadata()->getFilename()))); $testName = basename($event->getTest()->getMetadata()->getFilename()); try { file_put_contents($indexFile, $html); } catch (Exception $exception) { $this->skipRecording[] = $testPath; $this->appendErrorMessage( $testPath, "⏺ An exception occurred while saving index.html for {$testPath}: " . "{$exception->getMessage()}" ); } $this->recordedTests["{$suite} ({$environment})"][$testName][] = [ 'name' => $event->getTest()->getMetadata()->getName(), 'path' => $testPath, 'status' => $status, 'index' => substr($indexFile, strlen(codecept_output_dir())), ]; } } public function afterStep(StepEvent $event): void { if ($this->webDriverModule === null || $this->dir === null) { return; } if ($event->getStep() instanceof CommentStep) { return; } // only taking the ignore step into consideration if that step has passed if ($this->isStepIgnored($event) && !$event->getStep()->hasFailed()) { return; } $filename = str_pad((string)$this->stepNum, 3, '0', STR_PAD_LEFT) . '.png'; try { if ($this->webDriverModule->webDriver === null) { throw new ExtensionException($this, 'Failed to save screenshot as webDriver is not set'); } $this->webDriverModule->webDriver->takeScreenshot($this->dir . DIRECTORY_SEPARATOR . $filename); } catch (Exception $exception) { $testPath = codecept_relative_path(Descriptor::getTestFullName($event->getTest())); $this->appendErrorMessage( $testPath, "⏺ Unable to capture a screenshot for {$testPath}/{$event->getStep()->getAction()}" ); } ++$this->stepNum; $this->slides[$filename] = $event->getStep(); $this->timeStamps[$filename] = (new DateTime())->format($this->dateFormat); } protected function isStepIgnored(StepEvent $event): bool { $configIgnoredSteps = $this->config['ignore_steps']; $annotationIgnoredSteps = $event->getTest()->getMetadata()->getParam('skipRecording'); $ignoredSteps = array_unique( array_merge( $configIgnoredSteps, is_array($annotationIgnoredSteps) ? $annotationIgnoredSteps : [] ) ); foreach ($ignoredSteps as $stepPattern) { $stepRegexp = '/^' . str_replace('*', '.*?', $stepPattern) . '$/i'; if (preg_match($stepRegexp, $event->getStep()->getAction())) { return true; } if ( $event->getStep()->getMetaStep() !== null && preg_match($stepRegexp, $event->getStep()->getMetaStep()->getAction()) ) { return true; } } return false; } /** * @param StepEvent|TestEvent $event */ private function getTestName(Event $event): string { return basename($event->getTest()->getMetadata()->getFilename()) . '_' . preg_replace('/[^A-Za-z0-9\-\_]/', '_', $event->getTest()->getMetadata()->getName()); } protected function writeln(iterable|string $messages): void { parent::writeln( $this->ansi ? $messages : trim(preg_replace('/[ ]{2,}/', ' ', str_replace('⏺', '', $messages))) ); } private function appendErrorMessage(string $testPath, string $message): void { $this->errorMessages[$testPath] = array_merge( array_key_exists($testPath, $this->errorMessages) ? $this->errorMessages[$testPath] : [], [$message] ); } } */ protected array $config = ['sleep' => 0]; /** * @var array */ protected static array $events = [ Events::SUITE_BEFORE => 'runProcess', Events::SUITE_AFTER => 'stopProcess' ]; /** * @var Process[] */ private array $processes = []; public function _initialize(): void { if (!class_exists(Process::class)) { throw new ExtensionException($this, 'symfony/process package is required'); } } public function runProcess(): void { $this->processes = []; foreach ($this->config as $key => $command) { if (!$command) { continue; } if (!is_int($key)) { continue; // configuration options } $process = Process::fromShellCommandline($command, $this->getRootDir(), null, null, null); $process->start(); $this->processes[] = $process; $this->output->debug('[RunProcess] Starting ' . $command); } sleep($this->config['sleep']); } public function __destruct() { $this->stopProcess(); } public function stopProcess(): void { foreach (array_reverse($this->processes) as $process) { /** @var Process $process */ if (!$process->isRunning()) { continue; } $this->output->debug('[RunProcess] Stopping ' . $process->getCommandLine()); $process->stop(); } $this->processes = []; } /** * Disable the deserialization of the class to prevent attacker executing * code by leveraging the __destruct method. * * @see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection */ public function __sleep() { throw new \BadMethodCallException('Cannot serialize ' . __CLASS__); } /** * Disable the deserialization of the class to prevent attacker executing * code by leveraging the __destruct method. * * @see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection */ public function __wakeup() { throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__); } } */ public static array $events = [ Events::RESULT_PRINT_AFTER => 'saveFailed' ]; /** @var string filename/groupname for failed tests */ protected string $group = 'failed'; public function _initialize(): void { if (array_key_exists('fail-group', $this->config) && $this->config['fail-group']) { $this->group = $this->config['fail-group']; } $logPath = str_replace($this->getRootDir(), '', $this->getLogDir()); // get local path to logs $this->_reconfigure(['groups' => [$this->group => $logPath . $this->group]]); } public function saveFailed(PrintResultEvent $event): void { $file = $this->getLogDir() . $this->group; $result = $event->getResult(); if ($result->wasSuccessful()) { if (is_file($file)) { unlink($file); } return; } $output = []; foreach ($result->failures() as $fail) { $output[] = $this->localizePath(Descriptor::getTestFullName($fail->getTest())); } foreach ($result->errors() as $fail) { $output[] = $this->localizePath(Descriptor::getTestFullName($fail->getTest())); } file_put_contents($file, implode("\n", $output)); } protected function localizePath(string $path): string { $root = realpath($this->getRootDir()) . DIRECTORY_SEPARATOR; if (substr($path, 0, strlen($root)) === $root) { return substr($path, strlen($root)); } return $path; } } _reconfigure(['settings' => ['silent' => true]]); // turn off printing for everything else $this->standardReporter = new CodeceptConsole($this->options); $this->width = $this->standardReporter->detectWidth(); } /** * We are listening for events * * @var array */ public static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::TEST_SUCCESS => 'success', Events::TEST_FAIL => 'fail', Events::TEST_ERROR => 'error', Events::TEST_SKIPPED => 'skipped', Events::TEST_FAIL_PRINT => 'printFailed', Events::RESULT_PRINT_AFTER => 'afterResult', ]; public function beforeSuite(): void { $this->output->writeln(''); } public function success(): void { $this->printChar('.'); } public function fail(FailEvent $event): void { $this->printChar('F'); } public function error(FailEvent $event): void { $this->printChar('E'); } public function skipped(): void { $this->printChar('S'); } protected function printChar(string $char): void { if ($this->currentPos >= $this->width) { $this->output->writeln(''); $this->currentPos = 0; } $this->write($char); ++$this->currentPos; } public function printFailed(FailEvent $event): void { $this->standardReporter->printFail($event); } public function afterResult(PrintResultEvent $event): void { $this->output->writeln(''); $this->output->writeln(''); $this->standardReporter->afterResult($event); } } _reconfigure(['settings' => ['silent' => true]]); // turn off printing for everything else } /** * We are listening for events * * @var array */ public static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::TEST_END => 'after', Events::TEST_SUCCESS => 'success', Events::TEST_FAIL => 'fail', Events::TEST_ERROR => 'error', ]; public function beforeSuite(): void { $this->output->writeln(''); } public function success(): void { $this->output->write('[+] '); } public function fail(): void { $this->output->write('[-] '); } public function error(): void { $this->output->write('[E] '); } // we are printing test status and time taken public function after(TestEvent $event): void { $secondsInput = $event->getTime(); // See https://stackoverflow.com/q/16825240 $milliseconds = (int)($secondsInput * 1000); $seconds = (int)($milliseconds / 1000); $time = ($seconds % 60) . (($milliseconds === 0) ? '' : '.' . $milliseconds); $this->output->write(Descriptor::getTestSignature($event->getTest())); $this->output->writeln(' (' . $time . 's)'); } } -.log`. * * To enable this module add to your `codeception.yml`: * * ``` yaml * extensions: * enabled: [Codeception\Extension\Logger] * ``` * * #### Config * * * `max_files` (default: 3) - how many log files to keep * */ class Logger extends Extension { /** * @var array */ public static array $events = [ Events::SUITE_BEFORE => 'beforeSuite', Events::TEST_BEFORE => 'beforeTest', Events::TEST_AFTER => 'afterTest', Events::TEST_END => 'endTest', Events::STEP_BEFORE => 'beforeStep', Events::TEST_FAIL => 'testFail', Events::TEST_ERROR => 'testError', Events::TEST_INCOMPLETE => 'testIncomplete', Events::TEST_SKIPPED => 'testSkipped', ]; protected ?RotatingFileHandler $logHandler = null; protected static ?\Monolog\Logger $logger = null; protected ?string $path = null; /** * @var array */ protected array $config = ['max_files' => 3]; public function _initialize(): void { if (!class_exists('\Monolog\Logger')) { throw new ConfigurationException('Logger extension requires Monolog library to be installed'); } $this->path = $this->getLogDir(); // internal log $logHandler = new RotatingFileHandler($this->path . 'codeception.log', $this->config['max_files']); $formatter = $logHandler->getFormatter(); if ($formatter instanceof LineFormatter) { $formatter->ignoreEmptyContextAndExtra(true); } self::$logger = new \Monolog\Logger('Codeception'); self::$logger->pushHandler($logHandler); } public static function getLogger(): \Monolog\Logger { return self::$logger; } public function beforeSuite(SuiteEvent $event): void { $suiteLogFile = str_replace('\\', '_', $event->getSuite()->getName()) . '.log'; $this->logHandler = new RotatingFileHandler($this->path . $suiteLogFile, $this->config['max_files']); } public function beforeTest(TestEvent $event): void { self::$logger = new \Monolog\Logger(Descriptor::getTestFullName($event->getTest())); self::$logger->pushHandler($this->logHandler); self::$logger->info('------------------------------------'); self::$logger->info('STARTED: ' . ucfirst(Descriptor::getTestAsString($event->getTest()))); } public function afterTest(TestEvent $event): void { } public function endTest(TestEvent $event): void { self::$logger->info('PASSED'); } public function testFail(FailEvent $event): void { self::$logger->alert($event->getFail()->getMessage()); self::$logger->info('# FAILED #'); } public function testError(FailEvent $event): void { self::$logger->alert($event->getFail()->getMessage()); self::$logger->info('# ERROR #'); } public function testSkipped(FailEvent $event): void { self::$logger->info('# Skipped #'); } public function testIncomplete(FailEvent $event): void { self::$logger->info('# Incomplete #'); } public function beforeStep(StepEvent $event): void { self::$logger->info((string) $event->getStep()); } } expectThrowable(MyThrowable::class, function() { * $this->doSomethingBad(); * }); * * $I->expectThrowable(new MyException(), function() { * $this->doSomethingBad(); * }); * ``` * If you want to check message or throwable code, you can pass them with throwable instance: * ```php * expectThrowable(new MyError("Don't do bad things"), function() { * $this->doSomethingBad(); * }); * ``` * * @param \Throwable|string $throwable */ public function expectThrowable($throwable, callable $callback): void { if (is_object($throwable)) { $class = get_class($throwable); $msg = $throwable->getMessage(); $code = $throwable->getCode(); } else { $class = $throwable; $msg = null; $code = null; } try { $callback(); } catch (\Throwable $t) { $this->checkThrowable($t, $class, $msg, $code); return; } $this->fail("Expected throwable of class '{$class}' to be thrown, but nothing was caught"); } /** * Check if the given throwable matches the expected data, * fail (throws an exception) if it does not. */ protected function checkThrowable(\Throwable $throwable, string $expectedClass, ?string $expectedMsg, $expectedCode = null): void { if (!($throwable instanceof $expectedClass)) { $this->fail(sprintf( "Exception of class '%s' expected to be thrown, but class '%s' was caught", $expectedClass, get_class($throwable) )); } if (null !== $expectedMsg && $throwable->getMessage() !== $expectedMsg) { $this->fail(sprintf( "Exception of class '%s' expected to have message '%s', but actual message was '%s'", $expectedClass, $expectedMsg, $throwable->getMessage() )); } if (null !== $expectedCode && $throwable->getCode() !== $expectedCode) { $this->fail(sprintf( "Exception of class '%s' expected to have code '%s', but actual code was '%s'", $expectedClass, $expectedCode, $throwable->getCode() )); } $this->assertTrue(true); // increment assertion counter } } :@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 `platformName`, 'browserVersion', and 'browserName' under `LT:Options` to define test environments. * 4. If your website is available only locally or via VPN you should use LambdaTest tunnel. In this case, you can add capability "tunnel":true;. * * ```yaml * modules: * enabled: * - WebDriver: url: "https://openclassrooms.com" host: 'hub.lambdatest.com' port: 80 browser: 'Chrome' capabilities: LT:Options: platformName: 'Windows 10' browserVersion: 'latest-5' browserName: 'Chrome' 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 { /** * @var string[] */ protected array $requiredFields = ['browser', 'url']; protected array $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 ?string $wdHost = null; /** * @var mixed */ protected $capabilities; /** * @var float|int|null */ protected $connectionTimeoutInMs; /** * @var float|int|null */ protected $requestTimeoutInMs; protected array $sessions = []; protected array $sessionSnapshots = []; /** * @var mixed */ protected $webdriverProxy; /** * @var mixed */ protected $webdriverProxyPort; public ?RemoteWebDriver $webDriver = null; protected ?WebDriverSearchContext $baseElement = null; public function _requires(): array { return [RemoteWebDriver::class => '"php-webdriver/webdriver": "^1.0.1"']; } /** * @throws ModuleException */ protected function getBaseElement(): WebDriverSearchContext { 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 */ public function _capabilities(Closure $capabilityFunction): void { $this->capabilities = $capabilityFunction($this->capabilities); } public function _conflicts(): string { return WebInterface::class; } public function _before(TestInterface $test) { if ($this->webDriver === null && $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 * ``` * * @api */ public function _restart(array $config = []): void { $this->webDriver->quit(); if (!empty($config)) { $this->_reconfigure($config); } $this->_initializeSession(); } protected function onReconfigure() { $this->_initialize(); } protected function loadFirefoxProfile(): void { if (!array_key_exists('firefox_profile', $this->config['capabilities'])) { return; } $firefox_profile = $this->config['capabilities']['firefox_profile']; if (!file_exists($firefox_profile)) { 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(): void { if ($this->config['window_size'] == 'maximize') { $this->maximizeWindow(); return; } $size = explode('x', (string) $this->config['window_size']); if (count($size) == 2) { $this->resizeWindow((int) $size[0], (int) $size[1]); } } public function _after(TestInterface $test) { if ($this->config['restart']) { $this->stopAllSessions(); return; } if ($this->config['clear_cookies'] && $this->webDriver !== null) { try { $this->webDriver->manage()->deleteAllCookies(); } catch (Exception $exception) { // may cause fatal errors when not handled $this->debug("Error, can't clean cookies after a test: " . $exception->getMessage()); } } } public function _failed(TestInterface $test, $fail) { if (!$test instanceof SelfDescribing) { // this exception should never been throw because all existing test types implement SelfDescribing throw new InvalidArgumentException('Test class does not implement SelfDescribing interface'); } $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 */ public function debugWebDriverLogs(TestInterface $test = null): void { if ($this->webDriver === null) { $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 */ protected function formatLogEntries(array $logEntries): string { $formattedLogs = ''; foreach ($logEntries as $logEntry) { // Timestamp is in milliseconds, but date() requires seconds. $time = date('H:i:s', intval($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. */ protected function logJSErrors(ScenarioDriven $test, array $browserLogEntries): void { foreach ($browserLogEntries as $logEntry) { if ( isset($logEntry['level']) && isset($logEntry['message']) && $this->isJSError($logEntry['level'], $logEntry['message']) ) { // Timestamp is in milliseconds, but date() requires seconds. $time = date('H:i:s', intval($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. */ protected function isJSError(string $logEntryLevel, string $message): bool { 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(): void { foreach ($this->sessions as $session) { $this->_closeSession($session); } $this->webDriver = null; $this->baseElement = null; } public function amOnSubdomain(string $subdomain): void { $url = $this->config['url']; $url = preg_replace('#(https?://)(.*\.)(.*\.)#', "$1$3", $url); // removing current subdomain $url = preg_replace('#(https?://)(.*)#', sprintf('$1%s.$2', $subdomain), $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(): ?array { $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. * @api * @throws ModuleException */ public function _getCurrentUri(): string { $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(string $filename) { if ($this->webDriver === null) { $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; } } /** * @param string|array|WebDriverBy $selector */ public function _saveElementScreenshot($selector, string $filename): void { if ($this->webDriver === null) { $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): array { return $this->match($this->webDriver, $locator); } /** * Saves HTML source of a page to a file */ public function _savePageSource(string $filename): void { if ($this->webDriver === null) { $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 * ``` */ public function makeScreenshot(string $name = null): void { if (empty($name)) { $name = uniqid(date("Y-m-d_H-i-s_")); } $debugDir = codecept_log_dir() . 'debug'; if (!is_dir($debugDir)) { mkdir($debugDir); } $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 WebDriverBy|array $selector */ public function makeElementScreenshot($selector, string $name = null): void { if (empty($name)) { $name = uniqid(date("Y-m-d_H-i-s_")); } $debugDir = codecept_log_dir() . 'debug'; if (!is_dir($debugDir)) { mkdir($debugDir); } $screenName = $debugDir . DIRECTORY_SEPARATOR . $name . '.png'; $this->_saveElementScreenshot($selector, $screenName); $this->debugSection('Screenshot Saved', "file://{$screenName}"); } public function makeHtmlSnapshot(string $name = null): void { if (empty($name)) { $name = uniqid(date("Y-m-d_H-i-s_")); } $debugDir = codecept_output_dir() . 'debug'; if (!is_dir($debugDir)) { mkdir($debugDir); } $fileName = $debugDir . DIRECTORY_SEPARATOR . $name . '.html'; $this->_savePageSource($fileName); $this->debugSection('Snapshot Saved', "file://{$fileName}"); } /** * Resize the current window. * * ``` php * resizeWindow(800, 600); * * ``` */ public function resizeWindow(int $width, int $height): void { $this->webDriver->manage()->window()->setSize(new WebDriverDimension($width, $height)); } private function debugCookies(): void { $result = []; $cookies = $this->webDriver->manage()->getCookies(); foreach ($cookies as $cookie) { $result[] = $cookie->toArray(); } $this->debugSection('Cookies', json_encode($result, JSON_THROW_ON_ERROR)); } public function seeCookie($cookie, array $params = [], bool $showDebug = true): void { $cookies = $this->filterCookies($this->webDriver->manage()->getCookies(), $params); $cookies = array_map( fn($c) => $c['name'], $cookies ); if ($showDebug) { $this->debugCookies(); } $this->assertContains($cookie, $cookies); } public function dontSeeCookie($cookie, array $params = [], bool $showDebug = true): void { $cookies = $this->filterCookies($this->webDriver->manage()->getCookies(), $params); $cookies = array_map( fn($c) => $c['name'], $cookies ); if ($showDebug) { $this->debugCookies(); } $this->assertNotContains($cookie, $cookies); } public function setCookie($name, $value, array $params = [], $showDebug = true): void { $params['name'] = $name; $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 = [], bool $showDebug = true): void { $this->webDriver->manage()->deleteCookieNamed($cookie); if ($showDebug) { $this->debugCookies(); } } public function grabCookie($cookie, array $params = []): mixed { $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(): string { // Make sure that some page was opened. $this->_getCurrentUri(); return $this->webDriver->getPageSource(); } /** * @param Cookie[] $cookies * @param array $params * @return Cookie[] */ protected function filterCookies(array $cookies, array $params = []): array { foreach (['domain', 'path', 'name'] as $filter) { if (!isset($params[$filter])) { continue; } $cookies = array_filter( $cookies, fn($item): bool => $item[$filter] == $params[$filter] ); } return $cookies; } public function amOnUrl($url): void { $host = Uri::retrieveHost($url); $this->_reconfigure(['url' => $host]); $this->debugSection('Host', $host); $this->webDriver->get($url); } public function amOnPage($page): void { $url = Uri::appendPath($this->config['url'], $page); $this->debugSection('GET', $url); $this->webDriver->get($url); } public function see($text, $selector = null): void { if (!$selector) { $this->assertPageContains($text); return; } $this->enableImplicitWait(); $nodes = $this->matchVisible($selector); $this->disableImplicitWait(); $this->assertNodesContain($text, $nodes, $selector); } public function dontSee($text, $selector = null): void { if (!$selector) { $this->assertPageNotContains($text); } else { $nodes = $this->matchVisible($selector); $this->assertNodesNotContain($text, $nodes, $selector); } } public function seeInSource($raw): void { $this->assertPageSourceContains($raw); } public function dontSeeInSource($raw): void { $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. */ public function dontSeeInPageSource(string $text): void { $this->assertThatItsNot( $this->webDriver->getPageSource(), new PageConstraint($text, $this->_getCurrentUri()) ); } public function click($link, $context = null): void { $page = $this->webDriver; if ($context) { $page = $this->matchFirstOrFail($this->webDriver, $context); } $el = $this->_findClickable($page, $link); if ($el === null) { // check one more time if this was a CSS selector we didn't match try { $els = $this->match($page, $link); } catch (MalformedLocatorException $exception) { 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'); * * ``` * @param WebDriverSearchContext $page WebDriver instance or an element to search within * @param string|array|WebDriverBy $link A link text or locator to click * @api */ public function _findClickable(WebDriverSearchContext $page, $link): ?WebDriverElement { if (is_array($link) || $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 = self::xPathLiteral(trim((string) $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) > 0) { 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) > 0) { return reset($els); } return null; } /** * @param WebDriverElement|WebDriverBy|array|string $selector * @return WebDriverElement[] * @throws ElementNotFound */ protected function findFields($selector): array { if ($selector instanceof WebDriverElement) { return [$selector]; } if (is_array($selector) || ($selector instanceof WebDriverBy)) { $fields = $this->match($this->getBaseElement(), $selector); if (empty($fields)) { throw new ElementNotFound($selector); } return $fields; } $locator = self::xPathLiteral(trim((string) $selector)); // by text or label $xpath = Locator::combine( ".//*[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')]" ); $fields = $this->getBaseElement()->findElements(WebDriverBy::xpath($xpath)); if (!empty($fields)) { return $fields; } // by name $xpath = ".//*[self::input | self::textarea | self::select][@name = {$locator}]"; $fields = $this->getBaseElement()->findElements(WebDriverBy::xpath($xpath)); if (!empty($fields)) { return $fields; } // try to match by CSS or XPath $fields = $this->match($this->getBaseElement(), $selector, false); if (!empty($fields)) { return $fields; } throw new ElementNotFound($selector, "Field by name, label, CSS or XPath"); } /** * @param string|array|WebDriverBy|WebDriverElement $selector * @throws ElementNotFound */ protected function findField($selector): WebDriverElement { $arr = $this->findFields($selector); return reset($arr); } public function seeLink(string $text, string $url = null): void { $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(string $text, string $url = ''): void { $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}" ); } } private function filterNodesByHref(string $url, array $nodes): array { //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); return array_filter( $nodes, function (WebDriverElement $e) use ($expectedUrl, $absoluteCurrentUrl): bool { $elementHref = Uri::mergeUrls($absoluteCurrentUrl, $e->getAttribute('href') ?? ''); return $elementHref === $expectedUrl; } ); } public function seeInCurrentUrl(string $uri): void { $this->assertStringContainsString($uri, $this->_getCurrentUri()); } public function seeCurrentUrlEquals(string $uri): void { $this->assertEquals($uri, $this->_getCurrentUri()); } public function seeCurrentUrlMatches(string $uri): void { $this->assertRegExp($uri, $this->_getCurrentUri()); } public function dontSeeInCurrentUrl(string $uri): void { $this->assertStringNotContainsString($uri, $this->_getCurrentUri()); } public function dontSeeCurrentUrlEquals(string $uri): void { $this->assertNotEquals($uri, $this->_getCurrentUri()); } public function dontSeeCurrentUrlMatches(string $uri): void { $this->assertNotRegExp($uri, $this->_getCurrentUri()); } public function grabFromCurrentUrl($uri = null): mixed { 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): void { $this->assertTrue($this->findField($checkbox)->isSelected()); } public function dontSeeCheckboxIsChecked($checkbox): void { $this->assertFalse($this->findField($checkbox)->isSelected()); } public function seeInField($field, $value): void { $els = $this->findFields($field); $this->assert($this->proceedSeeInField($els, $value)); } public function dontSeeInField($field, $value): void { $els = $this->findFields($field); $this->assertNot($this->proceedSeeInField($els, $value)); } public function seeInFormFields($formSelector, array $params): void { $this->proceedSeeInFormFields($formSelector, $params, false); } public function dontSeeInFormFields($formSelector, array $params): void { $this->proceedSeeInFormFields($formSelector, $params, true); } /** * @param string|array|WebDriverBy $formSelector * @throws ModuleException */ protected function proceedSeeInFormFields($formSelector, array $params, bool $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) { [$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 WebDriverElement $form The form in which to search for fields. * @param string $name The field's name. * @param mixed $values */ protected function pushFormField(array &$els, WebDriverElement $form, string $name, $values): void { $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 WebDriverElement[] $elements * @param mixed $value */ protected function proceedSeeInField(array $elements, $value): array { $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': if (!$el->isSelected()) { break; } $currentValues[] = $el->getText(); // no break we need the trim text and the value also case 'textarea': $currentValues[] = trim($el->getText()); // we include trimmed and real value of textarea for check 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): void { $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 $exception) { } } } if ($matched) { return; } if (key($option) !== 'text') { foreach ($option as $opt) { try { $wdSelect->selectByValue($opt); $matched = true; } catch (NoSuchElementException $exception) { } } } 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 $exception) { // exception treated at the end } } if ($matched) { return; } throw new ElementNotFound( json_encode($option, JSON_THROW_ON_ERROR), "Option inside {$select} matched by name or value" ); } /** * Manually starts a new browser session. * * ```php * getModule('WebDriver')->_initializeSession(); * ``` * * @api */ public function _initializeSession(): void { 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 (UnexpectedResponseException $exception) { codecept_debug('Curl error: ' . $exception->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 * * @param RemoteWebDriver $session * @api */ public function _loadSession($session): void { $this->webDriver = $session; $this->setBaseElement(); } /** * Returns current WebDriver session for saving * * @api */ public function _backupSession(): WebDriverInterface { 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 RemoteWebDriver|null $webDriver a specific webdriver session instance */ public function _closeSession($webDriver = null): void { if (!$webDriver && $this->webDriver) { $webDriver = $this->webDriver; } if (!$webDriver) { return; } try { $webDriver->quit(); unset($webDriver); } catch (UnknownErrorException $exception) { // Session already closed so nothing to do } } /** * Unselect an option in the given select box. * * @param string|array|WebDriverBy $select * @param string|array|WebDriverBy $option */ public function unselectOption($select, $option): void { $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 string|array|WebDriverBy|WebDriverElement $radioOrCheckbox */ protected function findCheckable( WebDriverSearchContext $context, $radioOrCheckbox, bool $byValue = false ): ?WebDriverElement { if ($radioOrCheckbox instanceof WebDriverElement) { return $radioOrCheckbox; } if (is_array($radioOrCheckbox) || $radioOrCheckbox instanceof WebDriverBy) { return $this->matchFirstOrFail($this->getBaseElement(), $radioOrCheckbox); } $locator = self::xPathLiteral($radioOrCheckbox); if ($context instanceof WebDriverElement && $context->getTagName() === 'input') { $contextType = $context->getAttribute('type'); if (!in_array($contextType, ['checkbox', 'radio'], true)) { return null; } $nameLiteral = self::xPathLiteral($context->getAttribute('name')); $typeLiteral = self::xPathLiteral($contextType); $inputLocatorFragment = "input[@type = {$typeLiteral}][@name = {$nameLiteral}]"; $xpath = Locator::combine( "ancestor::form//{$inputLocatorFragment}[(@id = ancestor::form//label[contains(normalize-space(string(.)), {$locator})]/@for) or @placeholder = {$locator}]", "ancestor::form//label[contains(normalize-space(string(.)), {$locator})]//{$inputLocatorFragment}" ); if ($byValue) { $xpath = Locator::combine($xpath, "ancestor::form//{$inputLocatorFragment}[@value = {$locator}]"); } } else { $xpath = Locator::combine( "//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), {$locator})]/@for) or @placeholder = {$locator} or @name = {$locator}]", "//label[contains(normalize-space(string(.)), {$locator})]//input[@type = 'radio' or @type = 'checkbox']" ); if ($byValue) { $xpath = Locator::combine( $xpath, sprintf("//input[@type = 'checkbox' or @type = 'radio'][@value = %s]", $locator) ); } } $els = $context->findElements(WebDriverBy::xpath($xpath)); if (count($els) > 0) { return reset($els); } $els = $context->findElements(WebDriverBy::xpath(str_replace('ancestor::form', '', $xpath))); if (count($els) > 0) { return reset($els); } $els = $this->match($context, $radioOrCheckbox); if (count($els) > 0) { return reset($els); } return null; } /** * @param string|array|WebDriverBy $selector * @return WebDriverElement[] */ protected function matchCheckables($selector): array { $els = $this->match($this->webDriver, $selector); if ($els === []) { throw new ElementNotFound($selector, "Element containing radio by CSS or XPath"); } return $els; } public function checkOption($option): void { $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): void { $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): void { $el = $this->findField($field); $el->clear(); $el->sendKeys((string)$value); } /** * Clears given field which isn't empty. * * ``` php * clearField('#username'); * ``` * * @param string|array|WebDriverBy $field */ public function clearField($field): void { $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 int $delay [sec] */ public function type(string $text, int $delay = 0): void { $keys = str_split($text); foreach ($keys as $key) { sleep($delay); $this->webDriver->getKeyboard()->pressKey($key); } sleep($delay); } public function attachFile($field, string $filename): void { $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. */ protected function getVisibleText(): ?string { 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): mixed { $els = $this->match($this->getBaseElement(), $cssOrXPathOrRegex, false); if ($els !== []) { return $els[0]->getText(); } if ( is_string($cssOrXPathOrRegex) && @preg_match($cssOrXPathOrRegex, $this->webDriver->getPageSource(), $matches) ) { return $matches[1]; } throw new ElementNotFound($cssOrXPathOrRegex, 'CSS or XPath or Regex'); } public function grabAttributeFrom($cssOrXpath, $attribute): ?string { $el = $this->matchFirstOrFail($this->getBaseElement(), $cssOrXpath); return $el->getAttribute($attribute); } public function grabValueFrom($field): ?string { $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): array { $els = $this->match($this->getBaseElement(), $cssOrXpath); return array_map( function (WebDriverElement $e) use ($attribute): ?string { 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, fn(WebDriverElement $el): bool => $el->getAttribute($attr) == $value ); } return $els; } public function seeElement($selector, array $attributes = []): void { $this->enableImplicitWait(); $els = $this->matchVisible($selector); $this->disableImplicitWait(); $els = $this->filterByAttributes($els, $attributes); $this->assertNotEmpty($els); } public function dontSeeElement($selector, array $attributes = []): void { $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 string|array|WebDriverBy $selector */ public function seeElementInDOM($selector, array $attributes = []): void { $this->enableImplicitWait(); $els = $this->match($this->getBaseElement(), $selector); $els = $this->filterByAttributes($els, $attributes); $this->disableImplicitWait(); $this->assertNotEmpty($els); } /** * Opposite of `seeElementInDOM`. * * @param string|array|WebDriverBy $selector */ public function dontSeeElementInDOM($selector, array $attributes = []): void { $els = $this->match($this->getBaseElement(), $selector); $els = $this->filterByAttributes($els, $attributes); $this->assertEmpty($els); } public function seeNumberOfElements($selector, $expected): void { $counted = count($this->matchVisible($selector)); if (is_array($expected)) { [$floor, $ceil] = $expected; $this->assertTrue( $floor <= $counted && $ceil >= $counted, 'Number of elements counted differs from expected range' ); } else { $this->assertSame( $expected, $counted, 'Number of elements counted differs from expected number' ); } } /** * @param string|array|WebDriverBy $selector * @param int|array $expected * @throws ModuleException */ public function seeNumberOfElementsInDOM($selector, $expected) { $counted = count($this->match($this->getBaseElement(), $selector)); if (is_array($expected)) { [$floor, $ceil] = $expected; $this->assertTrue( $floor <= $counted && $ceil >= $counted, 'Number of elements counted differs from expected range' ); } else { $this->assertSame( $expected, $counted, 'Number of elements counted differs from expected number' ); } } public function seeOptionIsSelected($selector, $optionText): void { $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, fn($e): bool => $e && $e->isSelected() ) ); } else { $select = new WebDriverSelect($el); $this->assertNodesContain($optionText, $select->getAllSelectedOptions(), 'option'); } } public function dontSeeOptionIsSelected($selector, $optionText): void { $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, fn($e): bool => $e && $e->isSelected() ) ); } else { $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](https://jster.net/category/windows-modals-popups). */ public function acceptPopup(): void { 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(): void { 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. * * @throws ModuleException */ public function seeInPopup(string $text): void { 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 (PHPUnitAssertionFailedError $failedError) { $alert->dismiss(); throw $failedError; } } /** * Checks that the active JavaScript popup, * as created by `window.alert`|`window.confirm`|`window.prompt`, does NOT contain the given string. * * @throws ModuleException */ public function dontSeeInPopup(string $text): void { 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 (PHPUnitAssertionFailedError $e) { $alert->dismiss(); throw $e; } } /** * Enters text into a native JavaScript prompt popup, as created by `window.prompt`. * * @throws ModuleException */ public function typeInPopup(string $keys): void { 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(): void { $this->webDriver->navigate()->refresh(); } /** * Moves back in history. */ public function moveBack(): void { $this->webDriver->navigate()->back(); $this->debug($this->_getCurrentUri()); } /** * Moves forward in history. */ public function moveForward(): void { $this->webDriver->navigate()->forward(); $this->debug($this->_getCurrentUri()); } protected function getSubmissionFormFieldName(string $name): string { 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 string|array|WebDriverBy $selector * @param string|array|WebDriverBy|null $button */ public function submitForm($selector, array $params, $button = null): void { $form = $this->matchFirstOrFail($this->getBaseElement(), $selector); $fields = $form->findElements( WebDriverBy::cssSelector( 'input:enabled[name],textarea:enabled[name],select:enabled[name],input[type=hidden][name]' ) ); 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, JSON_THROW_ON_ERROR)); $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 string|array|WebDriverBy $element * @throws ElementNotFound */ public function waitForElementChange($element, Closure $callback, int $timeout = 30): void { $el = $this->matchFirstOrFail($this->getBaseElement(), $element); $checker = fn() => $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 string|array|WebDriverBy $element * @param int $timeout seconds * @throws Exception */ public function waitForElement($element, int $timeout = 10): void { $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 string|array|WebDriverBy $element * @param int $timeout seconds * @throws Exception */ public function waitForElementVisible($element, int $timeout = 10): void { $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 string|array|WebDriverBy $element * @param int $timeout seconds * @throws Exception */ public function waitForElementNotVisible($element, int $timeout = 10): void { $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 string|array|WebDriverBy $element * @param int $timeout seconds * @throws Exception */ public function waitForElementClickable($element, int $timeout = 10): void { $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 int $timeout seconds * @param null|string|array|WebDriverBy $selector * @throws Exception */ public function waitForText(string $text, int $timeout = 10, $selector = null): void { $message = sprintf( 'Waited for %d secs but text %s still not found', $timeout, Locator::humanReadableString($text) ); if (!$selector) { $condition = WebDriverExpectedCondition::elementTextContains(WebDriverBy::xpath('//body'), $text); $this->webDriver->wait($timeout)->until($condition, $message); return; } $condition = WebDriverExpectedCondition::elementTextContains($this->getLocator($selector), $text); $this->webDriver->wait($timeout)->until($condition, $message); } /** * Wait for $timeout seconds. * * @param int|float $timeout secs * @throws TestRuntimeException */ public function wait($timeout): void { 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((int)($timeout * 1_000_000)); } /** * 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('https://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 Closure $function * @return mixed */ 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); * }); * ``` */ public function switchToWindow(string $name = null): void { $this->webDriver->switchTo()->window($name); } /** * Switch to another iframe on the page. * * Example: * ``` html *